Ansible AWS Cloud Management
Ansible's cloud automation capabilities extend well beyond configuring servers — it can provision the servers themselves, create the network infrastructure they run on, manage storage, configure security groups, and orchestrate complex cloud architectures. This lesson introduces AWS automation with Ansible, covering the modules that practitioners use most frequently and the patterns that keep cloud automation maintainable.
Prerequisites: AWS Collection and Credentials
# Install the AWS collection ansible-galaxy collection install amazon.aws # Install Python dependencies pip3 install boto3 botocore
Configure AWS credentials using any of these methods (in order of preference for automation):
# Environment variables (good for CI/CD) export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY export AWS_DEFAULT_REGION=us-east-1 # AWS credentials file (~/.aws/credentials) [default] aws_access_key_id = AKIAIOSFODNN7EXAMPLE aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
For production automation, use IAM roles attached to the control node EC2 instance rather than long-lived access keys. This eliminates credential management overhead and follows the principle of least privilege.
Provisioning EC2 Instances
---
- name: Provision AWS infrastructure
hosts: localhost
connection: local
gather_facts: false
vars:
region: us-east-1
instance_type: t3.micro
ami_id: ami-0c02fb55956c7d316 # Ubuntu 22.04 LTS us-east-1
key_name: my-key-pair
instance_count: 3
tasks:
- name: Create EC2 instances
amazon.aws.ec2_instance:
name: "webserver-{{ item }}"
key_name: "{{ key_name }}"
instance_type: "{{ instance_type }}"
image_id: "{{ ami_id }}"
region: "{{ region }}"
vpc_subnet_id: subnet-12345678
security_groups:
- web-sg
state: running
wait: true
tags:
Environment: production
Role: webserver
ManagedBy: Ansible
loop: "{{ range(1, instance_count + 1) | list }}"
register: ec2_instances
- name: Display instance IDs and IPs
debug:
msg: "{{ item.instance_id }}: {{ item.public_ip_address }}"
loop: "{{ ec2_instances.results | map(attribute='instances') | flatten }}"Managing Security Groups
- name: Create web server security group
amazon.aws.ec2_security_group:
name: web-sg
description: Security group for web servers
region: "{{ region }}"
vpc_id: vpc-12345678
rules:
- proto: tcp
ports: [80, 443]
cidr_ip: 0.0.0.0/0
rule_desc: Allow HTTP and HTTPS from anywhere
- proto: tcp
ports: [22]
cidr_ip: 10.0.0.0/8
rule_desc: Allow SSH from internal network only
rules_egress:
- proto: all
cidr_ip: 0.0.0.0/0
register: web_sgCreating VPC Infrastructure
- name: Create VPC
amazon.aws.ec2_vpc_net:
name: production-vpc
cidr_block: 10.0.0.0/16
region: "{{ region }}"
tags:
Environment: production
register: vpc
- name: Create public subnet
amazon.aws.ec2_vpc_subnet:
vpc_id: "{{ vpc.vpc.id }}"
cidr: 10.0.1.0/24
az: "{{ region }}a"
map_public: true
tags:
Name: public-subnet-a
register: public_subnetManaging S3 Buckets
- name: Create S3 bucket for application assets
amazon.aws.s3_bucket:
name: myapp-assets-{{ env }}
state: present
region: "{{ region }}"
versioning: true
encryption: AES256
tags:
Environment: "{{ env }}"
- name: Upload static assets
amazon.aws.s3_object:
bucket: myapp-assets-{{ env }}
object: "assets/{{ item | basename }}"
src: "{{ item }}"
mode: put
loop: "{{ lookup('fileglob', 'files/assets/*', wantlist=True) }}"Managing RDS Databases
- name: Create RDS MySQL instance
amazon.aws.rds_instance:
id: myapp-production-db
state: present
engine: mysql
engine_version: "8.0"
instance_type: db.t3.micro
master_username: admin
master_user_password: "{{ vault_rds_password }}"
db_name: myapp
allocated_storage: 20
storage_type: gp3
multi_az: true
backup_retention_period: 7
region: "{{ region }}"
tags:
Environment: production
register: rds_instance
no_log: trueInfrastructure State Management
Cloud provisioning playbooks need to handle the full lifecycle: create, update, and destroy. Ansible's cloud modules are idempotent — running the EC2 provisioning playbook on already-existing instances makes no changes. For destruction:
- name: Terminate EC2 instances (use with extreme caution)
amazon.aws.ec2_instance:
instance_ids: "{{ item }}"
state: absent
region: "{{ region }}"
loop: "{{ instance_ids_to_terminate }}"
when: teardown_environment | default(false) | boolThe when: teardown_environment guard prevents accidental destruction — the teardown only runs when explicitly passed as a variable: -e teardown_environment=true.
Combining Provisioning and Configuration
The most powerful pattern is a single playbook that provisions infrastructure and then immediately configures it:
---
# Play 1: Provision infrastructure (runs on localhost)
- name: Provision AWS infrastructure
hosts: localhost
connection: local
tasks:
- name: Create EC2 instances
amazon.aws.ec2_instance: ...
register: new_instances
- name: Add new instances to inventory for next play
add_host:
name: "{{ item.public_ip_address }}"
groups: newly_provisioned
ansible_user: ubuntu
ansible_ssh_private_key_file: ~/.ssh/my-key.pem
loop: "{{ new_instances.instances }}"
- name: Wait for SSH to become available
wait_for:
host: "{{ item.public_ip_address }}"
port: 22
timeout: 120
loop: "{{ new_instances.instances }}"
# Play 2: Configure the new instances
- name: Configure newly provisioned servers
hosts: newly_provisioned
become: true
roles:
- common
- nginx
- myappTry This: Provision and Configure a Server
Using your AWS Free Tier account, write a playbook that provisions one t2.micro Ubuntu EC2 instance with a security group allowing SSH and HTTP, waits for it to be reachable, and then configures it with your Nginx role from Topic 22. Run the full playbook and verify Nginx is serving a page. Then add a state: absent task and re-run to terminate the instance. This end-to-end exercise demonstrates the complete infrastructure lifecycle management capability that employers look for in DevOps candidates.
Summary
Ansible's AWS modules (from the amazon.aws collection) enable full infrastructure lifecycle management: provisioning EC2 instances, creating VPCs and security groups, managing S3 buckets, and provisioning RDS databases. All cloud modules are idempotent. Destruction operations should be guarded by explicit boolean conditions. The add_host module bridges provisioning and configuration plays, enabling complete infrastructure-as-code workflows in a single playbook run.
