AWS Infrastructure Automation with Ansible: Complete Guide
Teach me Ansible |
2025-01-08 |
22 min read
Automate AWS infrastructure with Ansible. Learn to provision EC2 instances, configure VPCs, manage security groups, and deploy applications on AWS using Ansible's powerful cloud modules.
Why Use Ansible for AWS?
- Unified tool for both infrastructure provisioning and configuration
- No need to learn CloudFormation or Terraform syntax
- Idempotent operations ensure safe re-runs
- Easy integration with existing Ansible workflows
- Supports multi-cloud environments
Prerequisites
# Install AWS collections
ansible-galaxy collection install amazon.aws community.aws
# Install boto3 (AWS SDK for Python)
pip install boto3 botocore
# Configure AWS credentials
export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
export AWS_DEFAULT_REGION=us-east-1
Example 1: Create VPC and Subnet
---
- name: Create AWS VPC Infrastructure
hosts: localhost
gather_facts: no
vars:
vpc_cidr: "10.0.0.0/16"
subnet_cidr: "10.0.1.0/24"
region: "us-east-1"
tasks:
- name: Create VPC
amazon.aws.ec2_vpc_net:
name: "my-vpc"
cidr_block: "{{ vpc_cidr }}"
region: "{{ region }}"
tags:
Environment: Production
register: vpc
- name: Create Internet Gateway
amazon.aws.ec2_vpc_igw:
vpc_id: "{{ vpc.vpc.id }}"
region: "{{ region }}"
tags:
Name: "my-igw"
register: igw
- name: Create Subnet
amazon.aws.ec2_vpc_subnet:
vpc_id: "{{ vpc.vpc.id }}"
cidr: "{{ subnet_cidr }}"
az: "{{ region }}a"
region: "{{ region }}"
tags:
Name: "my-subnet"
register: subnet
- name: Create Route Table
amazon.aws.ec2_vpc_route_table:
vpc_id: "{{ vpc.vpc.id }}"
region: "{{ region }}"
tags:
Name: "my-route-table"
subnets:
- "{{ subnet.subnet.id }}"
routes:
- dest: "0.0.0.0/0"
gateway_id: "{{ igw.gateway_id }}"
Example 2: Launch EC2 Instances
---
- name: Launch EC2 Instances
hosts: localhost
gather_facts: no
vars:
instance_type: "t2.micro"
ami_id: "ami-0c55b159cbfafe1f0" # Amazon Linux 2
key_name: "my-key-pair"
region: "us-east-1"
tasks:
- name: Create Security Group
amazon.aws.ec2_security_group:
name: "web-server-sg"
description: "Security group for web servers"
region: "{{ region }}"
rules:
- proto: tcp
ports: 80
cidr_ip: 0.0.0.0/0
rule_desc: "Allow HTTP"
- proto: tcp
ports: 443
cidr_ip: 0.0.0.0/0
rule_desc: "Allow HTTPS"
- proto: tcp
ports: 22
cidr_ip: 0.0.0.0/0
rule_desc: "Allow SSH"
register: security_group
- name: Launch EC2 instances
amazon.aws.ec2_instance:
name: "web-server-{{ item }}"
instance_type: "{{ instance_type }}"
image_id: "{{ ami_id }}"
key_name: "{{ key_name }}"
region: "{{ region }}"
security_group: "{{ security_group.group_id }}"
tags:
Environment: Production
Role: WebServer
wait: yes
loop: [1, 2, 3]
register: ec2_instances
- name: Display instance IPs
debug:
msg: "Instance {{ item.instance_id }}: {{ item.public_ip_address }}"
loop: "{{ ec2_instances.results | map(attribute='instances') | flatten }}"
Example 3: Configure Launched Instances
---
- name: Configure EC2 Instances
hosts: tag_Role_WebServer
become: yes
gather_facts: yes
tasks:
- name: Update system packages
yum:
name: '*'
state: latest
- name: Install web server
yum:
name:
- httpd
- php
state: present
- name: Start and enable Apache
service:
name: httpd
state: started
enabled: yes
- name: Deploy application
copy:
content: |
dest: /var/www/html/index.php
Example 4: Create Application Load Balancer
---
- name: Create Application Load Balancer
hosts: localhost
gather_facts: no
tasks:
- name: Create Target Group
community.aws.elb_target_group:
name: "web-servers-tg"
protocol: http
port: 80
vpc_id: "{{ vpc_id }}"
health_check_path: "/"
health_check_interval: 30
region: "{{ region }}"
register: target_group
- name: Create Application Load Balancer
amazon.aws.elb_application_lb:
name: "web-alb"
region: "{{ region }}"
subnets:
- "{{ subnet1_id }}"
- "{{ subnet2_id }}"
security_groups:
- "{{ alb_security_group_id }}"
listeners:
- Protocol: HTTP
Port: 80
DefaultActions:
- Type: forward
TargetGroupArn: "{{ target_group.target_group_arn }}"
register: alb
- name: Register instances with target group
community.aws.elb_target:
target_group_arn: "{{ target_group.target_group_arn }}"
target_id: "{{ item.instance_id }}"
region: "{{ region }}"
state: present
loop: "{{ ec2_instances }}"
Example 5: S3 Bucket Management
---
- name: Manage S3 Buckets
hosts: localhost
gather_facts: no
tasks:
- name: Create S3 bucket
amazon.aws.s3_bucket:
name: "my-app-static-files"
region: "{{ region }}"
versioning: yes
encryption: "AES256"
tags:
Environment: Production
- name: Upload files to S3
amazon.aws.s3_object:
bucket: "my-app-static-files"
object: "{{ item.key }}"
src: "{{ item.src }}"
mode: put
loop:
- { key: "css/style.css", src: "/local/path/style.css" }
- { key: "js/app.js", src: "/local/path/app.js" }
- name: Set bucket policy for public read
amazon.aws.s3_bucket:
name: "my-app-static-files"
region: "{{ region }}"
policy:
Version: "2012-10-17"
Statement:
- Sid: "PublicReadGetObject"
Effect: "Allow"
Principal: "*"
Action: "s3:GetObject"
Resource: "arn:aws:s3:::my-app-static-files/*"
Example 6: RDS Database Provisioning
---
- name: Create RDS MySQL Database
hosts: localhost
gather_facts: no
tasks:
- name: Create RDS subnet group
amazon.aws.rds_subnet_group:
name: "my-db-subnet-group"
description: "Subnet group for RDS"
region: "{{ region }}"
subnets:
- "{{ subnet1_id }}"
- "{{ subnet2_id }}"
- name: Create RDS instance
amazon.aws.rds_instance:
db_instance_identifier: "my-database"
engine: mysql
engine_version: "8.0"
instance_type: "db.t3.micro"
allocated_storage: 20
master_username: "admin"
master_user_password: "{{ db_password }}"
db_subnet_group_name: "my-db-subnet-group"
vpc_security_group_ids:
- "{{ db_security_group_id }}"
backup_retention_period: 7
region: "{{ region }}"
register: rds
- name: Wait for RDS to be available
amazon.aws.rds_instance_info:
db_instance_identifier: "my-database"
region: "{{ region }}"
register: rds_info
until: rds_info.instances[0].db_instance_status == "available"
retries: 30
delay: 60
Dynamic Inventory for AWS
Use AWS EC2 dynamic inventory to automatically discover instances:
aws_ec2.yml (Inventory Plugin)
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
- us-west-2
filters:
instance-state-name: running
keyed_groups:
- key: tags.Environment
prefix: env
- key: tags.Role
prefix: role
hostnames:
- ip-address
compose:
ansible_host: public_ip_address
Use it:
# List discovered hosts
ansible-inventory -i aws_ec2.yml --graph
# Run playbook against discovered hosts
ansible-playbook -i aws_ec2.yml site.yml
Best Practices for AWS Automation
- Use IAM Roles - Assign IAM roles to EC2 instances instead of embedding credentials
- Tag Everything - Use consistent tagging for resource management
- Use Variables - Store AMI IDs, regions, and configurations in variables
- Implement State Management - Save resource IDs for reuse (registered variables)
- Use Ansible Vault - Encrypt sensitive data like database passwords
- Test in Staging - Always test infrastructure changes in non-production first
- Use Check Mode - Run with --check before making changes
Complete Infrastructure Example
Combine everything into a complete stack:
---
- name: Deploy Complete AWS Infrastructure
hosts: localhost
gather_facts: no
roles:
- aws_vpc
- aws_security_groups
- aws_ec2_instances
- aws_load_balancer
- aws_rds
- aws_s3
- name: Configure Launched Instances
hosts: tag_Environment_Production
become: yes
roles:
- common
- nginx
- app_deployment
Cost Warning
Remember that AWS resources incur costs! Always terminate resources when done testing:
ansible-playbook teardown.yml
Conclusion
Ansible provides powerful AWS automation capabilities without needing to learn CloudFormation or Terraform. Start small, test thoroughly, and gradually build more complex infrastructures.