Ansible is an open-source tool used for automating tasks related to management, configuration, and maintenance of servers or network devices.
Official website and documentation: https://docs.ansible.com/
So, what does Ansible do? It connects to selected nodes (hosts) from your inventory and sequentially executes tasks to bring them into the desired state.
In addition, Ansible has a large community that develops and supports various modules.
Main use cases:
It’s important to understand that Ansible is just a tool. Choosing the right tool depends on the task at hand. For example, theoretically, you can use Ansible to manage Kubernetes (k8s) configurations, but there are better tools for that purpose: Helm, helmwave, and kustomize.
Another example: with Ansible, you can create virtual machines and containers in clouds or hypervisors, but Terraform is better suited for such tasks.
Ansible can be installed as a standalone package or via Python libraries.
Installation instructions for different operating systems are available in the documentation.
For installation, the following VPS at Hostman is sufficient:
Path to the configuration file:
/srv/ansible/ansible.cfg
Example configuration:
[defaults]
inventory = ./hosts.yaml
host_key_checking = False
log_path = /var/log/ansible.log
vault_password_file = ./vault.key
force_color = 1
callback_result_format = yaml
[ssh_connection]
timeout = 10
retries = 2
server_alive_interval = 60
pipelining = True
ssh_args = "-o StrictHostKeyChecking=no"
scp_if_ssh = True
allow_world_readable_tmpfiles = True
inventory = ./hosts.yaml
: specifies the path to the hosts file. This file stores information about all managed hosts and organizes them into logical groups.
host_key_checking = False
: by default, Ansible enables host key checking. Host key checking protects against server spoofing. Not recommended in production, but convenient for development.
log_path = /var/log/ansible.log
: path to the log file.
vault_password_file = ./vault.key
: path to the master password file, used for Ansible Vault encryption.
force_color = 1
: enables colored logs for readability.
callback_result_format = yaml
: formats output results as YAML.
Some parameters can be set as environment variables, for example:
ANSIBLE_HOST_KEY_CHECKING=False
ANSIBLE_FORCE_COLOR=1
Other parameters, such as inventory
or vault_password_file
, can be set in the command line when launching:
ansible-playbook ansible/debug.yaml -i ansible/hosts.yaml
But it is more convenient and clearer to store basic settings in the file ansible/ansible.cfg
.
To manage hosts, the file ansible/hosts.yaml
is used.
Example:
all:
vars:
ansible_user: ansible
hosts:
5.181.182.204: # your host IP or domain
ansible_user: root
# ansible_password: 'SuperPass'
ansible_ssh_private_key_file: ./ansible/ssh/id_rsa
As connection points, you can use either an IP address or a domain name. In this example, there is no domain name, so we specify the external IP address obtained when creating the Hostman VM.
Note: the user password must be enclosed in quotes.
Connections usually use either a password or a certificate (public key must be pre-installed on remote nodes).
For experiments, it’s easier to use a password. When using a private key, you must check file permissions for id_rsa
; only the owner should have read access; no one else can copy or modify the key:
chmod -R 400 ./ansible/ssh/id_rsa
In the file ansible/hosts.yaml
, the variable vars.ansible_user
is set for demonstration. Inside hosts.ansible_user
, the same variable is redefined as root
. If you don’t override it and only define it at the higher vars
level, you can avoid duplicating common variables across different hosts.
Let’s start with simple tasks that don’t make changes to the system but help to understand the structure of commands.
For example, we have the ansible/debug.yaml
file, a simple playbook example.
Run it:
ansible-playbook ansible/debug.yaml
In the output, we’ll see various system information and go through the commands from the file ansible/debug.yaml
in order:
- hosts: all
vars:
my_variable: aaaawww
tasks:
- name: Show ansible variable
debug:
msg: "variable: {{ my_variable }}"
- name: Show environment variable
debug:
var: lookup('env', 'ANSIBLE_CONFIG')
- name: Show OS release info
debug:
msg: "{{ ansible_distribution }} {{ ansible_distribution_version}} {{ansible_distribution_release}}"
hosts
: defines which nodes (hosts) to run commands on. Since we specified only one host in ansible/hosts.yaml
, all tasks will be executed on that host.
vars
: allows us to set variables at the playbook level.
tasks
: sequence of tasks to run.
Output of the variable set in vars
:
- name: Show ansible variable
debug:
msg: "variable: {{ my_variable }}"
Output of an environment variable:
- name: Show environment variable
debug:
var: lookup('env', 'ANSIBLE_CONFIG')
Output of OS and hardware info:
- name: Show OS release info
debug:
msg: "{{ ansible_distribution }} {{ ansible_distribution_version}} {{ansible_distribution_release}}"
- name: Show total CPU + RAM
debug:
msg: |
CPU cores: {{ ansible_processor_cores * ansible_processor_count }}
Total memory: {{ ansible_memtotal_mb }} MB
By default, unless gather_facts: false
is set, Ansible automatically collects system information when connecting to a host.
To view all collected information, you can use this task:
tasks:
- name: Show all facts
debug:
var: ansible_facts
Each task is described using a module and its parameters:
name
: task name (arbitrary)
debug
: name of the module (from the list of available ones, depending on goals)
debug.var
: module parameter
In the documentation, you may also see examples like:
tasks:
- name: Show all facts
ansible.builtin.debug:
var: ansible_facts
ansible.builtin.debug
is the same as the debug
module (the ansible.builtin
prefix can be omitted).
debug
and other standard modules are part of Ansible’s core. All built-in modules can be found in the documentation.
See Documentation for the module.
- name: Install packages
apt:
pkg:
- htop
- curl
Even without documentation, it’s clear that this task will install the htop
and curl
packages (equivalent to apt install -y htop curl
).
Goal: Check that the variable my_age
contains a number between 18 and 100.
- name: Check var
hosts: all
vars:
my_age: 42
tasks:
- name: Checking the type of a variable
fail:
msg: "The variable must be an integer"
when: my_age is not integer
- name: The value of the my_age variable must be between 18 and 100
assert:
that:
- my_age <= 100
- my_age >= 18
fail_msg: "Incorrect my_age value - must be from 0 to 100"
success_msg: "The value of my_age is correct"
At the start, the variable my_age = 42
is set.
The first task checks with when if it’s actually an integer; if not, the playbook stops with an error.
The second task checks if the value is within the range.
If you run the playbook, it will succeed. But if you override the variable in the launch command:
ansible-playbook ansible/playbooks/assert.yaml --extra-vars "{ my_age: 769 }"
You’ll get an error and the playbook will stop:
TASK [The value of the my_age variable must be between 18 and 100] **************
fatal: [5.181.182.204]: FAILED! =>
assertion: my_age <= 100
changed: false
evaluated_to: false
msg: Incorrect my_age value - must be from 0 to 100
Loops in Ansible look like this:
- hosts: all
tasks:
- name: Register loop output as a variable
shell: "echo {{ item }}"
loop:
- "test"
- "test2"
- "test3"
register: echo
In this task, the echo
command will run for each element in the loop list.
Additional functions:
map
: a basic for
loop; iterates over list items.
select
/ reject
: conditional for
; creates a subset of a list matching (or not matching) conditions.
selectattr
/ rejectattr
: similar, but works on specific attributes of list elements.
Example: select only red fruits from a list using selectattr
.
- hosts: all
vars:
fruits_list:
- name: apple
color: red
- name: banana
color: yellow
- name: cherry
color: red
tasks:
- name: Selectattr show red fruits
debug:
msg: "{{ item.name }} is red."
loop: "{{ fruits_list | selectattr('color', '==', 'red') | list }}"
Playbook: ansible/playbooks/copy.yaml
See Documentation for the module.
- name: Copy files to remote locations
hosts: all
tasks:
- name: Copy file with owner and permissions
copy:
src: ../files
dest: /tmp
owner: root
group: root
mode: '0644'
This task copies the directory from ansible/files
to /tmp/files
on the remote host.
If src
is a directory, it is copied recursively. If the path ends with /
, only the contents are copied into the destination. This behavior is similar to rsync
.
To copy files from a remote host back to local, use the fetch module (see documentation).
See Documentation for the module.
Templates let you create dynamic files by inserting variable values. Ansible uses the Jinja2 templating engine.
Example template file ansible/files/simple_template.j2
(not required to use .j2
extension, but recommended):
# This is a simple example of using a template.
name: {{ name }}
task: {{ task }}
Playbook using the template:
- name: Template a file out to a target host
hosts: all
tasks:
- name: Simple template a file
template:
src: ../files/simple_template.j2
dest: /tmp/test.conf
vars:
name: "Ansible"
task: "Template"
As the result, the remote host receives the file with substituted variables.
You may want to check an online service for creating and testing templates: tech-playground.com.
The main way to run and use Ansible is the command line, but there are projects that provide a graphical interface for managing tasks:
Infrastructure automation tools fall into two main categories:
Configuration management tools:
Their main job: configuring and managing software on already existing servers. They automate software installation, package updates, and system settings.
Provisioning tools:
Their job: creating the infrastructure itself: virtual machines, networks, databases. This is a broader approach that starts from the foundation of IT systems.
Often, these tools are combined, with Terraform + Ansible being the most common pairing.
Ansible
Chef
SaltStack
Puppet
In any case, we recommend starting with Ansible when learning infrastructure automation.
In this article, we aimed to show Ansible’s built-in modules and basic usage examples as clearly as possible. However, the most effective way to learn such tools is practice.
Ansible skills are often required in job postings for system administrators, DevOps engineers, and SREs.
Next steps for learning: