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?