9

I want 'lucy' to follow the user module creators' default behaviour which is to create and use a group matching the user name 'lucy'. However for 'frank' I want the primary group to be an existing one; gid 1003. So my hash looks like this:

lucy:
  comment: dog
frank:
  comment: cat
  group: 1003

And my task looks like this:

- name: Set up local unix user accounts
  user:
    name: "{{ item.key }}"
    comment: "{{ item.value.comment }}"
    group: "{{ item.value.group | default(undef) }}"
  loop: "{{ users|dict2items }}"

This doesn't work, as undef is not recognised. Nor is anything else I can think of. 'null', 'None' etc. all fail. '' creates an empty string which is not right either. I can't find out how to do it. Any ideas?

spoovy
  • 311
  • 1
  • 3
  • 13
  • The title might be misleading. If you want to `"create a null default"` use *`default(None)`*. This use case is abut omitting a parameter of a module when the variable is undefined. – Vladimir Botka Mar 31 '23 at 14:38

1 Answers1

25

default(omit) is what you are looking for. For example,

- name: Set up local Unix user accounts
  user:
    name: "{{ item.key }}"
    comment: "{{ item.value.comment }}"
    group: "{{ item.value.group | default(omit) }}"
  loop: "{{ users|dict2items }}"

Comments

Comment by Lucas Basquerotto: "... omit only works correctly when used directly in a module, it won't work in a set_fact ..."

A: You're wrong. For example, default(omit) works both in set_fact and in the module. The first item in the list defaults to false with the result "VARIABLE IS NOT DEFINED!". The second item defaults to omit. Omitted parameter get_checksum defaults to true with the checksum in the results

shell> cat pb.yml
- hosts: localhost
  tasks:
    - set_fact:
        test:
          - "{{ gchk|default(false) }}"
          - "{{ gchk|default(omit) }}"
    - stat:
        path: /etc/passwd
        get_checksum: "{{ item }}"
      loop: "{{ test }}"
      register: result
    - debug:
        var: item.stat.checksum
      loop: "{{ result.results }}"

gives

shell> ansible-playbook pb.yml | grep item.stat.checksum
  item.stat.checksum: VARIABLE IS NOT DEFINED!
  item.stat.checksum: 7c73e9f589ca1f0a1372aa4cd6944feec459c4a8

In addition to this, default(omit) works as expected also in some expressions. For example

    - debug:
        msg: "{{ {'a': item}|combine({'b': true}) }}"
      loop: "{{ test }}"

gives

  msg:
    a: false
    b: true

  msg:
    b: true

See the results without default values

shell> ansible-playbook pb.yml -e "gchk={{ true|bool }}"

It does not work when passing vars to a module (a template in my case ...). When using omit, the template gets the value of the placeholder instead.

A: Of course, it doesn't work in this use case. For example,

shell> cat pb.yml
- hosts: localhost
  tasks:
    - debug:
        msg: |
          {{ var1 }}
          {{ var2 }}
      vars:
        var1: "{{ foo }}"
        var2: "{{ bar|default(omit) }}"

gives

shell> ansible-playbook pb.yml -e foo=123
  ...
  msg: |-
    123
    __omit_place_holder__73431d735cecaedbd9c4386e2ebc77ded8eaee51

The goal is to omit a parameter in a module. The question says: I can think of. 'null', 'None' etc. all fail. '' creates an empty string which is not right either. I can't find out how to do it. Any ideas? This is not your use case. The title is misleading, perhaps? Set the default to None if you want to use null. Perhaps, this will do what you want.

      vars:
        var1: "{{ foo }}"
        var2: "{{ bar|default(None) }}"
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63
  • Have in mind that `omit` only works correctly when used directly in a module, it won't work in a `set_fact` call to use the (undefined) variable later on, or when including a task or [role](https://github.com/ansible/ansible/issues/22417). – Lucas Basquerotto Nov 13 '20 at 11:42
  • *default(omit)* in *set_fact* works for me. Wouldn't you mind open an [mcve](https://stackoverflow.com/help/minimal-reproducible-example) question with a reasonable use-case supporting your claim? – Vladimir Botka Nov 13 '20 at 12:53
  • Maybe I wasn't clear enough, `default(omit)` in `set_fact` works (because `set_fact` is a module) when using the resulting variable in a module, if you use the variable in other places it won't work as you might expect (you can see more [here](https://github.com/ansible/ansible/issues/22417), this is about including in a role, but will also happen when including tasks and other places that aren't specifically a module). – Lucas Basquerotto Nov 13 '20 at 13:14
  • You're wrong again "*... if you use the variable in other places it won't work ...*". See the example. *default(omit)* works in the *combine* filter. The [expected result](https://github.com/ansible/ansible/issues/22417#issue-212783620) in the issue you reference, is not **reasonable**. It's not reasonable to expect the role's default if the variable defaults to omit in higher precedence. Still the same, open **reasonable** mcve question if you have a problem. – Vladimir Botka Nov 13 '20 at 13:32
  • Recently I had a repro of a edge case when using `set_fact` with `omit` and ended up here too. I hadn' made a repro before because I had already changed the project with the repro, and didn't remember the cause. I posted the case [here](https://github.com/ansible/ansible/issues/75607). This is not an Ansible issue, but is something to be careful abou, because the omitted the variable will act as if it doesn't exist, so you need to use `omit` again whenever you use the variable, otherwise you will get an undefined variable error (in newer versions the `set_fact` itself will give error). – Lucas Basquerotto Aug 31 '21 at 16:19
  • Use [*|default()*](https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#providing-default-values) whenever a variable might be undefined. The alias [*|d()*](https://jinja.palletsprojects.com/en/latest/templates/#jinja-filters.default) is cheap. – Vladimir Botka Aug 31 '21 at 16:23
  • Just to point out, in your case it worked because you used a list (so the `fact` is the list, and not the list elements). It would also work fine if you accessed a dict property that has `omit`. But if you use `omit` in the fact directly (like in the issue I created), it will be as if it was not defined, so you should be careful in this case (or avoid using `omit` in `set_fact`). The case is similar with roles: it's omitted due to variable precedence, but not expected from the user POV and make roles difficult to [reuse](https://github.com/ansible/ansible/issues/22417#issuecomment-372864723). – Lucas Basquerotto Aug 31 '21 at 16:24
  • Thanks for the `| d()` approach, I always used default and it became ugly. `| d()` is much better. I didn't find it in the docs, but was able to reproduce with `| d()` instead of `| default()`. – Lucas Basquerotto Aug 31 '21 at 16:25
  • It does not work when passing `vars` to a module (a template in my case, where the template has renamed variable names and deals with undefined variables) (When using omit, the template gets the value of the placeholder instead) – Gert van den Berg Mar 31 '23 at 13:35
  • Of course, it doesn't. The goal is to *omit* a parameter in a module. The question says: `I can think of. 'null', 'None' etc. all fail. '' creates an empty string which is not right either. I can't find out how to do it. Any ideas?` This is not your use case. The title is misleading, perhaps? – Vladimir Botka Mar 31 '23 at 14:07