
Image by: Tara Winstead
What is infrastructure as code and why it matters
Did you know that 90% of cloud cost overruns stem from manual infrastructure management? Infrastructure as Code (IaC) revolutionizes how sysadmins and DevOps teams manage environments by treating servers, networks, and services as version-controlled code. This beginner-friendly guide demystifies implementing IaC using Ansible and Terraform – the dynamic duo for automating cloud infrastructure. You’ll learn to eliminate “snowflake servers” (those manually configured systems nobody dares touch) while achieving reproducible, auditable environments. According to Puppet’s 2022 State of DevOps Report, teams using IaC deploy 200x more frequently with 50% fewer failures. Here’s why this matters:
- Consistency: Define environments in code to avoid configuration drift
- Speed: Provision resources in minutes instead of days
- Risk reduction: Version control enables rollbacks and peer reviews
While Terraform specializes in provisioning cloud resources (like AWS EC2 instances or Azure VPCs), Ansible excels at configuring those resources post-creation. This powerful combination covers the full infrastructure lifecycle.
| Tool | Primary function | Language | State management | Cloud support |
|---|---|---|---|---|
| Terraform | Provisioning | Declarative (HCL) | State files | Multi-cloud |
| Ansible | Configuration | Imperative (YAML) | Agentless | Hybrid environments |
| CloudFormation | Provisioning | Declarative (JSON/YAML) | AWS-managed | AWS only |
| Puppet | Configuration | Declarative (Ruby DSL) | Agent-based | Multi-cloud |
“Infrastructure as Code is the bridge between traditional sysadmin roles and modern DevOps practices” – Kief Morris, Principal Cloud Technologist at ThoughtWorks
Getting started with Ansible: writing your first playbook
Ansible’s agentless architecture makes it ideal for beginners. Unlike Puppet or Chef, it requires no installed agents on managed nodes – just SSH access and Python. Start by installing Ansible on your control node:
sudo apt install ansible # Ubuntu/Debian brew install ansible # macOS
Creating your inventory
The inventory file defines your target servers. Create hosts.ini:
[web_servers] server1 ansible_host=192.168.1.10 server2 ansible_host=192.168.1.11 [all:vars] ansible_user=admin ansible_ssh_private_key_file=~/.ssh/id_rsa
Building a playbook
Let’s create nginx-install.yml that installs and configures Nginx:
---
- name: Configure web servers
hosts: web_servers
become: true
tasks:
- name: Install Nginx
apt:
name: nginx
state: latest
update_cache: yes
- name: Start and enable Nginx
service:
name: nginx
state: started
enabled: yes
- name: Copy custom index.html
copy:
src: files/index.html
dest: /var/www/html/index.html
Run it with ansible-playbook -i hosts.ini nginx-install.yml. This playbook demonstrates key Ansible features: idempotency (safe reruns), module-based tasks (apt, service, copy), and privilege escalation (become: true). For complex configurations, break playbooks into reusable roles.
Provisioning cloud resources with Terraform
Terraform’s HashiCorp Configuration Language (HCL) lets you define infrastructure components as code. Start by installing Terraform from official downloads. Configure AWS credentials (or other providers) using environment variables:
export AWS_ACCESS_KEY_ID="AKIAXXXX" export AWS_SECRET_ACCESS_KEY="YYYY"
Your first Terraform script
Create main.tf to provision an EC2 instance:
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0" # Ubuntu 20.04 LTS
instance_type = "t2.micro"
tags = {
Name = "Terraform-Example"
}
}
output "instance_ip" {
value = aws_instance.web_server.public_ip
}
Run terraform init to initialize the working directory, then terraform plan to preview changes. Apply with terraform apply. Terraform’s state file (terraform.tfstate) tracks resource mappings – never edit this manually! For team environments, use remote state storage like AWS S3.
Key Terraform concepts
- Providers: Plugins that interact with cloud APIs (AWS, Azure, GCP)
- Resources: Infrastructure components (servers, databases, networks)
- Modules: Reusable configurations (e.g., VPC modules)
- State: The blueprint of your deployed infrastructure
Integrating Ansible and Terraform in a CI/CD workflow
Combining Terraform (provisioning) and Ansible (configuration) creates a complete Infrastructure as Code pipeline. In a typical workflow:
- Terraform creates cloud resources
- Terraform outputs inventory data
- Ansible configures provisioned resources
Automated pipeline example
Here’s how to connect both tools in GitHub Actions:
name: IaC Pipeline
on: [push]
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Terraform Apply
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.3.7
run: |
terraform init
terraform apply -auto-approve
echo "public_ip=$(terraform output -raw instance_ip)" >> $GITHUB_ENV
ansible:
needs: terraform
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure server
uses: ansible/ansible-runner-action@v1
with:
playbook: nginx-install.yml
inventory: ${{ env.public_ip }},
This pipeline automatically provisions and configures infrastructure on code changes. For production, add approval gates and infrastructure testing.
Common pitfalls and how to avoid them
When adopting Infrastructure as Code, teams often encounter these challenges:
1. State file mismanagement
Mistake: Storing terraform.tfstate locally or in version control.
Solution: Use remote backends like AWS S3 with state locking via DynamoDB.
2. Hardcoded secrets
Mistake: Committing API keys or passwords in playbooks.
Solution: Use Ansible Vault or Terraform environment variables.
3. Ignoring idempotence
Mistake: Writing Ansible tasks that fail on reruns.
Solution: Always use official modules that support state parameters.
4. Monolithic codebases
Mistake: Single Terraform file managing all environments.
Solution: Adopt a workspace strategy with environment-specific variables.
5. Skipping testing
Mistake: Applying changes directly to production.
Solution: Implement Molecule for Ansible and Terratest for Terraform.
Frequently asked questions
Can Ansible replace Terraform for cloud provisioning?
While Ansible has cloud modules, Terraform excels at provisioning due to its state management and declarative approach. Ansible is better suited for configuration management on existing resources. For complex cloud topologies, Terraform’s dependency graph and resource tracking are superior.
How should I manage secrets in IaC?
Never store secrets in code. Use Terraform environment variables or secret managers like HashiCorp Vault. For Ansible, leverage ansible-vault to encrypt sensitive files. Cloud-native solutions like AWS Secrets Manager also integrate well with both tools.
What’s the fastest way to learn Terraform and Ansible?
Start with the official Terraform tutorials and Ansible’s getting started guide. Practice by rebuilding existing manual infrastructure as code. Join communities like r/devops on Reddit for real-world scenarios.
How do I handle Terraform state conflicts in teams?
Use remote state with locking. AWS S3 + DynamoDB prevents concurrent state modifications. Implement collaboration workflows with Terraform Cloud or Atlantis for automated plan/apply cycles via pull requests.
Conclusion
Implementing Infrastructure as Code with Ansible and Terraform transforms infrastructure management from error-prone manual processes into version-controlled, repeatable workflows. You’ve learned to write Ansible playbooks for configuration management, create Terraform scripts for cloud provisioning, integrate both in CI/CD pipelines, and dodge common pitfalls. Remember: IaC isn’t just about automation – it’s about creating self-documenting, auditable infrastructure that enables rapid innovation. Start small by converting one manual server to code today. Explore our ready-made IaC templates to accelerate your journey. What will you automate first?
