Ansible is a great tool for configuring servers to the state you desire. You can create a playbook, and if correctly written, it always yields the same state no matter how many times you run it. This is called idempotency. You can also run an Ansible playbook with the --check option and verify what the playbook would change if it were run so that you don't unexpectedly make a change when you do not want to.

But what if you need to verify the configuration of existing servers that were not originally configured with Ansible? There are tools out there for this purpose, such as Chef's InSpec, testinfra, and serverspec. But, if you already know Ansible, you can use some of its built-in functionality to do this.

Example 1

In this example, I have six hosts of various Linux distributions and versions. Because the actual memory reported can vary, I'm going to check for memory between 800 MB and 1100 MB. The goal is for hosts that meet the criteria to pass with changed=0 and hosts that fail the criteria to output changed=1:

- name: assert
  hosts: all

  tasks:

  - name: check if memory is between 800 and 1100MB
    assert:
      that:
        - ansible_memtotal_mb | int >= 800
        - ansible_memtotal_mb | int <= 1100
      fail_msg: "Memory is {{ ansible_memtotal_mb }}MB not 1024MB"
    register: result
    changed_when:
      - result.evaluated_to is defined
      - result.evaluated_to == False
    failed_when: False

The results:

[kpirkle@defiant config-verify]$ ansible-playbook assert.yml


PLAY [assert] ****************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [wookie]
ok: [ewok]
ok: [centurion]
ok: [web]
ok: [venture]
ok: [c3po]

TASK [check if memory is between 800 and 1100MB] *****************************************************************************************************************************************************************
ok: [venture] => {
    "changed": false,
    "failed_when_result": false,
    "msg": "All assertions passed"
}
ok: [ewok] => {
    "changed": false,
    "failed_when_result": false,
    "msg": "All assertions passed"
}
ok: [web] => {
    "changed": false,
    "failed_when_result": false,
    "msg": "All assertions passed"
}
changed: [centurion] => {
    "assertion": "ansible_memtotal_mb | int <= 1100",
    "changed": true,
    "evaluated_to": false,
    "failed_when_result": false,
    "msg": "Memory is 7737MB not 1024MB"
}
ok: [wookie] => {
    "changed": false,
    "failed_when_result": false,
    "msg": "All assertions passed"
}
ok: [c3po] => {
    "changed": false,
    "failed_when_result": false,
    "msg": "All assertions passed"
}

PLAY RECAP *******************************************************************************************************************************************************************************************************
c3po                       : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
centurion                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ewok                       : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
venture                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web                        : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
wookie                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

assert

The function of the assert module, per the documentation, is to "assert given expressions are true."

In this case, five of the six passed - one failed because host centurion had more memory than the range I was checking. I used the fail_msg option of the assert module to provide useful information as to why it failed. There are also options to always display a custom message with msg and to pass a custom message when the assertion passes called success_msg.

changed_when

The changed_when option is not a module but rather a built-in function that alters how error handling is done for a task. In this case, we are saving the results from the assert test to the result variable. We then alter the changed value based on two tests: 1) does the evaluated_to variable exist, and 2) is it false? If it exists and is false, we know the test failed, and we return the result as "changed."

failed_when

To prevent the play from failing and ending the run of the playbook, set the failed_when option to false.

Example 2

Another useful configuration comparison strategy is to use a checksum tool like md5sum to compare files.

---
- name: compare
  hosts: all
  gather_facts: no
 
  vars:
 
    std_conf_crontab: 'c39252b11aad842fcb75e05c6a27eef8'
    std_conf_lvm: '2d90187abd40dbcb6fc6de41640fd022'
    std_conf_resolv: 'db323688118c844a76ebd6c70508b434'

  tasks:

  - name: compare config files
    stat:
      path: '{{ item.file }}'
      checksum_algorithm: md5
    register: result
    changed_when: item.md5sum != result.stat.checksum
    failed_when: False
    loop:
      - { file: /etc/crontab, md5sum: '{{ std_conf_crontab }}' }
      - { file: /etc/lvm/lvm.conf, md5sum: '{{ std_conf_lvm }}' }
      - { file: /etc/resolv.conf, md5sum: '{{ std_conf_resolv }}'}   

The result:

[kpirkle@defiant config-verify]$ ansible-playbook compare.yml

PLAY [compare] ****************************************************************************************************************************

