Why Infrastructure as Code

Manually creating cloud resources through web consoles is fine for a single server. But when your infrastructure grows to include load balancers, databases, CDNs, queues, caches, and monitoring — across development, staging, and production environments — manual management becomes a liability. Configuration drift, undocumented changes, and the inability to reproduce environments reliably are problems that Infrastructure as Code (IaC) solves definitively.

At StrikingWeb, we manage all client infrastructure through Terraform. Every server, database, DNS record, and SSL certificate is defined in code, version-controlled in Git, and deployed through automated pipelines. This approach has eliminated configuration drift, made disaster recovery straightforward, and allowed us to spin up identical environments in minutes rather than days.

Why Terraform

Terraform, created by HashiCorp, is the most widely adopted IaC tool for several reasons:

HCL — HashiCorp Configuration Language

Terraform uses HCL, a declarative language designed specifically for infrastructure configuration. HCL is intentionally not a general-purpose programming language — it is optimized for readability and for describing infrastructure resources.

Basic Resource Definition

# Configure the AWS provider
provider "aws" {
  region = "ap-south-1"
}

# Create a VPC
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name        = "production-vpc"
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

# Create a public subnet
resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "ap-south-1a"
  map_public_ip_on_launch = true

  tags = {
    Name = "public-subnet-1a"
  }
}

Each resource block defines a single infrastructure component. Terraform automatically determines the order of creation based on dependencies — the subnet references the VPC, so Terraform knows to create the VPC first.

Variables and Outputs

Variables make your Terraform configurations reusable. Instead of hardcoding values, you parameterize them:

# variables.tf
variable "environment" {
  description = "Deployment environment"
  type        = string
  default     = "staging"
}

variable "instance_type" {
  description = "EC2 instance type"
  type        = string
  default     = "t3.medium"
}

variable "db_password" {
  description = "Database password"
  type        = string
  sensitive   = true
}

# main.tf
resource "aws_instance" "app" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = var.instance_type

  tags = {
    Name        = "${var.environment}-app-server"
    Environment = var.environment
  }
}

# outputs.tf
output "app_server_ip" {
  description = "Public IP of the application server"
  value       = aws_instance.app.public_ip
}

Outputs expose values from your Terraform configuration that can be used by other Terraform modules or by scripts and CI/CD pipelines.

State Management

Terraform state is a JSON file that maps your configuration to real-world resources. It records which AWS resources correspond to which Terraform resources, enabling accurate change detection. State management is the most critical aspect of working with Terraform in a team.

Remote State with S3

For team environments, state must be stored remotely so that all team members work with the same state. We use S3 for state storage with DynamoDB for state locking:

terraform {
  backend "s3" {
    bucket         = "strikingweb-terraform-state"
    key            = "production/infrastructure.tfstate"
    region         = "ap-south-1"
    dynamodb_table = "terraform-state-lock"
    encrypt        = true
  }
}

State locking prevents two team members from applying changes simultaneously, which could corrupt the state file. DynamoDB provides the locking mechanism — when one person runs terraform apply, others are blocked until the operation completes.

Modules — Reusable Infrastructure Components

Modules are Terraform's mechanism for creating reusable infrastructure components. A module encapsulates a set of resources that work together and exposes a clean interface through variables and outputs.

# modules/web-app/main.tf
resource "aws_instance" "app" {
  count         = var.instance_count
  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = var.subnet_id

  tags = {
    Name = "${var.app_name}-${count.index + 1}"
  }
}

resource "aws_lb" "app" {
  name               = "${var.app_name}-lb"
  internal           = false
  load_balancer_type = "application"
  subnets            = var.public_subnet_ids
}

# Using the module
module "api_service" {
  source         = "./modules/web-app"
  app_name       = "api"
  instance_count = 3
  instance_type  = "t3.large"
  ami_id         = "ami-0c55b159cbfafe1f0"
  subnet_id      = aws_subnet.private.id
  public_subnet_ids = [aws_subnet.public_1a.id, aws_subnet.public_1b.id]
}

module "admin_service" {
  source         = "./modules/web-app"
  app_name       = "admin"
  instance_count = 1
  instance_type  = "t3.medium"
  ami_id         = "ami-0c55b159cbfafe1f0"
  subnet_id      = aws_subnet.private.id
  public_subnet_ids = [aws_subnet.public_1a.id, aws_subnet.public_1b.id]
}

Modules allow you to create standardized infrastructure patterns. At StrikingWeb, we maintain a library of modules for common patterns: web applications with load balancers, RDS databases with read replicas, S3 buckets with CloudFront distributions, and ECS services with auto-scaling.

Best Practices

Environment Separation

Use separate state files for each environment (development, staging, production). This prevents accidental changes to production when working on development infrastructure. We organize our Terraform code by environment with shared modules:

infrastructure/
  modules/
    web-app/
    database/
    networking/
  environments/
    dev/
      main.tf
      variables.tf
      terraform.tfvars
    staging/
      main.tf
      variables.tf
      terraform.tfvars
    production/
      main.tf
      variables.tf
      terraform.tfvars

Code Review for Infrastructure Changes

Treat infrastructure code with the same rigor as application code. All changes go through pull requests with code review. Include the output of terraform plan in the PR description so reviewers can see exactly what will change.

Tagging Strategy

Tag every resource with at least the environment, project name, and the fact that it is managed by Terraform. Tags make it easy to track costs, identify resources, and avoid accidentally modifying Terraform-managed resources through the console.

Avoid Manual Changes

Once you adopt Terraform, never modify infrastructure through the cloud console. Manual changes create drift between your Terraform state and the actual infrastructure, leading to confusing errors and potentially destructive operations. If someone makes a manual change, use terraform import to bring it under Terraform management or terraform refresh to update the state.

Infrastructure as Code is not just about automation — it is about treating your infrastructure with the same discipline you apply to your application code. Version control, code review, testing, and documentation are as important for servers and databases as they are for APIs and user interfaces.

Getting Started

If you are managing cloud infrastructure manually, start small. Pick one environment or one service and define it in Terraform. Once you experience the confidence that comes from knowing your infrastructure is documented, reproducible, and version-controlled, you will want to bring everything under Terraform management. At StrikingWeb, we help teams adopt Terraform, design their module architecture, and set up the CI/CD pipelines that make IaC practical for everyday use.

Share: