DISCLAIMER : Please note that blog owner takes no responsibility of any kind for any type of data loss or damage by trying any of the command/method mentioned in this blog. You may use the commands/method/scripts on your own responsibility.If you find something useful, a comment would be appreciated to let other viewers also know that the solution/method work(ed) for you.


Infrastructure as a code best practices : Terraform

 In this post we will go over what a typical infrastructure would look like for an enterprise product and the best practices for the same.

1. High level infrastructure overview :

A typical infrastructure of a product can be categorized into the following three categories :

2. Consistent code structure :

All the terraform code written should follow a consistent code structure. Below can be an example :

iaac code structure

For dev and region — eu-central-1 , the expected file name should be : dev-eu-central-1.tfvars .

For int and region — eu-central-1 , the expected file name should be : int-eu-central-1.tfvars and so on.

The files contain initialized values for the variables declared in variables.tf file.

3. Use terraform modules :

Use of terraform modules wherever possible should be done and such modules should be kept in a common place in gitlab/code repository . Whenever we need a new resource for a service, a repo for such a resource should be created containing the terraform code. Such modules can then be directly referred by the service infra code . In case another service needs the same resource in future , we don’t need to duplicate the terraform code and can use the tried and tested version present .

4. Consistent Naming convention :

Terraform recommends the following naming conventions :

Good: resource "aws_route_table" "public" {}

Bad: resource "aws_route_table" "public_route_table" {}

Bad: resource "aws_route_table" "public_aws_route_table" {}

Use “-" inside arguments values and in places where value will be exposed to a human (eg, inside DNS name of RDS instance).

use “count” as the first argument in a resource

use “tag” as the last argument followed by “depends” and “lifecycle” .

5. Organizing state file :

The state should be stored remotely in an s3 bucket and each of the infra layers : Global, Common and Service should have their own state file. Further more , each of the services in the service layer should have their own state file as well. Organizing state files like this ensure loose coupling. Each service per env should have its own state file . Following shows how the state file should be organized.

Organizing State File

6. Use an automated testing framework to write unit and functional tests that validate your terraform modules :

Automated testing is every bit as important for writing infrastructure code as it is for writing application code. Example of such a framework is terratest.

7. Infrastructure service names :

A typical infrastructure created using terraform can follow the below naming style for consistency purpose :

For a single resource :
infra_name = ${name}-${resource_type}
name = ${env}-${service_name}

Examples :
env = dev , service_name = customer-svc , resource_type = db-cluster
rds_cluster_name : dev-customer-svc-db-cluster

For multiple instance of a resource :
infra_name = ${name}-${resource_type}-${count.index}
name = ${env}-${service_name}

Examples :
env = dev , service_name = customer-svc , resource_type = db-cluster-instance , count.index = 0
rds_instance_name : dev-customer-svc-db-cluster-instance-0

env = dev , service_name = customer-svc , resource_type = db-cluster-instance , count.index = 1
rds_instance_name : dev-customer-svc-db-cluster-instance-1

The reasons for the variables used are :

env : we want env to be a part of the infra name so that we can use a single account for creating multiple envs. i.e in case we want to have dev and test env resources within a single aws account the value of this variable we help us distinguish the resources of different env.

service_name : we want the service which owns the resource in the infra name because it becomes easier to search/find resource by service names when there are lot of similar resources . For example if we have lots of queues in our account and if we want to look for a particular queue and we know the service which owns the queue we can search that queue by just typing the service name.

8. Tags :

Tagging the resources is an important part of infra creation in cloud. A resource which supports tagging can have the following set of tags :

Name = “The actual resource name . This should be the ${infra_name} stated above. The key “Name” is case sensitive. In some of the resources this will help to give the resource a name in the aws console . Example in case of subnets or security groups.”
environment = “The env like : dev , int, prod. Useful in case we have multiple env in one account.”
owner-service = “The service which uses this resource and is the owner of the resource. Useful to list resources used by a particular service.”
infra-region = “The region of the resource.”
team-name = “ The team name who is the owner for the resource.”
time-zone = “ The primary time of the operating resource.”
start-time = “The start time of the resource.”
stop-time = “The stop time of the resource. The start and stop times helps a scheduler script know when to start and stop a resource.”
contact = “The team email to contact in case of any queries related to the resource.

These key-value pairs can be utilized for billing, ownership, automation, access control, and many other use cases.