TL;DR
Master Infrastructure as Code using Terraform and AWS CloudFormation for efficient cloud resource management.
Infrastructure as Code: Best Practices with Terraform and CloudFormation
Infrastructure as Code (IaC) is a fundamental DevOps practice that enables teams to manage and provision infrastructure through code rather than manual processes.
$1
$1
1. Version Control
2. Reproducibility
3. Consistency
4. Automation
5. Documentation
$1
$1
`` terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
} provider "aws" {
region = "us-west-2"
} resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro" tags = {
Name = "WebServer"
Environment = "Production"
}
}
hcl
`main.tf
$1
` variable "environment" {
type = string
description = "Environment name"
default = "development"
} variable "instance_type" {
type = string
description = "EC2 instance type"
default = "t2.micro"
} output "instance_ip" {
value = aws_instance.web.public_ip
description = "Public IP of the web server"
}
hcl
`variables.tf
outputs.tf
$1
$1
` AWSTemplateFormatVersion: '2010-09-09'
Description: 'Web Server Infrastructure' Parameters:
EnvironmentName:
Type: String
Default: Development
Resources:
WebServerInstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-0c55b159cbfafe1f0
Tags:
- Key: Name
Value: WebServer
- Key: Environment
Value: !Ref EnvironmentName Outputs:
WebServerIP:
Description: Public IP of the web server
Value: !GetAtt WebServerInstance.PublicIp
yaml
`
$1
$1
` module "vpc" {
source = "terraform-aws-modules/vpc/aws" name = var.vpc_name
cidr = var.vpc_cidr azs = var.availability_zones
private_subnets = var.private_subnet_cidrs
public_subnets = var.public_subnet_cidrs enable_nat_gateway = true
enable_vpn_gateway = false tags = {
Environment = var.environment
Terraform = "true"
}
}
hcl
`modules/vpc/main.tf
$1
` Resources:
NetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/templates/network.yaml
Parameters:
EnvironmentName: !Ref EnvironmentName ApplicationStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/templates/application.yaml
Parameters:
VpcId: !GetAtt NetworkStack.Outputs.VpcId
yaml
`
$1
$1
` terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "prod/terraform.tfstate"
region = "us-west-2"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
hcl
`backend.tf
$1
` resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID" attribute {
name = "LockID"
type = "S"
}
}
hcl
`
$1
$1
` data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/db/password"
} resource "aws_db_instance" "database" {
password = data.aws_secretsmanager_secret_version.db_password.secret_string
# Other configuration...
}
hcl
`Using AWS Secrets Manager
$1
` {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::terraform-state-bucket",
"arn:aws:s3:::terraform-state-bucket/*"
]
}
]
}
json
`
$1
$1
` package test import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
) func TestTerraformWebServer(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../",
Vars: map[string]interface{}{
"environment": "test",
},
} defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions) instanceID := terraform.Output(t, terraformOptions, "instance_id")
assert.NotEmpty(t, instanceID)
}
hcl
`test/main_test.go
$1
` AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31 Resources:
TestFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs14.x
CodeUri: ./test
Events:
TestApi:
Type: Api
Properties:
Path: /test
Method: get
yaml
`test-template.yaml
$1
$1
` name: Terraform CI on:
push:
branches: [ main ]
pull_request:
branches: [ main ] jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
- name: Terraform Init
run: terraform init
- name: Terraform Format
run: terraform fmt -check
- name: Terraform Plan
run: terraform plan
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve
yaml
`
$1
` pipeline {
agent any
environment {
AWS_CREDENTIALS = credentials('aws-credentials')
}
stages {
stage('Terraform Init') {
steps {
sh 'terraform init'
}
}
stage('Terraform Plan') {
steps {
sh 'terraform plan -out=tfplan'
}
}
stage('Terraform Apply') {
when {
branch 'main'
}
steps {
sh 'terraform apply -auto-approve tfplan'
}
}
}
}
groovy
`
$1
$1
` provider "aws" {
region = "us-west-2"
} resource "aws_instance" "web" {
instance_type = "t2.micro"
# Other configuration... # Monthly cost: ~$8.50
} resource "aws_s3_bucket" "storage" {
bucket = "my-app-storage"
# Monthly cost: ~$0.023 per GB
}
hcl
`Using Infracost
$1
` locals {
common_tags = {
Environment = var.environment
Project = var.project_name
Owner = var.team
CostCenter = var.cost_center
}
} resource "aws_instance" "web" {
# ... other configuration ...
tags = merge(
local.common_tags,
{
Name = "WebServer"
}
)
}
hcl
`
$1
$1
` resource "aws_backup_plan" "example" {
name = "tf_example_backup_plan" rule {
rule_name = "tf_example_backup_rule"
target_vault_name = aws_backup_vault.example.name
schedule = "cron(0 12 * ? )"
lifecycle {
delete_after = 14
}
}
} resource "aws_backup_selection" "example" {
name = "tf_example_backup_selection"
plan_id = aws_backup_plan.example.id
iam_role_arn = aws_iam_role.example.arn resources = [
aws_db_instance.example.arn,
aws_ebs_volume.example.arn
]
}
hcl
`
$1
` provider "aws" {
alias = "primary"
region = "us-west-2"
} provider "aws" {
alias = "dr"
region = "us-east-1"
} module "primary" {
source = "./application"
providers = {
aws = aws.primary
}
} module "dr" {
source = "./application"
providers = {
aws = aws.dr
}
}
hcl
``
$1
Implementing Infrastructure as Code requires:
1. Proper planning and architecture
2. Version control integration
3. Automated testing and deployment
4. Security best practices
5. Cost optimization
Remember to:
$1
1. [Terraform Documentation](https://www.terraform.io/docs)
2. [AWS CloudFormation User Guide](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide)
3. [Infrastructure as Code Best Practices](https://docs.aws.amazon.com/whitepapers/latest/introduction-devops-aws/infrastructure-as-code.html)
4. [Terraform Best Practices](https://www.terraform-best-practices.com/)
Why This Matters
Understanding the business and technical context helps you make informed decisions rather than blindly following patterns.
Trade-offs to Consider
Every architectural decision involves trade-offs. Consider your specific requirements, team expertise, and scale when evaluating options.
When NOT to Use This
Knowing when a solution doesn't apply is as valuable as knowing when it does. Consider alternatives for your specific situation.
Decision Framework
Use this framework to evaluate whether this approach is right for your use case based on your specific constraints and requirements.