Ansible Playbooks

What is Ansible Playbook?

Ansible Playbooks are the way of sending commands to remote systems through scripts. Ansible playbooks are used to configure complex system environments to increase flexibility by executing a script to one or more systems.
An Ansible Playbook can be as simple as running a series of commands on different server in sequence and restarting those servers in a particular order, or it could be as complex as deploying hundreds of VMs in a public and private cloud infrastructure!
1
---
2
3
- name: Simple Playbook
4
hosts: webservers
5
tasks:
6
- name: ensure apache is at the latest version
7
yum:
8
name: httpd
9
state: latest
Copied!
Ansible playbooks tend to be more of a configuration language than a programming language. Ansible playbook commands use YAML format.
  • Playbook -A single YAML file
    • Play - Defines a set of activities (tasks) to be run on hosts
      • Task - An action to be performed on the host. examples:
        • Execute a command
        • Run a script
        • Install a package
        • Shutdown/Restart

Sample Ansible Playbooks

Here we have a simple ansible Playbook, which helps you to get the idea:
1
---
2
3
#Sample Ansible Playbook1.yml
4
-
5
name: Play1
6
hosts: centos
7
tasks:
8
- name : Execute command 'date'
9
command: date
10
11
- name : Execute script on server
12
script: my_script.sh
13
14
- name : Install httpd service
15
yum:
16
name: httpd
17
state: present
18
19
- name : Start web server
20
service:
21
name: httpd
22
state: started
Copied!
Remember the host we want to perform these operations against is always set at a play level. You could have any host or groups specified here but you must ensure that the host or group is first defined in the inventory file we created earlier. The host defined in the inventory file must match the host used in the Playbook .
All connection information for the host is retrieved from the inventory file. There is no hard rule to use all the hosts defined in the inventory file. We can choose one or multiple or a group or multiple groups from the inventory file in the play.
We can also split the list of tasks into two separate plays (using our YAML skills):
1
---
2
3
#Sample Ansible Playbook2.yml
4
-
5
name: Play1
6
hosts: centos
7
tasks:
8
- name : Execute command 'date'
9
command: date
10
11
- name : Execute script on server
12
script: my_script.sh
13
14
-
15
name: Play2
16
hosts: centos
17
tasks:
18
- name : Install httpd service
19
yum:
20
name: httpd
21
state: present
22
23
- name : Start web server
24
service:
25
name: httpd
26
state: started
Copied!
The '-' indicates that it is an item in the list. So the Playbook is a list of dictionaries. Each play is a dictionary and has a set of properties called name, hosts and tasks . Remember these are properties of a dictionary and so the order doesn't really matter. So even if you swap the position of name and hosts, it's still a valid play.
How ever this is not the same for tasks. The tasks is a list as denoted by the dashes. List are ordered collections, So the position of entries matter. Swapping the position of entries here, really matters.
The different actions run by tasks are called modules. In our example, command, script, yum and service are Ansible Modules. There hundreds of other modules available. We will talk about them later.
The YAML format is key while developing Playbooks. We must pay extra attention to the indentation and structure of this file. For testing yaml files:

Running Ansible

There are Two ways of running Ansible:
  • ansible command (ad-hoc commands) : Used when we want to use ansible for One task, such as Testing connectivity between ansible controller and target, Shutting down a set of server, ... . In that case we can run ansible with out writing a playbook.
  • ansible-playbook command : used when you have a playbook.
