In this tutorial, we are going to learn how to install Ansible on Ubuntu 24.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 easy to automate repetitive tasks.
Table of Contents
Basic Ansible Concepts/Architecture
Ansible automation tool is made up various components. So, what are these components that makes up Ansible?
- Control Node: As you already know, Ansible is used to automate various tasks on your infrastructure nodes. As such, Ansible has to be installed on one node that is then used to connect to (via SSH) and orchestrate tasks on the remote nodes. The node on which you install Ansible, becomes the
control node
. - Managed Node: Ansible managed node refers to nodes that Ansible will connect to and execute various tasks or commands on them. They can be virtual machines, physical servers, or any other systems that you can be connected to via SSH.
- Inventory: Ansible inventory is file that lists managed hosts, either individually or as grouped hosts depending on a common role. As such, inventory defines hostnames or IP addresses of the managed nodes, along with any variables or groupings.
- Playbooks: Ansible playbooks are YAML files that define a set of tasks, here in called plays, that are meant for execution on the managed hosts. They are execute one by one, and in the order they are defined, against all or specific hosts in the inventory. “playbooks are made up of plays; plays are made up of tasks“.
- Tasks: Ansible tasks define specific actions or commands to be executed against all or specific subset of hosts defined in the inventory. Tasks can include actions to install packages, manage files, start or stop services, run scripts, …
- Modules: Ansible modules are the building blocks of Ansible. They represent small Python codes that does the actual execution of the tasks defined in the Ansible playbooks on managed hosts. They contain metadata that defines when and where a specific automation task is executed and which users can execute it. By default, Ansible provides a large collection of built-in modules, that facilitates execution of a wide range of tasks.
- Plugins: Ansible plugins are used together with Ansible modules extend the functionality of Ansible.
- Handlers: Ansible handlers are used to define specific actions to be taken whenever Ansible tasks meet specific condition. For example, you can define an handler to restart application service whenever a change is made to that application configuration.
- Roles: You can think of Ansible roles define a group related tasks, variables, templates, and other Ansible resources in a standard directory structure.
- Ansible Vault: It is possible that some of your Ansible tasks requires sensitive information such as credentials to perform specific action. Such sensitive information can be securely stored in an Ansible vault in an encrypted format.
- Variables: Ansible uses variables to store values that can be re-used across Ansible tasks/playbooks.
Installing Ansible on Ubuntu 24.04
Run System Update
Before you can proceed, ensure that your system package cache is up-to-date;
sudo apt update
Install Ansible on Ubuntu 24.04
There different methods you can use to install Ansible on Ubuntu 24.04 systems;
Install Ansbile using PIP on Ubuntu
To install Ansbile using Python PIP command, install PIP before you can proceed;
sudo apt install python3-pip python3-venv
If PIP is already installed, you can upgrade it;
However, recent versions of Python PIP now discourages glabol Python package installation or removal for the purposes of ensuring system stability. As a result, it encourages the installation of Python packages on user virtual environment. Read more on PEP 668.
As a result, create your Python virtual environment where you can have control of what Python packages you can ins without introducing any conflicts with other system-wide Python installations.
python3 -m venv ansible
This will create a self contained Ansible Python Virtual environment in the current working directory.
ls -1 ansible
bin
include
lib
lib64
pyvenv.cfg
Next, activate your Python virtual environment and upgrade PIP (if it needs upgrade);
source ansible/bin/activate
python3 -m pip install --upgrade pip
Then install Ansbile for the current user only by running the command below;
python3 -m pip install ansible
All the Ansible binaries are installed in ‘$HOME/ansible/bin
‘. As a result, you can only access Ansible when you login and activate the users Python virtual environment created above.
Check Ansible version;
ansible --version
ansible [core 2.16.6]
config file = None
configured module search path = ['/home/kifarunix/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/kifarunix/ansible/lib/python3.12/site-packages/ansible
ansible collection location = /home/kifarunix/.ansible/collections:/usr/share/ansible/collections
executable location = /home/kifarunix/ansible/bin/ansible
python version = 3.12.3 (main, Apr 10 2024, 05:33:47) [GCC 13.2.0] (/home/kifarunix/ansible/bin/python3)
jinja version = 3.1.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 24.04;
ansible:
Installed: (none)
Candidate: 9.2.0+dfsg-0ubuntu5
Version table:
9.2.0+dfsg-0ubuntu5 500
500 http://de.archive.ubuntu.com/ubuntu noble/universe amd64 Packages
Read more on Ansible changelogs.
The most recent version of Ansible provided by the default Ubuntu 24.04 repositories as of this writing is v9.4.0.
You can install the latest version from the Ansible PPA repositories. However, Ubuntu 24.04, Noble Numbat PPA repos are currently unavailable.
As such, install Ansible from the default Ubuntu 24.04 repositories.
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
When the PPA repos for Ubuntu 24.04 are available, you can install latest Ansible release versions by running the commands;
sudo apt -y install software-properties-common
sudo apt-add-repository ppa:ansible/ansible --yes
sudo apt update
Installing Ansible on Ubuntu 24.04 from PPA repossudo apt install ansible
Setup Ansible on Control Node
Now that Ansible is installed on an Ubuntu 24.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.
Running Ansible Installed System-Wide and Python Virtualenv
If you have installed Ansible system-wide, then you can run normally because all required binaries must already be on the path.
If you are running Ansible on a user’s Python virtual environment, then you must activate that virtual environment for you to access Ansible binaries.
For example, our user’s Ansible virtual env is, ~/ansible. To activate this environment;
source ~/ansible/bin/activate
After that, you should be able to now use Ansible normally.
Configure Ansible Inventory
If you have installed /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.
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 -p $HOME/etc/ansible
vim $HOME/etc/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.122.92
# Range of hosts
192.168.122.[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/etc/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.122.111: {}
192.168.122.112: {}
192.168.122.92: {}
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.
Setup Ansible SSH key authentication
Generate SSH Keys
Ansible uses SSH to connect to managed hosts. Use of SSH key authentication (usually passwordless) is one of the common methods that by default Ansible uses for managing remote hosts. It is also possible to have Ansible use username/password authentication to connect to managed hosts,however not generally recommended.
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 ed25519 key pair.
Enter file in which to save the key (/home/kifarunix/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/kifarunix/.ssh/id_ed25519
Your public key has been saved in /home/kifarunix/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:7qHPByiZZuog9Z3IRgTBhd0aKQ8wuXgBoh0EZZ98tVg kifarunix@noble
The key's randomart image is:
+--[ED25519 256]--+
|*OB=.o E |
|++*=+..+ . |
|o..==oo . |
|o ..o. |
| .. .o .S |
| . +*o.o. |
|o +=.o o. |
|.... + .. |
| .. ..+. |
+----[SHA256]-----+
Read more about other connection methods on Ansible connection plugins page.
Copy SSH Public keys to Managed Hosts
Next, copy the keys to the remote hosts. Use a remote user with sudo rights.
for i in 192.168.122.11 192.168.122.92; 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.
sudo EDITOR=vim visudo /etc/sudoers.d/johndoe
Paste the following, replacing the usernames accordingly;
johndoe ALL=(ALL:ALL) NOPASSWD: ALL
Save and exit the file.
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/etc/ansible/hosts --list -y
all:
children:
centos:
hosts:
192.168.122.11: {}
ubuntu:
hosts:
192.168.122.92: {}
To test the connection, as user johndoe
, whose keys we copied above;
ansible -i $HOME/etc/ansible/hosts -m ping -u johndoe all
192.168.122.11 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
192.168.122.92 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
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/etc/ansible/hosts -a "ping 8.8.8.8 -c 3" all -u johndoe
192.168.122.11 | CHANGED | rc=0 >>
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=59 time=5.06 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=59 time=5.71 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=59 time=5.35 ms
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 5.061/5.370/5.706/0.263 ms
192.168.122.92 | CHANGED | rc=0 >>
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=59 time=5.12 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=59 time=5.22 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=59 time=5.26 ms
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 5.121/5.199/5.258/0.057 ms
To check free memory;
ansible -i $HOME/etc/ansible/hosts -a "free -h" all -u johndoe
192.168.122.92 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 3.8Gi 268Mi 1.4Gi 1.0Mi 2.1Gi 3.3Gi
Swap: 2.1Gi 0B 2.1Gi
192.168.122.11 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 1.2Gi 316Mi 827Mi 6.0Mi 278Mi 955Mi
Swap: 0B 0B 0B
To install a package, for example a vim editor, as user johndoe
with sudo rights (--become
);
ansible -i $HOME/etc/ansible/hosts -m apt -a "name=vim state=present" all -u johndoe --become
ansible -i $HOME/etc/ansible/hosts -m yum -a "name=vim state=present" centos -u johndoe --become
192.168.122.11 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"msg": "",
"rc": 0,
"results": [
"Installed: vim-filesystem-2:8.2.2637-20.el9.noarch",
"Installed: vim-common-2:8.2.2637-20.el9.x86_64",
"Installed: gpm-libs-1.20.7-29.el9.x86_64",
"Installed: vim-enhanced-2:8.2.2637-20.el9.x86_64"
]
}
Read more about ad-hoc commands on Introduction to ad-hoc commands.
If you want to auto-detect the package manager, simply use package module against all hosts;
ansible -i $HOME/etc/ansible/hosts -m package -a "name=vim state=present" all -u johndoe --become
192.168.122.11 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"msg": "Nothing to do",
"rc": 0,
"results": []
}
192.168.122.92 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"cache_update_time": 1713503759,
"cache_updated": false,
"changed": false
}
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;
tree myplaybooks
myplaybooks/
├── main.yml
└── roles
├── useraccounts
│ └── tasks
│ └── main.yml
└── webserver
└── tasks
└── main.yml
6 directories, 3 files
To create the relevant directories as per above structure;
mkdir -p myplaybooks/roles/{useraccounts,webserver}/tasks
Create master playbook, main.yml to define values that will be applicable to all other tasks;
vim myplaybooks/main.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_crypt', 65534) }}"
groups: "{{ 'sudo' if ansible_distribution == 'Ubuntu' else 'wheel' }}"
shell: /bin/bash
comment: Jane Doe
expires: 1584993090
Ensure passlib for hashing the password is installed.
pip install passlib
Ansible Playbook to Install Apache Web server
Similarly, create task file for installing Apache web server on systems;
vim myplaybooks/roles/webserver/tasks/main.yml
---
#
# Install Apache Web server on CentOS 9
#
- name: Install Apache HTTP Server on CentOS
ansible.builtin.package:
name: httpd
state: latest
when: ansible_distribution == 'CentOS'
- name: Install Apache2 on Ubuntu/Debian
ansible.builtin.package:
name: apache2
state: latest
when: ansible_distribution in ['Ubuntu', 'Debian']
To list master Ansible Playbook tasks;
ansible-playbook myplaybooks/main.yml --list-tasks -i etc/ansible/hosts
playbook: myplaybooks/main.yml
play #1 (all): all TAGS: []
tasks:
useraccounts : Add User Jane Doe to the system TAGS: []
webserver : Install Apache HTTP Server on CentOS TAGS: []
webserver : Install Apache2 on Ubuntu/Debian 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/main.yml --check -i etc/ansible/hosts
PLAY [all] ************************************************************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [192.168.122.11]
ok: [192.168.122.92]
TASK [useraccounts : Add User Jane Doe to the system] *****************************************************************************************************************************
changed: [192.168.122.92]
changed: [192.168.122.11]
TASK [webserver : Install Apache HTTP Server on CentOS] ***************************************************************************************************************************
skipping: [192.168.122.92]
changed: [192.168.122.11]
TASK [webserver : Install Apache2 on Ubuntu/Debian] *******************************************************************************************************************************
skipping: [192.168.122.11]
changed: [192.168.122.92]
PLAY RECAP ************************************************************************************************************************************************************************
192.168.122.11 : ok=3 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
192.168.122.92 : ok=3 changed=2 unreachable=0 failed=0 skipped=1 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/main.yml -i etc/ansible/hosts
PLAY [all] ************************************************************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [192.168.122.11]
ok: [192.168.122.92]
TASK [useraccounts : Add User Jane Doe to the system] *****************************************************************************************************************************
changed: [192.168.122.92]
changed: [192.168.122.11]
TASK [webserver : Install Apache HTTP Server on CentOS] ***************************************************************************************************************************
skipping: [192.168.122.92]
changed: [192.168.122.11]
TASK [webserver : Install Apache2 on Ubuntu/Debian] *******************************************************************************************************************************
skipping: [192.168.122.11]
changed: [192.168.122.92]
PLAY RECAP ************************************************************************************************************************************************************************
192.168.122.11 : ok=3 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
192.168.122.92 : ok=3 changed=2 unreachable=0 failed=0 skipped=1 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: all
remote_user: johndoe
become: yes
gather_facts: yes
tasks:
- name: Gather service facts
ansible.builtin.service_facts:
- name: Check if user Jane Doe (janedoe) is created.
getent:
database: passwd
key: janedoe
- name: Check if Apache service on CentOS 9.
getent:
database: services
key: http
when: ansible_distribution == 'CentOS'
- name: Check if Apache service on Ubuntu.
ansible.builtin.debug:
msg: "Apache service is {{ 'present' if 'apache2' in ansible_facts.services.keys() else 'not present' }}"
when: ansible_distribution in ['Ubuntu', 'Debian']
Execute the playbook;
ansible-playbook myplaybooks/check.yml -i etc/ansible/hosts
PLAY [all] ************************************************************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [192.168.122.11]
ok: [192.168.122.92]
TASK [Gather service facts] *******************************************************************************************************************************************************
ok: [192.168.122.11]
ok: [192.168.122.92]
TASK [Check if user Jane Doe (janedoe) is created.] *******************************************************************************************************************************
ok: [192.168.122.92]
ok: [192.168.122.11]
TASK [Check if Apache service on CentOS 9.] ***************************************************************************************************************************************
skipping: [192.168.122.92]
ok: [192.168.122.11]
TASK [Check if Apache service on Ubuntu.] *****************************************************************************************************************************************
skipping: [192.168.122.11]
ok: [192.168.122.92] => {
"msg": "Apache service is present"
}
PLAY RECAP ************************************************************************************************************************************************************************
192.168.122.11 : ok=4 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
192.168.122.92 : ok=4 changed=0 unreachable=0 failed=0 skipped=1 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.