Skip to content
Home
Infrastructure as Code: Terraform, Ansible & Pulumi Guide

Infrastructure as Code: Terraform, Ansible & Pulumi Guide

DevOps DevOps 8 min read 1648 words Beginner ExcellentWiki Editorial Team

Infrastructure as Code (IaC) is the practice of managing infrastructure — servers, networks, databases, and configuration — through machine-readable definition files rather than manual processes. Instead of SSHing into a server to install packages or clicking through a cloud console to provision resources, you write code that defines the desired state and let tools apply it. This transformation, pioneered by companies like HashiCorp, Chef, and Puppet, has become the foundation of modern DevOps practice. According to the 2024 Stack Overflow Developer Survey, over 65% of professional developers now use some form of IaC in their workflows, and the market for IaC tools is projected to exceed $3.5 billion by 2028.

Why Infrastructure as Code?

The benefits of IaC extend far beyond convenience. Organizations that adopt IaC report 40% fewer deployment failures and 60% faster environment provisioning compared to manual processes, according to the 2024 State of CloudOps Report.

  • Repeatability — the same code produces the same infrastructure every time, eliminating manual errors and the snowflake server problem
  • Version control — infrastructure changes go through code review, just like application code, with full audit trail
  • Auditability — every change is tracked in Git with author, timestamp, and diff
  • Automation — provisioning and configuration happen without manual intervention, enabling self-service environments that developers can spin up in minutes
  • Consistency — no more “works on my machine” for infrastructure; development, staging, and production environments are defined identically

Declarative vs Imperative IaC

# Imperative: "How to do it" — step by step
gcloud compute instances create web-server \
  --zone=us-central1-a \
  --machine-type=e2-medium \
  --image=ubuntu-2204

