1- name: get network adapter for each Windows host 2 hosts: windows 3 gather_facts: no 4 tasks: 5 - name: get network connection for private adapter 6 win_shell: | 7 foreach ($instance in (Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "Netenabled='True'")) { 8 $instance_config = Get-CimInstance -ClassName WIn32_NetworkAdapterConfiguration -Filter "Index = '$($instance.Index)'" 9 if ($instance_config.IPAddress -contains "{{ansible_host}}") { 10 $instance.NetConnectionID 11 } 12 } 13 changed_when: no 14 register: network_connection_name_raw 15 16 - name: fail if we didn't get a network connection name 17 fail: 18 msg: Failed to get the Windows network connection name 19 when: network_connection_name_raw.stdout_lines | count != 1 20 21 - name: set fact of network connection name 22 set_fact: 23 network_connection_name: '{{ network_connection_name_raw.stdout | trim }}' 24 25- name: create Domain Controller 26 hosts: win_controller 27 gather_facts: no 28 tasks: 29 - name: set the DNS for the specified adapter to localhost 30 win_dns_client: 31 adapter_name: '{{ network_connection_name }}' 32 ipv4_addresses: 127.0.0.1 33 34 - name: ensure domain exists and DC is promoted as a domain controller 35 win_domain: 36 dns_domain_name: '{{ domain_name }}' 37 safe_mode_password: '{{ domain_password }}' 38 register: domain_setup_res 39 40 - name: reboot DC if required after install 41 win_reboot: 42 when: domain_setup_res.reboot_required 43 44 - name: create domain username 45 win_domain_user: 46 name: '{{ domain_username }}' 47 upn: '{{ domain_upn }}' 48 description: '{{ domain_username }} Domain Account' 49 password: '{{ domain_password }}' 50 password_never_expires: yes 51 update_password: on_create 52 groups: 53 - Domain Admins 54 state: present 55 register: domain_user_result 56 # ADWS may not be online after first reboot, need to keep on retrying 57 retries: 30 58 delay: 15 59 until: domain_user_result is successful 60 61 - name: test out domain user that was created 62 win_whoami: 63 register: become_res 64 failed_when: become_res.upn != domain_upn 65 become: yes 66 become_method: runas 67 vars: 68 ansible_become_user: '{{ domain_upn }}' 69 ansible_become_pass: '{{ domain_password }}' 70 71- name: join Windows host to domain 72 hosts: win_children 73 gather_facts: no 74 tasks: 75 - name: set the DNS for the private adapter to point to the DC 76 win_dns_client: 77 adapter_names: '{{ network_connection_name }}' 78 ipv4_addresses: '{{ hostvars[groups["win_controller"][0]]["ansible_host"] }}' 79 80 - name: join host to domain 81 win_domain_membership: 82 dns_domain_name: '{{ domain_name }}' 83 domain_admin_user: '{{ domain_upn }}' 84 domain_admin_password: '{{ domain_password }}' 85 state: domain 86 register: domain_join_result 87 88 - name: trust hosts for delegation in AD 89 win_shell: | 90 $computerName = '{{ inventory_hostname }}' 91 $actual = (Get-ADComputer -Identity $computerName -Property TrustedForDelegation).TrustedForDelegation 92 if ($actual) { 93 $false 94 } else { 95 Set-ADComputer -Identity $computerName -TrustedForDelegation $true 96 $true 97 } 98 when: inventory_hostname == 'SERVER2022' # We only want to have this hosted with delegation for testing 99 register: delegate_actual 100 changed_when: delegate_actual.stdout | trim | bool 101 delegate_to: '{{ groups["win_controller"][0] }}' 102 103 - name: reboot host to finalise domain join 104 win_reboot: 105 when: domain_join_result.reboot_required 106 107 - name: test out domain user logon 108 win_whoami: 109 register: become_res 110 failed_when: become_res.upn != domain_upn 111 become: yes 112 become_method: runas 113 vars: 114 ansible_become_user: '{{ domain_upn }}' 115 ansible_become_pass: '{{ domain_password }}' 116 117- name: set up Python interpreters on test Windows host 118 hosts: SERVER2012R2 119 gather_facts: no 120 tasks: 121 - name: install Python interpreters 122 win_package: 123 path: '{{ item.url }}' 124 arguments: '{{ item.arguments }}' 125 product_id: '{{ item.product_id }}' 126 state: present 127 with_items: 128 - url: https://www.python.org/ftp/python/3.6.8/python-3.6.8.exe 129 product_id: '{B56829C6-1C25-469E-B351-1467C6295566}' 130 arguments: /quiet InstallAllUsers=1 Shortcuts=0 131 - url: https://www.python.org/ftp/python/3.6.8/python-3.6.8-amd64.exe 132 product_id: '{E1155302-B578-4D8C-8431-FAE677FBC58C}' 133 arguments: /quiet InstallAllUsers=1 Shortcuts=0 134 - url: https://www.python.org/ftp/python/3.7.9/python-3.7.9.exe 135 product_id: '{65048DA1-5996-4FF9-B20A-66EB2E68D0A4}' 136 arguments: /quiet InstallAllUsers=1 Shortcuts=0 137 - url: https://www.python.org/ftp/python/3.7.9/python-3.7.9-amd64.exe 138 product_id: '{FF740026-2FC0-4F8A-A046-8B316AF4ECA6}' 139 arguments: /quiet InstallAllUsers=1 Shortcuts=0 140 - url: https://www.python.org/ftp/python/3.8.10/python-3.8.10.exe 141 product_id: '{4196628C-AE5C-4304-B166-B7C1E93CDC25}' 142 arguments: /quiet InstallAllUsers=1 Shortcuts=0 143 - url: https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe 144 product_id: '{080E0048-853C-49FB-96ED-30DEF7AB6E34}' 145 arguments: /quiet InstallAllUsers=1 Shortcuts=0 146 - url: https://www.python.org/ftp/python/3.9.7/python-3.9.7.exe 147 product_id: '{E3343646-507D-4269-BF4C-68D78153FD0D}' 148 arguments: /quiet InstallAllUsers=1 Shortcuts=0 149 - url: https://www.python.org/ftp/python/3.9.7/python-3.9.7-amd64.exe 150 product_id: '{05903EEF-72A2-4C1A-AD35-41AD6C7094A8}' 151 arguments: /quiet InstallAllUsers=1 Shortcuts=0 152 - url: https://www.python.org/ftp/python/3.10.0/python-3.10.0.exe 153 product_id: '{88B95F8C-ECD7-42F1-A216-0880C9DE2F34}' 154 arguments: /quiet InstallAllUsers=1 Shortcuts=0 155 - url: https://www.python.org/ftp/python/3.10.0/python-3.10.0-amd64.exe 156 product_id: '{BB3BA776-4C84-43FB-9CE6-5A37FFC23032}' 157 arguments: /quiet InstallAllUsers=1 Shortcuts=0 158 159 - name: ensure virtualenv package is installed for each Python install 160 win_command: '"{{ item }}\python.exe" -m pip install virtualenv' 161 args: 162 creates: '{{ item }}\Scripts\virtualenv.exe' 163 with_items: '{{ python_interpreters }}' 164 165 - name: create virtualenv for each Python install 166 win_command: '"{{ item }}\python.exe" -m virtualenv "{{ python_venv_path }}\{{ item | win_basename }}"' 167 args: 168 creates: '{{ python_venv_path }}\{{ item | win_basename }}' 169 with_items: '{{ python_interpreters }}' 170 171 - name: copy across wheel artifacts 172 win_copy: 173 src: artifact.zip 174 dest: C:\temp\wheels.zip 175 176 - name: ensure wheel dir exists 177 win_file: 178 path: C:\temp\wheels 179 state: directory 180 181 - name: extract wheel from archive 182 win_unzip: 183 src: C:\temp\wheels.zip 184 dest: C:\temp\wheels 185 186 - name: install pyspnego wheel into virtualenv 187 win_shell: | 188 &'{{ python_venv_path }}\{{ item | win_basename }}\Scripts\pip.exe' install --no-index --find-links=C:/temp/wheels --no-deps pyspnego 189 &'{{ python_venv_path }}\{{ item | win_basename }}\Scripts\pip.exe' install pyspnego pytest requests 190 args: 191 creates: '{{ python_venv_path }}\{{ item | win_basename }}\Lib\site-packages\spnego' 192 with_items: '{{ python_interpreters }}' 193 194 - name: template out test integration file 195 win_template: 196 src: test_integration.py.tmpl 197 dest: C:\temp\test_integration.py 198 block_start_string: '{!!' 199 block_end_string: '!!}' 200 tags: 201 - template 202 203- name: set up WinRM config and SMB shares on Windows hosts 204 hosts: windows 205 gather_facts: no 206 tasks: 207 - name: set WinRM Cbt value to Strict 208 win_shell: | 209 $val = (Get-Item -LiteralPath WSMan:\localhost\Service\Auth\CbtHardeningLevel).Value 210 if ($val -eq 'Strict') { 211 $false 212 } else { 213 Set-Item -LiteralPath WSMan:\localhost\Service\Auth\CbtHardeningLevel -Value Strict 214 $true 215 } 216 register: cbt_res 217 changed_when: cbt_res.stdout | trim | bool 218 219 - name: enable WSMan CredSSP Server 220 win_shell: | 221 $val = (Get-Item -LiteralPath WSMan:\localhost\Service\Auth\CredSSP).Value 222 if ($val -eq 'true') { 223 $false 224 } else { 225 $null = Enable-WSManCredSSP -Role Server 226 $true 227 } 228 register: credssp_res 229 changed_when: credssp_res.stdout | trim | bool 230 231 - name: allow SMB traffic in 232 win_firewall_rule: 233 name: File and Printer Sharing (SMB-In) 234 state: present 235 enabled: yes 236 237- name: set up Linux host 238 hosts: linux_children 239 gather_facts: no 240 become: yes 241 handlers: 242 - name: restart NetworkManager.service 243 service: 244 name: NetworkManager.service 245 state: restarted 246 247 tasks: 248 - name: install base packages 249 yum: 250 name: 251 - dnsmasq 252 - epel-release 253 - gcc 254 - python3 255 - python3-devel 256 - unzip 257 - vim 258 state: present 259 260 - name: install kerberos packages 261 yum: 262 name: '{{ krb_packages }}' 263 state: present 264 265 - name: ensure virtualenv is installed on base Python interpreters 266 pip: 267 name: 268 - virtualenv 269 - wheel 270 executable: /usr/bin/pip3 271 272 - name: setup NetworkManager to use dnsmasq 273 copy: 274 dest: /etc/NetworkManager/conf.d/dns.conf 275 content: | 276 [main] 277 dns=dnsmasq 278 notify: restart NetworkManager.service 279 280 - name: set dnsmasq to forward requests for domain to DC 281 copy: 282 dest: /etc/NetworkManager/dnsmasq.d/{{ domain_name }} 283 content: server=/{{ domain_name }}/{{ hostvars[groups['win_controller'][0]]["ansible_host"] }} 284 notify: restart NetworkManager.service 285 286 - name: template krb5.conf file 287 template: 288 src: krb5.conf.tmpl 289 dest: /etc/krb5.conf 290 291 - name: create AD principal for Linux keytabs 292 win_domain_user: 293 name: '{{ inventory_hostname }}_{{ item }}' 294 description: Kerberos principal for {{ inventory_hostname }} {{ item }} keytab 295 password: '{{ domain_password }}' 296 password_never_expires: yes 297 update_password: on_create 298 attributes: 299 msDS-SupportedEncryptionTypes: 16 # AES256_CTS_HMAC_SHA1_96 300 state: present 301 become: no 302 delegate_to: DC01 303 with_items: 304 - HTTP 305 - cifs 306 307 - name: create keytab for Linux hosts 308 win_command: >- 309 ktpass.exe 310 -out C:\temp\{{ inventory_hostname }}-{{ item }}.keytab 311 -princ {{ item }}/{{ inventory_hostname }}.{{ domain_name }}@{{ domain_name | upper }} 312 -mapUser {{ inventory_hostname }}_{{ item }}@{{ domain_name | upper }} 313 +rndpass 314 -mapOp set 315 -crypto AES256-SHA1 316 -ptype KRB5_NT_PRINCIPAL 317 args: 318 creates: C:\temp\{{ inventory_hostname }}-{{ item }}.keytab 319 become: no 320 delegate_to: DC01 321 with_items: 322 - HTTP 323 - cifs 324 325 - name: fetch the keytab 326 fetch: 327 src: C:\temp\{{ inventory_hostname }}-{{ item }}.keytab 328 dest: '{{ inventory_hostname }}-{{ item }}.keytab' 329 flat: yes 330 become: no 331 delegate_to: DC01 332 with_items: 333 - HTTP 334 - cifs 335 336 - name: copy keytabs to host 337 copy: 338 src: '{{ inventory_hostname }}-{{ item }}.keytab' 339 dest: /etc/{{ item }}.keytab 340 with_items: 341 - HTTP 342 - cifs 343 344 - name: ensure wheel dir exists 345 file: 346 path: ~/wheels 347 state: directory 348 become: no 349 350 - name: extract wheel artifacts 351 unarchive: 352 src: artifact.zip 353 dest: ~/wheels 354 become: no 355 356 - name: find the universal wheel 357 find: 358 paths: ~/wheels 359 patterns: pyspnego-*-py2.py3-none-any.whl 360 recurse: no 361 file_type: file 362 become: no 363 register: wheel_artifact 364 365 - name: create a virtualenv for each Python interpeter 366 pip: 367 name: 368 - gssapi 369 - krb5 370 - pytest 371 - pytest-forked 372 - requests 373 - wheel 374 - '{{ wheel_artifact.files[0].path }}' 375 virtualenv: '{{ python_venv_path }}/{{ item | basename }}' 376 virtualenv_python: '{{ item }}' 377 become: no 378 with_items: '{{ python_interpreters }}' 379 380 - name: template out test integration file 381 template: 382 src: test_integration.py.tmpl 383 dest: ~/test_integration.py 384 block_start_string: '{!!' 385 block_end_string: '!!}' 386 tags: 387 - template 388 become: no 389