Install and Setup Ansible on Ubuntu 20.04/Ubuntu 22.04

How to setup Ansible in Ubuntu? In this tutorial, we are going to learn how to install and setup Ansible on Ubuntu 20.04/Ubuntu 22.04. Ansible is a simple agentless IT automation tool. It handles configuration management, application deployment, cloud provisioning, ad-hoc task execution, network automation, and multi-node orchestration over the SSH protocol. Ansible makes complex changes like zero-downtime rolling updates with load balancers easy.

Installing Ansible on Ubuntu 20.04/Ubuntu 22.04

Since Ansible is used to manage remote hosts via SSH protocol, the node on which you install Ansible, becomes the control node and the node which you will manage using Ansible is called managed hosts.

Run System Update

Before you can proceed, ensure that your system package cache is up-to-date;

sudo apt update

Install Ansible on Ubuntu 20.04/Ubuntu 22.04

There different methods you can use to install Ansible on Ubuntu 20.04/Ubuntu 22.04 systems;

  1. Install Ansbile using PIP on Ubuntu
  2. Install Ansible using APT Package Manager

Install Ansbile using PIP on Ubuntu

To install Ansbile using Python PIP command, proceed as follows;

Ensure that PIP is available on the system;

python3 -m pip -V

Such an output should be printed if PIP is available;

pip 22.3.1 from /usr/local/lib/python3.8/dist-packages/pip (python 3.8)

If PIP is available, then you can proceed to install Ansible.

Otherwise, install PIP before you can proceed;

apt install python3-pip

If PIP is already installed, you might want to upgrade it;

python3 -m pip install --upgrade pip

With PIP being available on the system, you can install Ansbile for the current user only by running the command below;

python3 -m pip install --user ansible

All the Ansible binaries are installed in ‘$HOME/.local/bin‘ which is not on PATH.

You can add this path to PATH environment variable temporarily using;

export PATH=$PATH:~/.local/bin

Or load it when you start your shell (if using bash);

echo "export PATH=$PATH:~/.local/bin" >> ~/.bashrc
source ~/.bashrc

Or, you simply install Ansible for system wide users using the command below;

sudo python3 -m pip install ansible

Check Ansible version;

ansible --version

ansible [core 2.14.2]
  config file = None
  configured module search path = ['/home/kifarunix/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/kifarunix/.local/lib/python3.10/site-packages/ansible
  ansible collection location = /home/kifarunix/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/kifarunix/.local/bin/ansible
  python version = 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] (/usr/bin/python3)
  jinja version = 3.0.3
  libyaml = True

Install Ansible using APT Package Manager

Ubuntu default repos provides Ansible packages. However, such packages may not be up-to-date.

To check available versions of the Ansible package, run the command below;

sudo apt-cache policy ansible

Sample output from Ubuntu 20.04;


ansible:
  Installed: (none)
  Candidate: 2.9.6+dfsg-1
  Version table:
     2.9.6+dfsg-1 500
        500 http://us.archive.ubuntu.com/ubuntu focal/universe amd64 Packages

You can as well install Ansible PPA repos that will also provide latest Ansible packages;

apt -y install software-properties-common
sudo apt-add-repository ppa:ansible/ansible --yes

You can then install Ansible by executing the command below;

sudo apt install ansible

Once the installation is done, you can check the version of install Ansible as follows;

ansible --version

Setting up Ansible on Control Node

Now that Ansible is installed on an Ubuntu 20.04/Ubuntu 22.04 control node, it is time to set it up to enable you automate deployment of your tasks on the managed remote hosts via SSH protocol.

Setup Ansible Inventory

An Ansible inventory is a file that list or defines the hosts to be managed via Ansible. /etc/ansible/hosts is the default Ansible inventory file. It is also possible to define a custom file as your Ansible inventory file. When you use a non-default inventory file, you have to specify the path to it with -i <path> option while running ansible.

An inventory file can also be used to store variable values that relate to a specific host or group in inventory.

In the inventory file:

  • you can specify the details of the hosts using resolvable hostnames or IP addresses,
  • A group of hosts are delimited by [header] elements
  • A hostname/IP address can be a member of multiple groups

See examples below to create a hosts file with ungrouped hosts, grouped hosts and a range of hosts.

In this tutorial, I will create my inventory in my home directory.

mkdir $HOME/ansible
vim $HOME/ansible/hosts

NOTE: Any individual host, (Ungrouped hosts),should be specified before any group headers.


# Ungrouped list of hosts
server01.kifarunix-demo.com
192.168.58.5

# Range of hosts
192.168.56.[111:112]

[webapps]
app[01:04].kifarunix-demo.com

# Grouped Hosts
[database]
db[01:03].kifarunix-demo.com
db05.kifarunix-demo.com
db07.kifarunix-demo.com

Save and exit the file.

