Jam Notes #1: Infrastructure
as Code Best Practices

Raphael Socher
5 min readMay 14, 2021

Contributions by Mark Smith and Hardik Desai

Welcome to our note series on our weekly Jam sessions, where we cover relevant DevOps topics with rotating guest speakers.

To save you time and energy, we’ll be uploading each Jam session topic’s notes, along with helpful tips.

For the week of April 28th, we covered a far-reaching topic: Infrastructure as Code (IaC) best practices. We talked with Mark Smith and Hardik Desai about their tips for best practices, including structuring IaC, creating a naming convention, handling Terraform state files…and more.

Join our Slack to stay in the loop about our future weekly Jam Sessions on DevOps topics ranging from IaC to Terrraform Templates.

Infrastructure as Code Best Practices
by Mark Smith

1. Structuring

Tree view snippet below:

$ tree terraform_project/

terraform_project/

├── dev

│ ├── main.tf

│ ├── outputs.tf

│ └── variables.tf

├── modules

│ ├── ec2

│ │ ├── ec2.tf

│ │ └── main.tf

│ └── vpc

│ ├── main.tf

│ └── vpc.tf

├── prod

│ ├── main.tf

│ ├── outputs.tf

│ └── variables.tf

└── uat

├── main.tf

├── outputs.tf

└── variables.tf

2. Naming Convention

Naming conventions are used in Terraform to make things easily understandable. For example, let’s say you want to make three different workspaces for different environments in a project. So, rather than naming them as env1, en2, env3, you should call them as a dev, stage, prod. From the name itself, it becomes pretty clear that there are three different workspaces for each environment. So, if you follow the naming conventions correctly, it will be easier to understand even complex codes.

3. Use Shared Modules

It is strongly suggested to use official Terraform modules available. No need to reinvent a module that already exists. It saves a lot of time and pain. The Terraform registry has plenty of modules readily available. Make changes to the existing modules as per the need. Each module should concentrate on only one aspect of the infrastructure, such as creating an AWS EC2 instance, setting RDS database, creating a Peering Connection or Transient Gateway resource etc. For example, if you want to use AWS VPC in your Terraform code, you can use — simple VPC.

module "vpc_example_simple-vpc" {
source
= "terraform-aws-modules/vpc/aws//examples/simple-vpc"
version = "2.48.0"
}

4. Backup System State

These files keep track of the metadata and resources of the infrastructure. By default, these files called terraform.tfstate are stored locally inside the workspace directory. Always backup the state files of Terraform. Without these files, Terraform will not be able to figure out which resources are deployed on the infrastructure and by default, a file with the name terraform.tfstate.backup will get created to keep a backup of the state file. It’s highly recommended to use a remote backend like AWS S3.

terraform {
backend "s3" {
bucket = "s3-terraform-bucket"
key = "terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform_locks"
}
}

5. Lock State File

There can be multiple scenarios where more than one developer tries to run the Terraform configuration at the same time. This can lead to the corruption of the Terraform state file or even data loss. The locking mechanism helps to prevent such scenarios. It makes sure that at a time, only one person is running the Terraform configurations, and there is no conflict. It’s highly recommended to use AWS DynamoDB.

terraform {
backend "s3" {
bucket = "s3-terraform-bucket"
key = "terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform_locks"
}
}

6. Use Self Variable

Self variable is a special kind of variable that is used when you don’t know the value of the variable before deploying an infrastructure. Let’s say you want to use the IP address of an instance which will be deployed only after the Terraform “apply” command, so you don’t know the IP address until it is up and running. In such cases, you use self variables, and the syntax to use it is self.ATTRIBUTE. So, in this case, you will use self.ipv4_address as a self variable to get the IP address of the instance. These variables are only allowed on connection and provisioner blocks of Terraform configuration.

connection {
host = self.ipv4_address
type = "ssh"
user = var.users[2]
private_key = file(var.private_key_path)
}

7. Minimize Blast Radius

The blast radius is nothing but the measure of damage that can happen if things do not go as planned. For example, if you are deploying some Terraform configurations on the infrastructure and the configuration does not get applied correctly, what will be the amount of damage to the infrastructure. So, to minimize the blast radius, it is always suggested to push a few configurations on the infrastructure at a time. That way, if something went wrong, the damage to the infrastructure will be minimal and can be corrected quickly. Deploying plenty of configurations at once is very risky.

Infrastructure as Code Best Practices
by Hardik Desai

1. Integrate IaC into the CI/CD Pipeline

When you integrate IaC tools into the CI/CD pipeline, your infrastructure configuration becomes code. In an automated environment, when a change is made to the infrastructure, it will automatically trigger automated tests. With continuous testing and continuous monitoring, you can ensure a robust, automated and error-free environment.

2. Immutable infrastructure is important

Immutable infrastructure is where the cloud resources consisting of servers and virtual machines cannot be modified after deployment. When an update is required, the existing resource will be replaced by a new one. This ensures consistency and security across the infrastructure.

3. Microservices architecture best suits IaC

IaC can be broken into smaller, independent modular bits like microservices so that you can independently manage different parts of the infrastructure with customized and role-based access controls while collectively automating the entire infrastructure.

IaC use cases:

  • To automate deployment
  • For multi-cloud deployment
  • In disposable environments
  • To create immutable infrastructure
  • For disaster recovery and backup
  • For resource provisioning, for example, replicating environments
  • For blue/green deployment

Terraform Best Practices:

1. Use reusable modules to create/replicate cloud resources without duplication of code

The same modules are used across different environments from testing to development, user acceptance testing (UAT) and production.

2. Store Terraform remote state files in S3 bucket or any other cloud storage

On the backend, a single DynamoDB table can be used to lock multiple remote state files. Terraform will generate key names that include the values of the bucket and key variables. This backend also supports state locking and consistency checks using DynamoDB.

Below is a table comparing 3 IaC tools: Terraform, CloudFormation, Pulumi

Conclusion

We hope you found these Infrastructure as Code best practices to be helpful.

If you’re looking to try a new (free) tool to spin up your infrastructure easily, we recommend that you check out InfraSketch.

And, if you’re looking to get more Infrastructure as Code tips? Join our Slack community to connect with other DevOps professionals.

PS: Did you know we have weekly Jam sessions on DevOps topics? The weekly invites go out on our Slack and Twitter channels. We hope to see you there!

--

--

Raphael Socher

Founder at InfraCode — customizable, reliable Infrastructure as Code tools. Simplifying the lives of DevOps professionals. www.infrastructurecode.io