In the previous post, we looked at running Ad-Hoc commands on remote systems. In this post, we will see how can a playbook (a set of instructions and data in yaml format) be written to execute a set of tasks. We will continue to use the inventory file that we defined in the previous post.



server_oracle_1 primary_dns='' secondary_dns=''







Let’s look at the below example to understand the working of Ansible playbook. The playbook begins with three dashes, signaling the beginning of the document. This is followed by some yaml formatted data which are ‘hosts specification’, followed by a ‘set of tasks’ that should run on these hosts. We also see a ‘vars’ section where in, some variables are defined.

We already know from the previous post that, variables can be defined in the inventory file specific to hosts and groups. Variables can also be defined in a playbook. Variables defined in a playbook takes precedence over the inventory variables. The below example define a different primary and secondary DNS than the ones defined in the inventory. A typical task block includes a name section to describe the task, followed by the name of the ansible module with options. In the below example, we use the shell module to update the DNS and the service module to disable the NetworkManager service. These tasks are run on the group “application_servers” only and the user ID used to ssh to these machines is defined as “admin”.

- hosts: application_servers
  remote_user: admin
  - name: Update DNS records on remote systems
    shell: |
      echo "{{ primary_dns }}" > /etc/resolv.conf	  
      echo "{{ secondary_dns }}" >> /etc/resolv.conf	  
	  echo "search" >> /etc/resolv.conf

  - name: stop and disable network manager service
      name: NetworkManager
	  state: stopped 
      disabled: yes

Running an Ansible playbook

Command: ‘ansible-playbook path/to/playbooks/myplaybook.yml -u username -k -K’

(we already know that -k and -K makes Ansible prompt for password and sudo password respectively)

Playbooks can contain multiple plays. You may have a playbook that targets first the application_servers, and then the DB_servers servers. For example:

- hosts: application_servers
  remote_user: root

  - name: ensure wget package is at the latest version
      name: wget
      state: latest

- hosts: DB_servers
  remote_user: admin

  - name: ensure openjdk-devel is at the latest version
      name: java-1.8.0-openjdk-devel
      state: latest
      become: yes
  - name: ensure that firewalld is started
      name: firewalld
      state: started
      become: yes

NOTE: ‘become’ parameter when set to yes, makes the task run with sudo root privileges. we can also make the task run as a specific user by specifying the username. example ‘become: admin’. When the ‘become’ keyword is not specified, the task by default runs with the username that was used to ssh to the remote system.

Now let’s save this file as ‘test.yaml’ and run it on our inventory file ‘myinventory.ini’


[admin@unixutils ~]# ansible-playbook -i myinventory.ini test.yaml -k -K
SSH password:
SUDO password[defaults to SSH password]:

PLAY [application_servers] **************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************
ok: [server_java_app1]
ok: [server_php_app1]
ok: [server_php_app2]
ok: [marketing_server_app]
ok: [monitoring_server_app]
ok: [ftp_server_app1]

TASK [ensure wget package is at the latest version] *************************************************************************************
changed: [marketing_server_app]
ok: [ftp_server_app1]
ok: [monitoring_server_app]

PLAY [DB_servers] ***********************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************
ok: [server_oracle_1]
ok: [server_mysql_1]

PLAY RECAP ******************************************************************************************************************************
ftp_server_app1            : ok=2    changed=0    unreachable=0    failed=0
marketing_server_app       : ok=2    changed=1    unreachable=0    failed=0
monitoring_server_app      : ok=2    changed=0    unreachable=0    failed=0
server_java_app1           : ok=2    changed=0    unreachable=0    failed=0
server_mysql_1             : ok=1    changed=1    unreachable=0    failed=0
server_oracle_1            : ok=1    changed=1    unreachable=0    failed=0
server_php_app1            : ok=2    changed=0    unreachable=0    failed=0
server_php_app2            : ok=2    changed=0    unreachable=0    failed=0

[admin@unixutils ~]#

Ansible reports the status of the tasks for each host. ‘ok’ indicates that a task was not run since the expected result of a task is already satisfied and in place and hence successful. Whereas, a ‘Changed’ status indicates that the task was run and it was successful.

In this post we looked at how to write a set of tasks in a single playbook. Ansible allows us to create ‘roles’ which are a set of tasks that can be re-used in multiple playbooks. This not only makes the code reusable but also helps in organizing the tasks. For instance, you can write an “add user” role to create user IDs, which can be imported into different playbooks to create a different set of users defined separately for the playbook. The idea of the role is to have the functionality of the code independent of the data that it uses to run the task. In future posts, we will look at how to define a role with examples.