TASK [compare config files] ***************************************************************************************************************
changed: [wookie] => (item={'file': '/etc/crontab', 'md5sum': 'c39252b11aad842fcb75e05c6a27eef8'})
ok: [centurion] => (item={'file': '/etc/crontab', 'md5sum': 'c39252b11aad842fcb75e05c6a27eef8'})
ok: [web] => (item={'file': '/etc/crontab', 'md5sum': 'c39252b11aad842fcb75e05c6a27eef8'})
changed: [wookie] => (item={'file': '/etc/lvm/lvm.conf', 'md5sum': '2d90187abd40dbcb6fc6de41640fd022'})
ok: [centurion] => (item={'file': '/etc/lvm/lvm.conf', 'md5sum': '2d90187abd40dbcb6fc6de41640fd022'})
ok: [venture] => (item={'file': '/etc/crontab', 'md5sum': 'c39252b11aad842fcb75e05c6a27eef8'})
ok: [wookie] => (item={'file': '/etc/resolv.conf', 'md5sum': 'db323688118c844a76ebd6c70508b434'})
ok: [ewok] => (item={'file': '/etc/crontab', 'md5sum': 'c39252b11aad842fcb75e05c6a27eef8'})
ok: [centurion] => (item={'file': '/etc/resolv.conf', 'md5sum': 'db323688118c844a76ebd6c70508b434'})
ok: [web] => (item={'file': '/etc/lvm/lvm.conf', 'md5sum': '2d90187abd40dbcb6fc6de41640fd022'})
changed: [venture] => (item={'file': '/etc/lvm/lvm.conf', 'md5sum': '2d90187abd40dbcb6fc6de41640fd022'})
ok: [web] => (item={'file': '/etc/resolv.conf', 'md5sum': 'db323688118c844a76ebd6c70508b434'})
changed: [ewok] => (item={'file': '/etc/lvm/lvm.conf', 'md5sum': '2d90187abd40dbcb6fc6de41640fd022'})
ok: [ewok] => (item={'file': '/etc/resolv.conf', 'md5sum': 'db323688118c844a76ebd6c70508b434'})
ok: [venture] => (item={'file': '/etc/resolv.conf', 'md5sum': 'db323688118c844a76ebd6c70508b434'})
ok: [c3po] => (item={'file': '/etc/crontab', 'md5sum': 'c39252b11aad842fcb75e05c6a27eef8'})
changed: [c3po] => (item={'file': '/etc/lvm/lvm.conf', 'md5sum': '2d90187abd40dbcb6fc6de41640fd022'})
ok: [c3po] => (item={'file': '/etc/resolv.conf', 'md5sum': 'db323688118c844a76ebd6c70508b434'})

PLAY RECAP ********************************************************************************************************************************
c3po                       : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
centurion                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ewok                       : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
venture                    : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web                        : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
wookie                     : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

In this example, we are setting variables for the md5sums of three files that we want to check for consistency. Our task then uses the stat module to calculate the MD5 checksum of each file and compare it to the defined variable. Once again, we are using the changed_when function to make the comparison and render a "changed" state when there isn't a match.

Example 3

My final example is a way to check the contents of a file for a specific item. One way to do this is by using the shell module and using grep.

---
- name: grep
  hosts: all
  gather_facts: no

  tasks:

  - name: grep for nameserver
    shell: grep 'nameserver 192.168.0.1' /etc/resolv.conf
    register: result
    changed_when: result.rc != 0
    failed_when: False

The result:

[kpirkle@defiant config-verify]$ ansible-playbook grep.yml

PLAY [grep] *******************************************************************************************************************************

TASK [grep for nameserver] ****************************************************************************************************************
ok: [wookie]
ok: [web]
ok: [centurion]
ok: [ewok]
changed: [venture]
ok: [c3po]

PLAY RECAP ********************************************************************************************************************************
c3po                       : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
centurion                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ewok                       : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
venture                    : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
web                        : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
wookie                     : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Here again, we are using the combination of changed_when and failed_when to manipulate the output to be useful. By registering the output of grep to the result variable, we can then check the return code of the command and find out if the desired nameserver string is present. More complex commands can be used if needed.

Wrap up

Hopefully, others find these tips useful. You can use Ansible as not only a tool to configure servers to the desired state but also as an investigative tool to verify the configuration of servers or possibly as an ad-hoc monitoring tool.

[ Need more on Ansible? Take a free technical overview course from Red Hat. Ansible Essentials: Simplicity in Automation Technical Overview. ]


Sobre o autor

Kent is a Linux Systems Engineer with over 20 years of experience with Linux and UNIX systems. His current focus is on Ansible, automation, and infrastructure-as-code. He is a member of the Red Hat Accelerators and is a Red Hat Certified Engineer.  

UI_Icon-Red_Hat-Close-A-Black-RGB

Navegue por canal

automation icon

Automação

Últimas novidades em automação de TI para empresas de tecnologia, equipes e ambientes

AI icon

Inteligência artificial

Descubra as atualizações nas plataformas que proporcionam aos clientes executar suas cargas de trabalho de IA em qualquer ambiente

open hybrid cloud icon

Nuvem híbrida aberta

Veja como construímos um futuro mais flexível com a nuvem híbrida

security icon

Segurança

Veja as últimas novidades sobre como reduzimos riscos em ambientes e tecnologias

edge icon

Edge computing

Saiba quais são as atualizações nas plataformas que simplificam as operações na borda

Infrastructure icon

Infraestrutura

Saiba o que há de mais recente na plataforma Linux empresarial líder mundial

application development icon

Aplicações

Conheça nossas soluções desenvolvidas para ajudar você a superar os desafios mais complexos de aplicações

Virtualization icon

Virtualização

O futuro da virtualização empresarial para suas cargas de trabalho on-premise ou na nuvem