Automate AWS deployments with Ansible + Terraform
This installation is made from a bastion server already available with the proper network permissions.
For different deployment types, you should adapt it to your need.
Install requirements
Ansible installation:
sudo apt update
sudo apt install -y software-properties-common
sudo apt-add-repository --yes --update ppa:ansible/ansible
sudo apt install -y ansible
Terraform installation:
wget https://releases.hashicorp.com/terraform/0.12.24/terraform_0.12.24_linux_amd64.zip
sudo unzip -d /usr/local/bin/ ./terraform_0.12.24_linux_amd64.zip
ref: https://www.techrepublic.com/article/how-to-install-terraform-on-ubuntu-server/
AWS Client installation:
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
ref: https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html
Connect your environment to your AWS cloud
$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: eu-central-a
Default output format [None]: json
ref: https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html#cli-quick-configuration
Create a new Ansible project
That is my favorite Ansible layout. The one I’ve seen as the best so far:
mkdir -p ~/my_aws_project/inventory
mkdir -p ~/my_aws_project/playbooks
mkdir -p ~/my_aws_project/roles
Add the EC2 dynamic inventory
Ansible can work with a different kind of inventory called the dynamic inventory.
Instead of having a static declaration in a static file of your inventory, you can generate it from a source.
This source can be a database, an active directory, etc. A dynamic inventory is a scrip that outputs a JSON in a structure that Ansible can handle. We could then develop a script that discovers our EC2 infrastructure that would take some time. Or we can use the one already provide with Ansible:
Install prerequisites:
sudo apt install -y python3-pip
sudo pip3 install boto
sudo pip3 install ansible
sudo rm /usr/bin/python
sudo ln -s /usr/bin/python3 /usr/bin/python
Get the EC2 dynamic inventory:
wget -O ~/my_aws_project/inventory/ec2.py \
https://raw.githubusercontent.com/ansible/ansible/stable-2.9/contrib/inventory/ec2.py
wget -O ~/my_aws_project/inventory/ec2.ini \
https://raw.githubusercontent.com/ansible/ansible/stable-2.9/contrib/inventory/ec2.ini
chmod +x ~/my_aws_project/inventory/ec2.py
There are multiple configuration options you can do with the ini file. For this blog I’ll change those vars:
regions = eu-central-1
vpc_destination_variable = private_ip_address
Test the inventory script:
~/my_aws_project/inventory/ec2.py ## ---> return JSON description of your AWS infrastructure
Because I want to work on one AWS region in the private network only. Since my bastion is already in
the AWS infrastructure.
Add a role for our deployment
I’ll create a role with the only purpose to deploy my infrastructure into AWS.
ansible-galaxy init --init-path ~/my_aws_project/roles ec2_instances_dep
Cable the components (Ansible configuration)
To have that layout working fine and the simpliest way, I use that configuration:
## file ~/my_aws_project/ansible.cfg
[defaults]
roles_path = ./roles
inventory = ./inventory/ec2.py
Test the ansible inventory:
cd ~/my_aws_project
ansible-inventory --graph ## ---> return the Ansible interpreted inventory
Terraform with Ansible
When I need to do something with Ansible, I first check in the list of modules is the work is already done.
And, nicely, there is a module for Terraform.
So I can add this module in my task main file of my role:
## ~/my_aws_project/roles/ec2_instances_dep/tasks/main.yml
---
- name: I create a directory to store my Terraform config
file:
path: "~/aws_terraform"
state: directory
recurse: yes
- name: I copy my Terraform template into the working directory create above
template:
src: "my_ec2_infra.tf"
dest: "~/aws_terraform/my_ec2_infra.tf"
- name: I deploy my configuration into AWS from Ansible
terraform:
project_path: "~/aws_terraform"
force_init: true
state: "present"
register: r_aws
- name: I do whatever I need to do in my EC2 infrastructure
debug: msg="update, install, create user, start services, etc"
- name: I destroy my AWS infrastructure
terraform:
project_path: "~/aws_terraform"
state: "absent"
Terraform content
Add this file into the template directory: ~/my_aws_project/roles/ec2_instances_dep
## file ~/my_aws_project/roles/ec2_instances_dep/my_ec2_infra.tf
provider "aws" {
region = "eu-central-1"
}
resource "aws_instance" "dba-essential" {
count = "5"
ami = "ami-0e342d72b12109f91"
availability_zone = "eu-central-1a"
instance_type = "t2.micro"
associate_public_ip_address = false
security_groups = ["my_sg_01"]
vpc_security_group_ids = ["sg-602eff2724d52a0b7"]
key_name = "my_key_01"
root_block_device {
delete_on_termination = true
encrypted = false
volume_size = 15
volume_type = "gp2"
}
tags = {
Owner = "Nicolas"
Name = "crash-test-${count.index + 1}"
}
}
Create a playbook to call the role
## file ~/my_aws_project/playbooks/deploy.yml
---
- name: Deploy my infrastructure
hosts: localhost
roles:
- ec2_instances_dep
Run the playbook
cd my_aws_project
ansible-playbook playbooks/deploy.yml
Boom! Here it is. Now imagine that you can generate a unique key and unique directory for each deployment and you can deploy as much infrastructure as your credit card will accept it.
I hope this helps, and please comment below for any questions.