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:
- Cloud agnostic: Terraform works with AWS, Google Cloud, Azure, DigitalOcean, Cloudflare, and hundreds of other providers through a plugin system. You can manage multi-cloud infrastructure with a single tool.
- Declarative syntax: You describe the desired state of your infrastructure, and Terraform figures out what changes need to be made to achieve that state. You do not write procedural scripts that execute in order.
- Plan before apply: Terraform shows you exactly what it will create, modify, or destroy before making any changes. This review step prevents accidental infrastructure damage.
- State management: Terraform tracks the current state of your infrastructure, enabling accurate change detection and dependency management.
- Large ecosystem: Thousands of providers and modules are available, and the community is active and well-supported.
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.