For years, I saw a recurring debate in the DevOps community: “Should I use Terraform or Ansible?” It’s a false dichotomy. After managing dozens of production environments, I’ve found that the real magic happens when you stop treating them as competitors and start using them as a duo. If you’re wondering how to automate infrastructure with Ansible and Terraform, the secret lies in understanding the boundary between provisioning and configuration management.
In my experience, Terraform is the undisputed king of the “outer shell”—the networks, the load balancers, and the virtual machines. Ansible, however, is the master of the “inner life”—the packages, the users, and the application configs. When combined, you get a fully automated pipeline from a blank cloud account to a running production app.
Prerequisites
Before we dive into the code, ensure you have the following installed and configured in your local environment:
- Terraform: Installed and authenticated with your cloud provider (I’ll use AWS for this example).
- Ansible: Installed on your control node (Mac/Linux).
- SSH Key Pair: A public/private key pair that Terraform can inject into your instances.
- Cloud Account: An active AWS account with appropriate IAM permissions.
If you’re still deciding on your toolstack for containers, I recommend checking out my guide on the best IaC tools for Kubernetes to see where this fits into a larger orchestration strategy.
Step-by-Step Implementation
Step 1: Provisioning with Terraform
First, we define our infrastructure. I prefer using a modular approach to keep things clean. In my setup, I use a dedicated module for the VPC and another for the EC2 instances. If you’re scaling this, definitely follow terraform module best practices to avoid the “monolith state file” nightmare.
# main.tf
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
key_name = "my-ssh-key"
tags = {
Name = "Web-Server-Ansible"
Role = "webserver"
}
}
output "web_server_public_ip" {
value = aws_instance.web_server.public_ip
}
Run terraform apply to spin up your instance. At this point, you have a server, but it’s an empty shell.
Step 2: Bridging the Gap (The Dynamic Inventory)
The hardest part of learning how to automate infrastructure with Ansible and Terraform is handling the IP addresses. You shouldn’t hardcode IPs into your Ansible inventory. Instead, use a dynamic inventory or pass the output of Terraform to Ansible.
For simple setups, I use a local shell script to generate a hosts file. For enterprise setups, I use the terraform-inventory plugin. Here is the manual approach for clarity:
# Extract IP and add to Ansible inventory
terraform output -raw web_server_public_ip > hosts
```
Step 3: Configuring with Ansible
Now that the server exists and we have the IP, we use Ansible to turn that raw Linux box into a web server. I've found that using a playbook.yml is the most maintainable way to handle this.
---
- name: Configure Web Server
hosts: all
become: yes
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Start Nginx
service:
name: nginx
state: started
enabled: yes
Run the playbook with: ansible-playbook -i hosts playbook.yml. As shown in the architecture flow described earlier, Terraform handled the "where" and Ansible handled the "what".
Pro Tips for Production
- Use Terraform Local-Exec: If you want a one-click deployment, you can use a
local-execprovisioner in Terraform to trigger the Ansible playbook immediately after the instance is created. - State Management: Always store your Terraform state in an S3 bucket with DynamoDB locking to prevent state corruption during team collaborations.
- Ansible Vault: Never commit passwords or API keys in your playbooks. Use
ansible-vaultto encrypt sensitive data.
Troubleshooting Common Issues
In my experience, 90% of failures when combining these tools come from SSH connectivity. If your Ansible playbook hangs at "Gathering Facts", check these three things:
- Security Groups: Ensure your Terraform code opened port 22 (SSH) to your specific IP.
- SSH Key Permissions: Run
chmod 400 my-key.pem. Ansible will fail if your private key is too "open". - Usernames: Remember that AWS Ubuntu images use the user
ubuntu, while Amazon Linux usesec2-user. Specify this in your Ansible inventory:ansible_user=ubuntu.
What's Next?
Now that you've mastered the basics of how to automate infrastructure with Ansible and Terraform, the next step is moving toward Immutable Infrastructure. Instead of configuring servers on the fly, you can use HashiCorp Packer to create a pre-configured image (AMI) using Ansible, and then use Terraform to deploy that image. This reduces deployment time from minutes to seconds.
Ready to optimize your workflow? Start by refining your Terraform modules to handle multi-region deployments.