Terraform and Ansible Working Together workflow
Terraform and Ansible Working Together workflow
Terraform and Ansible together create a complete infrastructure automation workflow. Terraform is responsible for provisioning cloud infrastructure such as EC2 instances, networking components, and security groups on platforms like Amazon Web Services. Once the infrastructure is ready, Ansible takes over to configure the system by installing software, managing dependencies, setting up environments, and deploying applications. This separation of responsibilities ensures clean architecture and better maintainability. Terraform manages infrastructure state, while Ansible handles configuration management in an idempotent way. By combining both tools, we achieve scalable, repeatable, and production-ready automation. This layered approach reflects real-world DevOps practices used in modern cloud environments.
Terraform → creates EC2
Ansible → installs and configures everything inside EC2
Terraform → Creates infrastructure
Ansible → Configures servers
Docker → Packages application
Kubernetes → Runs containers at scale
🔎 Summary of Your Analysis — Layered DevOps Approach
When building and deploying an application, different tools operate at different architectural layers, each solving a specific problem. First, infrastructure is required to run the application, and this is where Terraform comes in — it provisions cloud resources such as EC2, networking, and storage as Infrastructure as Code on platforms like Amazon Web Services. Once the infrastructure exists, configuration is needed to install dependencies, system packages, and application software, which is handled by Ansible. After the application is properly configured and running, Docker ensures consistency by packaging the application and its dependencies into a portable container image so it behaves the same across environments. Finally, Kubernetes manages and orchestrates those containers, handling scaling, self-healing, load balancing, and high availability.
All these tools operate at different layers — infrastructure, configuration, packaging, and orchestration — and together they form a complete, structured DevOps deployment ecosystem.
In this project, we first used Terraform to provision an EC2 instance on Amazon Web Services, defining the infrastructure as code instead of manually creating resources through the console. Once the EC2 instance was successfully created, we leveraged Ansible to configure that server by installing Python, setting up a virtual environment, and deploying a simple Django application. Terraform handled the infrastructure layer, while Ansible managed the post-provisioning configuration inside the EC2 instance. This clear separation of responsibilities makes the system modular, scalable, and easier to maintain. By combining both tools, we automated the complete workflow — from infrastructure creation to application deployment. This layered DevOps approach reflects real-world cloud practices, where infrastructure provisioning and configuration management work together to deliver production-ready applications efficiently.
🔥 Why Not Use Terraform for This?
Terraform can create infrastructure, but:
- It is not designed for continuous configuration
- It is not ideal for installing software repeatedly
- It does not manage application state
Ansible:
- Is idempotent
- Handles configuration management
- Automates OS-level tasks
- Works perfectly post-provisioning
📌 Final Architecture
Infrastructure Layer → Terraform
Configuration Layer → Ansible
Application Layer → Django🎯 Conclusion
- Infrastructure provisioning and configuration management are two different responsibilities.
- Terraform builds the infrastructure.
Ansible prepares it to run applications.
Together, they create a powerful DevOps automation workflow.
- If you are deploying directly on EC2 virtual machines, use Ansible for configuration.
- If you are deploying containerized applications using Docker and Kubernetes, you generally do not need Ansible for application setup.
- Terraform is always relevant because infrastructure must exist in both cases.
Phase 1 (Understand Configuration Management)
Terraform → EC2
Ansible → Install DjangoThis teaches:
- SSH automation
- Server configuration
- Idempotency
- Infrastructure layering
Phase 2 (Modern Cloud-Native)
Terraform → Create EKS
Docker → Build image
Kubernetes → Deploy appThis teaches:
- Immutable infrastructure
- Containerization
- Orchestration
- Production scaling
There are two models:
1️⃣ Mutable Infrastructure (VM-based)
- Create server
- Install things inside it
- Modify over time
- Uses Ansible
2️⃣ Immutable Infrastructure (Container-based)
- Build image once
- Never modify server
- Replace containers when needed
- No manual configuration
- Uses Docker + Kubernetes
If you use EC2 directly:
- Yes — Terraform creates VM
- Yes — you will need Ansible (or user-data scripts) to install things
If you use EKS:
- Terraform creates cluster
- Docker contains all dependencies
- Kubernetes runs containers
- No need for Ansible for app installation
Terraform = Infrastructure provisioning
Ansible = Configuration inside servers
Docker = Package app with dependencies
Kubernetes = Orchestrate containers
let’s summarize this clearly in a clean paragraph form —
In the traditional deployment approach, Terraform is used to provision infrastructure such as EC2 instances on Amazon Web Services, and then Ansible is used to configure those virtual machines by installing required software like Python, Nginx, Docker, and deploying the application. This model is server-based, meaning we directly manage and configure the virtual machines after they are created.
In the container-based (cloud-native) approach, Terraform provisions infrastructure such as an EKS cluster, Docker is used to build an application image that already contains all dependencies, and Kubernetes handles container orchestration and deployment. In this case, we do not manually configure servers because the application and its dependencies are packaged inside the Docker image, making Ansible generally unnecessary for application setup.
In short:
Traditional model = Terraform + Ansible (server configuration).
Cloud-native model = Terraform + Docker + Kubernetes/EKS (container orchestration).
Terraform + Ansible + AWS (Better Practice)
Flow:
Terraform → Creates EC2 Ansible → Connects to EC2 → Installs everything automatically
Get Pranav Bakare’s stories in your inbox
Join Medium for free to get updates from this writer.
Architecture:
Terraform → EC2 instance
Ansible → Configure EC2 (Python, pip, Gunicorn, Nginx, pull code)
Django app runs
S3 stores media
RDS stores databaseThis is clean separation of responsibilities:
- Terraform = Infrastructure Provisioning
- Ansible = Configuration Management
- EC2 = Compute
- S3 = Storage
- RDS = Database
- Terraform folder → contains all AWS infrastructure definitions (VPC, EC2, security groups, ELB, etc.)
- Ansible folder → contains inventory and playbooks. It does not need to be inside Terraform folder, but it must reference Terraform outputs if you want dynamic IPs.
Step 2: Correct command
If your .pem key is in the current folder, copy it to your WSL home with:
cp terraform-key.pem ~/terraform-key.pemOr using the full path:
cp /mnt/c/Terraform/terrfaform_ansible/terraform-key.pem ~/terraform-key.pem📌 Tiny Diagram: Terraform + Ansible Workflow
┌──────────────────────┐
│ Control Machine │ <- Your Windows PC / PowerShell / WSL
│----------------------│
│ C:\Terraform │ <- Terraform files
│ - main.tf │
│ - variables.tf │
│ - outputs.tf │
│ │
│ C:\Ansible │ <- Ansible files
│ - hosts.ini │ <- Inventory (EC2 IPs)
│ - setup.yml │ <- Playbook
└─────────┬────────────┘
│ (1) Run Terraform -> create EC2 in AWS
▼
┌──────────────────────┐
│ AWS / EC2 Instance │ <- Ubuntu EC2
│----------------------│
│ Private IP: 172.31..│
│ Public IP: 13.126.. │
│ Terraform-key.pem │ <- SSH key used by Ansible
└─────────▲────────────┘
│ (2) Ansible connects via SSH
│ and executes playbook tasks
▼
┌──────────────────────┐
│ Tasks executed on │
│ EC2 instance: │
│ - Update packages │
│ - Install Git │
│ - Create directories│
└──────────────────────┘- Terraform folder → contains all AWS infrastructure definitions (VPC, EC2, security groups, ELB, etc.)
- Ansible folder → contains inventory and playbooks. It does not need to be inside Terraform folder, but it must reference Terraform outputs if you want dynamic IPs.
Step 0: Prerequisites
- You have Terraform installed on your machine.
- You have Ansible installed on your machine (WSL Ubuntu or Windows).
- You have your AWS key file (
Terraform-key.pem). - You have created Terraform folder (
C:\Terraform) with your EC2 config. - You have created Ansible folder (
C:\Ansible) where playbooks will reside.
Step 1: Terraform creates the EC2 instance
- Go to Terraform folder:
cd C:\Terraform- Initialize Terraform:
terraform init- Apply Terraform configuration:
terraform apply- Type
yeswhen prompted. - Terraform will create:
- EC2 instance
- Security group allowing SSH
- Any other resources you defined
- Output the EC2 IPs:
terraform output ec2_public_ip
terraform output ec2_private_ip- ✅ Now you have your EC2 public/private IP.
Step 2: Create Ansible inventory and playbooks (on your control machine)
Important: All these files live on your control machine, not on EC2.
- Go to Ansible folder:
cd C:\Ansible- Create
hosts.ini(inventory) dynamically using the Terraform output:
echo [ec2_instances] > hosts.ini
echo my_ec2 ansible_host=<EC2_PUBLIC_IP> ansible_user=ubuntu ansible_ssh_private_key_file=C:\path\to\Terraform-key.pem >> hosts.iniReplace
<EC2_PUBLIC_IP>with the value from Terraform output.
- Create
setup.ymlplaybook:
@"
---
- name: Basic EC2 Setup
hosts: ec2_instances
become: yes
gather_facts: true
tasks:
- name: Update apt packages
ansible.builtin.apt:
update_cache: yes
- name: Install Git
ansible.builtin.apt:
name: git
state: present
- name: Create demo directory
ansible.builtin.file:
path: /home/ubuntu/demo
state: directory
mode: '0755'
"@ > setup.ymlStep 3: Test connectivity with Ansible
- Test SSH connection via Ansible:
ansible -i hosts.ini ec2_instances -m ping✅ If successful, you’ll see:
my_ec2 | SUCCESS => {
"changed": false,
"ping": "pong"
}- No manual SSH is required. Ansible uses the
.pemkey to connect.
Step 4: Run the playbook
- Execute the tasks on the EC2:
ansible-playbook -i hosts.ini setup.yml- Tasks in
setup.ymlrun on the EC2 instance: - Updates packages
- Installs Git
- Creates
/home/ubuntu/demodirectory
Step 5: Optional manual SSH (inspection / troubleshooting)
- Only needed if you want to check the EC2 state:
ssh -i ~/terraform-key.pem ubuntu@13.201.3.241- Here, you can manually check installed packages or created directories.
Step 6: How the components interact (Flow)
Control Machine (Windows / WSL)
┌─────────────────────────────────────┐
│ - Terraform folder: creates EC2 │
│ - Ansible folder: hosts.ini + setup.yml │
│ - Run ansible-playbook │
└─────────────┬───────────────────────┘
│ SSH connection (Ansible)
▼
AWS EC2 Instance (Ubuntu)
┌─────────────────────────────────────┐
│ - Receives commands from Ansible │
│ - Executes tasks: │
│ - Update packages │
│ - Install Git │
│ - Create directories │
└─────────────────────────────────────┘Key points:
- Terraform → EC2 creation + IP output
- Ansible files created on control machine
- Ansible uses SSH → EC2 runs tasks
- No need to SSH manually before creating Ansible files
- Manual SSH is optional for inspection only
Visual Text Diagram of the Flow
Windows PC (Host Machine)
│
├── C:\Terraform
│ ├── terraform_ansible <-- Terraform files (main.tf, variables.tf, outputs.tf)
│ └── Ansible <-- Optional editing folder for Ansible files (hosts.ini, setup.yml)
│
└── WSL (Ubuntu on Windows)
├── /home/pranav
│ └── Terraform-key.pem <-- Copy of private key with chmod 600
│
└── /home/pranav/Terraform/Ansible
├── hosts.ini <-- Inventory file pointing to EC2 public IP and WSL key
└── setup.yml <-- Playbook (update packages, install Git, create dirs)
Flow of Actions / Workflow:
1️⃣ Terraform Execution (Windows PowerShell or WSL)
- `terraform init`
- `terraform plan`
- `terraform apply`
→ Creates EC2 instance
→ Outputs Public IP (e.g., 13.201.71.143)
2️⃣ Copy Private Key into WSL
- `cp /mnt/c/Terraform/terrfaform_ansible/Terraform-key.pem ~/Terraform-key.pem`
- `chmod 600 ~/Terraform-key.pem`
3️⃣ Create Ansible Inventory
- File: `hosts.ini`
- Content:
[ec2_instances]
my_ec2 ansible_host=13.201.71.143 ansible_user=ubuntu ansible_ssh_private_key_file=/home/pranav/Terraform-key.pem ansible_ssh_common_args='-o StrictHostKeyChecking=no'
4️⃣ Create Ansible Playbook
- File: `setup.yml`
- Tasks:
- Update apt packages
- Install Git
- Create demo directory `/home/ubuntu/demo`
5️⃣ Test Connection
- Command: `ansible -i hosts.ini ec2_instances -m ping`
- Expected Output: `"ping": "pong"`
6️⃣ Run Playbook
- Command: `ansible-playbook -i hosts.ini setup.yml`
- Ansible connects via SSH using the key in WSL
- Executes tasks on the EC2 instance
7️⃣ Verify on EC2 (Optional)
- `ssh -i ~/Terraform-key.pem ubuntu@13.201.71.143`
- Check directory or installed packagesKey Takeaways
- Terraform → creates EC2 → gives public IP
- Private Key → must be in WSL (
/home/pranav) for proper permissions - Ansible → uses inventory to SSH → executes tasks on EC2
- Hosts.ini & playbooks → can exist in Windows folder for editing, but execution works in WSL
If you want, I can also make a tiny simplified visual arrow diagram showing it like:
Terraform → EC2
^
|
Ansible (WSL)1️⃣ Run your playbook
From your WSL Ubuntu terminal:
cd ~/Terraform/Ansible
ansible-playbook -i hosts.ini setup.yml- You’ll see output like:
TASK [Update apt packages] ***********
ok: [my_ec2]
TASK [Install Git] *******************
changed: [my_ec2]
TASK [Create demo directory] *********
changed: [my_ec2]ok→ nothing changed (already in desired state)changed→ task executed and updated EC2
2️⃣ Verify the tasks directly on EC2
SSH into the EC2 instance:
ssh -i ~/Terraform-key.pem ubuntu@13.201.71.143Check the results
- Directory created:
ls -l /home/ubuntu/demo- You should see the
demodirectory.
- Git installed:
git --version- Should return the installed Git version, e.g.,
git version 2.25.1.
- Packages updated (check last update time):
ls -lt /var/lib/apt/periodic/ | head- Confirms that the update task ran.
3️⃣ Optional: Run Ansible ad-hoc commands to verify
You can also check from WSL without SSHing manually:
# Check if Git is installed
ansible -i hosts.ini ec2_instances -m command -a "git --version"
# Check if directory exists
ansible -i hosts.ini ec2_instances -m command -a "ls -l /home/ubuntu/demo"- Output should confirm that Git exists and
demodirectory is there.
✅ Using either method (direct SSH or Ansible ad-hoc), you can verify that your playbook changes actually applied to the EC2 instance.










