AWS Advanced VPC + ALB + EC2 Instances (v2)


Terraform Introduction
Terraform CLI
Terraform Language
Wrap Up
Start course
1h 41m

Terraform is an open source "Infrastructure as Code" tool, used by DevOps and SysOps engineers to codify their cloud infrastructure requirements.

In this course you'll learn about Terraform from the ground up, and how it can be used to codify infrastructure. Terraform can be used to provision infrastructure across multiple cloud providers including AWS which this course will focus on.

resource "aws_instance" " cloudacademy " {
ami =
instance_type = var.instance_type
key_name = var.key_name 
subnet_id =
security_groups = []
user_data =<<EOFF
read -r -d '' META <<- EOF
CloudAcademy ♥ Terraform!
For any feedback, queries, or suggestions relating to this course
please contact us at:
echo "$META"

tags = {
Org = "CloudAcademy"
Course = "Terraform 1.0"
Author = "Jeremy Cook"

Learning Objectives

  • Learn about Terraform and how to use it to provision AWS infrastructure
  • Learn how to build and create Terraform configurations and modules
    Learn how to use the Terraform CLI to launch and manage infrastructure on AWS

Intended Audience

  • Anyone interested in learning about Terraform, and the benefits of using it to codify infrastructure
  • Anyone interested in building and launching AWS infrastructure using Terraform
  • Anyone interested in deploying cloud native applications on AWS


Prerequisites which would be considered useful for this course are:

  • Knowledge of the AWS cloud platform and the various services within it – particularly VPC, EC2, and IAM
  • Basic System administration experience
  • Basic Infrastructure and Networking Knowledge
  • Basic SysOps and/or DevOps Knowledge


All Terraform configuration as used within the provided demonstrations is located in GitHub here:


Welcome back. In this demonstration, I'll show you how to create the same AWS architecture as used in the previous demonstration. But in this demonstration, I'll refactor the Terraform templates to use the count meta argument for configuring the public and private subnets, as well as their respective route tables, thereby simplifying the overall Terraform configuration. Let's begin.

As per the previous demonstrations, all of the Terraform configuration, which will be demonstrated here, is available online, this time in the exercise three folder within the repo. The AWS VBC architecture that we'll build in exercise three, is identical in structure to the AWS VPC architecture as used within exercise two, the previous exercise. The purpose of this demonstration is to show you an alternative approach to provisioning multiple similar resources. In this case, our subnets and route titles, et cetera. The key technique as used here is based on using Terraform's resource count meta argument to dynamically create multiple similar resources for us, as will be used to create our subnets and route tables, for example. Jumping into Visual Studio Code, I'll open up just the file since. This file is the only one that has modifications within it, compared to exercise two. Again, I need to perform an initialization of the current working directory, which I'll do now. And then, while that is initializing, I'll explain the modifications.

Jumping down to where the subnets are declared, you can see that we now only have two subnet resource blocks. Whereas previously we had four. The first subnet block declares a subnet per AZ for the public zone. The second subnet block declares a subnet per AZ for the private zone. Now the key attribute that makes this possible is what's referred to is the count meta argument declared within both subnet resource blocks. The count value here is being derived by using the inbuilt length function to determine how many AZ's have been declared within the availability zones variable. To make this clearer, let's fire up the Terraform consult and examine the related expressions.

Firstly, I'll examine the contents of the availability zones variable. Here we can see that it contains two values, US West two A and US West to B. Now, if we use the link functional in this variable, we expectedly get the value to returned. Next, the inbuilt cidr of subnet function is used to dynamically calculate the cidr block for the current subnet. And it does so by making use of the count.index, which is zero based and X as an index for the current resource.

In the scenario, Terraform creates two public subnet resources for us, even though we've only declared a single public subnet block. This is a language feature of Terraform and one that helps us to write less configuration, less is better, but for which is also more dynamic. To help with this explanation, let's copy of the cidr sub-net function into the Terraform console, and then evaluate it for various values of count on index, starting with zero, then one and then two.

Here, we can see that the first public subnet will have the cidr and the second one will have I'll also do the same for the element and built function, just for the sake of clarity. Note that the element function, which is being used to return a single AZ actually wraps around when the index is greater than the number of elements which exist in the list being indexed. The same element based expression is then also used here to interpolate the evaluation into the meta tag configured on the sub-net resource.

So, this should now make it clear how a single resource blog can be provisioned multiple times. The same configuration is used to configure the private subnets. The only difference is that the cidr sub-net function starts two/24 blocks along, caused by the addition of the length of the AZ list. Therefore, as you can see, our two private subnets will be and

Okay, moving on, scrolling down to the net elastic IP resource, we can observe that it has been updated to use the count meta argument as well, which in this case will cause it to provision two EIPs, one per AZ. Each of which will be referenced within the immediately following net gateway resource, which as you might expect, also makes use of the count meta argument to create a net gateway in each AZ.

In the net gateway resource, the subnet ID key is set to take on one of the public subnet IDs. Here, the public subnet IDs are returned by making use of the split notation. That is the asterix character. This basically returns all matching public subnet IDs. The same syntax is used to wire up the elastic IPs from the previous resource block, assigning one to each of the net gateways.

Moving on down, a public route table as established, which contains a single default route entry for internet traffic to be sent out through the internet gateway. This public route table is then attached to all public subnets, again, by leveraging this split notation. Likewise, private route tables are created, two of them, one for each AZ. Each private route table has a default route entry, which routes outbound internet traffic out through a net gateway, which is co located in the same AZ. Since in this architecture, we now have AZ independent net gateways configured. Most of the remaining Terraform configuration from here down to the application load balancer remains the same as per the previous demonstration. The application load balancer resource is updated such that its nodes are deployed into the public subnets, which again are identified and using the previously explained split notation. And again, likewise for the auto scaling group. Here we can observe the VPC zone identifier has been updated. With the private subnets being identified, again, using split notation.

Okay, with all of these updates in place, let's now build the AWS infrastructure. To do so, I'll run the Terraform apply command to launch the infrastructure. Okay, I'll speed it up to the point where it completes. Again, we get confirmation that our resources have been successfully created. And for our convenience, we have the various outputs available. This time, I've updated the outputs to print out the subnet IDs and cidr blocks for both the public and private subnets. We can see how this is accomplished by viewing the file Here we can see the use of the split notation to grab the subnet IDs and the cidr blocks for the two public subnets. The same goes for the private subnets.

Let's finally confirm that again, we get a valid HTTP response back from our application load balancer. I'll copy the updated DNS address and browse to it. And perfect, everything works again as per the last example. Okay, that concludes this demo. Again, if you've been following along, don't forget to perform a Terraform destroy to tear down your AWS resources.

About the Author
Learning Paths

Jeremy is a Content Lead Architect and DevOps SME here at Cloud Academy where he specializes in developing DevOps technical training documentation.

He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 25+ years. In recent times, Jeremy has been focused on DevOps, Cloud (AWS, Azure, GCP), Security, Kubernetes, and Machine Learning.

Jeremy holds professional certifications for AWS, Azure, GCP, Terraform, Kubernetes (CKA, CKAD, CKS).