Module 7

Ansible on AWS

Refresher on Ansible for AWS provisioning and configuration. Playbooks, roles, dynamic inventory, and AWS modules.

PlaybooksRolesAWS ModulesDynamic Inventory

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.

AzureAWSNotes
Ansible (azure.azcollection)Ansible (amazon.aws)Different collections, same tool
Azure Resource ManagerAnsible + boto3Ansible uses boto3 SDK under the hood
Custom Script ExtensionUser Data / AnsibleAnsible is more powerful and idempotent
Azure AutomationSSM + AnsibleAWS 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

bash
# 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.aws
๐Ÿ“˜ Key Concept

Ansible 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:

yamlansible/provision-and-configure.yml
---
- 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
bash
# Run the playbook
ansible-playbook ansible/provision-and-configure.yml

# Verify the app is running
curl http://<instance-public-ip>:3000/health

๐Ÿงช

Demo 2: Deploy App with an Ansible Role

Structure the deployment as a reusable role:

text
ansible/
โ”œโ”€โ”€ playbook.yml
โ”œโ”€โ”€ inventory/
โ”‚   โ””โ”€โ”€ hosts
โ””โ”€โ”€ roles/
    โ””โ”€โ”€ app-deploy/
        โ”œโ”€โ”€ tasks/
        โ”‚   โ””โ”€โ”€ main.yml
        โ”œโ”€โ”€ handlers/
        โ”‚   โ””โ”€โ”€ main.yml
        โ”œโ”€โ”€ templates/
        โ”‚   โ””โ”€โ”€ env.j2
        โ””โ”€โ”€ defaults/
            โ””โ”€โ”€ main.yml
yamlroles/app-deploy/tasks/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 }}"
yamlroles/app-deploy/defaults/main.yml
---
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
bash
# Deploy using the role
ansible-playbook -i inventory/hosts playbook.yml

๐Ÿงช

Demo 3: Dynamic Inventory with AWS EC2 Plugin

Instead of listing IPs manually, let Ansible auto-discover your EC2 instances:

yamlansible/inventory/aws_ec2.yml
---
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'"
bash
# 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.yml
โ˜๏ธ Azure Parallel

Azure 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

ModulePurposeExample
ec2_instanceLaunch/manage EC2Create instances with tags
ec2_security_groupManage security groupsCreate/update SG rules
rds_instanceManage RDS databasesCreate PostgreSQL instances
s3_bucketManage S3 bucketsCreate buckets, set policies
ec2_vpc_netManage VPCsCreate VPCs with CIDR blocks
ec2_vpc_subnetManage subnetsCreate public/private subnets
elb_application_lbManage ALBsCreate load balancers
iam_roleManage IAM rolesCreate 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.aws collection) can provision infrastructure too
  • Ansible complements Terraform: Terraform creates infra, Ansible configures apps