Ansible Playbooks

Let's first change the message of the day on centos hosts.

First let's create our ansible.cfg file,

[defaults]
inventory = hosts
host_key_checking = False

Next the message of the day itself in a file with the the name centos_motd,

Welcome to CentOs Linux - Ansible Rocks

Next out inventory file hosts,

[control]
ubuntu-c ansible_connection=local

[centos]
centos1 ansible_port=2222
centos[2:3]

[centos:vars]
ansible_user=root

[ubuntu]
ubuntu[1:3]

[ubuntu:vars]
ansible_become=true
ansible_become_pass=password

[linux:children]
centos
ubuntu

Finally our playbook motd_playbook.yaml,

---
# YAML documents begin with the document separator ---

# The minus in YAML this indicates a list item.  The playbook contains a list 
# of plays, with each play being a dictionary
-

  # Hosts: where our play will run and options it will run with
  hosts: centos
  user: root

  # Vars: variables that will apply to the play, on all target systems

  # Tasks: the list of tasks that will be executed within the playbook
  tasks:
    - name: Configure a MOTD (message of the day)
      copy:
        src: centos_motd
        dest: /etc/motd

  # Handlers: the list of handlers that are executed as a notify key from a task

  # Roles: list of roles to be imported into the play

# Three dots indicate the end of a YAML document
...

Let's run our playbook now,

$ ansible-playbook motd_playbook.yaml 

PLAY [centos] ***************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************
ok: [centos3]
ok: [centos1]
ok: [centos2]

TASK [Configure a MOTD (message of the day)] ********************************************************************************
changed: [centos1]
changed: [centos2]
changed: [centos3]

PLAY RECAP ******************************************************************************************************************
centos1                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
centos2                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
centos3                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

This playbook can further improve like below,

---
# YAML documents begin with the document separator ---

# The minus in YAML this indicates a list item.  The playbook contains a list
# of plays, with each play being a dictionary
-

  # Hosts: where our play will run and options it will run with
  hosts: linux

  # Vars: variables that will apply to the play, on all target systems
  vars:
    motd_centos: "Welcome to CentOS Linux - Ansible Rocks\n"
    motd_ubuntu: "Welcome to Ubuntu Linux - Ansible Rocks\n"

  # Tasks: the list of tasks that will be executed within the playbook
  tasks:
    - name: Configure a MOTD (message of the day)
      copy:
        content: "{{ motd_centos }}"
        dest: /etc/motd
      notify: MOTD changed
      when: ansible_distribution == "CentOS"

    - name: Configure a MOTD (message of the day)
      copy:
        content: "{{ motd_ubuntu }}"
        dest: /etc/motd
      notify: MOTD changed
      when: ansible_distribution == "Ubuntu"

  # Handlers: the list of handlers that are executed as a notify key from a task
  handlers:
    - name: MOTD changed
      debug:
        msg: The MOTD was changed

  # Roles: list of roles to be imported into the play

# Three dots indicate the end of a YAML document
...

So in here I've specified hosts as linux so that it will pick both centos and ubuntu hosts. And the motd has been defined as a variable for each os. Then I've added two tasks with a condition to execute based on the ansible_distribution. Finally a handler to inform us after exeuting each task.

Facts

Ansible facts are host specific confiruations presented to us as variables. Basically when ansible executes it's setup module we can usually notice that it collects facts. In order to get an understanding of how the facts are collected let's execute this command.

$ ansible centos1 -m setup -a 'gather_subset=network'
centos1 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "172.18.0.6"
        ],
        "ansible_all_ipv6_addresses": [],
        "ansible_apparmor": {
            "status": "disabled"
        },
        "ansible_architecture": "x86_64",
        "ansible_cmdline": {
            "console": "ttyS1",
            "earlyprintk": "serial",
            "mitigations": "off",
            "no_stf_barrier": true,
            "noibpb": true,
            "noibrs": true,
            "nospec_store_bypass_disable": true,
            "page_poison": "1",
            "panic": "1",
            "vpnkit.connect": "connect://2/1999",
            "vsyscall": "emulate"
        },
        "ansible_date_time": {
            "date": "2021-07-12",
            "day": "12",
            "epoch": "1626069384",
            "hour": "05",
...

In the output of above command you can notice that there's a root dictionary named ansible_facts. Ansible itself will populate all the key value pairs of this dictionary at the root of each hosts variable section, so that these are accessible without any prefix. For example, if we create a file like below,

# facts_playbook.yaml
---
# YAML documents begin with the document separator ---

# The minus in YAML this indicates a list item.  The playbook contains a list
# of plays, with each play being a dictionary
-

  # Hosts: where our play will run and options it will run with
  hosts: all

  # Tasks: the list of tasks that will be executed within the play, this section
  # can also be used for pre and post tasks
  tasks:
    - name: Show IP Address
      debug:
        msg: "{{ ansible_default_ipv4.address }}"

# Three dots indicate the end of a YAML document
...

You can notice that the IPv4 address of the host is accessed in ansible_default_ipv4.address form, where it actually is represented in ansible_facts.ansible_default_ipv4.address in the facts. Now if we run the playbook we might be able to see the IPv4 address of each host specified in our host file.

$ ansible-playbook facts_playbook.yaml 

PLAY [all] *********************************************************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************************************************
ok: [centos1]
ok: [centos3]
ok: [centos2]
ok: [ubuntu1]
ok: [ubuntu-c]
ok: [ubuntu2]
ok: [ubuntu3]

TASK [Show IP Address] *********************************************************************************************************************************************************************
ok: [ubuntu-c] => {
    "msg": "172.18.0.9"
}
ok: [centos1] => {
    "msg": "172.18.0.6"
}
ok: [centos2] => {
    "msg": "172.18.0.7"
}
ok: [centos3] => {
    "msg": "172.18.0.8"
}
ok: [ubuntu1] => {
    "msg": "172.18.0.2"
}
ok: [ubuntu2] => {
    "msg": "172.18.0.3"
}
ok: [ubuntu3] => {
    "msg": "172.18.0.4"
}

PLAY RECAP *********************************************************************************************************************************************************************************
centos1                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
centos2                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
centos3                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ubuntu-c                   : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ubuntu1                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ubuntu2                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ubuntu3                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Custom facts

It is also possible to define custom facts in ansible that will be gatherred during the fact gathering. A custom fact can be any script that returns a JSON structure or ini file. By default it expects to keep these custom facts in /etc/ansible/facts.d/.

In order to try this our let's create two files inside /etc/ansible/facts.d/.

getdate1.fact:

#!/bin/bash
echo {\""date\"" : \""`date`\""}

and

gatedate2.fact:

#!/bin/bash
echo [date]
echo date=`date`

Now if we execute below,

$ ansible ubuntu-c -m setup | more

We'll get the data output from the ansible_local section.

Last updated