How to Install Ansible in AWS EC2 server - RHEL 8.3

Introduction

Ansible is one of the most sought out DevOps skills today for automating deployment and configurations of applications. Built with Python  Ansible is mainly run on linux-unix systems.

Ansible was a startup based of Santa Barbara which had raised 6 million USD seed funding, later acquired by RedHat for about 150 million USD in 2015.

AWX is an open source project which provides a web based interface and APIs to interact with Ansible for managing Inventory and Jobs. Ansible Tower is the enterprise version of AWX licensed by RedHat.

We will be installing and configuring Ansible in AWS EC2 RHEL 8.2 server and launch Ansible playbooks to deploy a simple Apache HTTP server in 2 different servers.

Prerequisites

To get started we need a 64 bit linux server with Python Version 2.7 , 3.5 or higher. It is always better to choose the latest python version. You can choose one of these OS - Red Hat, Debian, CentOS or macOS. In this article, for demonstration purpose we will be using RHEL 8.3 64 bit EC2 instance.

You may refer to our guide on How to launch an AWS EC2 Server if you don't have a server already.

Installation

Updating yum repository

Before starting to install any package, it is always better to update the package repositories to the latest available version.

Login with ec2-user and execute,

$ sudo yum update

Installing Python3

To install Python3 execute

$ sudo yum install python3

When prompted for confirmation to proceed, give Y and press Enter.

Output:
Total                                                                                                                                                        920 kB/s | 202 kB     00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :                                                                                                                                                                     1/1
  Installing       : python3-setuptools-39.2.0-6.el8.noarch                                                                                                                              1/3
  Installing       : python36-3.6.8-2.module+el8.1.0+3334+5cb623d7.x86_64                                                                                                                2/3
  Running scriptlet: python36-3.6.8-2.module+el8.1.0+3334+5cb623d7.x86_64                                                                                                                2/3
  Installing       : python3-pip-9.0.3-18.el8.noarch                                                                                                                                     3/3
  Running scriptlet: python3-pip-9.0.3-18.el8.noarch                                                                                                                                     3/3
  Verifying        : python3-pip-9.0.3-18.el8.noarch                                                                                                                                     1/3
  Verifying        : python36-3.6.8-2.module+el8.1.0+3334+5cb623d7.x86_64                                                                                                                2/3
  Verifying        : python3-setuptools-39.2.0-6.el8.noarch                                                                                                                              3/3

Installed:
  python3-pip-9.0.3-18.el8.noarch                      python3-setuptools-39.2.0-6.el8.noarch                      python36-3.6.8-2.module+el8.1.0+3334+5cb623d7.x86_64

Complete!

Once the installation is complete, verify the version of Python installed as

$ python3 -V
Output:
Python 3.6.8

Installing Ansible

Ansible can be installed in multiple ways.

Method 1 - With RHEL Repository

Enable the RHEL8 Ansible repository as,

$ sudo subscription-manager repos --enable ansible-2.9-for-rhel-8-x86_64-rpms

Method 2 - With PIP

To install Ansible with PIP execute

$ python3 -m pip install --user ansible

At the end of the output, make sure you see a line similar to the below one

Output:

Successfully installed ansible-3.3.0 ansible-base-2.10.9 packaging-20.9 pyparsing-2.4.7

Verify Installation

Execute the below command to verify the installation

If you have installed using ec2-user, execute the below command

 $ /home/ec2-user/.local/bin/ansible --version

If you have installed using root user, execute the below command

 $ /root/.local/bin/ansible --version

You will find the output similar to below:

Output: 
ansible 2.10.9
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /root/.local/lib/python3.6/site-packages/ansible
  executable location = /home/ec2-user/.local/bin/ansible
  python version = 3.6.8 (default, Aug 18 2020, 08:33:21) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]

Adding ansible to $PATH variable

You can add the path of ansible to the $PATH env variable to use the ansible command without absolute path.

Edit the ~/.bash_profile file to add the /.local/bin to the PATH variable

vi ~/.bash_profile

if [ -f ~/.bashrc ]; then. ~/.bashrcfi
PATH=$PATH:$HOME/bin:$HOME/.local/bin/
export PATH

Open a new session and execute the command from any directory to confirm ansible is available in $PATH now.

$ansible --version
Output:
ansible 2.10.9
config file = None
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /root/.local/lib/python3.6/site-packages/ansible
executable location = /root/.local/bin/ansible
python version = 3.6.8 (default, Aug 18 2020, 08:33:21) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]

