Jinja2 warning using conditionnals

Hi

I’m using the following tasks to add servers to my AD Domain only if there’s not already joined.

  - name: Domain discovery
    ansible.builtin.shell: |
      realm discover my_domain.net
    register: join

  - name: Domain joining
    ansible.builtin.expect:
      command: realm join my_domain.net -U {{ domain_account }} --install=/
      responses:
        Password for *: "{{ domain_password }}"
    when: join.stdout_lines is search('configured{{":"}} no')

  • 1st task is used to keep command line result into a register

  • 2nd task is used to add server to the domain only if I found “configured: no” in the previous result I record in the register. This is what is very important to me- to detect if server have to be add or not.

  • Note: I must to escape the semicolon with {{ " "}} else the task fails cause it’s seen as a YAML syntax.

Here’s the content of the register I’m using.

ok: [X.X.X.X] => {
    "join": {
        "changed": true,
        "cmd": "realm discover my_domain.net\n",
        "delta": "0:00:00.036477",
        "end": "2024-03-05 12:02:17.484044",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2024-03-05 12:02:17.447567",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "my_domain.net\n  type: kerberos\n  realm-name: my_domain.net\n  domain-name: my_domain.net\n  configured: no\n  server-software: active-directory\n  client-software: sssd\n  required-package: sssd-tools\n  required-package: sssd\n  required-package: libnss-sss\n  required-package: libpam-sss\n  required-package: adcli\n  required-package: samba-common-bin",
        "stdout_lines": [
            "my_domain.net",
            "  type: kerberos",
            "  realm-name: my_domain.net",
            "  domain-name: my_domain.net",
            "  configured: no",
            "  server-software: active-directory",
            "  client-software: sssd",
            "  required-package: sssd-tools",
            "  required-package: sssd",
            "  required-package: libnss-sss",
            "  required-package: libpam-sss",
            "  required-package: adcli",
            "  required-package: samba-common-bin"
        ]
    }
}

As you can see, stdout_lines is a list of list, BUT, there is a 2 spaces in front of every line after quotes.
Example " configured: no" instead of "configured: no"

Problem :
Both tasks are working well, but I still got a warning on the second one
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: join.stdout_lines is search('configured{{":"}} no')

  • I’ve tested to select list, but in the case of configured is set to yes, there’s more lines and so, list selection isn’t fair.
  • I can’t use selectattr as this isn’t YAML format.
  • I know i can use regex_search in case of debug (like "{{ join.stdout_lines | regex_search('configured: no') }}" but I can’t use it with the when conditionnal.

I’m looking for a solution or a best practices of filtering data to avoid this warning.

Thanks for your reading,

Gael

Did you try wrapping the whole thing in "?

when: "join.stdout_lines | regex_search('configured: no')"

Conditionals are already templated (string or list of strings), so the brackets shouldn’t be included in the expression.

2 Likes

Just tested it and it works !
So it means when using " " in a conditional, all the content is seen as ‘ansible content’ and not interpreted like yaml/json etc… ?

Another thing learn today :slight_smile: thanks a lot @shertel

1 Like

It’s just a way to escape : and ensure it’s a string (not a dictionary). You could also use a folded block scalar:

when: >-
    join.stdout_lines | regex_search('configured: no')
1 Like

I know you already have a solution, but I thought you might enjoy this snippet. This converts all values to string (since login-formats: for e.g. may have an invalid leading character ‘%’), and converts each discovered domain as a yaml dictionary.

- name: Domain discovery
    ansible.builtin.shell: >-
      realm discover my_domain.net | sed 's/:\s\(.*\)/: "\1"/g;s/\([[:alnum:]]$\)/\1:/g'
    register: join

  - name: Domain joining
    ansible.builtin.expect:
      command: realm join my_domain.net -U {{ domain_account }} --install=/
      responses:
        Password for *: "{{ domain_password }}"
    when: (join.stdout | from_yaml)['my_domain.net']['configured'] == "no"

Also, it’s important to note that you can join a realm or domain using different casing than the default discovery casing, and each casing will show up as different realms. For e.g. realm discover may show you configured as a member of My_Domain.net, but not my_domain.net, even though they may be the exact same realm-name/domain-name in the results. This behavior will create false positives if you’re not careful, in your/@shertel’s example or mine. In my example though, you can be case specific on the realm you’re acting upon, and is particularly useful in case you are working with multiple realms.

Additionally, if you’re joining to ActiveDirectory, you might find adcli info my_domain.net and adcli testjoin useful as well. adcli info my_domain.net is less ambiguous about casing, and adcli testjoin will validate that the host is a trusted member of its domain, not just configured to one (although it doesn’t let you specify a realm, it does let you specify a keytab, which you should have multiple of in multi-realm scenerios).

2 Likes

@Denney-tech thanks for your solution too, it’s a bit more complex but as you said it may works better in case of different casing scenarios.

Thanks also for the advice on adcli I have to try this for sure !

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.