Terraform
Modules

11. Modules

Terraform modules are reusable, self-contained packages of Terraform configurations that encapsulate a set of resources with a specific purpose. A module consists of a collection of .tf files kept together in a directory (commonly under ./modules/).

The Root Module

Every Terraform configuration has at least one module, known as its root module, which consists of the resources defined in the .tf files in the main working directory.

Child Modules

A Terraform module (usually the root module of a configuration) can call other modules to include their resources into the configuration. A module that has been called by another module is often referred to as a child module, but in the day to day work you'll hear them referred as just "modules".

Child modules can be called multiple times within the same configuration, and multiple configurations can use the same module. The recommended convention is to store all your children modules in a directory named ./modules/. For example you have created a module for creating CPS (Certificate Provisioning System) enrollments:

    • main.tf
    • outputs.tf
    • providers.tf
    • variables.tf
        • main.tf
        • outputs.tf
        • providers.tf
        • variables.tf
  • If you also noted from the above structure, the providers.tf may also need to be defined at the child module level. Although any parameters for the provider will be passed by the root module so no need to re-define all the provider parameters.

    It may not come by surprise at this point that the Terraform code you've created (root module) so far can be used as a child module with little or no changes at all.

    Calling a Child Module

    Modules are called from within other modules using module blocks:

    main.tf
    module "cert_enrollment" {
      source = "./modules/cps"  # Path to module
      
      # Input variables
      contract   = "C-0N7R4C7"
      common_name = "www.example.com"
    }

    Key Components:

    • The label immediately after the module keyword is a local name (cert_enrollment in above's example), which the calling module can use to refer to this instance of the module.
    • Source: Where the module code lives (local path in this case but it could also be a Git repo or Terraform Registry)
    • Input Variables: Parameters you can pass to customize the module. These parameters must exist as variables in the child module.
    • Resources: The my-module contains the resources.

    Passing values from Child Module

    The resources defined in a module are encapsulated, so the calling module cannot access their attributes directly. However, the child module can declare output values to selectively export certain values to be accessed by the calling module.

    ./modules/cps/variables.tf
    variable "contract" {}
    variable "common_name" {}
    ./modules/cps/main.tf
    resource "akamai_cps_dv_enrollment" "my_enrollment" {
      contract_id = var.contract
      common_name = var.common_name
      # ... other configuration ...
    }
    ./modules/cps/outputs.tf
    output "dns_challenges" {
      value = akamai_cps_dv_enrollment.my_enrollment.dns_challenges
    }
     
    output "enrollment_id" {
      value = akamai_cps_dv_enrollment.my_enrollment.id
    }

    The outputs are values that the module returns for other parts of your code to use. In the example above the certificate enrollment ID can be used in the akamai_edge_hostname resource, and the DNS Challenges for the certificate domain validation can be used to update Edge DNS records in a different module:

    main.tf
    module "cert_enrollment" {
      source = "./modules/cps"  # Path to module
      contract   = "C-0N7RAC7"
      common_name = "www.example.com"
      # ... other configuration ...
    }
     
    resource "akamai_edge_hostname" "my_edge_hostname" {
      product_id    = "Fresca"
      contract_id   = "C-0N7RAC7"
      group_id      = "12345"
      ip_behavior   = "IPV4"
      edge_hostname = "example.com.edgekey.net"
      certificate   = module.cert_enrollment.enrollment_id
    }
     
    module "akamai_edgedns" {
      source                = "./modules/dns"
      dns_zone              = "example.com"
      challenge_records     = module.cert_enrollment.dns_challenges
    }

    Exercises

    Exercise #1

    New Directory

    Create a new directory named modules, and inside it a sub-directory named edgeworkers. So you end up with ./modules/edgeworkers.

    New Module

    In your ./modules/edgeworkers create all the necessary *.tf files to create a new EdgeWorker ID. Make sure that the name, group_id and resource_tier_id are parameters that can be set by the root module.

    New File

    Back in your root directory create a new edgeworkers.tf file and add the necessary Terraform code to call the EdgeWorkers module.

    Plan

    Run a terraform plan. Why do you need to run a terraform init?

    Init

    After running the terraform init try to run the terrform plan again. What happened?

    ℹ️

    Hint: remember to define the akamai provider in the child modules.

    Plan/Apply

    After defining the provider in your child module run terraform plan and terraform apply.

    Commit

    Commit your changes and push them to GitHub.

    Exercise #2

    Configure

    In your property.tf you should have the rule tree defined in a data.akamai_property_rules_builder. Convert this data source to a Terraform module instead.

    • Create a new directory inside the modules directory and name it rules. So ./modules/rules
    • Add all the necessary child module *.tf files inside this directory.
    ℹ️

    Hint: remember to create the variables.tf with any of the parameters needed for your rule tree. If you've been following along you should have 2 parameters in the rule tree: ab_test for the origin selection and the akamai_cp_code.my_cp_code.id. These 2 values will be arguments when you call the module.

    Update

    Use your new child module in you property.tf to build the rule tree and update the akamai_property accordingly.

    ℹ️

    Hint: remember that the akamai_property resource requires the rule_format and rules argument which should be updated with the expression to reference the newly created module. Also keep in mind that you need to define these values as outputs in the child module.

    Plan/Apply

    Run Terraform.

    Commit

    Commit your changes and push them to GitHub.