Skip to content
Home
Infrastructure as Code: Terraform, Ansible, CloudFormation

Infrastructure as Code: Terraform, Ansible, CloudFormation

CI/CD CI/CD 8 min read 1503 words Beginner ExcellentWiki Editorial Team

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:
    - main

The 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 EnvironmentName

Compliance 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 terraform

tfsec 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

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.

Section: CI/CD 1503 words 8 min read Beginner 756 articles in section Report inaccuracy Back to top