Provisioning an AWS Infrastructure with Ansible

One of the main advantages of cloud computing is the ability to provision a complete infrastructure using lines of code.

Infrastructure as a code, or IaC, allows more agility, consistency and security.

There are some tools that are commonly used for provisioning and managing infrastructure via code. One of them is Ansible.

Ansible works by configuring client machines from an computer with Ansible components installed and configured. It communicates over normal SSH channels in order to retrieve information from remote machines, issue commands, and copy files. Because of this, an Ansible system does not require any additional software to be installed on the client computers.

In this article we are going to set up a complete infrastructure on AWS using only codes via Ansible.


We will configure the following components.

Preparing Your Ansible Control Node

To provision the infrastructure we will need a machine, called Control Node, where we will install Ansible and write our codes.

You can set up this machine within your account in AWS or outside.

For this demo we will use the following machine configurations.

Linux CentOS 7

Installing Ansible on Your Control Node

Log into your Control Node machine.

Ensure that the CentOS 7 EPEL repository is installed.

$ sudo yum -y install epel-release

Install pip and boto3 packages.

$ sudo yum -y install python-pip
$ sudo pip install boto3

Finally, install Ansible.

$ sudo yum -y install ansible

Ansible is a powerful tool and has many forms of use for the most different purposes. Check the official documentation page.

Ansible by default is installed in /etc/ansible directory.

Jump to Ansible directory.

$ cd /etc/ansible

Creating an AWS User, Access Key and Secret Key

To make our code interact with the AWS infrastructure we have to create a user and the keys so that we can make API calls.

In the next step you will need to download the .csv file. This is the only opportunity to save this file. This file contain the Access Key and Secret Key that we are use to interact with AWS.

Configuring the Network Infrastructure

We are ready to start deploying our infrastructure. In this section we will configure a variable file to authenticate in AWS and a playbook that contains the network infrastructure and one EC2 instance.

Creating a Variable File to Authenticate in AWS.

First, we need to create a file that contains the Access Key and Secret Key.

Create a directory and a file to store the variables.

$ cd /etc/ansible
$ mkdir vars
$ touch info.yml

Edit info.yml and insert the following lines.

$ sudo vim info.ymlaws_id: YOUR_AWS_ACCESS_KEY
aws_region: YOUR_REGION
ssh_keyname: ansible-user
remote_user: ansibleuser

Save and close.

This file contains a lots of confidential informations. We need to secure this file.

We will use the utility ansible-vault that allow us encrypt the data.

$ sudo ansible-vault encrypt info.ym
New Vault password: <must be a strong password>
Confirm New Vault password: <must be a strong password>

Now, if you try read this document you should see something like this.

$ cat info.yml$ANSIBLE_VAULT;1.1;AES256

Creating an Ansible Playbook

According to official documentation, a playbook record and execute Ansible’s configuration, deployment, and orchestration functions. They can describe a policy you want your remote systems to enforce, or a set of steps in a general IT process.

It is where we will configure our code.

One important point: playbooks are written in yml format. We need to be careful about the file indentation. Any extra space, the playbook will not run.

To make our work easy and to avoid errors, we need to configure vim to work in an yml way.

Create vimrc file and insert the lines below.

$ sudo vim ~/.vimrcset ts=2 
set sts=2
set sw=2
set expandtab
syntax on
filetype indent plugin on
set ruler

This configurations allows us identation with keytab without errors.

Create a playbook.

$ cd /etc/ansible/
$ sudo touch aws_infrastructure.yml

Part 1 — Start

- name: AWS Network Configuration
hosts: localhost
gather_facts: false
- /etc/ansible/vars/info.yml
  • The code below will be the typhical start for our playbook.
  • It runs on localhost because most of the EC2 cloud modules run on a managed host which talks to the EC2 API to make changes.
  • Fact gathering has beed disabled to speed up the play.
  • The vars/info.yml file contains our variables that set the credentials to access AWS.

Part 2 — VPC

- name: Creating a VPC
aws_access_key: "{{ aws_id }}"
aws_secret_key: "{{ aws_key }}"
region: "{{ aws_region }}"
name: test_vpc_net
module: ec2_vpc_net
tenancy: default
register: ansibleVPC
- name: Displaying the ansibleVPC output
var: ansibleVPC
  • The Access/Secret Keys and region are loaded from vars/info.yml as a variable.
  • A name for the VPC and the CIDR block.
  • Configure a tag as key-value pairs.
  • If tenancy is default, new instances in this VPC will run on shared hardware by default.
  • The results of the task are stored in the variable ansibleVPC.
  • Finally, to inspect ansibleVPC, we use debug module to display its contents.

Part 3 — Subnet

