Import Multiple AWS Modules Or Resources Into One Terraform Resource Block With The Count Meta-Argument

Macharia Muguku
4 min readNov 16, 2023

Also a crash course into the terraform import cli and the terraform import block

Photo courtesy of google images

TL;DR

  1. With the terraform import cli
# example terraform resource configuration block for multiple (3) aws route53 records
resource "aws_route53_record" "example_aws_route53_records" {
count = 3
...
}
# import multiple route53 records from aws into terraform state
# with 'terraform import' cli
terraform import 'aws_route53_record.example_aws_route53_records[0]' Z4KAPRWWNC7JR_stage.example.com_NS
terraform import 'aws_route53_record.example_aws_route53_records[1]' Z4KAPRWWNC7JR_preview.sample.com_CNAME
terraform import 'aws_route53_record.example_aws_route53_records[2]' Z4KAPRWWNC7JR_prod.specimen.com_A

2. With the import block

# example terraform resource configuration block for multiple (3) aws route53 records
resource "aws_route53_record" "example_aws_route53_records" {
count = 3
...
}

# import blocks
# with pointer to resource config block's individual resource index and identifier
import {
to = aws_route53_record.example_aws_route53_records[0]
id = "Z4KAPRWWNC7JR_stage.example.com_NS"
}
import {
to = aws_route53_record.example_aws_route53_records[1]
id = "Z4KAPRWWNC7JR_preview.sample.com_CNAME"
}
import {
to = aws_route53_record.example_aws_route53_records[2]
id = "Z4KAPRWWNC7JR_prod.specimen.com_A"
}

Explanation

When you create multiple resources or modules from a Terraform block with the count meta-argument, Terraform distinguishes between the block itself and the multiple resource or module instances associated with it. It assigns an index number to each independent instance of resources or modules created from a resource with the count meta-argument.

# Creating multiple (3) aws route53 records with the count meta-argument
# This creates 3 indipendent route53 records with indexes 0,1,2 respectively
resource "aws_route53_record" "example_aws_route53_records" {
count = 3
...
}

1. Import into the Terraform state only (does not generate configuration)

To import a resources into the Terraform state, you must first manually write a resource configuration block describing where Terraform should map the imported object for the resource. We then use the terraform import cli command to import the resource by referencing the resource block to map the imported object to and the identifier of the upstream resource. The command thus becomes a composite of:

  1. The resource type (e.g aws_route53_record)
  2. The resource name (e.g example_aws_route53_record)
  3. The upstream resource identifier (e.g Z4KAPRWWNC7JR_dev.example.com_NS_dev)
# example resource configuration block for a single aws route53 record
resource "aws_route53_record" "example_aws_route53_record" {
}
# import a single 'dev.example.com' route53 record from aws into terraform state
# and map it into the 'example_aws_route53_record' terraform resource block
terraform import aws_route53_record.example_aws_route53_record Z4KAPRWWNC7JR_dev.example.com_NS_dev

To import multiple resources into a resource block with the count meta-argument, you must first manually write a resource configuration block, then import the resources by referencing them by their index numbers. Ensure the resource configuration block part of the cli command is quoted

# example terraform resource configuration block for multiple (3) aws route53 records
resource "aws_route53_record" "example_aws_route53_records" {
count = 3
...
}
# import multiple route53 records from aws into terraform state
# with 'terraform import' cli
terraform import 'aws_route53_record.example_aws_route53_records[0]' Z4KAPRWWNC7JR_stage.example.com_NS
terraform import 'aws_route53_record.example_aws_route53_records[1]' Z4KAPRWWNC7JR_preview.sample.com_CNAME
terraform import 'aws_route53_record.example_aws_route53_records[2]' Z4KAPRWWNC7JR_prod.specimen.com_A

N/B:

  1. The terraform import cli command can only import one resource at a time
  2. When importing resources using their index, as is with a resource block with the count meta-argument, the resource configuration block part of the cli command must be quoted otherwise it’ll throw a “no matches found” error
  3. See how to compose a route53 resource identifier for import here (TL;DR: composite string made of underscore separated: zone identifier, record name, record type, and optional set identifier e.g “Z4KAPRWWNC7JR_preview.sample.com_CNAME”)

2. Import into the Terraform state and generate configuration

To import a resource into the Terraform state while also generating it’s config, you need two blocks:

  1. The resource configuration block describing where Terraform should map the imported object for the resource
  2. An import block with pointers to the configuration block to map the resources to and an identifier of the upstream resource to import
# import block with pointer to config block and resource identifier
import {
to = aws_route53_record.example_aws_route53_record
id = "Z4KAPRWWNC7JR_dev.example.com_A_dev"
}

# resource configuration block
resource "aws_route53_record" "example_aws_route53_record" {
type = "A"
zone_id = "Z4KAPRWWNC7JR"
name = "dev.example.com"
records = ["127.0.0.1"]
}

You then simply run terraform plan and thenterraform apply as usual and your resource will imported into the Terraform state and it’s configs updated in place.

To import multiple resources into a resource block with the count meta-argument with the import block, you do the same as is with a single resource but you use the individual upstream resource indexes instead of the block resource identifier

# example terraform resource configuration block for multiple (3) aws route53 records
resource "aws_route53_record" "example_aws_route53_records" {
count = 3
...
}

# import blocks
# with pointer to resource config block's individual resource index and identifier
import {
to = aws_route53_record.example_aws_route53_records[0]
id = "Z4KAPRWWNC7JR_stage.example.com_NS"
}
import {
to = aws_route53_record.example_aws_route53_records[1]
id = "Z4KAPRWWNC7JR_preview.sample.com_CNAME"
}
import {
to = aws_route53_record.example_aws_route53_records[2]
id = "Z4KAPRWWNC7JR_prod.specimen.com_A"
}

N/B:

  1. The import block is idempotent, meaning that applying an import action and running another plan will not generate another import action as long as that resource remains in your state
  2. You can remove import blocks after completing the import or safely leave them in your configuration as a record of the resource’s origin for future maintainers

--

--

Macharia Muguku

A student of the world ; My brain has too many tabs open ; 🇰🇪; www.muguku.co.ke