Mon Jun 09 2025
Akamai Cloudlet Terraform Module
Written by: Cesar
3 min read
In this blog I will build the following cloudlet module functionality in terraform:
Rule in Child | Rule in Baseline | Final Order | Source |
---|---|---|---|
✅ | ❌ | Top (prepended) | From Child |
✅ | ✅ | Baseline position | From Child |
❌ | ✅ | Baseline position | From Baseline |
Why is this useful?
With this moudle I will be able to reuse my cloudlet rules in multiple cloulets, instead of having to cut and paste similar rules into different cloudlet configurations. I would like to be able to use it like so:
in my child cloudlet, in my policy.tf I would call it like so:
module "base" {
source = "../../modules/common"
child_rules = data.akamai_cloudlets_edge_redirector_match_rule.child_rules.match_rules
}
I will pass in the child specific rules via the child_rules argument in the module. The child_rules argument will be defined with a default value of []. This way we can satisfy the all three requirements above.
In the module I will have in variables.tf:
variable "child_rules" {
description = "List of rules from child (to override or prepend)"
type = list(any)
default = []
}
The child specific rules are passed to the module as a list by simply calling .match_rules on the data source
After that we simply use the output of the module like so in our child policy resource:
resource "akamai_cloudlets_policy" "policy" {
name = "child_policy_name"
cloudlet_code = "ER"
description = ""
group_id = "xxxxx"
match_rule_format = "1.0"
match_rules = module.base.rules
is_shared = false
}
in the module I will have this in the output.tf:
output "rules" {
value = jsonencode(local.combined_match_rules)
}
Notice the jsonencode call, because the policy resource expects json
The last piece is the code in the module that will process the input list of rules from the child. In there I will use locals to process the input and either prepend, or override rules with the same name.
In the moudle main.tf I have this:
locals {
# Maps for lookup
child_map = {
for rule in var.child_rules :
rule.name => rule
}
base_map = {
for rule in data.akamai_cloudlets_edge_redirector_match_rule.base.match_rules :
rule.name => rule
}
# Prepend only rules from child not in base
child_new_rules = [
for rule in var.child_rules :
rule if !contains(keys(local.base_map), rule.name)
]
# child rules with replacement if overridden by base
base_merged_ordered = [
for rule in data.akamai_cloudlets_edge_redirector_match_rule.base.match_rules :
contains(keys(local.child_map), rule.name) ? local.child_map[rule.name] : rule
]
# Final ordered result
raw_match_rules = concat(local.child_new_rules, local.base_merged_ordered)
valid_raw_rules = [
for r in local.raw_match_rules : r
if r.match_url != "" && r.match_url != null
]
combined_match_rules = [
for r in local.valid_raw_rules : {
name = r.name
type = "erMatchRule"
statusCode = r.status_code
redirectURL = r.redirect_url
matches = [
{
matchType = "path"
matchOperator = "equals"
matchValue = r.match_url
}
]
}
]
}
To recap, defined a module that takes as input a list of rules from a child and either prepends those to the top, or replaces existing rules which have the same rule name in the base. If no child rules are passed into the module the child will simply reuse the rules in the base module to build its ruleset.
Note this first version only handles simple rules, to handle more complex rules I will need to add addional logic to the combined_match_rules logic above.