How to use Ansible for verifying configurations

You are already using Ansible for configuring Linux servers. Here's how to use Ansible for verifing configurations.

Ansible is a handy tool for configuration management.

Sometimes, you want to verify configurations without actually changing. You can do that with Ansible assert module.

The assert module can evaluate Jinja2 expressions. Coupled with Ansible variable assignment, you can write Ansible playbooks to check for specific configuration parameters.

How to use Ansible assert

The assert module accepts three main parameters.

  - name: task name
      - "condition"
      success_msg: "Success message"
      fail_msg: "Fail message" 

The parameter that is a list of Jinja2 expressions.

The task prints the success_msg if all the expressions are evaluated true or fail_msg if any expression is false.

To build the Jinja2 expressions we can use Ansible variables.

Registering variables

An Ansible task is an invocation of an Ansible module which returns a value. The register keyword stores this value in memory and makes it available for subsequent tasks.

- name: list files
    cmd: ls -k /home/ubuntu
  register: cmd_ls

The list files task invokes the module cmd which executes the Linux command ls -k /home/ubuntu. The task stores the return value in the variable cmd_ls. Any task that comes below this task in the play, can refer the variable cmd_ls.

Let's inspect the variable using the debug module.

- name: debug cmd_ls
    var: cmd_ls

The complete playbook variable-test.yml is available in ansible_assert GitHub repository. Clone the repo and run the playbook to check the output.

