Ansible on AWS
Refresher on Ansible for AWS provisioning and configuration. Playbooks, roles, dynamic inventory, and AWS modules.
Ansible Refresher
Ansible is an agentless automation tool for provisioning, configuration management, and application deployment. It uses SSH to connect to servers and YAML playbooks to define tasks. This module is a refresherfocused on using Ansible with AWS.
| Azure | AWS | Notes |
|---|---|---|
Ansible (azure.azcollection) | Ansible (amazon.aws) | Different collections, same tool |
Azure Resource Manager | Ansible + boto3 | Ansible uses boto3 SDK under the hood |
Custom Script Extension | User Data / Ansible | Ansible is more powerful and idempotent |
Azure Automation | SSM + Ansible | AWS Systems Manager can run Ansible playbooks |
Core Concepts Refresher
๐ Playbook
YAML file defining a series of tasks to execute on target hosts. The main unit of work.
๐ฆ Inventory
List of target hosts. Can be static (file) or dynamic (auto-discover from AWS API).
๐ญ Role
Reusable package of tasks, handlers, templates, and variables. Like a function for Ansible.
๐ง Module
Built-in commands (e.g., apt, copy, service). AWS modules: ec2_instance, rds_instance, s3_bucket.
๐ Handler
Tasks that run only when notified (e.g., restart nginx after config change). Prevents unnecessary restarts.
๐ Template (Jinja2)
Dynamic config files. Inject variables into templates before copying to hosts.
Setup
# Install Ansible + AWS collection
pip install ansible boto3 botocore
# Install the AWS collection
ansible-galaxy collection install amazon.aws
# Verify
ansible --version
ansible-galaxy collection list | grep amazon.awsAnsible is agentless โ it uses SSH to connect to targets. No agent installation needed on EC2 instances (unlike Chef/Puppet). For AWS API operations, it uses the boto3 Python SDK.
Demo 1: Provision EC2 + Configure with Ansible
Create an EC2 instance and configure it in one playbook:
---
- name: Provision EC2 Instance
hosts: localhost
connection: local
vars:
instance_type: t3.micro
region: us-east-1
key_name: aws-sandbox-key
tasks:
- name: Launch EC2 instance
amazon.aws.ec2_instance:
name: ansible-sandbox
instance_type: "{{ instance_type }}"
image_id: ami-0c02fb55956c7d316 # Amazon Linux 2023
key_name: "{{ key_name }}"
region: "{{ region }}"
security_group: aws-sandbox-ec2-sg
subnet_id: subnet-xxxxx
state: running
wait: true
tags:
Environment: sandbox
ManagedBy: ansible
register: ec2
- name: Add instance to inventory
add_host:
hostname: "{{ ec2.instances[0].public_ip_address }}"
groupname: webservers
ansible_user: ec2-user
ansible_ssh_private_key_file: ~/.ssh/aws-sandbox-key.pem
- name: Wait for SSH
wait_for:
host: "{{ ec2.instances[0].public_ip_address }}"
port: 22
delay: 10
timeout: 120
- name: Configure the EC2 Instance
hosts: webservers
become: true
tasks:
- name: Install Node.js
shell: |
curl -fsSL https://rpm.nodesource.com/setup_18.x | bash -
yum install -y nodejs
args:
creates: /usr/bin/node
- name: Install pm2 globally
npm:
name: pm2
global: true
- name: Create app directory
file:
path: /home/ec2-user/aws-sandbox-app
state: directory
owner: ec2-user
- name: Copy application files
synchronize:
src: ../app/
dest: /home/ec2-user/aws-sandbox-app/
- name: Install npm dependencies
npm:
path: /home/ec2-user/aws-sandbox-app
production: true
- name: Create environment file
template:
src: templates/env.j2
dest: /home/ec2-user/aws-sandbox-app/.env
owner: ec2-user
mode: '0600'
notify: restart app
- name: Start application with pm2
shell: |
cd /home/ec2-user/aws-sandbox-app
pm2 start server.js --name aws-sandbox-app
pm2 save
become_user: ec2-user
handlers:
- name: restart app
shell: pm2 restart aws-sandbox-app
become_user: ec2-user# Run the playbook
ansible-playbook ansible/provision-and-configure.yml
# Verify the app is running
curl http://<instance-public-ip>:3000/healthDemo 2: Deploy App with an Ansible Role
Structure the deployment as a reusable role:
ansible/
โโโ playbook.yml
โโโ inventory/
โ โโโ hosts
โโโ roles/
โโโ app-deploy/
โโโ tasks/
โ โโโ main.yml
โโโ handlers/
โ โโโ main.yml
โโโ templates/
โ โโโ env.j2
โโโ defaults/
โโโ main.yml---
- name: Ensure app directory exists
file:
path: "{{ app_dir }}"
state: directory
owner: "{{ app_user }}"
- name: Sync application code
synchronize:
src: "{{ playbook_dir }}/../app/"
dest: "{{ app_dir }}"
notify: restart app
- name: Install dependencies
npm:
path: "{{ app_dir }}"
production: true
- name: Deploy environment config
template:
src: env.j2
dest: "{{ app_dir }}/.env"
owner: "{{ app_user }}"
mode: '0600'
notify: restart app
- name: Run database migrations
shell: "cd {{ app_dir }} && npm run migrate"
become_user: "{{ app_user }}"
- name: Ensure app is running
shell: |
pm2 describe {{ app_name }} > /dev/null 2>&1 || \
pm2 start {{ app_dir }}/server.js --name {{ app_name }}
become_user: "{{ app_user }}"---
app_name: aws-sandbox-app
app_dir: /home/ec2-user/aws-sandbox-app
app_user: ec2-user
app_port: 3000
db_host: localhost
db_port: 5432
db_name: aws_sandbox
db_user: dbadmin
db_password: changeme# Deploy using the role
ansible-playbook -i inventory/hosts playbook.ymlDemo 3: Dynamic Inventory with AWS EC2 Plugin
Instead of listing IPs manually, let Ansible auto-discover your EC2 instances:
---
plugin: amazon.aws.ec2
regions:
- us-east-1
filters:
tag:Environment: sandbox
instance-state-name: running
keyed_groups:
# Group by the 'Name' tag
- key: tags.Name
prefix: tag_name
# Group by instance type
- key: instance_type
prefix: type
hostnames:
- private-ip-address # Use private IP (within VPC)
compose:
ansible_user: "'ec2-user'"
ansible_ssh_private_key_file: "'~/.ssh/aws-sandbox-key.pem'"# List discovered hosts
ansible-inventory -i inventory/aws_ec2.yml --list
# Ping all discovered instances
ansible -i inventory/aws_ec2.yml all -m ping
# Run a playbook against auto-discovered hosts
ansible-playbook -i inventory/aws_ec2.yml playbook.ymlAzure has a similar dynamic inventory plugin: azure.azcollection.azure_rm. Both auto-discover VMs/instances using tags and API queries. Same concept, different provider plugin.
Ansible + AWS Modules Quick Reference
| Module | Purpose | Example |
|---|---|---|
ec2_instance | Launch/manage EC2 | Create instances with tags |
ec2_security_group | Manage security groups | Create/update SG rules |
rds_instance | Manage RDS databases | Create PostgreSQL instances |
s3_bucket | Manage S3 buckets | Create buckets, set policies |
ec2_vpc_net | Manage VPCs | Create VPCs with CIDR blocks |
ec2_vpc_subnet | Manage subnets | Create public/private subnets |
elb_application_lb | Manage ALBs | Create load balancers |
iam_role | Manage IAM roles | Create roles with policies |
Key Takeaways
- Ansible is agentless โ uses SSH, no agents to install on targets
- Playbooks are idempotent โ run them repeatedly without side effects
- Roles make your automation modular and reusable across projects
- Dynamic inventory auto-discovers EC2 instances โ no manual IP management
- AWS modules (via
amazon.awscollection) can provision infrastructure too - Ansible complements Terraform: Terraform creates infra, Ansible configures apps