Virtual Event
Join us for the next HashiConf Digital October 12-15, 2020 Register for Free

Write Terraform Configuration

beta

Manage Similar Resources With Count

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.

»Prerequisites

»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

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.