Win_shell + loops + delegate_to does not work (not at all or as expected)

msperl
Thu, 28 Nov 2024 10:33:39 +0000
Get Help

I am trying to run several win_shell command to different windows hosts for a single linux server.

The number of iterations we want to run is totally defined by the inventory (i.e group membership).

This unfortunately fails as the kerberos ticket is created for the linux host name and NOT the delegate_to name.
Other issues also show in other possible variants.

Code:

- name: test windows delegate loop
  run_once: true
  ansible.windows.win_shell: hostname
  delegate_to: '{{ item }}'
  loop:
    - windows_host_a
    - windows_host_b
  vars:
   ansible_delegated_vars:
	ansible_pipelining:                   False
	ansible_user:                         '{{ hostvars[item | default(inventory_hostname)].ansible_user | default("ansible") }}'
	ansible_password:                     '{{ hostvars[item | default(inventory_hostname)].ansible_password                     | default() }}'
	ansible_connection:                   '{{ hostvars[item | default(inventory_hostname)].ansible_connection                   | default("winrm") }}'
    ansible_winrm_host:                   '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_host                   | default(hostvars[item | default(inventory_hostname)].inventory_hostname) | default(item | default(inventory_hostname)) }}'
    remote_addr:                          '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_host                   | default(hostvars[item | default(inventory_hostname)].inventory_hostname) | default(item | default(inventory_hostname)) }}'
    ansible_winrm_kerberos_delegation:    '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_kerberos_delegation    | default(False) }}'
    ansible_winrm_message_encryption:     '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_message_encryption     | default("always") }}'
    ansible_winrm_send_cbt:               '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_send_cbt               | default(True) }}'
	ansible_winrm_server_cert_validation: '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_server_cert_validation | default("ignore") }}'
	ansible_winrm_service:                '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_service                | default("wsman") }}'
	ansible_winrm_transport:              '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_transport              | default("kerberos") }}'
    remote_address:                       '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_host                   | default(hostvars[item | default(inventory_hostname)].inventory_hostname) | default(item | default(inventory_hostname)) }}'

as long as I have the delegate_to + ansible_delegated_vars in there we get in our case:

<undelegated inventory_hostname> ESTABLISH WINRM CONNECTION FOR USER: ansible@<DOMAIN> on PORT 5986 TO <undelegated inventory_hostname>
creating Kerberos CC at /tmp/tmpv10gaw0w
calling kinit with subprocess for principal ansible@<DOMAIN>
kinit succeeded for principal ansible@<DOMAIN>
<undelegated inventory_hostname> WINRM CONNECT: transport=kerberos endpoint=https://<undelegated inventory_hostname>:5986/wsman
<undelegated inventory_hostname> WINRM CONNECTION ERROR: authGSSClientStep() failed: (('Unspecified GSS failure.  Minor code may provide more information', 851968), ('Server not found in Kerberos database', -1765328377))

So the kerberos ticket is created for a linux host that is not in the Windows AD.
Hence the “Server not Found”.

When I just remove the ansible_delegated_vars and assign the ansible_* directly in vars:

- name: test windows delegate loop
  run_once: true
  ansible.windows.win_shell: hostname
  delegate_to: '{{ item }}'
  loop:
    - windows_host_a
    - windows_host_b
  vars:
	ansible_pipelining:                   False
	ansible_user:                         '{{ hostvars[item | default(inventory_hostname)].ansible_user | default("ansible") }}'
	ansible_password:                     '{{ hostvars[item | default(inventory_hostname)].ansible_password                     | default() }}'
	ansible_connection:                   '{{ hostvars[item | default(inventory_hostname)].ansible_connection                   | default("winrm") }}'
    ansible_winrm_host:                   '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_host                   | default(hostvars[item | default(inventory_hostname)].inventory_hostname) | default(item | default(inventory_hostname)) }}'
    remote_addr:                          '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_host                   | default(hostvars[item | default(inventory_hostname)].inventory_hostname) | default(item | default(inventory_hostname)) }}'
    ansible_winrm_kerberos_delegation:    '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_kerberos_delegation    | default(False) }}'
    ansible_winrm_message_encryption:     '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_message_encryption     | default("always") }}'
    ansible_winrm_send_cbt:               '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_send_cbt               | default(True) }}'
	ansible_winrm_server_cert_validation: '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_server_cert_validation | default("ignore") }}'
	ansible_winrm_service:                '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_service                | default("wsman") }}'
	ansible_winrm_transport:              '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_transport              | default("kerberos") }}'
    remote_address:                       '{{ hostvars[item | default(inventory_hostname)].ansible_winrm_host                   | default(hostvars[item | default(inventory_hostname)].inventory_hostname) | default(item | default(inventory_hostname)) }}'

I get the following:

<undelegated inventory_hostname> ESTABLISH WINRM CONNECTION FOR USER: ansible on PORT 5986 TO <undelegated inventory_hostname>
<undelegated inventory_hostname> WINRM CONNECT: transport=kerberos endpoint=https://<undelegated inventory_hostname>:5986/wsman
<undelegated inventory_hostname> WINRM CONNECTION ERROR: authGSSClientInit() failed: (('No credentials were supplied, or the credentials were unavailable or inaccessible', 458752), ("Can't find client principal ansible@DOMAIN in cache collection", -1765328243))

not even a kerberos ticket is created…

when I run the same but also remove the delegate to (but keep all the ansible_* variables in vars):

<windows_host_a> ESTABLISH WINRM CONNECTION FOR USER: ansible@<DOMAIN> on PORT 5986 TO windows_host_a
creating Kerberos CC at /tmp/tmpd8ypqo_s
calling kinit with subprocess for principal ansible@<DOMAIN>
kinit succeeded for principal ansible@<DOMAIN>
<windows_host_a> WINRM CONNECT: transport=kerberos endpoint=https://weganeu.olymp.ontec.at:5986/wsman
<windows_host_a> WINRM OPEN SHELL: BF3CDFC3-1095-429A-8676-F8CF0033A736

and it works in principle.

But there is a snag: it ONLY connects to the windows_host_a host for both iterations so the loop is there but not as intended.

As a note: ansible -m win_ping windows_host_a,windows_host_b works fine…

As may be obvious: I have already spent lots of time trying to figure this out (especially those vars: tricks as well as the “ansible_delegated_vars” have been found out by inspecting the code.

For comparisson: the same works for a linux host without any issues:

- name: test linux delegate loop
  run_once: true
  shell: hostname
  delegate_to: '{{ item }}'
  loop:
    - linux_host_a
    - linux_host_b

So any ideas what I need to do to get loop + delegate work propperly in winrm context?

1 post - 1 participant