Skip to main content

Infrastructure as Code: Terraform and Ansible Guide

RAFSuNX
5 mins to read

Introduction

Infrastructure as Code (IaC) has become a foundational pillar in modern DevOps and cloud-native operations. As organizations scale and migrate toward microservices, containers, and multi-cloud environments, manual provisioning and configuration are no longer viable. IaC enables teams to define, provision, and manage infrastructure using code, ensuring consistency, versioning, automation, and repeatability. Two of the most widely adopted tools in this landscape are Terraform and Ansible.

Terraform excels at orchestrating infrastructure provisioning across cloud providers using a declarative language, while Ansible specializes in configuration management and automation using an imperative playbook-based approach. When combined, they deliver a powerful tooling stack that streamlines end-to-end infrastructure lifecycle management.

This blog will explore IaC principles, demonstrate real-world Terraform configurations for cloud infrastructure, illustrate Ansible’s role in post-provisioning configuration, cover best practices for versioning infrastructure, and present strategies for achieving fully reproducible and automated deployments.

Core Principles of Infrastructure as Code (IaC)

Modern infrastructure isn’t about manually launching VMs or clicking through UIs. IaC promotes control, scale, and efficiency.

Key principles include:

  • Declarative vs Imperative: Declarative tools (Terraform) describe what the infrastructure should be, while imperative tools (Ansible) define how to get there.
  • Idempotency: Multiple executions yield the same predictable result, reducing risk in repeat deployments.
  • Version Control: Entire infrastructure defined and managed in Git to allow collaboration, change tracking, and rollback.
  • Automation and CI/CD: Seamless integration into pipelines for safe, continuous delivery of infrastructure changes.

These principles enable stability, reliability, collaboration, and traceability in modern infrastructure operations.

Provisioning Cloud Resources with Terraform

Terraform, a tool by HashiCorp, uses HCL (HashiCorp Configuration Language) to create, manage, and update infrastructure across major cloud platforms.

Writing Terraform Configurations

A minimal set of files might look like:

provider.tf

provider "aws" {
  region = "us-east-1"
}

main.tf

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

  tags = {
    Name = "MyWebServer"
  }
}

Terraform Workflow

  1. terraform init - Initializes the working directory
  2. terraform plan - Previews changes
  3. terraform apply - Applies configuration
  4. terraform destroy - Tears down infrastructure

Modular Terraform Architecture

Break configuration into reusable modules:

module "network" {
  source     = "./modules/vpc"
  cidr_block = "10.0.0.0/16"
}

Best practices for structure:

  • main.tf - core resources
  • variables.tf - input parameters
  • outputs.tf - output values
  • provider.tf - cloud provider config

State Management & Version Control

Terraform uses a local or remote state file to track infrastructure changes. For team environments:

  • Store state remotely (S3 backend)
  • Lock state using DynamoDB
  • Encrypt state-at-rest
  • Limit access using IAM roles and policies

Example remote backend config:

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "env/dev/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-lock"
  }
}

Store Terraform code in Git using branches, tagging, and pull request reviews for controlled changes.

Configuration Management with Ansible

While Terraform builds resources, Ansible manages configuration after boot-up. Think “day two” operations: installing packages, setting permissions, deploying code.

What Is Ansible?

  • Agentless over SSH/WinRM
  • YAML-based playbooks
  • Idempotent automation
  • Strong inventory system, including dynamically from AWS, GCP, etc.

Ansible is ideal for tasks like:

  • Software installs & upgrades
  • OS configuration
  • Application deployments
  • Security hardening
  • Environment setup

Sample Playbook

- name: Install and start NGINX
  hosts: web
  become: true
  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Start nginx
      service:
        name: nginx
        state: started
        enabled: true

Best Practices with Ansible

  • Use roles to organize complex tasks
  • Maintain inventory dynamically
  • Secure secrets using Ansible Vault
  • Use handlers and tags to control execution
  • Test changes locally with Molecule

Orchestrating IaC Workflows: Terraform + Ansible

Terraform and Ansible play well together in structured workflows.

Scenario: Provision + Configure a Web Server

  1. Provision EC2 instance using Terraform
  2. Output IP to Ansible inventory
  3. Run Ansible to configure NGINX

Terraform Output

output "web_ip" {
  value = aws_instance.web.public_ip
}

Script to Glue the Tools

#!/bin/bash

# Provision infrastructure
terraform apply -auto-approve

# Extract IP for inventory
IP=$(terraform output -raw web_ip)
echo "[web]" > inventory.ini
echo "${IP} ansible_user=ubuntu" >> inventory.ini

# Run Ansible playbook
ansible-playbook -i inventory.ini playbooks/nginx.yml

This pipeline ensures consistent environment creation and post-configuration automation.

Reproducibility and Environment Parity

To prevent “works on my machine” issues:

  • Use parameterized modules for environment-specific values
  • Create separate workspaces (terraform workspace) or folders per environment
  • Pin versions in Terraform with required_providers and create a versions.tf file
  • Pin Ansible roles and use explicit collections

Example Folder Structure

/iac/
  └── terraform/
      ├── modules/
      └── envs/
          ├── dev/
          ├── staging/
          └── prod/
  └── ansible/
      ├── inventories/
          ├── dev/
          ├── prod/
      ├── roles/
      └── playbooks/

Track everything in Git and tag releases like code deployments (v1.0.0-infra).

Advanced Tips

Common Mistakes

  • Local Terraform state: Breaks in team environments. Always use remote backends.
  • Interleaved provisioning/configuration: Avoid logic mix. Terraform = infra; Ansible = config.
  • Missing version pins: Causes drift and unintended updates.
  • Skipping validation: Use terraform validate, tflint, ansible-lint

Troubleshooting: Common Issues & Fixes

Issue Cause Resolution
Terraform drift Manual changes outside code Use terraform plan regularly; audit differences
Ansible SSH timeout Instance not ready Add SSH wait logic or depend on AWS status check
Idempotency failure Bad Ansible task implementation Ensure proper state values and use conditionals
Dynamic inventory fails Missing AWS credentials Set up AWS plugin and export keys or use roles

Best Practices Checklist

  • Version infrastructure in Git
  • Use remote Terraform state with locking
  • Write reusable Terraform modules
  • Use Ansible roles and handlers
  • Extract variables to avoid hardcoding
  • Secure credentials using Vault or SSM
  • Integrate IaC into automated pipelines
  • Validate, lint, and test before deploying

Resources & Next Steps

Next Steps:

  1. Build a Terraform module repo and publish outputs.
  2. Add Ansible auto-configuration post-deploy hook.
  3. Secure and manage secrets with Vault or SSM.
  4. Set up CI/CD (GitHub Actions/GitLab/Jenkins) to automate.
  5. Apply monitoring and alerts using IaC (e.g., CloudWatch, Prometheus).

Conclusion

Managing modern infrastructure is impossible without automation and consistency. Infrastructure as Code with Terraform and Ansible allows teams to provision, version, and configure environments with speed and precision. Terraform handles provisioning predictably. Ansible configures reliably. Combined, they deliver automation excellence.

Key takeaways:

  • Terraform = Provisioning resource infrastructure declaratively
  • Ansible = Configuration and post-install automation
  • Versioning, testing, and remote state are essential for stability
  • Reproducible deployments improve consistency and scale
  • CI-CD pipelines empower teams to deliver infrastructure like software

Now is the time to bring software engineering discipline to systems engineering.

Happy coding!