ansible (Imperative)
ansible-playbook (Declarative)
ansible <host> -a <command>
ansible-playbook <playbook name>
ansible all -a '''/sbin/reboot'
ansible <host> -m <module>
ansible target1 -m ping

Demo - Running Ansible

We have already talked about ad-hoc commands, so go back for more examples:
1
[[email protected] demo-playbook]$ ansible all -m ping
2
centos | SUCCESS => {
3
"ansible_facts": {
4
"discovered_interpreter_python": "/usr/bin/python"
5
},
6
"changed": false,
7
"ping": "pong"
8
}
9
ubuntu | SUCCESS => {
10
"ansible_facts": {
11
"discovered_interpreter_python": "/usr/bin/python3"
12
},
13
"changed": false,
14
"ping": "pong"
15
}
Copied!

Demo - Running ansible playbooks

Lets do the same thing using a playbook:
1
---
2
3
#ping-playbook.yaml
4
-
5
name: Test Connectivity
6
hosts: all
7
tasks:
8
- name: Ping test
9
ping:
Copied!
1
[[email protected] demo-playbook]$ ansible-playbook ping-playbook.yaml
2
3
PLAY [Test Connectivity] ****************************************************************************************************************
4
5
TASK [Gathering Facts] ******************************************************************************************************************
6
ok: [centos]
7
ok: [ubuntu]
8
9
TASK [Ping test] ************************************************************************************************************************
10
ok: [centos]
11
ok: [ubuntu]
12
13
PLAY RECAP ******************************************************************************************************************************
14
centos : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
15
ubuntu : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Copied!
We do not need to any hosts here, because we have specified the hosts inside the playbook.
As second example lets create a new playbook using copy module, to copy some files to target systems. Always use ansible documentations it has good examples
1
### Creating a test file
2
[[email protected] demo-playbook]$ cat > /tmp/test-file.txt
3
Sample Text File!!!
4
^C
Copied!
1
---
2
3
# Sample copy playbook!
4
5
-
6
name: Copy file to target server(s)
7
hosts: all
8
tasks:
9
- name: Copy file
10
copy:
11
src: /tmp/test-file.txt
12
dest: /tmp/test-file.txt
Copied!
1
[[email protected] demo-playbook]$ ansible-playbook copy-playbook.yaml
2
3
PLAY [Copy file to target server(s)] ****************************************************************************************************
4
5
TASK [Gathering Facts] ******************************************************************************************************************
6
ok: [centos]
7
ok: [ubuntu]
8
9
TASK [Copy file] ************************************************************************************************************************
10
changed: [centos]
11
changed: [ubuntu]
12
13
PLAY RECAP ******************************************************************************************************************************
14
centos : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
15
ubuntu : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Copied!
If you check the target server(s) you would see the test-file is now there.
1
[email protected]:~# su - user1
2
$ cat /tmp/test-file.txt
3
Sample Text File!!!
4
$
Copied!
Know if we execute the playbook again what will happened? Copy the files? Over write it? Or just leave it as it is because the file is over there?
1
[[email protected] demo-playbook]$ ansible-playbook copy-playbook.yaml
2
3
PLAY [Copy file to target server(s)] ****************************************************************************************************
4
5
TASK [Gathering Facts] ******************************************************************************************************************
6
ok: [centos]
7
ok: [ubuntu]
8
9
TASK [Copy file] ************************************************************************************************************************
10
ok: [centos]
11
ok: [ubuntu]
12
13
PLAY RECAP ******************************************************************************************************************************
14
centos : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
15
ubuntu : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Copied!
As we said Ansible is smart enough and detects that the file is over there and does not change any thing. And that is the concept of Ansible Idempotency.
Idempotency: In simple words, Ansible only makes a change if it has to, if it is already in the same state then it really doesn't make the change. So we can run the ansible playbook as many time as we want, it is only going to make a change when there is a need.
Use ansible-playbook with -v or --verbose switches to get more information, use -vv or even -vvv for more information. -vvvv enables connection debugging!

Privilege Escalation

We have already get familiar with privilege escalation concept and why we need that. First lets run a sample playbook with out escalating privileges and see the results:
1
---
2
3
# Sample whoami-playbook.yaml
4
5
- hosts: all
6
tasks:
7
- name: do a uname
8
shell: uname -a > /home/user1/results.txt
9
10
- name: whoami
11
shell: whoami >> /home/user1/results.txt
Copied!
1
[[email protected] demo-playbook]$ ansible-playbook whoami-playbook.yaml
2
3
PLAY [all] ******************************************************************************************************************************
4
5
TASK [Gathering Facts] ******************************************************************************************************************
6
ok: [centos]
7
ok: [ubuntu]
8
9
TASK [do a uname] ***********************************************************************************************************************
10
changed: [centos]
11
changed: [ubuntu]
12
13
TASK [whoami] ***************************************************************************************************************************
14
changed: [ubuntu]
15
changed: [centos]
16
17
PLAY RECAP ******************************************************************************************************************************
18
centos : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
19
ubuntu : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Copied!
and the results:
1
###on ubuntu :
2
[email protected]:~$ cat results.txt
3
Linux ubuntu 5.4.0-42-generic #46~18.04.1-Ubuntu SMP Fri Jul 10 07:21:24 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
4
user1
5
6
### on centos:
7
[[email protected] ~]$ cat results.txt
8
Linux centos.example.com 3.10.0-1127.el7.x86_64 #1 SMP Tue Mar 31 23:36:51 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
9
user1
Copied!
There are two different ways we can run this as root:
  • use -b switch while running playbook: ansible-playbook -b <playbook.yaml>
  • add a line become: yes or become: true inside playbook.
1
---
2
3
# Sample whoami-playbook.yaml
4
5
- hosts: all
6
become: true
7
tasks:
8
- name: do a uname
9
shell: uname -a > /home/user1/results.txt
10
11
- name: whoami
12
shell: whoami >> /home/user1/results.txt
Copied!
Now run it again and check the results:
1
[[email protected] demo-playbook]$ ansible-playbook whoami-playbook.yaml
2
3
PLAY [all] ******************************************************************************************************************************
4
5
TASK [Gathering Facts] ******************************************************************************************************************
6
ok: [centos]
7
ok: [ubuntu]
8
9
TASK [do a uname] ***********************************************************************************************************************
10
changed: [ubuntu]
11
changed: [centos]
12
13
TASK [whoami] ***************************************************************************************************************************
14
changed: [ubuntu]
15
changed: [centos]
16
17
PLAY RECAP ******************************************************************************************************************************
18
centos : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
19
ubuntu : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Copied!
1
### ubuntu
2
[email protected]:~$ cat results.txt
3
Linux ubuntu 5.4.0-42-generic #46~18.04.1-Ubuntu SMP Fri Jul 10 07:21:24 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
4
root
5
6
### centos
7
[[email protected] ~]$ cat results.txt
8
Linux centos.example.com 3.10.0-1127.el7.x86_64 #1 SMP Tue Mar 31 23:36:51 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
9
root
Copied!
use become_user the user name that we want to switch to like compare it with sudo su - user .

Handlers

Sometimes you want a task to run only when a change is made on a machine. For example, you may want to start a service if a task updates the configuration of that service, but not if the configuration is unchanged. Ansible uses handlers to address this use case. Handlers are tasks that only run when notified. If a handler get notified multiple times, it just runs once. Each handler should have a globally unique name.
As an example lets install vsftp , vsftp is the name of the package on both ubuntu and centos:
1
---
2
3
### sample handler-playbook.yaml
4
5
- hosts: all
6
become: yes
7
tasks:
8
- name: install vsftpd on ubuntu
9
apt: name=vsftpd update_cache=yes state=latest
10
ignore_errors: yes
11
notify: start vsftpd
12
13
- name: install vsftpd on centos
14
yum: name=vsftpd state=latest
15
ignore_errors: yes
16
notify: start vsftpd
17
18
handlers:
19
- name: start vsftpd
20
service: name=vsftpd enabled=yes state=started
Copied!
As there is no apt on centos, ignore_error cause playbook continue running other tasks even if this task fails. So if there is an error keep going!
1
[[email protected] demo-playbook]$ ansible-playbook handler-playbook.yaml
2
3
PLAY [all] ******************************************************************************************************************************
4
5
TASK [Gathering Facts] ******************************************************************************************************************
6
ok: [centos]
7
ok: [ubuntu]
8
9
TASK [install vsftpd on ubuntu] *********************************************************************************************************
10
[WARNING]: Updating cache and auto-installing missing dependency: python-apt
11
fatal: [centos]: FAILED! => {"changed": false, "cmd": "apt-get update", "msg": "[Errno 2] No such file or directory", "rc": 2}
12
...ignoring
13
changed: [ubuntu]
14
15
TASK [install vsftpd on centos] *********************************************************************************************************
16
ok: [ubuntu]
17
changed: [centos]
18
19
RUNNING HANDLER [start vsftpd] **********************************************************************************************************
20
ok: [ubuntu]
21
changed: [centos]
22
23
PLAY RECAP ******************************************************************************************************************************
24
centos : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
25
ubuntu : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
26
Copied!
and lets run it again!
1
[[email protected] demo-playbook]$ ansible-playbook handler-playbook.yaml
2
3
PLAY [all] ******************************************************************************************************************************
4
5
TASK [Gathering Facts] ******************************************************************************************************************
6
ok: [centos]
7
ok: [ubuntu]
8
9
TASK [install vsftpd on ubuntu] *********************************************************************************************************
10
[WARNING]: Updating cache and auto-installing missing dependency: python-apt
11
fatal: [centos]: FAILED! => {"changed": false, "cmd": "apt-get update", "msg": "[Errno 2] No such file or directory", "rc": 2}
12
...ignoring
13
ok: [ubuntu]
14
15
TASK [install vsftpd on centos] *********************************************************************************************************
16
ok: [ubuntu]
17
ok: [centos]
18
19
PLAY RECAP ******************************************************************************************************************************
20
centos : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
21
ubuntu : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Copied!
and this time no RUNNING HANDLER [start vsftpd] if you have noticed! Because there is no changes made.
.
.
.
.
.
Last modified 3mo ago