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:
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.
variable "contract" {}
variable "common_name" {}
resource "akamai_cps_dv_enrollment" "my_enrollment" {
contract_id = var.contract
common_name = var.common_name
# ... other configuration ...
}
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:
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.