You can list the hosts in your inventory using the ansible-inventory command. For example, to display the contents of our hosts list above in YAML format;

ansible-inventory -i $HOME/ansible/hosts --list -y

all:
  children:
    database:
      hosts:
        db01.kifarunix-demo.com: {}
        db02.kifarunix-demo.com: {}
        db03.kifarunix-demo.com: {}
        db05.kifarunix-demo.com: {}
        db07.kifarunix-demo.com: {}
    ungrouped:
      hosts:
        192.168.56.111: {}
        192.168.56.112: {}
        192.168.58.5: {}
        server01.kifarunix-demo.com: {}
    webapps:
      hosts:
        app01.kifarunix-demo.com: {}
        app02.kifarunix-demo.com: {}
        app03.kifarunix-demo.com: {}
        app04.kifarunix-demo.com: {

Read more on how to build ansible inventory.

Setting up Ansible SSH key authentication

Generate SSH Keys

Use of passwordless SSH key authentication is one of the common methods that by default Ansible uses for managing remote hosts. Therefore, you need to generate passwordless SSH key and copy the keys to the remote hosts your want to manage with Ansible.

ssh-keygen

For passwordless SSH key generation, simply press ENTER for empty password.


Generating public/private rsa key pair.
Enter file in which to save the key (/home/koromicha/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/koromicha/.ssh/id_rsa
Your public key has been saved in /home/koromicha/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:Ba9LyP0epsCPIVN5nvIVT9zgnLCRL1P/dmgiAvWjbMc koromicha@koromicha
The key's randomart image is:
+---[RSA 3072]----+
|        .        |
|         o .     |
|        . * o    |
|     . = + X =   |
|      * S B B o  |
|     o * * B   o |
|    o = O E o o +|
|     o O B o o ..|
|      . + .      |
+----[SHA256]-----+

Read more about other connection methods on Ansible connection plugins page.

Copy the keys to remote hosts

Next, copy the keys to the remote hosts. Use a remote user with sudo rights.

for i in 192.168.58.5 192.168.56.111 192.168.56.112; do ssh-copy-id -o StrictHostKeyChecking=no johndoe@$i; done

In this demo, we will be using a user with passwordless sudo rights. To enable a user to run commands with sudo without being prompted for password, run the command below on the remote hosts being managed.

echo "johndoe ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/johndoe

Read more on Ansible privileges escalation page.

Test Ansible Connection to Remote Hosts

Ansible uses ping module to verify the reachability to the remote hosts defined on the inventory file. Below is our test inventory;

ansible-inventory -i $HOME/ansible/demo --list -y

all:
  children:
    centos:
      hosts:
        192.168.56.111: {}
        192.168.56.112: {}
    ungrouped: {}

To test the connection, as user johndoe, whose keys we copied above;

ansible -i $HOME/ansible/demo -m ping -u johndoe all

192.168.56.111 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
192.168.56.112 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

Both 192.168.56.111 and 192.168.56.112 are CentOS 8 servers.

Running Ansible ad-hoc commands

If you have tasks that you would rarely repeat, ad-hoc commands come in so handy. With ad-hoc commands, you can manage user accounts, services, packages, run ad-hoc system commands like check free RAM, gather system facts.

To see a few examples, check if the remote systems have internet connectivity using ping command;

 ansible -i $HOME/ansible/demo -a "ping 8.8.8.8 -c 3" all -u johndoe

To check free memory;

ansible -i $HOME/ansible/demo -a "free -h" all -u johndoe

192.168.56.112 | CHANGED | rc=0 >>
              total        used        free      shared  buff/cache   available
Mem:          818Mi       234Mi       196Mi        10Mi       387Mi       444Mi
Swap:         819Mi          0B       819Mi
192.168.56.111 | CHANGED | rc=0 >>
              total        used        free      shared  buff/cache   available
Mem:          818Mi       230Mi       214Mi        10Mi       373Mi       452Mi
Swap:         819Mi          0B       819Mi

To install a package, for example a vim editor, as user johndoe with sudo rights (--become);

ansible -i $HOME/ansible/demo -m apt -m yum -a "name=vim state=present" all -u johndoe --become

Read more about ad-hoc commands on Introduction to ad-hoc commands.

Using Ansible Playbooks

Playbooks are Ansible’s configuration, deployment, and orchestration language. They define roles and tasks (a set of steps to take to set up a specific process) to be executed on the remote hosts.

In this demo, we will cover two simple examples of playbooks;

So before we can proceed, create Ansible roles directory. Ansible Roles defines a collections of variables, tasks, files, templates and any other ansible artefacts.

For our example, we will have two roles, useraccount and apache, for defining what user account to create and the package to be installed.

Below is our simple sample Roles directory structure;


myplaybooks
	site.yml
	roles/
	     useraccounts
		tasks/
                     main.yml
	     webserver/
		tasks/
                     main.yml

To create the relevant directories as per above structure;

mkdir -p myplaybooks/roles/{useraccounts/tasks,webserver/tasks}

Create master playbook, site.yml to define values that will be applicable to all other tasks;

vim myplaybooks/site.yml

---
#
# A common Playbook to Create a user accounts.
#
- hosts: all 
  remote_user: johndoe
  become: yes
  roles:
  - useraccounts
  - webserver

Ansible Playbook to Create User Account

The playbook for creating user account is as per my roles directory structure above. So create a file in which you will define particulars of a user to be created.

vim myplaybooks/roles/useraccounts/tasks/main.yml

---
#
# Create a user account
#
- name: Add User Jane Doe to the system
  user:
          name: janedoe
          password: "{{ 'strongpassword' | password_hash('sha512') }}"
          groups: wheel
          shell: /bin/bash
          comment: Jane Doe
          expires: 1584993090

Ansible Playbook to Install Apache Web server

Similarly, create task file for installing Apache web server on Ubuntu 20.04 and CentOS 8 systems;

vim myplaybooks/roles/webserver/tasks/main.yml

---
#
# Install Apache Web server on CentOS 8
#
- name: Install Apache on CentOS 8
  yum:
          name: httpd
          update_cache: yes
          state: latest

To list master Ansible Playbook tasks;

ansible-playbook myplaybooks/site.yml --list-tasks -i demo

playbook: myplaybooks/site.yml

  play #1 (all): all	TAGS: []
    tasks:
      useraccounts : Add User Jane Doe to the system	TAGS: []
      webserver : Install Apache on CentOS 8	TAGS: []

Running Ansible Playbook Dry Run checks

To check your playbook without making any changes on the remote host, use the --check option.

ansible-playbook myplaybooks/site.yml --check -i $HOME/ansible/demo

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [192.168.56.112]
ok: [192.168.56.111]

TASK [useraccounts : Add User Jane Doe to the system] **********************************************************************************************************************
changed: [192.168.56.112]
changed: [192.168.56.111]

TASK [webserver : Install Apache on CentOS 8] ******************************************************************************************************************************
changed: [192.168.56.111]
changed: [192.168.56.112]

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

Executing Ansible Playbook

Once you have verified that there is not error with the checks above, proceed to execute the playbook;

ansible-playbook myplaybooks/site.yml -i $HOME/ansible/demo

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [192.168.56.112]
ok: [192.168.56.111]

TASK [useraccounts : Add User Jane Doe to the system] **********************************************************************************************************************
changed: [192.168.56.111]
changed: [192.168.56.112]

TASK [webserver : Install Apache on CentOS 8] ******************************************************************************************************************************
changed: [192.168.56.111]
changed: [192.168.56.112]

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

Verify User Account Creation and Package installed using Ansible

You can create yet another master playbook to verify if user account for janedoe is created as well as checking is Apache is installed by checking its service if available. Both of these can be checked using Ansible getent module.

vim myplaybooks/check.yml

---
- hosts: centos
  remote_user: johndoe
  become: yes
  gather_facts: no  # or it will fail on the setup step
  tasks:
          - name: Check if user Jane Doe (janedoe) is created.
            getent:
                  database: passwd
                  key: janedoe
          - name: Check if Apache service exists.
            getent:
                  database: services
                  key: http

Execute the playbook;

ansible-playbook myplaybooks/check.yml -i $HOME/ansible/demo

PLAY [centos] **************************************************************************************************************************************************************

TASK [Check if user Jane Doe (janedoe) is created.] ************************************************************************************************************************
ok: [192.168.56.112]
ok: [192.168.56.111]

TASK [Check if Apache service exists.] *************************************************************************************************************************************
ok: [192.168.56.112]
ok: [192.168.56.111]

PLAY RECAP *****************************************************************************************************************************************************************
192.168.56.111             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.56.112             : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

And there you go.

That marks the end of our tutorial on how to install and setup Ansible on Ubuntu 20.04. We hope this was informative. Enjoy.

Further Reading

Getting Started with Ansible

Other Tutorials

Install and Deploy Kubernetes Cluster on Ubuntu 20.04

Install Docker CE on Ubuntu 20.04

Setup GlusterFS Distributed Replicated Volume on CentOS 8

SUPPORT US VIA A VIRTUAL CUP OF COFFEE

We're passionate about sharing our knowledge and experiences with you through our blog. If you appreciate our efforts, consider buying us a virtual coffee. Your support keeps us motivated and enables us to continually improve, ensuring that we can provide you with the best content possible. Thank you for being a coffee-fueled champion of our work!

Photo of author
koromicha
I am the Co-founder of Kifarunix.com, Linux and the whole FOSS enthusiast, Linux System Admin and a Blue Teamer who loves to share technological tips and hacks with others as a way of sharing knowledge as: "In vain have you acquired knowledge if you have not imparted it to others".

Leave a Comment