DevOps: Using Ansible to provision AWS EC2 instances

A practical way to provision instances on Amazon Web Service EC2 with Ansible.

The following steps will be performed along the article to demonstrate the power around the integration of Ansible and AWS Cloud:

  • Create AWS user
  • Install Ansible and Ansible EC2 module dependencies
  • Create SSH keys
  • Create Ansible structure
  • Run Ansible to provision the EC2 instance
  • Connect to the EC2 instance via SSH

Open the AWS Console, search for IAM (Identity and Access Management) and follow this steps to create a user and take note of the Access Key and Secret Key that will be used by Ansible to set up the instances.

sudo apt install python
sudo apt install python-pip
pip install boto boto3 ansible
ssh-keygen -t rsa -b 4096 -f ~/.ssh/my_aws

Create the Ansible directory structure

mkdir -p AWS_Ansible/group_vars/all/
cd AWS_Ansible
touch playbook.yml

Create Ansible Vault file to store the AWS Access and Secret keys.

ansible-vault create group_vars/all/pass.yml
New Vault password:
Confirm New Vault password:

The password provided here will be asked every time the playbook is executed or when editing the pass.yml file.

This article will follow the approach above, however, if you don’t want to provide the password every time, an insecure approach can create the pass.yml file by specifying a hashed password file:

openssl rand -base64 2048 > vault.pass

ansible-vault create group_vars/all/pass.yml --vault-password-file vault.pass

With hashed password file you must specify the vault-password-file argument when running Ansible playbook and won’t be asked for the password:

ansible-playbook playbook.yml --vault-password-file vault.pass

Create the variables ec2_access_key and ec2_secret_key and set the values gathered after user creation (IAM).

ansible-vault edit group_vars/all/pass.yml 
Vault password:
ec2_access_key: AAAAAAAAAAAAAABBBBBBBBBBBB                                      
ec2_secret_key: afjdfadgf$fgajk5ragesfjgjsfdbtirhf
➜  AWS_Ansible tree
.
├── group_vars
│   └── all
│       └── pass.yml
└── playbook.yml2 directories, 2 files

# AWS playbook

– hosts: localhost
connection: local
gather_facts: False

vars:
key_name: my_aws
region: us-east-2
image: ami-0f93b5fd8f220e428 # https://cloud-images.ubuntu.com/locator/ec2/
id: “web-app”
sec_group: “{{ id }}-sec”

tasks:
– name: Facts
block:
– name: Get instances facts
ec2_instance_facts:
aws_access_key: “{{ec2_access_key}}”
aws_secret_key: “{{ec2_secret_key}}”
region: “{{ region }}”
register: result
– name: Instances ID
debug:
msg: “ID: {{ item.instance_id }} – State: {{ item.state.name }} – Public DNS: {{ item.public_dns_name }}”
loop: “{{ result.instances }}”

tags: always

– name: Provisioning EC2 instances
block:

– name: Upload public key to AWS
ec2_key:
name: “{{ key_name }}”
key_material: “{{ lookup(‘file’, ‘/Users/julianosilva/.ssh/{{ key_name }}.pub’) }}”
region: “{{ region }}”
aws_access_key: “{{ec2_access_key}}”
aws_secret_key: “{{ec2_secret_key}}”

– name: Create security group
ec2_group:
name: “{{ sec_group }}”
description: “Sec group for app {{ id }}”
# vpc_id: 12345
region: “{{ region }}”
aws_access_key: “{{ec2_access_key}}”
aws_secret_key: “{{ec2_secret_key}}”
rules:
– proto: tcp
ports:
– 22
cidr_ip: 0.0.0.0/0
rule_desc: allow all on ssh port
register: result_sec_group

– name: Provision instance(s)
ec2:
aws_access_key: “{{ec2_access_key}}”
aws_secret_key: “{{ec2_secret_key}}”
key_name: “{{ key_name }}”
id: “{{ id }}”
group_id: “{{ result_sec_group.group_id }}”
image: “{{ image }}”
instance_type: t2.micro
region: “{{ region }}”
wait: true
count: 1
# exact_count: 2
# count_tag:
# Name: App
# instance_tags:
# Name: App

tags: [‘never’, ‘create_ec2’]

If you execute Ansible without the tags argument the creation tasks won’t be performed.

ansible-playbook playbook.yml --ask-vault-pass
ansible-playbook playbook.yml --ask-vault-pass --tags create_ec2
ssh -i ~/.ssh/my_aws ubuntu@ec2-10-228-108-227.us-east-2.compute.amazonaws.com