Home > other >  Ansible: Change default values of nested vars
Ansible: Change default values of nested vars

Time:06-17

I have written a role to create users. Now I wanted to modify the role to also give the option to remove users. What looked very simple in the beginning is now giving me constant headaches.

Here is my role:

---
- name: Dictionary playbook example
  hosts: localhost
  vars:
    my_default_values:
        users: &def
            state: 'present'
            force: 'yes'
            remove: 'no'
    my_values:
        users:
            - username: bla
              <<: *def
              state: absent
            - username: bla2
              <<: *def

  tasks:
    - debug: var=my_default_values
    - debug: var=my_values

Here is the output:

PLAY [Dictionary playbook example] ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
        
        TASK [Gathering Facts] ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
        ok: [localhost]
        
        TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
        ok: [localhost] => {
            "my_default_values": {
                "users": {
                    "force": "yes",
                    "remove": "no",
                    "state": "present"
                }
            }
        }
        
        TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
        ok: [localhost] => {
            "my_values": {
                "users": [
                    {
                        "force": "yes",
                        "remove": "no",
                        "state": "absent",
                        "username": "bla"
                    },
                    {
                        "force": "yes",
                        "remove": "no",
                        "state": "present",
                        "username": "bla2"
                    }
                ]
            }
        }
        
PLAY RECAP ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0
rescued=0    ignored=0

This is working fine as long as I always define <<: *def below every - username in my role. But this is problematic if let's say I want to have the users variables defined in an external file users.yml. I guess I would need some kind of loop?

In the end I want to use the role with Katello and so the users are defined inside of Katello. This is why I need to get this working in a more dynamic fashion.

CodePudding user response:

I can see two approaches:

If you plan on looping on those users and use the values of the dictionaries, then, just define the adequate default with the purposed default filter:

roles/demo/tasks/main.yml

- debug:
    msg:
      user:
        username: "{{ item.username }}"
        state: "{{ item.state | default('present') }}"
        force: "{{ item.force | default('yes') }}"
        remove: "{{ item.remove | default('no') }}"
  loop: "{{ users }}"
  loop_control:
    label: "{{ item.username }}"

Playbook:

- hosts: localhost
  gather_facts: no

  roles:
    - name: demo
      users:
        - username: bla
          state: absent
        - username: bla2

Which will yield:

ok: [localhost] => (item=bla) => 
  msg:
    user:
      force: 'yes'
      remove: 'no'
      state: absent
      username: bla
ok: [localhost] => (item=bla2) => 
  msg:
    user:
      force: 'yes'
      remove: 'no'
      state: present
      username: bla2

If you really do need a list inside your role, recreate a new list, combining the the default value to the input given to the role, taking advantage of the fact that, when combining two dictionaries containing the same key, the values of the second dictionary will override the values of the first one:

roles/demo/tasks/main.yml

- set_fact:
    users_with_default: >-
      {{
        users_with_default | default([])
          [user_defaults | combine(item)]
      }}
  loop: "{{ users }}"
  vars:
    user_defaults:
      state: 'present'
      force: 'yes'
      remove: 'no'

- debug:
    var: users_with_default

Playbook:

- hosts: localhost
  gather_facts: no

  roles:
    - name: demo
      users:
        - username: bla
          state: absent
        - username: bla2

Which will yield:

ok: [localhost] => 
  users_with_default:
  - force: 'yes'
    remove: 'no'
    state: absent
    username: bla
  - force: 'yes'
    remove: 'no'
    state: present
    username: bla2

CodePudding user response:

Use module_defaults. It's not necessary to include state: present and remove: no because these are defaults. In the list my_users include only required parameter name and parameters with non-default values

- hosts: localhost
  module_defaults:
    ansible.builtin.user:
      force: yes
  vars:
    my_users:
      - name: foo
        state: absent
      - name: bar

In the task, by default omit optional parameters that you want to use

  tasks:
    - ansible.builtin.user:
        name: "{{ item.name }}"
        state: "{{ item.state|default(omit) }}"
        force: "{{ item.force|default(omit) }}"
        remove: "{{ item.remove|default(omit) }}"
      loop: "{{ my_users }}"
  • Related