# Playbook Modules

Ansible consist of many pre-built modules that helps us to create playbooks. For example,

* set\_fact
* pause
* prompt
* wait\_for
* assemble
* add\_host
* group\_by
* fetch

### set\_fact

This module allows us to add or change facts during the execution.

For example if we consider the below playbook,

```
---
# 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: ubuntu3,centos3

  # 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: Set a fact
      set_fact:
        our_fact: Ansible Rocks!

    - name: Show custom fact
      debug:
        msg: "{{ our_fact }}"

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

In the first task we are setting a fact with the name `out_fact` and in the second task we print it's value using the `debug` module.

In addition to that, we can set multiple facts in the same iteration of the `set_fact` module. For example, let's modify our playbook in such a way,

```
---
# 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: ubuntu3,centos3

  # 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: Set a fact
      set_fact:
        our_fact: Ansible Rocks!
        ansible_distribution: "{{ ansible_distribution | upper }}"

    - name: Show our_fact
      debug:
        msg: "{{ our_fact }}"

    - name: Show ansible_distribution
      debug:
        msg: "{{ ansible_distribution }}"

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

Then, let's assume that we have to set facts conditionally, for example, based on the environment. In such cases, we can do this by using the `when` key word along with the `set_fact` module,

```
---
# 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: ubuntu3,centos3

  # 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: Set our installation variables for CentOS
      set_fact:
        webserver_application_port: 80
        webserver_application_path: /usr/share/nginx/html
        webserver_application_user: root
      when: ansible_distribution == 'CentOS'

    - name: Set our installation variables for Ubuntu
      set_fact:
        webserver_application_port: 8080
        webserver_application_path: /var/www/html
        webserver_application_user: nginx
      when: ansible_distribution == 'Ubuntu'

    - name: Show pre-set distribution based facts
      debug:
        msg: "webserver_application_port:{{ webserver_application_port }} webserver_application_path:{{ webserver_application_path }} webserver_application_user:{{ webserver_application_user }}"

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

`pause` module allows us to temporarily stop the playbook execution for a specified time period. It is also possible to interrput the pause by clieck control+C keys. For example,

```
---
# 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: ubuntu3,centos3

  # 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: Pause our playbook for 10 seconds
      pause:
        seconds: 10

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

Running this playbook will output the following:

```
$ ansible-playbook pause_playbook.yaml 

PLAY [ubuntu3,centos3] ****************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [ubuntu3]
ok: [centos3]

TASK [Pause our playbook for 10 seconds] **********************************************
Pausing for 10 seconds
(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)
ok: [ubuntu3]

PLAY RECAP ****************************************************************************
centos3                    : ok=1    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 
```

In a situation where we have to take a manual action, we can use the `pause` module with `prompt` option. This will allow us to show a message to the user and ask to perform an action. For example,

```
---
# 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: ubuntu3,centos3

  # 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: Prompt user to verify before continue
      pause:
        prompt: Please check that the webserver is running, press enter to continue

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

Running this playbook will result in the following output,

```
 ansible-playbook pause_playbook.yaml 

PLAY [ubuntu3,centos3] **********************************************************

TASK [Gathering Facts] **********************************************************
ok: [centos3]
ok: [ubuntu3]

TASK [Prompt user to verify before continue] ************************************
[Prompt user to verify before continue]
Please check that the webserver is running, press enter to continue:
ok: [ubuntu3]

PLAY RECAP **********************************************************************
centos3                    : ok=1    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   
```

In some cases when we want to wait for certian operations to be completed, we could use the `wait_for` module rather than using `pause`. For example, in a situation where we have to wait until a port becomes available.

```
---
# 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: ubuntu3,centos3

  # 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: Wait for the webserver to be running on port 80
      wait_for:
        port: 80

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

`assemble` module allows us to concatenate multiple files into one. For example, imagin a situation where we keep a comman ssh config file along with additional host specific configurations in separate files. Now that when we want to connect with those, we can use the `assemble` module to create a single ssh config file.

For example, let's start by creating a couple of ssh config files in `conf.d` directory like below,

`conf.d/defaults`:

```
## Defaults

Port 22
Protocol 2
ForwardX11 yes
GSSAPIAuthentication no
```

`conf.d/centos1`:

```
## Custom for centos1
Host centos1
  User root
  Port 2222
```

`assemble_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: ubuntu-c

  # 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: Assemble conf.d to sshd_config
      assemble:
        src: conf.d
        dest: sshd_config

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

In above playbook, it takes all content inside `config.d` directory and create a single config file with the name `sshd_config`. Which would contain a merged set of entries like below,

```
## Custom for centos1
Host centos1
  User root
  Port 2222

## Defaults

Port 22
Protocol 2
ForwardX11 yes
GSSAPIAuthentication no
```

`add_host` is another module where we can add hosts to our inventory dynamically. This useful when we create hosts in our playbook. Playbook example for this looks 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: ubuntu-c

  # 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: Add centos1 to adhoc_group
      add_host:
        name: centos1
        groups: adhoc_group1, adhoc_group2

# 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: adhoc_group1

  # 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: Ping all in adhoc_group1
      ping:

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

Above playbook will add `centos1` to `adhoc_group1` and `adhoc_group2` and then ping all instances in `adhoc_group1`.

Similarly `group_by` module associates / group items based on what's been advised. For example, below playbook group each host by OS distribution and then ping all centos instances in the custom group.

```
---
# 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: Create group based on ansible_distribution
      group_by:
        key: "custom_{{ ansible_distribution | lower }}"

# 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: custom_centos

  # 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: Ping all in custom_centos
      ping:

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

Finally, `fetch` module allows us to get files from the remote machine. For example,

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

  # 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: Fetch /etc/redhat-release
      fetch:
        src: /etc/redhat-release
        dest: /tmp/redhat-release

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