If you're still creating cloud resources by clicking through a web console, you're building on quicksand. Console-created infrastructure is undocumented, unreproducible, and un-reviewable. When something breaks at 2 AM, nobody knows what the expected state should be.
Terraform solves this by treating infrastructure as code — version-controlled, peer-reviewed, and automatically applied. At Pillai Infotech, every piece of infrastructure we manage (for our own operations and for clients) is defined in Terraform. No exceptions.
Why Terraform Won the IaC Market
- Multi-cloud: One language (HCL) for AWS, Azure, GCP, and hundreds of other providers. No vendor lock-in.
- Declarative: You describe the desired state, Terraform figures out how to get there. No imperative scripting.
- Plan before apply:
terraform planshows exactly what will change before anything happens. No surprises. - State management: Terraform knows what exists and what needs to change. It doesn't blindly recreate everything.
- Massive ecosystem: 3,000+ providers covering everything from cloud infrastructure to DNS, monitoring, databases, and SaaS tools.
Terraform vs Alternatives
| Tool | Approach | Best For |
|---|---|---|
| Terraform | Declarative HCL, multi-cloud | Most teams, multi-cloud, largest ecosystem |
| Pulumi | Real programming languages (TypeScript, Python) | Teams who prefer code over config, complex logic |
| CloudFormation | AWS-native YAML/JSON | AWS-only shops, deep AWS integration |
| OpenTofu | Open-source Terraform fork | Teams concerned about HashiCorp licensing |
Core Concepts You Need to Understand
Resources
The fundamental building blocks. Each resource represents a single piece of infrastructure — an EC2 instance, an S3 bucket, a database, a DNS record.
Providers
Plugins that connect Terraform to cloud platforms. provider "aws", provider "azurerm", etc. You can use multiple providers in one configuration.
State
A file (local or remote) that tracks what Terraform manages. It maps your config to real-world resources. This is the most important concept to understand. State is how Terraform knows what exists, what to create, what to update, and what to destroy.
Plan and Apply
terraform plan shows you what will change. terraform apply makes those changes. Always plan before applying. In CI/CD, plan on PR, apply on merge.
Module Design: Reusable Infrastructure Components
Modules are Terraform's abstraction mechanism — reusable packages of infrastructure that accept inputs and produce outputs. Good module design is the difference between maintainable Terraform and copy-paste chaos.
Our Module Rules
- One module per logical component: A "web-app" module that creates load balancer + auto-scaling group + security group + CloudWatch alarms. Not separate modules for each.
- Inputs for everything that varies: Environment name, instance size, region, tags. Hard-code nothing that differs between staging and production.
- Sensible defaults: Most inputs should have defaults that work for the common case. Teams shouldn't need to specify 30 variables to create a database.
- Version your modules: Use semantic versioning (Git tags) for shared modules. A module change shouldn't break every team simultaneously.
State Management: Don't Learn This the Hard Way
State management is where most Terraform disasters happen. Here's how to get it right:
- Remote state: Store state in S3 + DynamoDB (AWS), Azure Blob + Table Storage, or GCS + Cloud Storage. Never in local files. Never in Git.
- State locking: DynamoDB table for AWS, built-in for Azure/GCP. Prevents two people from modifying state simultaneously.
- State encryption: Enable server-side encryption on your state bucket. State files contain sensitive information (resource IDs, sometimes passwords).
- State isolation: Separate state files per environment (staging, production) and per team/component. A single state file for everything is fragile — one bad apply takes down everything.
terraform destroy in the wrong directory, targeting production state instead of a test environment. State isolation would have prevented this. We now use separate AWS accounts per environment and separate state backends. The 30 minutes to set up isolation saves potentially catastrophic mistakes.
Workspaces and Environment Management
Terraform workspaces allow you to manage multiple environments from the same configuration. However, we prefer a different approach:
Our Approach: Separate Directories, Shared Modules
├── modules/ # Shared reusable modules
│ ├── web-app/
│ ├── database/
│ └── networking/
├── environments/
│ ├── staging/ # Staging-specific config + state
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── terraform.tfvars
│ └── production/ # Production-specific config + state
│ ├── main.tf
│ ├── variables.tf
│ └── terraform.tfvars
└── global/ # Shared resources (IAM, DNS)
Each environment has its own state backend, its own variables, and its own Terraform configuration that references shared modules. This is more verbose than workspaces but much safer — you literally cannot accidentally apply production changes to staging or vice versa.
Terraform in CI/CD Pipelines
- On PR: Run
terraform planand post the output as a PR comment. Reviewers see exactly what infrastructure changes the PR introduces. - On merge to main: Run
terraform apply -auto-approvefor staging. Require manual approval for production. - Drift detection: Schedule a daily
terraform planin production. If someone made manual changes (ClickOps), the plan will show drift.
Tools that make this easier: Atlantis (PR-based Terraform automation), Terraform Cloud, Spacelift. We use Atlantis for most clients — it's open source and runs in your own infrastructure.
The 5 Terraform Mistakes That Cost Us the Most Time
- Not using remote state from day one. Migrating from local to remote state is doable but nerve-wracking. Start with remote state, even for experiments.
- Monolithic state files. One state file for everything means one
terraform applycan touch everything. Split by environment and component. - Not pinning provider versions. A provider update changed API behavior and broke our plan. Always pin:
required_providers { aws = { version = "~> 5.0" } } - Ignoring import. When you have existing infrastructure, use
terraform importto bring it under management. Don't recreate resources that already exist. - Testing in production. Terraform has no "undo." Test in a disposable environment first. We maintain a "sandbox" environment specifically for Terraform experiments.
Need help setting up Terraform for your infrastructure? Our DevOps team implements IaC from scratch or takes over existing configurations. Let's talk.