Recarregando o inventário do Ansible durante a execução
Tags: ansible, sre, devops, iac
Published: 2021-10-11 23:42:05

## Introdução

Esta dica surgiu a partir de uma dúvida no Telegram da [LINUXtips](https://linuxtips.io) e é ótima para quem precisa trabalhar com inventários dinâmicos ou realiza modificações em seu arquivo de inventário.

Imagine o seguinte cenário: Você possui um playbook que realiza modificações no seu inventário como adicionar um novo *host*, e durante a mesma execução do *playbook* precisa que este *host* recém-adicionado seja levado em consideração para uma próxima *task* ou *role*.

Para melhor ilustrar, criei abaixo um arquivo de inventário e um *playbook* que simplesmente irá imprimir todos os *hosts* do inventário, adicionar um novo *host* chamado `host-03` e depois imprimir novamente os *hosts*:

```ini
# hosts
[all]
host-01
host-02
```

```yaml
# playbook.yml
--- 
- hosts: localhost
  become: true
  connection: local
  tasks:

    - name: Print list of hosts in inventory
      ansible.builtin.debug:
        msg: "{{ item }}"
      loop: "{{ groups['all'] }}"

    - name: Add host to inventory
      ansible.builtin.lineinfile:
        line: host-03
        path: ./hosts
        regexp: "^host-03"
        state: present

    - name: Print list of hosts in inventory
      ansible.builtin.debug:
        msg: "{{ item }}"
      loop: "{{ groups['all'] }}"
```

Vamos executar o nosso *playbook* com `ansible-playbook -i hosts playbook.yml`, obtendo o seguinte resultado:

```textile
PLAY [localhost] ************************************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************************************
ok: [localhost]

TASK [Print list of hosts in inventory] *************************************************************************************************************************************
ok: [localhost] => (item=host-01) => {
    "msg": "host-01"
}
ok: [localhost] => (item=host-02) => {
    "msg": "host-02"
}

TASK [Add host to inventory] ************************************************************************************************************************************************
changed: [localhost]

TASK [Print list of hosts in inventory] *************************************************************************************************************************************
ok: [localhost] => (item=host-01) => {
    "msg": "host-01"
}
ok: [localhost] => (item=host-02) => {
    "msg": "host-02"
}

PLAY RECAP ******************************************************************************************************************************************************************
localhost                  : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
```

Percebe que foi adicionado um novo *host* `host-03` (inclusive, você perceberá a nova entrada no arquivo `hosts`), porém na segunda impressão, ele continua não sendo impresso? O motivo disso é devido ao fato de que o Ansible apenas carrega na memória o inventário durante o início da execução do *playbook*, ignorando eventuais alterações no mesmo (inclusive para inventários dinâmicos).

Mas a pergunta que fica é: OK, e se eu precisar que alterações no meu inventário sejam à quente, para continuidade na execução? Bom, aí temos principalmente duas opções:

## Opção A: utilize o módulo add_host

O módulo `add_host` serve para adicionarmos *hosts* dinamicamente à execução do Ansible, porém não persistidas no arquivo de inventário. Podemos utilizá-lo, adicionando uma *task* extra ao nosso *playbook*, carregando o *host* `host-03` também na memória:

```yaml
--- 
- hosts: localhost
  become: true
  connection: local
  tasks:

    - name: Print list of hosts in inventory
      ansible.builtin.debug:
        msg: "{{ item }}"
      loop: "{{ groups['all'] }}"

    - name: Add host to inventory file
      ansible.builtin.lineinfile:
        line: host-03
        path: ./hosts
        regexp: "^host-03"
        state: present

    - name: Add host to inventory (memory)
      ansible.builtin.add_host:
        name: host-03

    - name: Print list of hosts in inventory
      ansible.builtin.debug:
        msg: "{{ item }}"
      loop: "{{ groups['all'] }}"
```

O resultado da execução é o seguinte:

```
PLAY [localhost] ************************************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************************************
ok: [localhost]

TASK [Print list of hosts in inventory] *************************************************************************************************************************************
ok: [localhost] => (item=host-01) => {
    "msg": "host-01"
}
ok: [localhost] => (item=host-02) => {
    "msg": "host-02"
}

TASK [Add host to inventory file] *******************************************************************************************************************************************
changed: [localhost]

TASK [Add host to inventory (memory)] ***************************************************************************************************************************************
changed: [localhost]

TASK [Print list of hosts in inventory] *************************************************************************************************************************************
ok: [localhost] => (item=host-01) => {
    "msg": "host-01"
}
ok: [localhost] => (item=host-02) => {
    "msg": "host-02"
}
ok: [localhost] => (item=host-03) => {
    "msg": "host-03"
}

PLAY RECAP ******************************************************************************************************************************************************************
localhost                  : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
```

## Opção B: utilize a metatask refresh_inventory

*Metatasks* são basicamente *tasks* que alteram o comportamento do Ansible durante sua execução. Vamos adicionar uma delas no nosso *playbook* após a adição do nosso *host* ao arquivo de inventário para que o mesmo seja recarregado antes da nova impressão:

```yaml
--- 
- hosts: localhost
  become: true
  connection: local
  tasks:

    - name: Print list of hosts in inventory
      ansible.builtin.debug:
        msg: "{{ item }}"
      loop: "{{ groups['all'] }}"

    - name: Add host to inventory file
      ansible.builtin.lineinfile:
        line: host-03
        path: ./hosts
        regexp: "^host-03"
        state: present

    - meta: refresh_inventory

    - name: Print list of hosts in inventory
      ansible.builtin.debug:
        msg: "{{ item }}"
      loop: "{{ groups['all'] }}"
```

```
PLAY [localhost] ************************************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************************************
ok: [localhost]

TASK [Print list of hosts in inventory] *************************************************************************************************************************************
ok: [localhost] => (item=host-01) => {
    "msg": "host-01"
}
ok: [localhost] => (item=host-02) => {
    "msg": "host-02"
}

TASK [Add host to inventory file] *******************************************************************************************************************************************
changed: [localhost]

TASK [Print list of hosts in inventory] *************************************************************************************************************************************
ok: [localhost] => (item=host-01) => {
    "msg": "host-01"
}
ok: [localhost] => (item=host-02) => {
    "msg": "host-02"
}
ok: [localhost] => (item=host-03) => {
    "msg": "host-03"
}

PLAY RECAP ******************************************************************************************************************************************************************
localhost                  : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
```

## Conclusão

Neste breve artigo nós vimos como o Ansible lê seu inventário, e como podemos adicionar novos *hosts* durante sua execucão para novas *tasks* e *roles*, este conhecimento sendo aplicável tanto para inventários dinâmicos quanto estáticos (ou quando nós queremos manipular o nosso arquivo `hosts`).

Espero que tenham curtido!