For years, I saw developers arguing over whether to use Terraform or Ansible. In my experience, this is the wrong question. The real magic happens when you stop treating them as competitors and start using them as a team. If you’re wondering how to automate infrastructure with Ansible and Terraform, the answer lies in understanding their distinct roles: Terraform handles the where (infrastructure), and Ansible handles the what (software and configuration).

In this tutorial, I’ll walk you through my preferred hybrid workflow. We’ll use Terraform to spin up a virtual machine on a cloud provider and then hand off the baton to Ansible to turn that raw machine into a fully functional web server. This approach allows you to follow terraform module best practices while maintaining the flexibility of agentless configuration management.

Prerequisites

Step 1: Provisioning Infrastructure with Terraform

First, we need a place for our code to live. I prefer using Terraform for this because its state management is superior for tracking physical resources. We’ll create a simple main.tf file to provision an EC2 instance.

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

resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  key_name      = "my-ssh-key"

  tags = {
    Name = "Ansible-Managed-Node"
  } 
} 

output "server_ip" {
  value = aws_instance.web_server.public_ip
}

Run terraform init and terraform apply. Once the process finishes, Terraform will output the public IP of your new server. This IP is the critical link we need for the next phase.

Step 2: Creating the Ansible Inventory

Ansible needs to know which servers to manage. While you can use static inventory files, I recommend using a dynamic approach or a simple hosts file for small projects. For this tutorial, we will create a hosts.ini file using the IP address provided by Terraform.

[webservers]
1.2.3.4 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

In larger setups, I usually automate this part using a Terraform local-exec provisioner or by exporting the IP to a JSON file that Ansible can read. If you’re scaling further, you might want to explore the best iac tools for kubernetes to handle orchestration at scale.

Step 3: Configuring the Server with Ansible

Now that the hardware is ready, we need to install our software. We’ll create a playbook called setup.yml to install Nginx and ensure it’s running. This is where the power of idempotency comes in—Ansible will only make changes if the server isn’t already in the desired state.

- name: Configure Web Server
  hosts: webservers
  become: yes
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Start Nginx service
      service:
        name: nginx
        state: started
        enabled: yes

As shown in the output we’ll get in the next step, Ansible will connect via SSH, elevate privileges using sudo, and ensure Nginx is live.

Step 4: Executing the Automation Pipeline

To bring it all together, I usually run these as two separate stages in a CI/CD pipeline. However, for local testing, you can run them sequentially:

# Provision the infra
terraform apply -auto-approve

# Run the configuration
ansible-playbook -i hosts.ini setup.yml

When you run the playbook, you’ll see a series of ok and changed messages. If you run it a second time, everything should show as ok, proving that your infrastructure is now stable and automated.

Terminal output showing successful Ansible playbook execution with changed and ok status
Terminal output showing successful Ansible playbook execution with changed and ok status

Pro Tips for Hybrid Automation

Troubleshooting Common Issues

Issue Cause Solution
SSH Connection Timeout AWS Security Group blocking port 22 Add an ingress rule for SSH from your IP in Terraform.
Ansible ‘Unreachable’ Instance still booting up Add a sleep timer or a wait-for-ssh task in your pipeline.
Sudo Permission Denied Wrong become setting Ensure become: yes is set in your playbook.

What’s Next?

Now that you’ve mastered the basics of how to automate infrastructure with Ansible and Terraform, I recommend looking into CI/CD integration. Try moving this workflow into GitHub Actions or GitLab CI. You can set it up so that a merge to the main branch triggers the Terraform apply and subsequently the Ansible playbook, creating a fully automated deployment pipeline.