I believe there are few best practices need to follow while using terraform for orchestrating the infrastructure
- Don't write the same code again ( Reusability)
- Keep environment configuration separate to maintain it easily.
- Use remote backend s3(encrypted) and dynamo DB to handle the concurrency locking
- Create a module and use that module in main infrastructure multiple time, its like a reusable function which can be called multiple time by passing different parameter.
Handle multiple environments
Most of the time recommended way is to use terraform 'workspace' to handle the multiple environments but I believe the usage of workspace could vary based on way of work in an organization.
Other is storing the Terraform code for each of your environments (e.g. stage, prod, QA) to separate the environment states. However, in this case we are just copying the same code at many places.
├── main.tf
├── dev
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
└── prod
├── main.tf
├── output.tf
└── variables.tf
I followed some different approach to handle and avoid the duplication of the same terraform code by keeping in each environment folder since I believe most of the time all environment would be 90% same.
├── deployment
│ ├── 01-network.tf
│ ├── 02-ecs_cluster.tf
│ ├── 03-ecs_service.tf
│ ├── 04-eks_infra.tf
│ ├── 05-db_infra.tf
│ ├── 06-codebuild-k8s.tf
│ ├── 07-aws-secret.tf
│ ├── backend.tf
│ ├── provider.tf
│ └── variables.tf
├── env
│ ├── dev
│ │ ├── dev.backend.tfvar
│ │ └── dev.variables.tfvar
│ └── prod
│ ├── prod.backend.tfvar
│ └── prod.variables.tfvar
├── modules
│ └── aws
│ ├── compute
│ │ ├── alb_loadbalancer
│ │ ├── alb_target_grp
│ │ ├── ecs_cluster
│ │ ├── ecs_service
│ │ └── launch_configuration
│ ├── database
│ │ ├── db_main
│ │ ├── db_option_group
│ │ ├── db_parameter_group
│ │ └── db_subnet_group
│ ├── developertools
│ ├── network
│ │ ├── internet_gateway
│ │ ├── nat_gateway
│ │ ├── route_table
│ │ ├── security_group
│ │ ├── subnet
│ │ ├── vpc
│ └── security
│ ├── iam_role
│ └── secret-manager
└── templates
Configuration related to environments
Keep environment related configuration and parameters separate in a variable file and pass that value to configure the infrastructure. e.g as below
dev.backend.tfvar
region = "ap-southeast-2"
bucket = "dev-samplebackendterraform"
key = "dev/state.tfstate"
dynamo_db_lock = "dev-terraform-state-lock"
dev.variable.tfvar
environment = "dev"
vpc_name = "demo"
vpc_cidr_block = "10.20.0.0/19"
private_subnet_1a_cidr_block = "10.20.0.0/21"
private_subnet_1b_cidr_block = "10.20.8.0/21"
public_subnet_1a_cidr_block = "10.20.16.0/21"
public_subnet_1b_cidr_block = "10.20.24.0/21"
Conditional skipping of infrastructure part
Create a configuration in env specific variable file and based on that variable decide to create or skipping that part. In this way based on need the specific part of the infrastructure can be skipped.
variable vpc_create {
default = "true"
}
module "vpc" {
source = "../modules/aws/network/vpc"
enable = "${var.vpc_create}"
vpc_cidr_block = "${var.vpc_cidr_block}"
name = "${var.vpc_name}"
}
resource "aws_vpc" "vpc" {
count = "${var.enable == "true" ? 1 : 0}"
cidr_block = "${var.vpc_cidr_block}"
enable_dns_support = "true"
enable_dns_hostnames = "true"
}
below command is required to initialize and execute the infra changes for each environment, cd to the required environment folder.
terraform init -var-file=dev.variables.tfvar -backend-config=dev.backend.tfvar ../../deployment/
terraform apply -var-file=dev.variables.tfvar ../../deployment
For reference: https://github.com/mattyait/devops_terraform