Ansible Hiera

We might be aware of Puppet Hiera flexibility. Below is an example to proxy hiera in native Ansible (without third party product/plugin).

An example, we have one external host inventory running on RedHat.

# cat demo
[web]
192.168.2.101

Below is the playbook. Once it connects to the inventory host, it will return ansible_facts[‘distribution’] = ‘RedHat’, similar to facter in Puppet. Using this O/S type, we can then define all O/S related roles specific variables in “vars/os_RedHat” variable file.

# cat main.yml
---
- name: talk to all hosts just so we can learn about them
  hosts: all
  vars_files:
    - "vars/os_{{ ansible_facts['distribution'] }}"
  roles:
    - sshd
  tasks:
    - name: tell us the variable in main.yml
      debug:
        var: sshd_ssh_packages

Below are variables that we defined in vars/os_RedHat variable file. By defining O/S specific variables in data hierarchy, the role can be made independent from O/S or any other hierarchy.

# cat vars/os_RedHat
# Information for the sshd for RedHat
sshd_package: "sshd"
sshd_ssh_packages:
  - openssh-server
  - openssh

The issue with Ansible hierarchy is their precedence order couldn’t be easily modified like in Puppet. The precedence order of role and host variables from highest is:

  • role vars
  • hosts vars
  • role default

Unfortunately role variable has higher precedence, therefore the nicest way to define variable is in role default, not in role vars. This role default can then be overwritten with host variables.

# cat roles/sshd/vars/main.yml
---

# cat roles/sshd/defaults/main.yml
---
sshd_package: "nothing"
sshd_ssh_packages: "nothing"

# cat roles/sshd/tasks/main.yml
---
- name: tell us the variable inside sshd role
  debug:
    var: sshd_ssh_packages

Let’s run the playbook.

# ansible-playbook -i demo main.yml

PLAY [talk to all hosts just so we can learn about them] **********

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

TASK [sshd : tell us the variable inside sshd role] **********
ok: [192.168.2.101] => {
    "sshd_ssh_packages": [
        "openssh-server",
        "openssh"
    ]
}

TASK [tell us the variable in main.yml] **********
ok: [192.168.2.101] => {
    "sshd_ssh_packages": [
        "openssh-server",
        "openssh"
    ]
}

PLAY RECAP **********
192.168.2.101              : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

If we comment out host variable, the role variable takes over. It means in case of finding undefined O/S, both host and role can warn/fail task from being processed.

# cat main.yml
---
- name: talk to all hosts just so we can learn about them
  hosts: all
#  vars_files:
#    - "vars/os_{{ ansible_facts['distribution'] }}"
  tasks:
    - name: tell us the variable in main.yml
      debug:
        var: sshd_ssh_packages
  roles:
    - sshd

# ansible-playbook -i demo main.yml

PLAY [talk to all hosts just so we can learn about them] 

TASK [Gathering Facts] 
ok: [192.168.2.101]

TASK [sshd : tell us the variable inside sshd role] 
ok: [192.168.2.101] => {
    "sshd_ssh_packages": "nothing"
}

TASK [tell us the variable in main.yml] 
ok: [192.168.2.101] => {
    "sshd_ssh_packages": "nothing"
}

PLAY RECAP 
192.168.2.101              : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

With ability to separate data and code completely, we can build similar data hierarchy like in Puppet Hiera. However, unfortunately in Ansible, we have to follow fixed Ansible precendence order and work from there.