In the previous article we talked about: Ansible ad hoc assignments† What we learned there will be applied in this article, but in a different way. This tutorial explains with examples what a Playbook is in Ansible and how to work with Ansible Playbooks.
What is Ansible Playbook?
The script is a YAML file used to perform one or more tasks against the managed hosts in the environment.
The main difference between ad hoc commands and playbooks is that ad hoc commands allow you to create simple one-liners or use the ad hoc command in shell scripts to perform simple tasks. If you want to perform complex repetitive operations, you have to write playbooks and store them in a central repository and use them when needed.
Let’s assume you are part of the system administrator team. Whenever a new server is built, your team is responsible for strengthening the server according to your company policy.
In this case, you can build a playbook and strengthen the server. Now the same playbook can be used against the N number of the new host to be hardened.
My Ansible Lab Structure
I’m going to use the ansible lab created with two Ubuntu 20.04LTS nodes. You can refer to the following article if needed to replicate the same lab setup.
Automated Ansible Lab Configuration with Vagrant and Virtualbox in Linux
Create your first Ansible Playbook
As I mentioned earlier, Playbooks are written in YAML format. The script must have a .yml
or .yaml
extension. Check out the link below if you want to learn more about YAML.
YAML Tutorial | Getting Started with YAML
Below is the sample playbook that I will use to demonstrate how the playbook works. This is a simple playbook that takes care of setting up a custom banner message when I log into the node.
- name: First Play - Modify banner message hosts: ubuntu2 gather_facts: false become: yes tasks: - name: Remove Execute Permission from /etc/update-motd.d/* file: path: /etc/update-motd.d/ mode: u=rw,g=rw,o=rw recurse: yes - name: ADD BANNER MESSAGE copy: content: "Welcome to OSTechnix" dest: /etc/motd
Let’s take a look at each section of this playbook. The script is divided into two parts. The first section contains game definitions such as game name, target hosts, and privilege escalation.
- name – The playbook must have a play name that is nothing but a logical grouping of the target host, parameters, and tasks. You can have more than one game in the playbook. Provide a descriptive name for the piece.
- hosts – Target hosts. Ansible checks the inventory file and checks if the value specified in hosts is available in the inventory file.
- become – When set to ‘Yes’ will run the task with sudo privilege. Since I’m changing the permission under the /etc/ directory, the job must be submitted with elevated privileges.
Check out our in-depth article on privilege escalation to understand it in detail.
Ansible SSH Authentication and Privilege Escalation
In the second part, tasks are defined. You must use the keyword: “tasks” to define the task. Here I have created two tasks. Each task should be named with the keyword “name”I
- The first task removes the execute permission from
/etc/update-motd.d/*
folder. i use the File module for this. - The second task will write the string “Welcome to OSTechnix” to the
/etc/motd
file with the to copy module.

Before starting the playbook, I want to check the status of my target node (managed2.anslab.com) by logging into the node. You can see that when I log into the node I get the default banner message and the execute permission is set for all files.
$ ssh [email protected]

Run the following command from the terminal to run the playbook.
$ ansible-playbook <playbook-name> # SYNTAX $ ansible-playbook banner_mod.yml

Let’s try to understand the output.
1. When you run the playbook, the first thing you see is the play name.
PLAY [Modify banner message] *
2. The first task to be performed is: “Gathering Facts”† We have not defined this task in our playbook, but it will run unless you disable it.
TASK [Gathering Facts] *
ok: [managed2]
3. Now the task you defined in the YAML file as part of the game is executed one by one. Ansible reads the playbook from top to bottom so that tasks are performed in the order defined in the playbook.
TASK [Remove Execute Permission from /etc/update-motd.d/*] ******************************************************************************************* changed: [managed2] TASK [ADD BANNER MESSAGE] **************************************************************************************************************************** changed: [managed2]
4. Once all tasks are completed, you will have a game overview with the total number of statuses of different tasks. In our case, a total of three tasks (ok=3) were performed, including fact-gathering, and only two tasks (modified=2) made the changes to the managed node.
PLAY RECAP ****************************************** managed2 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Now I can validate the managed node to see if the changes were applied successfully.

Disable Fact Collection
As predicted in the previous section when you run the playbook, ansible collects facts about the managed nodes by the setup module† You can disable fact-gathering by adding the following line in the playbook.
gather_facts : false

Now when you run the playbook again, you will see only two tasks defined in the playbook. Because Ansible is idempotent, ansible will not attempt to change the state of the object if it has already been changed. So you will see the output as: OkayI

Fact-gathering makes the game slow when you have to play the playbook against a large number of hosts. This is because ansible needs to connect to the managed node and collect a lot of data about the node, which is not necessary in all cases.
Below you can see the difference in execution time with and without facts.
$ time ansible-playbook banner_mod.yml # WITHOUT FACTS real 0m1.642s user 0m0.731s sys 0m0.220s $ time ansible-playbook banner_mod.yml # WITH FACTS real 0m2.547s user 0m1.139s sys 0m0.187s
Play multiple times
You can have more than one game in the playbook. As you can see in the image below, my second game has one task which is the . will reboot sshd
service using the maintenance module in another host.

- name: Second Play - Restart SSHD service hosts: ubuntu1 gather_facts: false become: yes tasks: - name: Restart SSHD in managed1.anslab.com service: name: sshd state: restarted
Let’s run the script again and you can see from the output that both plays have been performed successfully.
$ ansible-playbook banner_mod.yml

You should now have a good understanding of how to write and run a script. Playbook supports additional useful features that we will see in the next section.
Validate playbook syntax
You can use the --syntax-check
flag to check for syntax errors in your playbook. I purposely made a syntactic error in the “collect_facts” line in my script.
$ ansible-playbook --syntax-check banner_mod.yml

Ask for confirmation
When you use the --step
flag, it will ask for your confirmation for each task in your game to continue with the task.
$ ansible-playbook --step banner_mod.yml

Perform a dry run
Instead of running the tasks in managed nodes, you can simulate the execution with: -C
or --check
flag.
$ ansible-playbook -C banner_mod.yml
Start with a specific task
You have an option to run the playbook from a particular task. You can see from the image below that I started from Play2 (task1), so both the task in play1 is skipped. Use the flag --start-at-task
and pass the task name as argument.
$ ansible-playbook banner_mod.yml --start-at-task "Restart SSHD in managed1.anslab.com"

You can group plays and tasks with tags† Using tags, you can perform only those tasks with certain tags or skip the tasks.
You can check out the image below where I used the “label” keyword and set some values to it. You can also add multiple values to a single tag.

You can run the following command to get the list of tasks along with their tags.
$ ansible-playbook banner_mod.yml --list-tasks
$ ansible-playbook banner_mod.yml --list-tags

Usage -t
mark and pass tag names as argument. You can perform a particular task based on tags or multiple tags as shown below. In the example below, I’m using two different tags from two different plays.
$ ansible-playbook -t "set_perm","restart service" banner_mod.yml

You can also skip a task and do all other tasks with --skip-tags
flag.
$ ansible-playbook -t "set_perm","restart service" --skip-tags "set_perm" banner_mod.yml

Increase Verbosity
Sometimes the script doesn’t behave as you expect. It could be something happening in the background or various errors.
To fix the problem, you can increase the verbosity (-v) when running the playbook. There are four levels of verbosity. You can set the verbosity in the ansible.cfg
file by the property “verbosity=<level
” or from the terminal with -v
or ANSIBLE_VERBOSITY environment variable.
$ ansible-playbook -vvvv -t "set_perm","restart service" --skip-tags "set_perm" banner_mod.yml # VERBOSE LEVEL SET TO 4(-vvvv)[OR]
$ ANSIBLE_VERBOSITY=4 ansible-playbook -vvvv -t "set_perm","restart service" --skip-tags "set_perm" banner_mod.yml # VERBOSE LEVEL SET TO 4(ENV VARIABLE)

Control parallelism
When you run the playbook, ansible runs the task in batches. By default, ansible runs a task in 5 nodes in parallel and once all tasks in all 5 nodes are completed, it moves to the next set of 5 nodes.
Likewise, it will run a task in batches of 5 until the task is completed in all nodes and then move to the next task and repeat the same step.
You can control the parallelism and set how many nodes should be processed in parallel by the “forks” parameter in the ansible.cfg
File.
[defaults]
inventory = inventory
host_key_checking = False
forks=20
I have set the value of forks to 20. It will now process a job on 20 nodes in parallel. Likewise, you can increase/decrease the number depending on your need.
You can change the value set in . Overwrite ansible.cfg
file by passing -f
or --forks
flag. When I run the following command, the fork value of 20 is overwritten by 15.
$ ansible-playbook banner_mod.yml -f 15
Conclusion
In this article, we’ve seen how to write Ansible playbooks and the different parameters and options supported in playbooks. There are plenty of other functions like variables, conditions, loops, etc. that can be used in playbooks that we will cover in our coming articles.