1) Quick conceptual recap (one-liner)
• IaC = provision the house (networks, VMs, load-balancers, security groups).
• CM = arrange & maintain the furniture & appliances inside the house (install packages, configure services, keep things consistent).
2) Tools — choose what fits
• IaC: Terraform (multi-cloud), CloudFormation (AWS native), Pulumi (code-first).
• CM: Ansible (agentless, SSH), Puppet/Chef (agent model), SaltStack.
(Use Terraform + Ansible is a very common, practical combo.)
3) Hands-on step-by-step (provision with Terraform → configure with Ansible)
Step 3.1 — Prepare a project layout
project/
terraform/
main.tf
variables.tf
outputs.tf
ansible/
inventory.ini <-- generated from terraform output
playbook.yml
.github/workflows/ci.yml (optional CI)
Step 3.2 — Example Terraform (IaC) — terraform/main.tf
This provisions a security group + one EC2 instance and outputs its public IP.
# terraform/main.tf
provider "aws" {
region = var.region
}
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-ebs"]
}
}
resource "aws_security_group" "web_sg" {
name = "day54-web-sg"
description = "allow ssh & http"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
vpc_security_group_ids = [aws_security_group.web_sg.id]
key_name = var.ssh_key_name
tags = {
Name = "day54-web"
}
}
output "public_ip" {
value = aws_instance.web.public_ip
}
terraform/variables.tf (simple vars)
variable "region" { default = "us-east-1" }
variable "instance_type" { default = "t3.micro" }
variable "ssh_key_name" { type = string }
Commands
cd terraform
terraform init
terraform plan -out plan.tfplan
terraform apply "plan.tfplan" # or 'terraform apply -auto-approve' for demos
Step 3.3 — Generate Ansible inventory from Terraform output
Simplest approach (Terraform >=0.12 supports -raw output for single values):
cd .. # project root
TF_IP=$(terraform -chdir=terraform output -raw public_ip)
cat > ansible/inventory.ini <<EOF
[web]
$TF_IP ansible_user=ec2-user ansible_ssh_private_key_file=~/.ssh/<your-key>.pem
EOF
If -raw not available, use terraform output -json + jq.)
Step 3.4 — Example Ansible playbook (CM) — ansible/playbook.yml
Installs and starts nginx on the EC2 (idempotent tasks).
- name: Configure web server
hosts: web
become: true
tasks:
- name: Install nginx (Amazon Linux / RedHat)
package:
name: nginx
state: present
- name: Ensure nginx is started and enabled
service:
name: nginx
state: started
enabled: yes
- name: Deploy index.html
copy:
src: ../terraform/index.html # or templates
dest: /usr/share/nginx/html/index.html
owner: root
mode: '0644'
Run Ansible:
cd ansible
ansible-playbook -i inventory.ini playbook.yml \
--private-key ~/.ssh/<your-key>.pem -u ec2-user
cd ansible
ansible-playbook -i inventory.ini playbook.yml \
--private-key ~/.ssh/.pem -u ec2-user
4) Automate with CI/CD (recommended flow)
Typical pipeline:
1. Commit Terraform code → CI runs terraform plan → produce plan artifact.
2. Human review / PR approval.
3. CI runs terraform apply on main (or post-approval).
4. After apply, CI fetches outputs (IP(s)), generates inventory, and runs Ansible to configure servers.
Example (GitHub Actions snippet) — Terraform job + Ansible job:
# .github/workflows/infra.yml (shortened)
on: [push]
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with: terraform_version: '1.5.0'
- name: Terraform Init & Plan
run: |
cd terraform
terraform init
terraform plan -out plan.tfplan
# optionally apply only on main
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: |
cd terraform
terraform apply -auto-approve plan.tfplan
configure:
needs: terraform
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Ansible
run: sudo apt-get update && sudo apt-get install -y ansible
- name: Generate inventory & run ansible
run: |
cd terraform
TF_IP=$(terraform output -raw public_ip)
echo "[web]" > ../ansible/inventory.ini
echo "$TF_IP ansible_user=ec2-user ansible_ssh_private_key_file=/tmp/key.pem" >> ../ansible/inventory.ini
cd ../ansible
ansible-playbook -i inventory.ini playbook.yml
Use secrets for SSH keys (GitHub Actions Secrets), never commit keys to repo.
6) Best practices / dos & don’ts (short)
• IaC
• Use a remote backend for state (S3 + DynamoDB lock for Terraform).
• Keep state per environment (prod, staging).
• Modularize (reusable modules).
• Review terraform plan before apply.
• CM
• Make playbooks idempotent and small.
• Use variables / templates for environment differences.
• Avoid baking sensitive secrets into playbooks; use Vault/Secrets Manager.
• Security
• Least privilege IAM roles for CI systems.
• Don’t store private keys in source control; use secret stores.
• Testing
• Test IaC with unit/integration tools (Terratest/kitchen-terraform).
• Use linting: terraform validate, tflint, ansible-lint.
• Drift detection
• Periodically run terraform plan to detect drift. Use monitoring/alerts.
7) Quick commands cheat-sheet
• Terraform: terraform init, terraform plan -out plan.tfplan, terraform apply plan.tfplan, terraform destroy
• Ansible: ansible-playbook -i inventory.ini playbook.yml --private-key ~/.ssh/key.pem -u ec2-user
• Build inventory from TF: terraform output -raw public_ip (or parse JSON)
Top comments (0)