Deploy WebApp on AWS EC2 using Ansible

kuldeep rajpurohit
9 min readSep 2, 2020
Image Credit : https://miro.medium.com/max/375/1*PzXLK0lbmPb6FdUasHC1EQ.png

What is AWS EC2?

Amazon Elastic Compute Cloud (Amazon EC2) is a web service that provides secure, resizable compute capacity in the cloud. It is designed to make web-scale cloud computing easier for developers.

What is Ansible?

Ansible is a software tool that provides simple but powerful automation for cross-platform computer support. It is primarily intended for IT professionals, who use it for application deployment, updates on workstations and servers, cloud provisioning, configuration management, intra-service orchestration, and nearly anything a systems administrator does on a weekly or daily basis. Ansible doesn’t depend on agent software and has no additional security infrastructure, so it’s easy to deploy.

Prerequisites :

  • Basic Knowledge of Ansible
  • Basic Knowledge of AWS
  • Basic Knowledge Linux privilege escalation
  • Ansible should be installed on the Controller node
  • AWS user with programmatic access

In this article, I will show you how to create customize ROLE for provisioning AWS EC2 Instance and configuring apache webserver on the instance, I will also show you how to set up dynamic inventory so we don't require to update inventory manually when we launch a new instance, terminate instance or if instance IP gets changed after reboot.

I will also show how to configure ansible config file for privilege escalation for ec2 instance because on most of the cloud instances root account is disabled.

Steps :

  • Configure Ansible config file
  • Setup Dynamic Inventory
  • Create a ROLE ec2
  • Create a ROLE webserver
  • Provision ec2 instance using ec2 ROLE
  • Configure Apache Webserver using web server ROLE

Let’s get Started…

Ansible config file

We will create a directory “/etc/ansible” by default ansible read config file from this directory, with in the same directory we will create two directory’s roles” for storing all roles which we will create further and “hosts” this will be used to keep our dynamic inventory files we will also store any ec2 instance private key’s in the same directory so management would be easier.

In the above snippet under defaults header, we have set the path for inventory, role, and private key.

Further in this article, you will see that we will use amazon Linux 2 AMI which has a default user ec2-user, so we have to mention it in the config file else ansible will ssh with the user which is logged in on controller node.

We also have to disable the host key checking because we are configuring ec2 instance using playbook we will not be prompted to accept host key , and the playbook will fail.

Under the privilege escalation header, we are mentioning what privilege escalation we want for ec2-user using sudo because we will install multiple packages on ec2 instance and ec2-user don't have the power to install packages, but since ec2-user is added in %wheel group ec2-user can become root using sudo.

We also required to disable become_ask_pass for the same reason we disabled host key checking.

dynamic inventory

Now we will set up dynamic inventory, we are using dynamic inventory rather than static because we might launch more instance (consider traffic on instances increase) and assume we have to launch 100 more servers if we use static inventory we have to manually add each IP entry when we launch new instances or even we have to manually remove the entry for instance which are terminated.

It is really a pain to manually update inventory when we talk about scale, and also ansible is automation tools right so why to do things manually.

The question here is how to create a dynamic inventory?

And the answer to the above question is we have use some scripts to do soo….but don't worry we don't have to write these scripts we can use scripts created by someone else.

Here is the link to Github repo which contains scripts for creating dynamic inventory for almost all tools, services, or devices.

Since we are only working on AWS ec2 we need two files to make our inventory dynamic

  1. ec2.py here is the link for ec2.py

ec2.py is the python script which fetch the ec2 instance ip dynamically it also gives ip in group by vpc id,tag name,instance type etc.

To fetch ip of instances first we are required to export accesskey,secretkey in environment variable or we can simply assign them to varaible in ec2.py file

You can download ec2.py in hosts directory using wget

wget https://github.com/ansible/ansible/blob/stable-2.9/contrib/inventory/ec2.py

2. ec2.ini here is the link for ec2.ini

ec2.ini is the file which is used to define what data we need ,like filtering ip based on tag name ,we can also define for which region we want to fetch ip of instances or we just need ip of some specific instances etc

You can download ec2.ini in hosts directory using wget

wget https://github.com/ansible/ansible/blob/stable-2.9/contrib/inventory/ec2.ini

We have to keep these both files at “/etc/ansible/hosts” directory remember we configured hosts directory for inventory in the ansible config file

We can specify in ec2.ini that we only want IP’s of a specific region, here we are specifying ap-south-1

We will also specify that we only need the IP of instances which have tag Name webserver using filter’s

Now we have to export AWS region, access key, secret key as an environment variable or we can pass them in ec2.py but I am exporting them as an environment variable

export AWS_REGION="YOUR_DESIRED_REGION"export AWS_ACCESS_KEY_ID="YOUR_AWS_ACCESS_KEY"export AWS_SECRET_ACCESS_KEY="YOUR_AWS_SECRET_KEY"

