Advanced Tips & Tricks to Optimize your Terraform Code

Oleg Levenkov
Slalom Technology
Published in
5 min readJan 28, 2019

Deployment and management infrastructure day-to-day isn’t an easy task. In this post, I’ll discuss how we can accomplish this using Terraform in AWS and my hope is that this post will bring in more confidence in using Terraform and open up possible approaches and solutions. I will demonstrate how to build complex infrastructures and how to avoid duplication in code using loops. My example will be focused on Amazon Web Services (AWS), but all the information should apply to other clouds as well.

Terraform is a tool created by HashiCorp that allows you to:

  • describe your infrastructure as code
  • outline exactly what will happen when you run your code
  • builds a graph of your resources
  • create and change your infrastructure with minimal human interaction.

Terraform is platform-agnostic and you can use it to manage bare metal or cloud servers — like AWS, GCP, OpenStack, Azure, etc...

Terraform uses a declarative language, called HashiCorp Configuration Language (HCL), to define an infrastructure. This allows for a cleaner, more authentic view of what is being deployed.

Specific types of tasks, like loops and if-statements, could become extra painful since declarative languages usually don’t have loops. With that in mind, defining multiple resources that are similar could end up looking like this.

Example of common needs to create multiple similar resources

The loop is used to eliminate the workload in terms of programming and for faster execution without consuming extra time coding.

Is there a better way?

Almost every Terraform resource has a metadata parameter. “count” is one of the parameters which simplifies configurations to scale your resources by simply incrementing a number. In the example below, I am using the function “length()” to calculate the number of values in my list.

Example “count” use
List

What if you wanted to give each resource a unique name?

To accomplish this, you can simply use “count.index”.

Example of “count.index”

I will show you how to optimize code with multiple examples of count and splat expression for the diagram below and provide you with the code.

For the code to work, minimum pre-requirements must be met:

  • Terraform version >= 0.11.7
  • AWS Account with the right level of permission
  • Visual Studio Code or Sublime

The following layout is typical of what you find in many data centers with DMZ (Public) & Internal (Application and Database)networks. This diagram is a cloud agnostic 3-tiered solution which has public, application, and database subnets in multi availability zones. I will also show how I used splat expressions to configure Auto Scaling for Bastion host.

The Network topology above display of what will be implemented:

  • VPC
  • Subnets
  • Availability Zone
  • NAT Gateway
  • Route Tables
  • Security Group
  • Internet Gateway
  • Auto Scaling Bastion host

Code Review

Global variables

  • Self-explanatory
Global variables

Input Variables

  • I am declaring input variables. Please note that “availability zones” is a list variable containing three values.
Input Variables also know as “terraform.tfvars” file

Subnets & Zones

  • I am using the meta parameter “count”. The meta parameter is available for all resources in Terraform. In this case, I am using the “length()” function combined with the availability zone list, which equals three.
  • The built-in “element()” function is also being used to have to subnets in use a different availability zone.
  • Furthermore, I am using the “cidrsubnet()” function to calculate a CIDR block for each subnet.
Public Subnet. Each subnet in a different AZ.
Application Subnet. Each subnet in a different AZ.
Database Subnet. Each subnet in a different AZ.
Subnets created by Terraform

NAT Gateway

  • I am creating NAT Gateways in each public zone using the function “count”, “count.index”, “element()” and splat expression to reference public subnets & EIP.
Nat Gateway. It enables instances in an Application & Database subnets to connect to the Internet or other AWS services but prevent the internet from establishing connections with those instances. NAT gateway is created in each Public AZ for high availability.
Nat Gateway created by Terraform

Route Tables

  • While creating route tables, I am using splat expression to reference subnet created previously by Terraform.
  • Also, you will find an example of how “count.index” can be used for tagging resources.
Public Route Table. Route the public subnet traffic through the Internet Gateway.
Application Route Table. Create a new route table for application subnets and route traffic through the NAT Gateway into the Internet
Database Route Table. Create a new route table for database subnets and route traffic through the NAT Gateway into the Internet
Routing Tables created by Terraform

Autoscaling Group

  • In the autoscaling group configuration, I am using splat expression to reference public availability zones.
Autoscaling group. Force a redeployment when launch configuration changes and reset the desired capacity if it was changed. It set to redeploys without an outage.

Output

  • Once again, I am using splat expression to output variables as a way to organize data to be easily queried and shown back to the Terraform user.
Outputs. I am using splat expression to get resources created by Terraform.
Outputs

This video demonstrates how I deploy Terraform code. Provisioning Network Infrastructure:

To see how I just as easily destroy these resources created by Terraform:

Outcome

In this post, we saw how we could use some built-in functions in Terraform to easily make and flexibly create and scale our infrastructure, with many more tricks to help us achieve this. It is important for maintainability, infrastructure as code cleanliness, and flexibility. For the code I have demonstrated here, you can find in my GitHub repository.

--

--