In this tutorial, you will use Terraform to provision a VPC, load balancer, and
EC2 instances on AWS. Then you will use the count
argument to provision
multiple EC2 instances per private subnet with a single resource block.
The count
argument replicates the given resource or module a specific
number of times with an incrementing counter. It works best when resources will
be identical, or nearly so.
Tip: Terraform 0.13 supports count
on both resource and module
blocks. Prior versions only supported it on resource blocks.
»Prerequisites
- The Terraform CLI, version 0.13 or later.
- AWS Credentials configured for use with Terraform.
- The git CLI.
»Apply initial configuration
Clone the example GitHub repository.
$ git clone https://github.com/hashicorp/learn-terraform-count-foreach.git
Change into the new directory.
$ cd learn-terraform-count-foreach
Check out the initial configuration.
$ git checkout tags/count-initial-configuration -b count-initial-configuration
The configuration in main.tf
will provision a new VPC with public and private
subnets, a load balancer, and two EC2 instances, one in each private subnet. The
variables located in variables.tf
allow you to configure the VPC. For
instance, the private_subnets_per_vpc
variable controls the number of private
subnets the configuration will create.
Initialize Terraform in this directory. Terraform will install the AWS provider
and the vpc
, app_security_group
, lb_security_group
, and elb_http
modules.
$ terraform init
Once your directory has been initialized, apply the configuration, and remember
to confirm with a yes
.
$ terraform apply
Note: The load balancer’s domain name is part of the output. It may take a few minutes after the apply step before you can visit this domain name. Be sure to connect via HTTP, not HTTPS.
This configuration has some limitations. Currently, each private
subnet only contains one EC2 instance. If you increase the
private_subnets_per_vpc
variable, Terraform won’t automatically add EC2
instances, because the EC2 instance resources are hard coded.
Make this configuration more robust by adding a variable to control the number
of EC2 instances in each private subnet with count
.
Either make the following configuration changes manually, or check out the tag
count-multiple-instances
to review the new configuration.
$ git checkout tags/count-multiple-instances -b count-multiple-instances
»Refactor the EC2 configuration
Refactor the EC2 configuration to make it more generic. Remove or comment out the entire block defining the app_b
EC2 instance from
main.tf
.
-resource "aws_instance" "app_b" {
- ami = data.aws_ami.amazon_linux.id
- instance_type = var.instance_type
-
- # ...truncated...
-
- tags = {
- Terraform = "true"
- Project = var.project_name
- Environment = var.environment
- }
-}
Next, rename the resource for the other EC2 instance from app_a
to app
.
-resource "aws_instance" "app_a" {
+resource "aws_instance" "app" {
»Declare a variable for instance number
Now, add the instances_per_subnet
variable to variables.tf
to define how
many instances each private subnet will have.
variable instances_per_subnet {
description = "Number of EC2 instances in each private subnet"
type = number
default = 2
}
»Scale EC2 configuration with count
Next, edit main.tf
to use count to provision multiple EC2 instances with the
app
resource block, based on the value of the new instances_per_subnet
variable and the number of private subnets.
resource "aws_instance" "app" {
+ count = var.instances_per_subnet * length(module.vpc.private_subnets)
+
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
- subnet_id = module.vpc.private_subnets[0]
+ subnet_id = module.vpc.private_subnets[count.index % length(module.vpc.private_subnets)]
vpc_security_group_ids = [module.app_security_group.this_security_group_id]
# ...truncated...
}
Each instance provisioned by the resource block with count
will have a
different incrementing value for count.index
- starting with zero. This
configuration uses count.index
and modulo division to assign each instance to
a private subnet.
Because the default value of instances_per_subnet
is 2
, Terraform will provision two EC2 instances per private subnet.
»Update the load balancer
Update the load balancer configuration in the elb_http
block to attach the
instances to the load balancer.
- number_of_instances = 2
- instances = [aws_instance.app_a.id, aws_instance.app_b.id]
+ number_of_instances = length(aws_instance.app)
+ instances = aws_instance.app.*.id
The name of resources or modules provisioned with count
refers to the entire
collection. In this example, aws_instance.app
now refers to all of the EC2
instances. You can reference individual items in collections with the same notation as
list indexing. For example, aws_instance.app[0]
refers to the first instance
Terraform provisions.
You can create a list of all of the values of a given attribute for the items in the
collection with a star. For instance, aws_instance.app.*.id
will be a list of
all of the IDs of the instances.
Update outputs.tf
to refer to the new aws_instance.app
block instead of
app_a
and app_b
.
output instance_ids {
description = "IDs of EC2 instances"
- value = [aws_instance.app_a.id, aws_instance.app_b.id]
+ value = aws_instance.app.*.id
}
»Apply scalable configuration
Apply this configuration now.
$ terraform apply
Be sure to respond to the confirmation prompt with yes
.
Terraform will output values for the VPC, load balancer, and instances.
# ...truncated...
Outputs:
instance_ids = [
"i-0ae41b60b701e04b2",
"i-09a9ee77a108effcd",
"i-00a80976736aec7d0",
"i-06b00b6352fdc1269",
]
public_dns_name = lb-2n32-client-webapp-dev-879083734.us-east-1.elb.amazonaws.com
vpc_arn = arn:aws:ec2:us-east-1:130490850807:vpc/vpc-077af8d1143193d7f
Now you have configured the number EC2 instances per private subnet using the
instances_per_subnet
variable and count
. Terraform configured that many
instances per subnet, assigned them to subnets, and attached them to the load
balancer.
»Clean up resources
After verifying that the resources were deployed successfully, run terraform destroy
to destroy them. Remember to respond to the confirmation prompt with
yes
.
$ terraform destroy
»Next steps
Now that you have used count
in your configuration, explore the following
resources.
- Read the Terraform documentation for the count meta-argument.
- Learn how to use for_each for more complex configurations.