TASK [debug cmd_ls] ********************************************************************************************************************************************************************************
ok: [740-1-k8s1] => {
    "cmd_ls": {
        "changed": true,
        "cmd": [
        "delta": "0:00:00.005862",
        "end": "2023-06-10 15:00:20.355971",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2023-06-10 15:00:20.350109",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "my_file\ntest_file",
        "stdout_lines": [

As you can see from this output, the return value of the cmd module is a Python dictionary.

Most Ansible modules return a similar data structure. You can use the keys and valuse in this data structure for building the Jinja2 expressions for that parameter in the assert module.


Let's see some usecases for verifying configurations with Ansible assert.

Check the existence of a file

The module stat can retrieve status of files and directories.

This task retrieves the status of test_file in the user's home directory.

- name: Get status of test_file
    path: /home/ubuntu/test_file
  register: test_file_1

Inspect the contents of the test_file_1 variable with debug.

  - name: debug test_file_1
      var: test_file_1

Task output.

TASK [debug test_file_1] ***************************************************************************************************************************************************************************
ok: [740-1-k8s1] => {
    "test_file_1": {
        "changed": false,
        "failed": false,
        "stat": {
            "atime": 1686121585.736978,
            "attr_flags": "e",
            "attributes": [
            "block_size": 4096,
            "blocks": 8,
            "charset": "us-ascii",
            "checksum": "6f2170a833122bdfb8382742c2ca693a36c3ac58",
            "ctime": 1685828081.3049781,
            "dev": 2049,
            "device_type": 0,
            "executable": false,
            "exists": true,
            "gid": 1000,
            "gr_name": "ubuntu",
            "inode": 258118,
            "isblk": false,
            "ischr": false,
            "isdir": false,
            "isfifo": false,
            "isgid": false,
            "islnk": false,
            "isreg": true,
            "issock": false,
            "isuid": false,
            "mimetype": "text/plain",
            "mode": "0664",
            "mtime": 1685828081.3049781,
            "nlink": 1,
            "path": "/home/ubuntu/test_file",
            "pw_name": "ubuntu",
            "readable": true,
            "rgrp": true,
            "roth": true,
            "rusr": true,
            "size": 45,
            "uid": 1000,
            "version": "1457468022",
            "wgrp": true,
            "woth": false,
            "writeable": true,
            "wusr": true,
            "xgrp": false,
            "xoth": false,
            "xusr": false

The stat module returns a range of key-value pairs that represent multiple parameters about the file. Some keys are self explanatory. Refer the Stat module docs for interpretation of the others.

Here are three usefule keys and how to use them in Jinja2 expressions in assert module.

The exists key is a boolean representation of the existence of the file.

- name: assert test_file exists
    - "test_file.stat.exists"
    success_msg: "OK: test_file exists"
    fail_msg: "NOK: test_file does not exists"

The pw_name key holds the username of the owner. We will check whether the value equals ubuntu.

  - name: assert test_file owner username is ubuntu
      - "test_file.stat.pw_name == 'ubuntu'"
      success_msg: "OK: test_file owner is ubuntu"
      fail_msg: "NOK: test_file owner is not ubuntu"

The gr_name key holds the group name of owner and here's how we check whether the owner does not belong to the root user group.

  - name: assert test_file owner group name is not root
      - "test_file.stat.gr_name != 'root'"
      success_msg: "OK: test_file owner group is not root"
      fail_msg: "NOK: test_file owner group is root"

Create the file /home/ubuntu/test_file and run the playbook file-check.yml in the ansible_assert to see the output.

Check the contents of a file

The Ansible module command can run Linux commands. Let's run cat command with the test_file and inpect the contents.

  - name: cat test_file
      cmd: cat /home/ubuntu/test_file
    register: test_file

Here's the debug output of the `test_file.

TASK [debug test_file] *****************************************************************************************************************************************************************************
ok: [740-1-k8s1] => {
    "test_file": {
        "changed": true,
        "cmd": [
        "delta": "0:00:00.005361",
        "end": "2023-06-07 07:32:53.087623",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2023-06-07 07:32:53.082262",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "test_string\nnew string\nThis string is in fil",
        "stdout_lines": [
            "new string",
            "This string is in fil"

The test_file.stdout contains the output of the cat command. We can use it in Jinja2 expressions to check the contents of the file.

  - name: assert line in file
      - "'This string is in file' in test_file.stdout"
      success_msg: "OK: String is in file"
      fail_msg: "NOK: String is not in file"

  - name: assert line not in file
      - "'This string is not in file' not in test_file.stdout"
      success_msg: "OK: String is not in file"
      fail_msg: "NOK: String is in file

Refer file-content-check.yml in the ansible_assert for the complete playbook.

Check the status of a service

The module ansible.builtin.service_facts can rerieve information related to services and store them in variable.

  - name: Populate service facts

Let's print the to see what it contains.

- name: Print service facts

We get a list of the services installed in the target system.

Note that a part of the output is omitted for brevity.

TASK [Print service facts] *************************************************************************************************************************************************************************
ok: [740-1-k8s1] => {
    "": {
        "ModemManager.service": {
            "name": "ModemManager.service",
            "source": "systemd",
            "state": "running",
            "status": "enabled"
        "NetworkManager.service": {
            "name": "NetworkManager.service",
            "source": "systemd",
            "state": "stopped",
            "status": "not-found"
        "accounts-daemon.service": {
            "name": "accounts-daemon.service",
            "source": "systemd",
            "state": "running",
            "status": "enabled"
        "apparmor": {
            "name": "apparmor",
            "source": "sysv",
            "state": "running"

Check whether apparmor service is running.

  - name: assert apparmor is running
      - " == 'running'"
      success_msg: "OK: apparmor is running"
      fail_msg: "NOK: apparmor is not running"

Some service names hava a dot notation such as ssh.service. For accessing such keys use the Python array notations - single quoted key within square brackets.

  - name: assert ssh.service is running
      - "['ssh.service'].state == 'running'"
      success_msg: "OK: ssh.service is running"
      fail_msg: "NOK: ssh.service is not running"

Run the playbook service-check.yml in the ansible_assert to see the output.

Check the status of a kernel module

Ansible does not have a module for checking status of Linux kernel modules. But, we can use the command module to get output of lsmod and analyze.

  - name: List loaded kernel modules
      cmd: "lsmod"
    register: loaded_modules

Checking the ip_tables module status.

  - name: Check iptables is loaded
      - "'ip_tables' in loaded_modules.stdout" 
      success_msg: "OK: ip_tables is loaded"
      fail_msg: "NOK: ip_tables is not loaded"

The command module does not use the Linux shell to execute commands. So, it cannot interpret $ notations. To run commands with such notations, use the shell module.

  - name: List all kernel modules
      cmd: "find /lib/modules/$(uname -r) -name '*.ko*'"
    register: all_modules

Run the playbook kernel-module-check.yml in the ansible_assert to see the output.

Ignoring errors

Ansible stops execution when a task fails. That's fine for configuring servers.

But, when we are using Ansible for verifying configurations, we need to continue even if a task evaluates to false. So, when using Ansible for configuration verification set ignore errors to true in the playbook.

  ignore_errors: true

Ansible can do many things

Ansible can do things other than configuring servers. We just used Ansible for verifying configurations. These configuration verification capabilities in Ansible are handy for automating security compliance checking and audits.

In future posts, let's explore more usecases of Ansible.

Written by Indika