# Declarative: "What I want" — desired state (Terraform)
resource "google_compute_instance" "web" {
  name         = "web-server"
  zone         = "us-central1-a"
  machine_type = "e2-medium"
  boot_disk {
    initialize_params {
      image = "ubuntu-os-cloud/ubuntu-2204-lts"
    }
  }
---

Declarative IaC describes the desired end state. The tool figures out how to reach that state from the current state, including what to create, update, or destroy. Imperative IaC describes the exact steps, which means the same script may behave differently depending on the starting state. Declarative approaches reduce the risk of unexpected side effects because the tool computes the minimal changes needed.

Terraform

Terraform is the most widely adopted declarative IaC tool for provisioning infrastructure across any cloud provider. It uses HashiCorp Configuration Language (HCL) and maintains state to track managed resources. Terraform was the first tool to popularize the concept of infrastructure provisioning as declarative code, and its provider ecosystem now covers over 3,000 infrastructure services.

Core Concepts

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
---

provider "aws" {
  region = "us-west-2"
---

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  tags = {
    Name = "WebServer"
    Env  = "production"
  }
---

Variables and Outputs

variable "environment" {
  description = "Deployment environment"
  type        = string
  default     = "development"
  validation {
    condition     = contains(["development", "staging", "production"], var.environment)
    error_message = "Must be one of: development, staging, production."
  }
---

output "instance_ip" {
  value       = aws_instance.web.public_ip
  description = "Public IP of web server"
---

Terraform Workflow

terraform init          # Download providers and modules
terraform plan          # Show what will change
terraform apply         # Provision infrastructure
terraform destroy       # Tear down everything
terraform fmt           # Format code
terraform validate      # Validate syntax

State Management

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "production/terraform.tfstate"
    region         = "us-west-2"
    dynamodb_table = "terraform-state-lock"
    encrypt        = true
  }
---

State files map Terraform configuration to real-world resources. Always use remote state with locking for team collaboration. Without locking, two team members applying changes simultaneously can corrupt the state file. Terraform Cloud and Terraform Enterprise provide managed state backends with policy enforcement and collaboration features.

Terraform Modules

Modules encapsulate reusable infrastructure patterns:

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"
  version = "5.0.0"

  name = "my-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"]

  enable_nat_gateway = true
  enable_vpn_gateway = false
---

The Terraform Registry hosts over 7,000 modules published by HashiCorp, partners, and the community. Using well-tested modules reduces boilerplate and incorporates best practices for security and high availability.

Ansible

Ansible is a configuration management and automation tool. It uses push-based, agentless architecture over SSH, making it the simplest IaC tool to adopt for server configuration. While Terraform provisions infrastructure, Ansible configures it — the complementary pairing is the most common production IaC pattern.

Inventory

[webservers]
web1.example.com
web2.example.com

[database]
db1.example.com

[production:children]
webservers
database

Playbooks

---
- name: Configure web servers
  hosts: webservers
  become: yes
  vars:
    nginx_port: 80

  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present

    - name: Configure nginx
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: restart nginx

  handlers:
    - name: restart nginx
      service:
        name: nginx
        state: restarted

Ad-Hoc Commands

ansible all -i inventory.ini -m ping
ansible webservers -i inventory.ini -a "uptime"
ansible-playbook -i inventory.ini site.yml --check

Pulumi

Pulumi lets you write IaC using general-purpose programming languages, which unlocks loops, conditionals, functions, and type safety:

import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";

const config = new pulumi.Config();
const environment = config.require("environment");

const securityGroup = new aws.ec2.SecurityGroup("web-sg", {
    ingress: [
        { protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"] },
    ],
    egress: [
        { protocol: "-1", fromPort: 0, toPort: 0, cidrBlocks: ["0.0.0.0/0"] },
    ],
    tags: { Name: "web-sg", Environment: environment },
---);

const instance = new aws.ec2.Instance("web", {
    instanceType: "t3.micro",
    ami: aws.ec2.getAmi({ ... }).then(a => a.id),
    vpcSecurityGroupIds: [securityGroup.id],
    tags: { Name: "WebServer", Environment: environment },
---);

export const publicIp = instance.publicIp;

Pulumi supports TypeScript, Python, Go, C#, Java, and YAML. This language flexibility makes it particularly attractive for teams that want to reuse existing testing frameworks, IDE features, and package ecosystems.

Comparison

FeatureTerraformAnsiblePulumi
LanguageHCLYAMLTypeScript, Python, Go, C#
ParadigmDeclarativeDeclarativeImperative + Declarative
AgentState-basedSSH (agentless)State-based
ProvisioningExcellentLimitedExcellent
Configuration MgmtNoExcellentNo
State ManagementRequiredNot requiredRequired
Cloud SupportAll major cloudsVia modulesAll major clouds

IaC Testing and Validation

Testing infrastructure code is as important as testing application code. Each IaC tool provides validation mechanisms that should be integrated into CI/CD pipelines.

Terraform Validation

# Syntax and configuration validation
terraform validate

# Policy as code with Sentinel or OPA
terraform plan -out=tfplan
terraform show -json tfplan | opa eval --data policies -i

# Compliance testing with Terratest
go test -v -run TestTerraformAwsBasic

Ansible Validation

# Syntax check
ansible-playbook site.yml --syntax-check

# Dry run
ansible-playbook site.yml --check

# Diff mode shows what changes
ansible-playbook site.yml --check --diff

# Idempotency test
ansible-playbook site.yml
ansible-playbook site.yml | grep -c "changed="

Pulumi Preview

# Preview changes before applying
pulumi preview

# Policy as code enforcement
pulumi policy publish

IaC Security Scanning

# Scan Terraform code for security issues
checkov -d terraform/
tfsec terraform/

# Scan Ansible playbooks
ansible-lint site.yml

Tools like Checkov, tfsec, and Terrascan analyze IaC files for misconfigurations — open security groups, unencrypted storage, IAM policy violations — before they reach production. Integrate these scans into CI/CD pipelines as mandatory gates.

IaC and GitOps Workflow

GitOps extends IaC principles by using Git as the single source of truth for declarative infrastructure and application configuration. Changes are made via pull requests, and a reconciliation operator (Argo CD, Flux) automatically applies the desired state from Git to the infrastructure.

Key GitOps principles:

  • Declarative description — the entire system is described declaratively in Git
  • Automated convergence — a controller continuously reconciles actual state with the desired state in Git
  • Pull-based deployment — the operator pulls from Git rather than pushing from CI/CD
  • Observability — drift from the desired state is detected and reported

GitOps is particularly popular in Kubernetes environments but applies to any infrastructure that can be described declaratively. Tools like Crossplane extend GitOps to cloud infrastructure beyond Kubernetes.

FAQ

Should I use Terraform or Ansible for infrastructure?

Use both together. Terraform provisions infrastructure (VMs, networks, databases) and Ansible configures servers (installing packages, deploying apps, managing configs). Terraform handles the “create and destroy” lifecycle; Ansible handles the “configure and maintain” lifecycle. This complementary pairing is the most common production IaC architecture.

What is Terraform state and why is it important?

State is Terraform’s mapping between configuration and real-world resources. It records resource IDs, attributes, and dependencies. Without state, Terraform cannot know what it manages. Always store state remotely (S3, Azure Storage, Terraform Cloud) with locking to prevent concurrent modification. State files contain sensitive information — encrypt them at rest and control access with IAM policies.

Can I use Pulumi if I don’t know TypeScript?

Yes. Pulumi supports Python, Go, C#, Java, and YAML. Choose the language your team is most comfortable with. The IaC concepts are the same regardless of language — the language choice affects expressiveness and type safety. Python is the second most popular Pulumi language after TypeScript.

How do I handle secrets in IaC?

Never hardcode secrets in configuration files. Use dedicated secret management: Terraform Vault provider, Ansible Vault, Pulumi Secrets, or a secrets manager (AWS Secrets Manager, HashiCorp Vault). Reference secrets by name in your IaC code and resolve them at apply time. Implement secret rotation policies and audit access to secrets regularly.

What is the best IaC workflow for a team?

Use Git-based workflows with code review. Terraform: create a branch, edit HCL, run terraform plan, submit PR, merge, apply in CI/CD. Ansible: create a branch, edit playbooks, run --check in CI, merge, apply in CI/CD. Always review infrastructure changes before they reach production. Include policy checks and security scanning as automated gates in the CI pipeline.

Conclusion

IaC is the foundation of modern DevOps. Terraform excels at provisioning infrastructure across clouds, Ansible handles server configuration and application deployment, and Pulumi offers the flexibility of general-purpose programming languages. Start with one tool, master it, and add others as your needs grow. Store everything in version control, review changes, and automate deployments through CI/CD.

For practical next steps, see our CI/CD pipeline tools and monitoring and observability guide.

Section: DevOps 1648 words 8 min read Beginner 756 articles in section Report inaccuracy Back to top