We also need to make both files executable in hosts directory, below is the command to make both files executable

chmod +x *

We can check that our dynamic inventory is working by running the command

ansible all --list-hosts

Since we yet didn't provision any instance it will show 0 hosts

roles

Roles provide a framework for fully independent, or interdependent collections of variables, tasks, files, templates, and modules. In Ansible, the role is the primary mechanism for breaking a playbook into multiple files. This simplifies writing complex playbooks, and it makes them easier to reuse.

Here we will create Two roles

  • ec2 this role will be used to provision ec2 instances
  • webserver this will be used to configure apache webserver

Let start with creating ec2 role…

We create all our roles in “/etc/ansible/roles” directory remember we configured this in the ansible config file

We can create a role using below command

ansible-galaxy init ec2

Here is the directory structure created by ansible-galaxy init

ec2 role - /task

Here we are using ec2 module to provision ec2 instance and we are using jinja templating to pass variables

The most important part in the above snippet is exact_count & count_tag using these two arguments we can achieve idempotency consider if exact_count is 2 and count_tag is webserver and 1 instance with tag webserver is already running then this module will only launch 1 new instance

ec2 role - /vars

This is var file of ec2 role where we have to declare all variables, variables value assigned here will be considered as default values if we want to override these values we do it in final playbook where we will use this role

We will pass tag_name ,accesskey, secretkey from the final playbook

Now we are done with ec2 role next we will create webserver role,just how we created ec2 role using ansible-galaxy init we can create webserver role ,but remember we have to create it in “/etc/ansible/roles” directory

ansible-galaxy init webserver

Here is the directory structure created by ansible-galaxy init

webserver role - task

Webserver role will do 2 things first it will install and configure apache webserver and second it will fetch webpage in servers document root and start-server service

In the above snippet, we are using the package module to install httpd, then we are creating a new document root using file module and we will pass the path of the directory from variable file

Then we are using the template module to copy the apache web-server config file here the important thing we are using notify whenever config file is changed it will notify handler, and handler task will restart the webserver

Then we are just downloading a webpage from Github into the servers document root directory and starting the httpd service using service module

webserver role - handler

This the handler which will be used to restart web server whenever config file is updated

when we update or change the template for config file it will notify to this handler and restart webserver else in another case it does nothing

webserver role - template

This the apache webserver config file , here we are using jinja templating to make it dynamic , we are passing httpport and documentroot variable from var file

We are also using ansible fact to assign ipv4 address.

webserver role - var

This the variable file we we declare all our variable used in webserver role, here we are just using two variable documentroot and httpport which we are using in template

That’s it we successfully created 2 roles ec2 and webserver.

You can verify that all thing we did till now is working fine by below command, this will show you all the available roles

ansible-galaxy list

ansible playbook

This is the final playbook where we will the roles which we created earlier

Here we are using a variable file called secret.yml this file will contain the access key and secret key and we will using ansible vault to encrypt this file , this secret.yml file will be used by ec2 role to provision ec2 instance.

We are running ec2 role on localhost because we are not configuring anything with this role we provision instance using this role and we are overriding a few variables of this role access key and secret key will be fetched from secret.yml and we are giving the tag name as webserver

Once this instance is provisioned then we will configure it using webserver role which we created earlier

We will play this role on host group “tag_Name_webserver” this is the host group given by dynamic inventory which will contain all IP of instances which has a tag Name=webserver.

We can check what host groups are provided by dynamic inventory by using below command in hosts directory

./ec2.py --list

ansible vault secret.yml

Now we will create a file named secret.yml which will contain 2 variables akey and skey

akey: "YOUR_AWS_ACESS_KEY"
skey: "YOUR_AWS_SECRET_KEY"

Then we will encrypt it using ansible-vault

ansible-vault encrypt secret.yml

This will prompt to create a password we will create a password and will hit enter, Now secret.yml has been encrypted

Run ansible playbook

Now we will run our playbook using below command

ansible-playbook --vault-password-file=vaultpass webapp.yml

we will create a file called vaultpass which will contain the vault password , If you don't want to use a file for vault password you can using another option to prompt for a password

ansible-playbook --ask-vault-password webapp.yml

That’s it it will automatically provision ec2 instance, dynamically fetch IP of instance have a tag name as webserver, and then automatically configure apache webserver on ec2 instance and deploy a web app on it.

Accessing the webapp

Now you can simply hit on the public IP 0f instance on port 81 to access the web app

That’s it for this Article. I hope you learned something new.

If you liked this article, please drop a clap so it can reach more people. You can follow me on Twitter at @callbyrefrence or find me on LinkedIn or take a look at my work on GitHub

--

--