You can also verify using the below command by using ansible to ping the localhost.

$ ansible -m ping localhost
Output:
[WARNING]: No inventory was parsed, only implicit localhost is available
localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Here, in the above output if you see "ping": "pong" , that's a success!!

Writing Your First Ansible Playbook

Below is a playbook to install Apache. Here we will be installing Apache HTTP Server in the Ansible server(localhost) itself. Copy the below contents to a file named install-apache.yml

Note: The three dashes at the starting of the playbook is mandatory. Also make sure the indentation is correct as per the yaml coding syntax.
---
- hosts: localhost
  tasks:
  - name: Install apche
    yum:
     name: httpd
     state: present
  - name: Start httpd service
    service:
     name: httpd
     state: started

Run the playbook using the below command.

ansible-playbook install-apache.yml

You will see an output similar to below for a successful installation

Output:
PLAY [localhost] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Install apche] ***********************************************************
changed: [localhost]

TASK [Start httpd service] *****************************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost:ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0    ignored=0                                                                                               

Installing Apache HTTP server on Remote Server

Now lets see how to install an application in a remote server form ansible

First we need to set up password less login. For this we need to copy the public key from ansible server(also called as the control server) and add it to the authorized keys of remote server. And copy the public key from remote server and add it to the authorized keys of ansible server.

Execute the command on both the ansible server and the remote server

ssh-keygen -t rsa

The output will look similar to below

Output:

Generating public/private rsa key pair.  
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:SHA256:uF+y0SO9KOvUj3rW6O5KT4DNyYaqL3sw1ee1WuTtzIU root@ip-172-31-24-118.ec2.internalThe key's randomart image is:+
---[RSA 3072]----+|                 
||                 ||   .             
||  . .*.oo        || .  ooO+So .     
||o  . ..++oE .    || o.   +o**=.     
||...  oo++X=o     ||o=.  .=@X o      
|+----[SHA256]-----+


Now copy the contents from the file ~/.ssh/id_rsa.pub of Ansible server and append it to the ~/.ssh/authorized_keys of remote server. Also copy the contents from the file ~/.ssh/id_rsa.pub of remote server and append it to the ~/.ssh/authorized_keys of Ansible server.

Once done it will look similar to the below.

$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC//tfpF/mpuviUXtQddTssrKe/TpvsQYIvn3celKPmisH07QfYMISEcXasLsYqnL599HmaisMUTu0l5rBAqYrAcXVdt9McX0nJOAqXm3zN/3ogmaMAYl71vREOUVg/qM+9+YoVGFvTi8+sTKXgiEbP13PmIq+SQ2oSlvgVoesnsBPVhzKi3MgdVaoR6Zu0re2cEErcDrjTGuYKnyJeX7W5vvc7gwcAXYQPqtLwHyvj/xHWePeSfL04768RqZ8Ilx1FMB5SFNMRueXRuz4Kp52Yhq1FOrtMwH91R+2vDovDAfUEQZiM6XEYqGFqqh12BR4Toyj9E2NuSGHPU2ff5CjkbAn8GMmeBbcK7J+HY4VfVpnI4+vFOWRyMD3YqZFQaayiqbJLgHsYCr1ud7PR43lHtNIKqEBPQM/E7sdqVyWJ2O1KhjtayEfLfcS3v4D52Da7J6tXWZOPq4CaygjoOBHB5P13RrgZULysu0QnnMpPDAKCLu93doalV18Xu31OhXM= root@ip-172-31-24-118.ec2.internal

