Infrastructure as Code: Terraform, Ansible, CloudFormation
Infrastructure as Code (IaC) treats infrastructure provisioning as software engineering. Instead of clicking through cloud console UIs or running ad-hoc shell commands, IaC defines servers, networks, databases, and load balancers in declarative configuration files that are version-controlled, reviewed, tested, and deployed through CI/CD pipelines. This guide covers the three dominant IaC tools — Terraform, Ansible, and CloudFormation — and the principles of state management, module design, and pipeline integration.
The IaC Paradigm
Before IaC, infrastructure was provisioned manually or through fragile shell scripts. Changes were untracked, environments drifted apart, and disaster recovery meant rebuilding from undocumented memory. IaC solves these problems through four key properties:
Idempotency. Applying the same configuration multiple times produces the same result. If a resource already exists with the correct configuration, the tool skips it. This makes IaC safe to run repeatedly and suitable for automated enforcement.
Version control. Infrastructure definitions live in Git alongside application code. Changes go through pull requests, code review, and CI/CD validation. Every change is auditable and revertible.
Reproducibility. The same configuration produces identical infrastructure in development, staging, and production environments. Environment-specific differences are parameterized through variables.
Self-documentation. The configuration file is the documentation. There is no separate wiki to maintain or knowledge to transfer verbally — new team members read the Terraform or CloudFormation code to understand the infrastructure.
Terraform
Terraform by HashiCorp is the most widely adopted IaC tool. It is cloud-agnostic, supporting over 2,000 providers for AWS, Azure, Google Cloud, Kubernetes, GitHub, Datadog, and virtually every infrastructure API.
Core Concepts
Providers are plugins that interact with cloud APIs. The AWS provider manages EC2, S3, RDS, IAM, VPC, and hundreds of other resources. Provider configuration specifies region, credentials, and optional features:
provider "aws" {
region = "us-west-2"
---Resources are infrastructure components. Each resource block declares a single component:
resource "aws_s3_bucket" "data" {
bucket = "myorg-data-${var.environment}"
force_destroy = var.environment != "production"
---Variables and outputs parameterize configuration and expose created resource attributes:
variable "environment" {
type = string
default = "development"
---
output "bucket_arn" {
value = aws_s3_bucket.data.arn
---State Management
Terraform state is the most critical operational concern. The state file maps configuration to real-world resources and tracks metadata, dependencies, and attributes. If state is lost or corrupted, Terraform cannot manage existing infrastructure.
Remote state must be configured for team use. Amazon S3 with DynamoDB locking is the standard backend:
terraform {
backend "s3" {
bucket = "myorg-terraform-state"
key = "infrastructure/terraform.tfstate"
region = "us-west-2"
dynamodb_table = "terraform-locks"
}
---State locking prevents concurrent modifications. Without locking, two team members running terraform apply simultaneously can corrupt state and create duplicate resources.
Module Design
Modules are reusable Terraform configurations packaged in directories. The Terraform Registry hosts thousands of community modules for common patterns — VPC, EKS cluster, RDS instance, CI/CD pipeline.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = "myapp-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
---Internal modules should version with semantic tags. Teams publish modules to a private module registry (Terraform Cloud or self-hosted) and pin to specific versions.
CI/CD Integration
Terraform pipelines run terraform plan to preview changes and terraform apply to execute. The plan output is a key artifact for code review:
# GitLab CI example
stages:
- validate
- plan
- apply
validate:
stage: validate
script:
- terraform fmt -check
- terraform validate
plan:
stage: plan
script:
- terraform plan -out=plan.tfplan
artifacts:
paths: [plan.tfplan]
apply:
stage: apply
script:
- terraform apply plan.tfplan
when: manual
only:
- mainThe apply stage is manual (requiring human approval) and restricted to the main branch. This guardrail prevents accidental infrastructure changes from feature branches.
AWS CloudFormation
CloudFormation is AWS’s native IaC service. It eliminates the state management problem — AWS manages state internally — and provides deep integration with AWS services. Templates are written in YAML or JSON and deployed as stacks.
Key Features
Change sets preview infrastructure changes before execution, similar to Terraform plan. The difference: change sets are computed by AWS and can be reviewed in the CloudFormation console or CLI.
Stack sets deploy infrastructure across multiple accounts and regions from a single template. Organizations use stack sets for baseline security controls, logging configuration, and network foundations across their entire AWS organization.
Drift detection compares the template’s desired state against the actual resource configuration. If someone modifies a resource outside of CloudFormation (through the console, CLI, or SDK), drift detection identifies the discrepancy.
Nested Stacks and Modules
CloudFormation nested stacks compose templates similarly to Terraform modules. A root template references child templates stored in S3:
NetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/myorg-templates/network.yml
Parameters:
EnvironmentName: !Ref EnvironmentNameCompliance and Guard
CloudFormation Guard is a policy-as-code tool that validates templates against rules:
let encryption_required = %s3_bucket.*.BucketEncryption !empty
rule check_encryption when encryption_required {
let SSEAlgorithm = %s3_bucket.*.BucketEncryption.ServerSideEncryptionConfiguration[0].ServerSideEncryptionByDefault.SSEAlgorithm
SSEAlgorithm == "AES256" or SSEAlgorithm == "aws:kms"
---Guard rules run in CI/CD pipelines, blocking non-compliant templates before deployment.
Ansible for Provisioning
While Ansible is primarily a configuration management tool, its cloud modules provision infrastructure across AWS, Azure, GCP, and VMware:
- name: Create EC2 instance
amazon.aws.ec2_instance:
name: "web-{{ env }}"
instance_type: t3.medium
image_id: ami-0c55b159cbfafe1f0
security_groups:
- web-sg
vpc_subnet_id: "{{ subnet_id }}"
tags:
Environment: "{{ env }}"Ansible’s push-based model suits provisioning tasks that are part of a larger workflow — provision a server, configure it, deploy the application. For pure provisioning at scale, Terraform or CloudFormation are more appropriate.
Pulumi for Modern IaC
Pulumi is a newer IaC tool that uses general-purpose programming languages (TypeScript, Python, Go, C#, Java) instead of DSLs. Infrastructure is defined as real programs with loops, functions, classes, and conditionals. This eliminates the configuration-language limitations of HCL and CloudFormation templates:
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("data", {
acl: "private",
tags: { Environment: process.env.ENV || "development" },
---);
export const bucketName = bucket.id;Pulumi manages state similarly to Terraform — it stores state in the Pulumi Cloud, local filesystem, or cloud object stores. Pulumi’s Automation API allows embedding IaC directly into application code, enabling patterns like ephemeral preview environments that are created and destroyed per pull request.
Policy as Code for IaC
Security misconfiguration is the leading cause of cloud breaches. Policy-as-code tools scan IaC templates for misconfigurations before resources are created.
Checkov scans Terraform, CloudFormation, Kubernetes, and Helm charts against hundreds of built-in policies:
checkov --directory . --framework terraformtfsec focuses specifically on Terraform security scanning. cfn-nag scans CloudFormation templates. Trivy now includes IaC scanning alongside container and vulnerability scanning.
Integrate these tools into the CI/CD pipeline:
iac-scan:
stage: validate
script:
- checkov --directory . --framework terraform --quiet
- tfsec .Block the pipeline if critical misconfigurations are detected. This prevents S3 buckets with public access, security groups with 0.0.0.0/0 ingress, and databases with encryption disabled from reaching production.
Frequently Asked Questions
Should I use Terraform or CloudFormation?
Terraform is the better choice for multi-cloud environments, organizations that want to avoid vendor lock-in, or teams already using Terraform for other providers. CloudFormation is superior for AWS-only shops because of its native integration (no credential management, built-in drift detection, StackSets for multi-account management). AWS also ensures CloudFormation supports new AWS services on launch day — Terraform providers may lag by weeks or months.
How do I manage secrets in Terraform?
Never hardcode secrets in Terraform configuration. Use Terraform variables set via environment variables, Vault provider for dynamic secrets, or AWS Secrets Manager / AWS SSM Parameter Store with data sources:
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/db/password"
---For local development, use .tfvars files that are gitignored and referenced explicitly.
What is Terraform Cloud and do I need it?
Terraform Cloud is HashiCorp’s managed service for Terraform state management, remote execution, policy enforcement (Sentinel), and collaboration. Teams of 3+ Terraform users benefit from remote state locking, private module registry, cost estimation, and run history. Small teams can manage state with S3/DynamoDB backends. Terraform Cloud has a generous free tier for up to 5 users.
How do I test Terraform modules?
Use Terratest (Go-based testing library) to write integration tests that create real infrastructure, validate attributes, and destroy resources. Use terraform plan assertions in CI/CD to verify expected changes. Structure modules with clear input/output contracts and test them independently from root modules.
Recommended Internal Links
- Configuration Management: Ansible, Puppet, Chef, and SaltStack — complements IaC with post-provisioning software configuration
- CI/CD Guide: Continuous Integration and Deployment Explained — CI/CD pipeline integration for IaC workflows
- Security in CI/CD: Secrets Management, SAST, and Supply Chain Security — policy as code and infrastructure scanning
Conclusion
Infrastructure as Code transforms cloud provisioning from manual, error-prone operations into automated, auditable software engineering. Terraform provides cloud-agnostic flexibility with the richest provider ecosystem. CloudFormation offers deep AWS integration with zero state management overhead. Ansible bridges provisioning and configuration in a single tool. Regardless of tool choice, the IaC principles of idempotency, version control, reproducibility, and policy enforcement are essential for reliable cloud operations.
For a comprehensive overview, read our article on Artifact Management.
For a comprehensive overview, read our article on Ci Cd Best Practices.