Terraform
for_each

The for_each Meta-Argument

The for_each meta-argument accepts a map or a set of strings, and creates an instance for each item in that map or set. Each instance has a distinct infrastructure object associated with it, and each is separately created, updated, or destroyed when the configuration is applied.

Example

In this case, the for_each meta-argument is used to create multiple DNS records based on a map of DNS records defined in the dns_records variable.

variables.tf
variable "dns_records" {
  default = {
    "record1" = {
      recordType = "A"
      ttl        = 60
      target     = "172.233.190.92"
      name       = "origin.example.com"
    },
    "record2" = {
      recordType = "MX"
      ttl        = 60
      target     = "10 email.example.com"
      name       = "example.com"
    }
  }
}

The above is a map of objects, in other words a list of "JSON-like" objects. This structure allows to add more information for each DNS record such as the recordType, ttl, target and name. With the help of for_each all thees values can be used of each resource instance.

main.tf
resource "akamai_dns_record" "tf_demo_dns_records" {
  for_each = var.dns_records
 
  zone       = "example.com"
  recordtype = each.value.recordType
  ttl        = each.value.ttl
  target     = [each.value.target]
  name       = each.value.name
}

for_each = var.additional_records tells Terraform to create one akamai_dns_record resource per element in the dns_records map.

Inside the resource block, you can access the current map element's key using each.key (e.g., "record1") and the value using each.value (which is another map with recordType, ttl, target, and name properties).

Using each.key to refer to the instances created by for_each:

akamai_dns_record.my_dns_hostnames["record1"]
akamai_dns_record.my_dns_hostnames["record2"]

Using each.value to populate the resource attributes

recordtype = each.value.recordType
ttl = each.value.ttl
target = [each.value.target]
name = each.value.name

So, in this example, the for_each will create a single akamai_dns_record resource with the values from the "record1" and "record2" elements of the dns_records map.

The key benefit of using for_each in this case is the ability to easily add or remove DNS records by updating the dns_records variable, without having to modify the resource configuration itself.

for_each vs. count

The main difference between count and for_each in Terraform is the way they handle iteration and resource identification.

count

  • Allows you to create multiple instances of a resource based on a numerical value.
  • The resources are identified by their numerical index, e.g. akamai_dns_record.my_dns_hostnames[0], akamai_dns_record.my_dns_hostnames[1].
  • You cannot use count and for_each on the same resource block.

for_each

  • Allows you to create multiple instances of a resource based on a map or set of strings.
  • The resources are identified by the map keys or set elements, e.g. akamai_dns_record.my_dns_hostnames["record1"], akamai_dns_record.my_dns_hostnames["record2"].
  • You cannot use a numerical list with for_each.

Exercise

Update

Create the following variable in your variables.tf:

variables.tf
variable "dns_records" {
  default = {
    "orgigin1" = {
      zone       = "example.com"
      recordType = "A"
      ttl        = 60
      target     = "172.233.190.92"
      name       = "origin-www.example.com"
    },
    "origin2" = {
      zone       = "example.com"
      recordType = "A"
      ttl        = 100
      target     = "173.233.190.93"
      name       = "origin-api.example.com"
    },
    "origin3" = {
      zone       = "example.com"
      recordType = "A"
      ttl        = 600
      target     = "174.233.190.94"
      name       = "origin-blog.example.com"
    }
  }
}

Configure

In your dns.tf with the use of the for_each meta-argument and a single akamai_dns_record resource block create all 3 DNS records from the dns_records variable above. The values for all the arguments must be Terraform expressions.

Plan/Apply

Run terraform

Commit

Commit your change and push it to GitHub.