- name: Creating a subnet
aws_access_key: "{{ aws_id }}"
aws_secret_key: "{{ aws_key }}"
region: "{{ aws_region }}"
state: present
map_public: yes
Name: public_subnet
register: public_subnet
- name: Displaying the public_subnet output
var: public_subnet
  • Use the ec2_vpc_subnet module to add a subnet to a VPC that we created.
  • You must specify the vpc_id of the VPC that the subnet is in.
  • State is present to specify that this subnet should exist.
  • Specify the CIDR block.
  • Set a tag to identify the subnet.
  • Use map_public to assign instance in a public IP address.
  • Use register: public_subnet contains results you will need later.

Part 4 — Internet Gateway

- name: Creating an Internet Gateway
aws_access_keyt: "{{ aws_id }}"
aws_secret_key: "{{ aws_key }}"
region: "{{ aws_region }}"
state: present
vpc_id: "{{ }}"
Name: ansibleVPC_IGW
register: ansibleVPC_IGW
- name: Displaying the ansibleVPC_IG output
var: ansibleVPC_IGW
  • vpc_id is the VPC ID, which is retrieve by reading data in the variable from the Part 2.
  • state control whether the IGW should be present.
  • register: ansibleVPC_igw is an ID that you will need to create the route table.

Part 5—Route Table

name: Creating a route table
aws_access_keyt: "{{ aws_id }}"
aws_secret_key: "{{ aws_key }}"
region: "{{ aws_region }}"
state: present
vpc_id: "{{ }}"
Name: rt_ansibleVPC_PublicSubnet
- "{{ }}"
- dest:
gateway_id: "{{ }}"
- name: Displaying the rt_ansibleVPC_PublicSubnet output
var: rt_ansibleVPC_PublicSubnet
  • State present means that you are creating the new route table.
  • vpc_id must be set to the ID of the VPC
  • subnets is a list of subnet IDS to attach to the route table
  • route is a list of routes
  • dest is the network being routed to.
  • gateway_id is the ID of an IGW

Part 6— Security Group

- name: Creating a Security Group
aws_access_keyt: "{{ aws_id }}"
aws_secret_key: "{{ aws_key }}"
region: "{{ aws_region }}"
name: "Ansible Security Group"
description: "Ansible Security Group"
vpc_id: "{{ }}"
Name: Ansible Security Group
- proto: "tcp"
ports: "22"
register: my_vpc_sg
- name: Displaying the my_vpc_sg output
var: my_vpc_sg
  • In order to launch an instance in AWS you need to assign a security group which act as a instance firewall.
  • The security group must be in the same VPC as the resources you want to protect.
  • If you want traffic to a port you need to add a rule specifying what port do you want to open.

Testing our Playbook

Before run our playbook in production, we have to check if there are syntax errors that could affect the runtime.

Check for syntax errors.

$ cd /etc/ansible
$ sudo ansible-playbook --syntax-check aws_infrastructure.yml

You should have no errors and the output looks like:

Now, we will run our playbook with a dry run flag, to see that everything runs properly. In this demo I’ll run with just one little part of the playbook as an example.

$ sudo ansible-playbook -C aws_infrastructure.yml

You should see something like this.

You can see that, in this case, the VPC was created.

Now, put Part 1 to Part 6 in a single playbook, and run.

$ sudo ansible-playbook aws_infrastructure.yml

Wait a little, and them check in your AWS account the components that we create.

Provisioning an EC2 instances

After configure properly the network components in your AWS infrastructure, we will now provisioning our EC2 instance.

The steps required to create an instance are:

In the same playbook that we work in the previous steps, add the following steps to configure EC2 instance.

- name: AWS EC2 Configuration
hosts: localhost
gather_facts: false
- vars/info.yml
- name: Find CentOS AMIs
aws_access_key: "{{ aws_id }}"
aws_secret_key: "{{ aws_key }}"
region: "{{ aws_region }}"
owners: 679593333241 # CentOS owner ID
architecture: x86_64
name: CentOS-7*HVM-*
register: amis
- name: Display the AMIs output
var: amis
- name: Store latest version
latest_ami: "{{ amis.images | sort(attribute='creation_date') | last }}"
- name: Display the latest_ami output
var: latest_ami
- name: Provisioning an EC2 instance
aws_access_key: "{{ aws_id }}"
aws_secret_key: "{{ aws_key }}"
region: "{{ aws_region }}"
image: "{{ latest_ami.imagem_id }}"
instance_type: t2.micro
key_name: "{{ ssh_keyname }}"
count: 1
state: present
group_id: "{{ my_vpc_sg.group_id }}"
wait: yes
vpc_subnet_id: "{{ public_subnet.subnet_id }}"
assign_public_ip: yes
Name: ansible_ec2_template
register: ec2info

When run our playbook again, ansible will skip the network configuration steps that we’ve already configured earlier in this article and go directly to the EC2 deployment.

Run the playbook.

$ sudo ansible-playbook aws_infrastructure.yml

You can see all the steps.

Go to your EC2 instances dashboard and see that EC2 instance created.


In this post I show you how to deploy a complete infrastructure in AWS, ranging from network devices and one EC2.

Information security for study purpose only and more!