# 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://isurus.gitbook.io/infrastructure-and-platform-notes/ansible-4/05-playbooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