$ vi ~/.ssh/authorized_keys
no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user \"ec2-user\" rather than the user \"root\".';echo;sleep 10;exit 142" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCDco3J5B4meE/wzz+LwdwLiohsu7qRADGiiuJV7Ks2CDPeQOIayAUAUuBjR3AC6wHPx27I6nDOlCjiJQXq8kq4UT1sGWwciQS+T9gGb1TZecksJecmda5QvN5f4fOpU7dM7ePecUuffE9LoEjrFRnqkjWfo3NRt3xhhYVfSowNrnHW93ENwotUbklY6luCjZWO8mbLb6Cjyfuxr1e2KJxV4PnPJ5OcRm9H6U4uSLRtaeA9gGJ5DVvewIYtjcyE5/aatwV1ezXQ/KEbAUYLp0j/x+h/7zSWmqzjmXfPkR6zu8b3f9y5vQ/zMqhi1CJEG39LB8F6t6+SnjcalxwLwbex admin@debugfactor.com
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCpj3To4yQQn8g/3fiL6PA6viWY0lgc473+DxXmHGhhZBtofLoVVtzac3u3Eeekvpe3aBNt0EfIkVrkqX2uJNmY1yu0tn3V2vheUWHhhWO2hidfIgAuewYkoD/9+meTtCg9rhPC7HQc0qlBGqJKGv+dVJwTq0LOb0TJyk503tB5UugH4AcPX4iUsWlayvSDXnmpKVq7JVnHEctJzykT5zxuHsOAR9TAX3MxdgFTfGDcNpZweABhOTWATO7pPAtqfsOTL+yMP/Xv0/EZ/RRYkEKFTGPqPylsWGvPxyji9kVMsuFAJRmcgndU5tO4Dc6g5qfBjxJFkzU7ptR6VwZkk8SDDjttn+KsIXCu9emqUIB8iJIcMHINfxAamop1BPGoin35RBCOLVjjrJJS1U6+Yj0hdFsJj1rQeqhiVgfODMiB1oZq2RPs4gjUQJrihFluOqPawcMNx0PaLCv8V8IJypSIpt0DTvyir2LQlxUwCVCyD2wySuXBTpUdVhDypWMLKLE= root@ip-172-31-20-230.ec2.internal

Now we need to configure the inventory to add the remote server. First  create a folder /etc/ansible if not present already.

$ mkdir /etc/ansible

Create ansible.cfg file under /etc/ansible folder

# vi /etc/ansible/ansible.cfg
[defaults]
doc_fragment_plugins  = ../doc_fragments
filter_plugins        = ../plugins/filters
library               = ../plugins/modules
module_utils          = ../plugins/module_utils
roles_path            = ../roles

Now when you check the ansible --version command you will see the config file being pointed to /etc/ansible/ansible.cfg

$ ansible --version
ansible 2.10.9
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/etc/plugins/modules']
  ansible python module location = /root/.local/lib/python3.6/site-packages/ansible
  executable location = /root/.local/bin/ansible
  python version = 3.6.8 (default, Aug 18 2020, 08:33:21) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]

Lets update the inventory now. Add the hostname of the remote server to the ansible inventory file

# vi /etc/ansible/hosts
ip-172-31-20-230.ec2.internal

Test the connection to the remote server using ping module. Provide yes when prompted for authentication.

# ansible -m ping ip-172-31-20-230.ec2.internal
The authenticity of host 'ip-172-31-20-230.ec2.internal (172.31.20.230)' can't be established.
ECDSA key fingerprint is SHA256:cAkTiKpPFVlgWM9//ty7WycQwa4SvVnWa+QOKXcgfkU.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
ip-172-31-20-230.ec2.internal | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

Lets try to install Apache using the same playbook above. We will change localhost to the remote server hostname.

# mkdir /etc/ansible/playbooks/
# vi /etc/ansible/playbooks/install_apache.yml

Copy the below contents.

---
- hosts: ip-172-31-20-230.ec2.internal
  tasks:
  - name: Install apche
    yum:
     name: httpd
     state: present
  - name: Start httpd service
    service:
     name: httpd
     state: started

Run the playbook.

# cd /etc/ansible/playbooks
# ansible-playbook install_apache.yml
Output:
PLAY [ip-172-31-20-230.ec2.internal] ************************************************************

TASK [Gathering Facts] ***********
ok: [ip-172-31-20-230.ec2.internal]

TASK [Install apche] ***************************************************************************
changed: [ip-172-31-20-230.ec2.internal]

TASK [Start httpd service] *****************************************************************************
changed: [ip-172-31-20-230.ec2.internal]

PLAY RECAP ***************************
ip-172-31-20-230.ec2.internal : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Once the playbook has ran successfully, provide the IP of the remote server where you have installed Apache HTTP Server in your browser and you would see a page similar to the below.

http://<yourpublicip>

If you do not see the page, make sure you have added port 80 in your EC2 servers security group - inbound rules.

Conclusion

In this guide, we have installed and configured Ansible in an AWS EC2 server. We also wrote a simple playbook to install Apache HTTP server.

The scope of Ansible is unlimited and there are lots of concepts and techniques to learn. To be an expert in DevOps, it is important to have an understanding of the larger eco-system of how different steps in Development and Operations comes together. We will be writing more about many related technologies like Tower, AWS, Jenkins, Terraform.

Asha

Asha

I am a self mentored web enthusiast who likes to inspect element everything I see on the web.
Vipin

Vipin

I am a dreamer. I admire the web. I admire anything about the web.