imfreedom/ansible

30b8274a7640
Parents 328311faa7fc
Children 598fa3498557
Support installing a Windows based teamcity agent

This will only cover what's needed to get a working teamcity agent and install
MSYS2, but what we will not cover in this commit is getting all the necessary
dependencies for building Pidgin in Windows

This commit will also update the README to mention that installing the ansible
collections we depend on is now necessary (this is our first use of ansible
collection in this repo and it makes our life easier since it provides some
handy modules for interacting with Windows)

Testing Done:
Ran `teamcity_simple_agent.yml` playbook against a Windows 10 virtual machine and a containerized teamcity server and I ended up getting all the way until a succesfully registered (but unauthorized) agent

Bugs closed: INFRASTRUCTURE-49

Reviewed at https://reviews.imfreedom.org/r/1008/
  • +13 -4
    README.md
  • +4 -0
    collections/requirements.yml
  • +3 -0
    requirements.txt
  • +4 -0
    roles/git/meta/main.yml
  • +2 -1
    roles/git/tasks/main.yml
  • +6 -0
    roles/git/tasks/windows.yml
  • +4 -0
    roles/mercurial/meta/main.yml
  • +2 -0
    roles/mercurial/tasks/main.yml
  • +29 -0
    roles/mercurial/tasks/windows.yml
  • +3 -0
    roles/mercurial/vars/windows.yml
  • +0 -1
    roles/meson/defaults/main.yml
  • +4 -0
    roles/meson/meta/main.yml
  • +2 -0
    roles/meson/tasks/main.yml
  • +10 -0
    roles/meson/tasks/windows.yml
  • +2 -0
    roles/openjdk/tasks/main.yml
  • +34 -0
    roles/openjdk/tasks/windows.yml
  • +3 -0
    roles/openjdk/vars/windows.yml
  • +5 -0
    roles/pidgin3_dependencies/meta/main.yml
  • +2 -0
    roles/pidgin3_dependencies/tasks/main.yml
  • +39 -0
    roles/pidgin3_dependencies/tasks/windows.yml
  • +3 -0
    roles/python/tasks/main.yml
  • +27 -0
    roles/python/tasks/windows.yml
  • +4 -0
    roles/python/vars/windows.yml
  • +3 -4
    roles/teamcity/defaults/main.yml
  • +6 -4
    roles/teamcity/meta/main.yaml
  • +2 -10
    roles/teamcity/tasks/agent.yml
  • +20 -15
    roles/teamcity/tasks/main.yml
  • +0 -8
    roles/teamcity/tasks/vars.yml
  • +149 -0
    roles/teamcity/tasks/windows.yml
  • +52 -0
    roles/teamcity/templates/buildAgent.properties.j2
  • +5 -0
    roles/teamcity/vars/nix.yml
  • +5 -0
    roles/teamcity/vars/windows.yml
  • +186 -0
    roles/win_common/defaults/main.yml
  • +31 -0
    roles/win_common/files/redhat.pem
  • +25 -0
    roles/win_common/tasks/main.yml
  • +11 -0
    roles/win_common/tasks/msys-install-package.yml
  • +29 -0
    roles/win_common/tasks/msys.yml
  • +39 -0
    roles/win_common/tasks/remove-annoyances.yml
  • +32 -0
    roles/win_common/tasks/remove-appxpackage.yml
  • +46 -0
    roles/win_common/tasks/spice-guest-tools.yml
  • --- a/README.md Tue Nov 23 05:37:26 2021 -0600
    +++ b/README.md Thu Nov 25 01:38:59 2021 -0600
    @@ -3,12 +3,21 @@
    This repository contains the ansible configuration for running our non
    kubernetes infrastructure.
    -# Setup
    +## Setup
    +
    +Before you can run our ansible playbooks, you need to meet the following
    +prerequisites:
    -Before you can run this ansible script, you need to creat a password named
    -`vault_password` in the repository root with the proper vault password.
    +* create a file named `vault_password` in the repository root with the proper
    + decryption password of the ansible vaulted file with our secrets.
    +* download all the collections our roles depend on with the following command
    -# Running
    + ```
    + ansible-galaxy collection install \
    + --requirements-file collections/requirements.yml
    + ```
    +
    +## Running
    There are a number of playbooks that you can run.
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/collections/requirements.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,4 @@
    +---
    +collections:
    + - name: community.crypto
    + - name: community.windows
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/requirements.txt Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,3 @@
    +ansible
    +requests-credssp
    +pywinrm
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/git/meta/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,4 @@
    +---
    +dependencies:
    + - role: win_common
    + when: ansible_facts['os_family'] == "Windows"
    --- a/roles/git/tasks/main.yml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/git/tasks/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -7,4 +7,5 @@
    when: ansible_system == "FreeBSD"
    - include_tasks: openbsd.yml
    when: ansible_system == "OpenBSD"
    -
    +- include_tasks: windows.yml
    + when: ansible_facts['os_family'] == "Windows"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/git/tasks/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,6 @@
    +---
    +- include_role:
    + name: win_common
    + tasks_from: msys-install-package
    + vars:
    + package_name: git
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/mercurial/meta/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,4 @@
    +---
    +dependencies:
    + - role: python
    + when: ansible_facts['os_family'] == "Windows"
    --- a/roles/mercurial/tasks/main.yml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/mercurial/tasks/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -7,4 +7,6 @@
    when: ansible_facts['os_family'] == "FreeBSD"
    - include_tasks: openbsd.yml
    when: ansible_facts['os_family'] == "OpenBSD"
    +- include_tasks: windows.yml
    + when: ansible_facts['os_family'] == "Windows"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/mercurial/tasks/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,29 @@
    +---
    +- include_vars: windows.yml
    +
    +- name: "install mercurial {{ mercurial_version }}"
    + ansible.windows.win_package:
    + path: "https://www.mercurial-scm.org/release/windows/mercurial-{{ mercurial_version }}-x64.msi"
    + product_id: "{{ mercurial_product_id }}"
    + arguments: /quiet
    + state: present
    +
    +# note: we assume python_install_path is defined in the `python` role that this
    +# role depends on
    +- name: install latest version of hg-evolve from pypi
    + become: yes
    + ansible.windows.win_command: "pip3.exe install --upgrade hg-evolve"
    + args:
    + chdir: '{{ python_install_path }}\Scripts'
    + register: install_output
    + changed_when: "'Successfully installed' in install_output.stdout"
    +
    +# evolve will only be enabled for the user ansible is running as, which we
    +# assume is the one that will be used to login interactively when needed
    +- name: "enable evolve extension in mercurial config for user {{ ansible_user }}"
    + ansible.windows.win_copy:
    + dest: '{{ ansible_env.USERPROFILE }}\.hgrc'
    + content: |
    + [extensions]
    + evolve = {{ python_install_path }}\Lib\site-packages\hgext3rd\evolve
    + force: yes
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/mercurial/vars/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,3 @@
    +---
    +mercurial_version: 5.9.3
    +mercurial_product_id: '{6CBAA69D-20AD-4B50-A715-7477201A02EF}'
    --- a/roles/meson/defaults/main.yml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/meson/defaults/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -1,3 +1,2 @@
    ---
    meson_install_from_pip: false
    -
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/meson/meta/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,4 @@
    +---
    +dependencies:
    + - role: python
    + when: ansible_facts['os_family'] == "Windows"
    --- a/roles/meson/tasks/main.yml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/meson/tasks/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -7,4 +7,6 @@
    when: ansible_facts['os_family'] == "FreeBSD"
    - include_tasks: openbsd.yml
    when: ansible_facts['os_family'] == "OpenBSD"
    +- include_tasks: windows.yml
    + when: ansible_facts['os_family'] == "Windows"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/meson/tasks/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,10 @@
    +---
    +# note: we assume python_install_path is defined in the `python` role that this
    +# role depends on
    +- name: install latest meson from pypi
    + become: yes
    + ansible.windows.win_command: "pip3.exe install --upgrade meson"
    + args:
    + chdir: '{{ python_install_path }}\Scripts'
    + register: install_output
    + changed_when: "'Successfully installed' in install_output.stdout"
    --- a/roles/openjdk/tasks/main.yml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/openjdk/tasks/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -7,3 +7,5 @@
    when: ansible_system == "FreeBSD"
    - include_tasks: openbsd.yml
    when: ansible_system == "OpenBSD"
    +- include_tasks: windows.yml
    + when: ansible_facts['os_family'] == "Windows"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/openjdk/tasks/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,34 @@
    +---
    +# note: for Windows we'll be using the Microsoft build of OpenJDK, mainly due to
    +# it being distributed with an installer that's easy to use with ansible
    +- include_vars: windows.yml
    +
    +- name: install microsoft openjdk
    + ansible.windows.win_package:
    + path: "https://aka.ms/download-jdk/microsoft-jdk-{{ microsoft_openjdk_version }}-windows-x64.msi"
    + product_id: "{{ microsoft_openjdk_product_id }}"
    + arguments: /passive
    + state: present
    +
    +- name: find path of java executable
    + ansible.windows.win_find:
    + paths: 'C:\Program Files\Microsoft\jdk-{{ microsoft_openjdk_version.split(".")[0:-1] | join(".") }}-hotspot'
    + patterns: java.exe
    + recurse: yes
    + register: java_executable
    +
    +# the following is so that we don't get a firewall prompt when the java process
    +# is started for the first time
    +- name: deny inbound tcp/udp connections for opendjk
    + community.windows.win_firewall_rule:
    + name: "OpenJDK {{ item }}"
    + program: "{{ java_executable.files[0].path }}"
    + localport: any
    + action: block
    + direction: in
    + protocol: "{{ item }}"
    + state: present
    + enabled: yes
    + loop:
    + - tcp
    + - udp
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/openjdk/vars/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,3 @@
    +---
    +microsoft_openjdk_version: 11.0.12.7.1
    +microsoft_openjdk_product_id: '{0D2DCFF8-7746-445B-B643-9E85ABE01173}'
    --- a/roles/pidgin3_dependencies/meta/main.yml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/pidgin3_dependencies/meta/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -1,3 +1,8 @@
    +---
    dependencies:
    - libhandy
    - libnice
    + - role: python
    + when: ansible_facts['os_family'] == "Windows"
    + - role: win_common
    + when: ansible_facts['os_family'] == "Windows"
    --- a/roles/pidgin3_dependencies/tasks/main.yml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/pidgin3_dependencies/tasks/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -7,4 +7,6 @@
    when: ansible_system == "FreeBSD"
    - include_tasks: openbsd.yml
    when: ansible_system == "OpenBSD"
    +- include_tasks: windows.yml
    + when: ansible_facts['os_family'] == "Windows"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/pidgin3_dependencies/tasks/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,39 @@
    +---
    +# note: we assume python_install_path is defined in the `python` role that this
    +# role depends on
    +- name: install latest ninja from pypi
    + become: yes
    + ansible.windows.win_command: "pip3.exe install --upgrade ninja"
    + args:
    + chdir: '{{ python_install_path }}\Scripts'
    + register: install_output
    + changed_when: "'Successfully installed' in install_output.stdout"
    +
    +- name: "install pidgin3 msys dependencies"
    + include_role:
    + name: win_common
    + tasks_from: msys-install-package
    + loop:
    + - help2man
    + - mingw-w64-x86_64-cmake
    + - mingw-w64-x86_64-cmark
    + - mingw-w64-x86_64-farstream
    + - mingw-w64-x86_64-gcc
    + - mingw-w64-x86_64-gcc-libgfortran
    + - mingw-w64-x86_64-gettext
    + - mingw-w64-x86_64-gi-docgen
    + - mingw-w64-x86_64-gnupg
    + - mingw-w64-x86_64-gobject-introspection
    + - mingw-w64-x86_64-gspell
    + - mingw-w64-x86_64-gst-plugins-bad-libs
    + - mingw-w64-x86_64-gst-plugins-base
    + - mingw-w64-x86_64-gstreamer
    + - mingw-w64-x86_64-gtk3
    + - mingw-w64-x86_64-gumbo-parser
    + - mingw-w64-x86_64-libglade
    + - mingw-w64-x86_64-libhandy
    + - mingw-w64-x86_64-libsoup
    + - mingw-w64-x86_64-vala
    + loop_control:
    + label: "{{ package_name }}"
    + loop_var: package_name
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/python/tasks/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,3 @@
    +---
    +- include_tasks: windows.yml
    + when: ansible_facts['os_family'] == "Windows"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/python/tasks/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,27 @@
    +---
    +- include_vars: windows.yml
    +
    +- name: "install python {{ python_version }}"
    + ansible.windows.win_package:
    + path: "https://www.python.org/ftp/python/{{ python_version }}/python-{{ python_version }}-amd64.exe"
    + product_id: "{{ python_product_id }}"
    + arguments: /quiet InstallAllUsers=1 PrependPath=1
    + state: present
    +
    +# we need to update pip using python -m pip since otherwise we'll hit
    +# https://github.com/pypa/pip/issues/6893
    +- name: update pip to latest version in pypi
    + become: yes
    + ansible.windows.win_command: "python.exe -m pip install --upgrade pip"
    + args:
    + chdir: '{{ python_install_path }}'
    + register: install_output
    + changed_when: "'Successfully installed' in install_output.stdout"
    +
    +- name: update setuptools and wheel to latest version in pypi
    + become: yes
    + ansible.windows.win_command: "pip3.exe install --upgrade setuptools wheel"
    + args:
    + chdir: '{{ python_install_path }}\Scripts'
    + register: install_output
    + changed_when: "'Successfully installed' in install_output.stdout"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/python/vars/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,4 @@
    +---
    +python_version: 3.10.0
    +python_product_id: '{B137EFE9-BD8A-4138-AC7F-360461C4EEAF}'
    +python_install_path: 'C:\Program Files\Python{{ python_version.split(".")[0:2] | join }}'
    --- a/roles/teamcity/defaults/main.yml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/teamcity/defaults/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -1,9 +1,8 @@
    ---
    -teamcity_agent_name: "{{ansible_host}}"
    +teamcity_agent_name: "{{ ansible_host }}"
    teamcity_user: teamcity
    -teamcity_group: teamcity
    -teamcity_groups: []
    -teamcity_home: /var/lib/teamcity/
    teamcity_properties: []
    teamcity_authorization_token:
    teamcity_jdk_version: openjdk11
    +
    +teamcity_build_agent_zip_url: "{{ teamcity_server }}update/buildAgent.zip"
    --- a/roles/teamcity/meta/main.yaml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/teamcity/meta/main.yaml Thu Nov 25 01:38:59 2021 -0600
    @@ -1,6 +1,8 @@
    ---
    dependencies:
    - - curl
    - - git
    - - mercurial
    - - openjdk
    + - role: curl
    + - role: git
    + - role: mercurial
    + - role: openjdk
    + - role: win_common
    + when: ansible_facts['os_family'] == "Windows"
    --- a/roles/teamcity/tasks/agent.yml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/teamcity/tasks/agent.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -39,17 +39,9 @@
    when: _build_agent_token.changed
    - name: configure agent
    - copy:
    + template:
    + src: buildAgent.properties.j2
    dest: "{{ teamcity_agent_home }}conf/buildAgent.properties"
    - content: |
    - serverUrl={{ teamcity_server }}
    - name={{ teamcity_agent_name }}
    - workDir=../work
    - tempDir=../temp
    - systemDir=../system
    - authorizationToken={{ teamcity_authorization_token }}
    - {% for item in teamcity_properties %}{{ item }}
    - {% endfor %}
    owner: "{{ teamcity_user }}"
    group: "{{ teamcity_group }}"
    mode: 0700
    --- a/roles/teamcity/tasks/main.yml Tue Nov 23 05:37:26 2021 -0600
    +++ b/roles/teamcity/tasks/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -1,26 +1,31 @@
    # vi:et:ts=2 sw=2 sts=2
    ---
    -- name: precondition - teamcity server
    - fail: msg="ERROR - required variable 'teamcity_server' missing"
    - when: teamcity_server is not defined
    +- block:
    + - name: precondition - teamcity server
    + fail: msg="ERROR - required variable 'teamcity_server' missing"
    + when: teamcity_server is not defined
    +
    + - include_tasks: dependencies.yml
    -- include_tasks: dependencies.yml
    + - include_vars: nix.yml
    -- include_tasks: vars.yml
    + - include_tasks: user.yml
    + when: ansible_system != "Darwin"
    -- include_tasks: user.yml
    - when: ansible_system != "Darwin"
    + - include_tasks: known_hosts.yml
    -- include_tasks: known_hosts.yml
    + - include_tasks: agent.yml
    -- include_tasks: agent.yml
    + - include_tasks: systemd.yml
    + when: ansible_system == "Linux"
    -- include_tasks: systemd.yml
    - when: ansible_system == "Linux"
    + - include_tasks: freebsd.yml
    + when: ansible_system == "FreeBSD"
    -- include_tasks: freebsd.yml
    - when: ansible_system == "FreeBSD"
    + - include_tasks: openbsd.yml
    + when: ansible_system == "OpenBSD"
    + when: ansible_facts['os_family'] != "Windows"
    -- include_tasks: openbsd.yml
    - when: ansible_system == "OpenBSD"
    +- include_tasks: windows.yml
    + when: ansible_facts['os_family'] == "Windows"
    --- a/roles/teamcity/tasks/vars.yml Tue Nov 23 05:37:26 2021 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,8 +0,0 @@
    ----
    -- name: set internal var for buildAgent.zip url
    - set_fact:
    - teamcity_build_agent_zip_url: "{{ teamcity_server }}update/buildAgent.zip"
    -- name: set internal var for teamcity_agent_home
    - set_fact:
    - teamcity_agent_home: "{{ teamcity_home }}tc/"
    -
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/teamcity/tasks/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,149 @@
    +---
    +# part of these set of tasks were taken from
    +# https://github.com/ccdc-opensource/ansible-role-ccdc-teamcity-agent
    +- include_vars: windows.yml
    +
    +- name: "create user {{ teamcity_user }}"
    + ansible.windows.win_user:
    + name: "{{ teamcity_user }}"
    + password: "{{ teamcity_user_password }}"
    + password_never_expires: yes
    + state: present
    + groups_action: replace
    + groups:
    + - Users
    +
    +- name: "create profile directory structure for user {{ teamcity_user }}"
    + community.windows.win_user_profile:
    + username: "{{ teamcity_user }}"
    + state: present
    +
    +# for some reason creating the profile for the user with win_user_profile does
    +# not create the `Startup` directory (although it does get all the way to
    +# creating the `Programs` directory which is its parent) so we need to ensure
    +# that it's effectively there
    +- name: ensure directory that contains autostart tasks on logon is present
    + ansible.windows.win_file:
    + path: "{{ teamcity_user_startup_directory }}"
    + state: directory
    +
    +# needed so that scheduled task that starts agent is triggered
    +- name: "set autologon for {{ teamcity_user }}"
    + community.windows.win_auto_logon:
    + username: "{{ teamcity_user }}"
    + password: "{{ teamcity_user_password }}"
    + state: present
    +
    +# note: autolock is not instantaneous and it can take up to 20 seconds from
    +# logon for the lock to take effect. For a potentially faster (but more
    +# complex to implement) approach to autolock we can see
    +# https://superuser.com/a/899927
    +- name: "set autolock after logon for {{ teamcity_user }}"
    + community.windows.win_shortcut:
    + src: C:\Windows\System32\rundll32.exe
    + arguments: "user32.dll, LockWorkStation"
    + dest: '{{ teamcity_user_startup_directory }}\Lock Computer.lnk'
    + directory: C:\Windows\System32
    +
    +- name: create teamcity agent install directory
    + ansible.windows.win_file:
    + path: "{{ teamcity_agent_install_dir }}"
    + state: directory
    +
    +- name: download agent files
    + ansible.windows.win_get_url:
    + url: "{{ teamcity_build_agent_zip_url }}"
    + dest: "{{ ansible_env.TEMP }}"
    + register: downloaded_file
    +
    +- name: get installed version of teamcity agent
    + ansible.windows.win_find:
    + paths: "{{ teamcity_agent_install_dir }}"
    + recurse: no
    + file_type: file
    + patterns: "BUILD*"
    + register: current_version
    +
    +- name: check if agent is installed
    + set_fact:
    + is_installed: "{{ true if (current_version.files | length > 0) else false }}"
    +
    +- block:
    + - name: ensure teamcity agent is stopped
    + ansible.windows.win_shell: bin\agent.bat stop force
    + args:
    + chdir: "{{ teamcity_agent_install_dir }}"
    + register: stop_process
    +
    + # the agent may take a bit to stop so we retry a few times before we
    + # actually fail the task
    + - name: delete teamcity agent files
    + ansible.windows.win_file:
    + path: "{{ teamcity_agent_install_dir }}"
    + state: absent
    + retries: 3
    + delay: 10
    + register: delete_files
    + until: not delete_files.failed
    + when: is_installed and teamcity_agent_force_reinstall
    +
    +- name: unzip agent files
    + community.windows.win_unzip:
    + src: "{{ downloaded_file.dest }}"
    + dest: "{{ teamcity_agent_install_dir }}"
    + when: not is_installed or teamcity_agent_force_reinstall
    +
    +- name: configure agent
    + ansible.windows.win_template:
    + src: buildAgent.properties.j2
    + dest: '{{ teamcity_agent_install_dir }}\conf\buildAgent.properties'
    + force: yes
    +
    +- name: "change ownership of all build agent files to {{ teamcity_user }}"
    + ansible.windows.win_owner:
    + path: "{{ teamcity_agent_install_dir }}"
    + user: "{{ teamcity_user }}"
    + recurse: true
    +
    +- name: "give {{ teamcity_user }} full control on files"
    + ansible.windows.win_acl:
    + user: "{{ teamcity_user }}"
    + path: "{{ teamcity_agent_install_dir }}"
    + rights: FullControl
    + type: allow
    + state: present
    + propagation: InheritOnly
    + inherit: ContainerInherit, ObjectInherit
    +
    +# we are not using a windows service to start the teamcity agent since its
    +# recognized by jetbrains that it may cause issues for processes executed by
    +# the agent (like a build pipeline)
    +# https://www.jetbrains.com/help/teamcity/setting-up-and-running-additional-build-agents.html#Automatic+Agent+Start+under+Windows
    +# note: the win_scheduled_task appears to have some sort of bug when using the
    +# logon type trigger that causes the task to always be marked as changed
    +- name: add scheduled task to start teamcity agent at logon
    + community.windows.win_scheduled_task:
    + name: TeamCityAgent
    + description: "Start the Teamcity agent in {{ teamcity_agent_install_dir }}"
    + path: \TCAgents
    + enabled: yes
    + logon_type: interactive_token
    + restart_count: 3
    + restart_interval: PT5M # 5 minutes in ISO8601 duration representation
    + execution_time_limit: PT0S # 0 seconds, i.e. no time limit
    + start_when_available: yes
    + actions:
    + - path: bin\agent.bat
    + arguments: start
    + working_directory: "{{ teamcity_agent_install_dir }}"
    + triggers:
    + - enabled: yes
    + type: logon
    + user_id: "{{ teamcity_user }}"
    + username: "{{ teamcity_user }}"
    + password: "{{ teamcity_user_password }}"
    + update_password: no
    +
    +- name: reboot computer to allow teamcity agent to start
    + ansible.windows.win_reboot:
    + when: not is_installed or teamcity_agent_force_reinstall
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/teamcity/templates/buildAgent.properties.j2 Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,52 @@
    +## TeamCity build agent configuration file
    +
    +######################################
    +# Required Agent Properties #
    +######################################
    +
    +## The address of the TeamCity server. The same as is used to open TeamCity web interface in the browser.
    +## Example: serverUrl=https://buildserver.mydomain.com:8111
    +serverUrl={{ teamcity_server }}
    +
    +## The unique name of the agent used to identify this agent on the TeamCity server
    +## Use blank name to let server generate it.
    +## By default, this name would be created from the build agent's host name
    +name={{ teamcity_agent_name }}
    +
    +## Container directory to create default checkout directories for the build configurations.
    +## TeamCity agent assumes ownership of the directory and will delete unknown directories inside.
    +workDir=../work
    +
    +## Container directory for the temporary directories.
    +## TeamCity agent assumes ownership of the directory. The directory may be cleaned between the builds.
    +tempDir=../temp
    +
    +## Container directory for agent state files and caches.
    +## TeamCity agent assumes ownership of the directory and can delete content inside.
    +systemDir=../system
    +
    +
    +######################################
    +# Optional Agent Properties #
    +######################################
    +
    +## A token which is used to identify this agent on the TeamCity server for agent authorization purposes.
    +## It is automatically generated and saved back on the first agent connection to the server.
    +authorizationToken={{ teamcity_authorization_token }}
    +
    +
    +######################################
    +# Default Build Properties #
    +######################################
    +## All properties starting with "system.name" will be passed to the build script as "name"
    +## All properties starting with "env.name" will be set as environment variable "name" for the build process
    +## Note that value should be properly escaped. (use "\\" to represent single backslash ("\"))
    +## More on file structure: http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html#load(java.io.InputStream)
    +
    +# Build Script Properties
    +{% for item in teamcity_properties %}
    +{{ item }}
    +{% endfor %}
    +
    +# Environment Variables
    +#env.exampleEnvVar=example Env Value
    \ No newline at end of file
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/teamcity/vars/nix.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,5 @@
    +---
    +teamcity_group: teamcity
    +teamcity_groups: []
    +teamcity_home: /var/lib/teamcity/
    +teamcity_agent_home: "{{ teamcity_home }}tc/"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/teamcity/vars/windows.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,5 @@
    +---
    +teamcity_agent_install_dir: C:\teamcity
    +teamcity_user: teamcity
    +teamcity_user_startup_directory: 'C:\Users\{{ teamcity_user }}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup'
    +teamcity_agent_force_reinstall: no
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/win_common/defaults/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,186 @@
    +---
    +update_windows: yes
    +
    +msys_install_path: C:\msys64
    +msys_force_reinstall: no
    +msys_version: 2021-07-25
    +msys_product_id: '{3c947ee0-f9a9-41af-b8a6-31e7375d164f}'
    +msys_checksum: 7e055b71306e64192e2612f959f54ae99a5cf57186206ac702d113ef00ba35c0
    +msys_installer_path: "{{ ansible_env.TEMP }}\\msys2.exe"
    +
    +spice_guest_tools_version: 0.141
    +spice_guest_tools_checksum: b5be0754802bcd7f7fe0ccdb877f8a6224ba13a2af7d84eb087a89b3b0237da2
    +spice_guest_tools_product_id: '{8122E54F-475E-4DA7-BEC2-B6B2DE16A988}'
    +spice_guest_tools_url: https://www.spice-space.org/download/windows/spice-guest-tools/spice-guest-tools-{{ spice_guest_tools_version }}/spice-guest-tools-{{ spice_guest_tools_version }}.exe
    +
    +# remove/disable various annoyances from Windows
    +remove_annoyances_regkeys:
    + - description: hide news and interests from taskbar
    + path: HKCU:\Software\Microsoft\Windows\CurrentVersion\Feeds
    + name: ShellFeedsTaskbarViewMode
    + data: 2
    + type: dword
    + - description: hide cortana button from taskbar
    + path: HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
    + name: ShowCortanaButton
    + data: 0
    + type: dword
    + - description: hide meet now button from taskbar
    + path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer
    + name: HideSCAMeetNow
    + data: 1
    + type: dword
    + - description: prevent suggested applications from returning
    + path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent
    + name: DisableWindowsConsumerFeatures
    + data: 1
    + type: dword
    + - description: opt out of telemetry
    + path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection
    + name: AllowTelemetry
    + data: 0
    + type: dword
    + - description: opt out of sharing advertising ID
    + path: HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\AdvertisingInfo
    + name: Enabled
    + data: 0
    + type: dword
    + - description: opt out of sending information about what's typed
    + path: HKCU:\SOFTWARE\Microsoft\Input\TIPC
    + name: Enabled
    + data: 0
    + type: dword
    + - description: opt out of sharing what the language list
    + path: HKCU:\Control Panel\International\User Profile
    + name: HttpAcceptLanguageOptOut
    + data: 0
    + type: dword
    + - description: opt out of sharing what app has been launched
    + path: HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced
    + name: Start_TrackProgs
    + data: 0
    + type: dword
    + - description: show hidden files
    + path: HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced
    + name: Hidden
    + data: 1
    + type: dword
    + - description: show file extensions
    + path: HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced
    + name: HideFileExt
    + data: 0
    + type: dword
    + - description: set default explorer view to this pc
    + path: HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
    + name: LaunchTo
    + data: 1
    + type: dword
    + - description: disable bing search from start menu
    + path: HKCU:\Software\Microsoft\Windows\CurrentVersion\Search
    + name: BingSearchEnabled
    + data: 0
    + type: dword
    + - description: disable privacy prompt on first logon
    + path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\OOBE
    + name: DisablePrivacyExperience
    + data: 1
    + type: dword
    + - description: disable ads via notifications
    + path: HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager
    + name: SubscribedContent-338389Enabled
    + data: 0
    + type: dword
    +
    +uac_regkey:
    + path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
    + name: EnableLUA
    + type: dword
    +
    +unwanted_apps:
    + # list from
    + # https://github.com/W4RH4WK/Debloat-Windows-10/blob/master/scripts/remove-default-apps.ps1
    + # default Windows 10 apps
    + - "Microsoft.549981C3F5F10" #Cortana
    + - "Microsoft.3DBuilder"
    + - "Microsoft.Appconnector"
    + - "Microsoft.BingFinance"
    + - "Microsoft.BingNews"
    + - "Microsoft.BingSports"
    + - "Microsoft.BingTranslator"
    + - "Microsoft.BingWeather"
    + - "Microsoft.GamingServices"
    + - "Microsoft.Microsoft3DViewer"
    + - "Microsoft.MicrosoftOfficeHub"
    + - "Microsoft.MicrosoftPowerBIForWindows"
    + - "Microsoft.MicrosoftSolitaireCollection"
    + - "Microsoft.MinecraftUWP"
    + - "Microsoft.NetworkSpeedTest"
    + - "Microsoft.Office.OneNote"
    + - "Microsoft.Print3D"
    + - "Microsoft.SkypeApp"
    + - "Microsoft.WindowsMaps"
    + - "Microsoft.WindowsPhone"
    + - "Microsoft.Xbox.TCUI"
    + - "Microsoft.XboxApp"
    + - "Microsoft.XboxGameOverlay"
    + - "Microsoft.XboxGamingOverlay"
    + - "Microsoft.XboxSpeechToTextOverlay"
    + - "Microsoft.ZuneMusic"
    + # Threshold 2 apps
    + - "Microsoft.ConnectivityStore"
    + - "Microsoft.Getstarted"
    + - "Microsoft.Messaging"
    + - "Microsoft.Office.Sway"
    + - "Microsoft.OneConnect"
    + - "Microsoft.WindowsFeedbackHub"
    + # Creators Update apps
    + - "Microsoft.Microsoft3DViewer"
    + # Redstone apps
    + - "Microsoft.BingFoodAndDrink"
    + - "Microsoft.BingHealthAndFitness"
    + - "Microsoft.BingTravel"
    + - "Microsoft.WindowsReadingList"
    + # Redstone 5 apps
    + - "Microsoft.MixedReality.Portal"
    + - "Microsoft.ScreenSketch"
    + - "Microsoft.XboxGamingOverlay"
    + # non-Microsoft
    + - "2FE3CB00.PicsArt-PhotoStudio"
    + - "46928bounde.EclipseManager"
    + - "4DF9E0F8.Netflix"
    + - "613EBCEA.PolarrPhotoEditorAcademicEdition"
    + - "6Wunderkinder.Wunderlist"
    + - "7EE7776C.LinkedInforWindows"
    + - "89006A2E.AutodeskSketchBook"
    + - "9E2F88E3.Twitter"
    + - "A278AB0D.DisneyMagicKingdoms"
    + - "A278AB0D.MarchofEmpires"
    + - "ActiproSoftwareLLC.562882FEEB491" # next one is for the Code Writer from Actipro Software LLC
    + - "CAF9E577.Plex"
    + - "ClearChannelRadioDigital.iHeartRadio"
    + - "D52A8D61.FarmVille2CountryEscape"
    + - "D5EA27B7.Duolingo-LearnLanguagesforFree"
    + - "DB6EA5DB.CyberLinkMediaSuiteEssentials"
    + - "DolbyLaboratories.DolbyAccess"
    + - "DolbyLaboratories.DolbyAccess"
    + - "Drawboard.DrawboardPDF"
    + - "Facebook.Facebook"
    + - "Fitbit.FitbitCoach"
    + - "Flipboard.Flipboard"
    + - "GAMELOFTSA.Asphalt8Airborne"
    + - "KeeperSecurityInc.Keeper"
    + - "NORDCURRENT.COOKINGFEVER"
    + - "PandoraMediaInc.29680B314EFC2"
    + - "Playtika.CaesarsSlotsFreeCasino"
    + - "ShazamEntertainmentLtd.Shazam"
    + - "SlingTVLLC.SlingTV"
    + - "SpotifyAB.SpotifyMusic"
    + - "ThumbmunkeysLtd.PhototasticCollage"
    + - "TuneIn.TuneInRadio"
    + - "WinZipComputing.WinZipUniversal"
    + - "XINGAG.XING"
    + - "flaregamesGmbH.RoyalRevolt2"
    + - "king.com.*"
    + - "king.com.BubbleWitch3Saga"
    + - "king.com.CandyCrushSaga"
    + - "king.com.CandyCrushSodaSaga"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/win_common/files/redhat.pem Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,31 @@
    +# Red Hat Publisher Certificate
    +# From https://gitlab.com/mon/autolab/-/wikis/home
    +-----BEGIN CERTIFICATE-----
    +MIIFBjCCA+6gAwIBAgIQVsbSZ63gf3LutGA7v4TOpTANBgkqhkiG9w0BAQUFADCB
    +tDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
    +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug
    +YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEuMCwGA1UEAxMl
    +VmVyaVNpZ24gQ2xhc3MgMyBDb2RlIFNpZ25pbmcgMjAxMCBDQTAeFw0xNjAzMTgw
    +MDAwMDBaFw0xODEyMjkyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRcwFQYDVQQIEw5O
    +b3J0aCBDYXJvbGluYTEQMA4GA1UEBxMHUmFsZWlnaDEWMBQGA1UEChQNUmVkIEhh
    +dCwgSW5jLjEWMBQGA1UEAxQNUmVkIEhhdCwgSW5jLjCCASIwDQYJKoZIhvcNAQEB
    +BQADggEPADCCAQoCggEBAMA3SYpIcNIEzqqy1PNimjt3bVY1KuIuvDABkx8hKUG6
    +rl9WDZ7ibcW6f3cKgr1bKOAeOsMSDu6i/FzB7Csd9u/a/YkASAIIw48q9iD4K6lb
    +Kvd+26eJCUVyLHcWlzVkqIEFcvCrvaqaU/YlX/antLWyHGbtOtSdN3FfY5pvvTbW
    +xf8PJBWGO3nV9CVL1DMK3wSn3bRNbkTLttdIUYdgiX+q8QjbM/VyGz7nA9UvGO0n
    +FWTZRdoiKWI7HA0Wm7TjW3GSxwDgoFb2BZYDDNSlfzQpZmvnKth/fQzNDwumhDw7
    +tVicu/Y8E7BLhGwxFEaP0xZtENTpn+1f0TxPxpzL2zMCAwEAAaOCAV0wggFZMAkG
    +A1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMCsGA1UdHwQkMCIwIKAeoByGGmh0dHA6
    +Ly9zZi5zeW1jYi5jb20vc2YuY3JsMGEGA1UdIARaMFgwVgYGZ4EMAQQBMEwwIwYI
    +KwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkM
    +F2h0dHBzOi8vZC5zeW1jYi5jb20vcnBhMBMGA1UdJQQMMAoGCCsGAQUFBwMDMFcG
    +CCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYTaHR0cDovL3NmLnN5bWNkLmNvbTAm
    +BggrBgEFBQcwAoYaaHR0cDovL3NmLnN5bWNiLmNvbS9zZi5jcnQwHwYDVR0jBBgw
    +FoAUz5mp6nsm9EvJjo/X8AUm7+PSp50wHQYDVR0OBBYEFL/39F5yNDVDib3B3Uk3
    +I8XJSrxaMA0GCSqGSIb3DQEBBQUAA4IBAQDWtaW0Dar82t1AdSalPEXshygnvh87
    +Rce6PnM2/6j/ijo2DqwdlJBNjIOU4kxTFp8jEq8oM5Td48p03eCNsE23xrZl5qim
    +xguIfHqeiBaLeQmxZavTHPNM667lQWPAfTGXHJb3RTT4siowcmGhxwJ3NGP0gNKC
    +PHW09x3CdMNCIBfYw07cc6h9+Vm2Ysm9MhqnVhvROj+AahuhvfT9K0MJd3IcEpjX
    +Z7aMX78Vt9/vrAIUR8EJ54YGgQsF/G9Adzs6fsfEw5Nrk8R0pueRMHRTMSroTe0V
    +Ae2nvuUU6rVI30q8+UjQCxu/ji1/JnitNkUyOPyC46zL+kfHYSnld8U1
    +-----END CERTIFICATE-----
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/win_common/tasks/main.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,25 @@
    +---
    +- name: install all windows updates and reboot as needed
    + ansible.windows.win_updates:
    + category_names: '*'
    + reboot: yes
    + when: update_windows
    +
    +- include_tasks: remove-annoyances.yml
    +
    +# required by win_psmodule
    +- name: ensure the required NuGet package provider is installed
    + ansible.windows.win_shell: Find-PackageProvider -Name Nuget -ForceBootstrap -IncludeDependencies -Force
    + changed_when: false
    +
    +# required by the win_unzip ansible module
    +- name: install powershell community extensions module
    + community.windows.win_psmodule:
    + name: Pscx
    + state: latest
    + allow_clobber: yes
    +
    +- include_tasks: spice-guest-tools.yml
    + when: ansible_system_vendor == "QEMU"
    +
    +- include_tasks: msys.yml
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/win_common/tasks/msys-install-package.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,11 @@
    +---
    +# this task is meant to be included when you need to install a package
    +# thus, it's not actually triggered by this role
    +- name: "install {{ package_name }}"
    + ansible.windows.win_command: "sh.exe --login -c {{ pacman_command | quote }}"
    + args:
    + chdir: "{{ msys_install_path }}\\usr\\bin"
    + vars:
    + pacman_command: "pacman --sync {{ '--needed' if not msys_force_reinstall }} --noconfirm {{ package_name }}"
    + register: install_output
    + changed_when: "'nothing to do' not in install_output.stdout"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/win_common/tasks/msys.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,29 @@
    +---
    +# just using the `win_package` module along the `product_id` parameter does not
    +# seem to work with MSYS2 since it throws the following exception
    +# CreateProcessW() failed (The system cannot find the file specified, Win32ErrorCode 2 - 0x00000002)"
    +# we'll have to investigate more but for now this setup works
    +- name: "check if MSYS2 {{ msys_version }} is installed"
    + ansible.windows.win_shell: Get-Package "MSYS2*" | Select-Object -ExpandProperty Version
    + changed_when: false
    + failed_when: false
    + register: msys_installed
    +
    +- block:
    + - name: "download MSYS2 {{ msys_version }} installer"
    + ansible.windows.win_get_url:
    + url: "https://github.com/msys2/msys2-installer/releases/download/{{ msys_version }}/msys2-x86_64-{{ msys_version | replace('-', '') }}.exe"
    + dest: "{{ msys_installer_path }}"
    + checksum: "{{ msys_checksum }}"
    + checksum_algorithm: sha256
    +
    + # TODO: check if updates are handled by this or if we need to check the version,
    + # uninstall it if it's older than the one desired and then install the desired
    + # version
    + - name: install MSYS2
    + become: yes
    + ansible.windows.win_package:
    + path: "{{ msys_installer_path }}"
    + state: present
    + arguments: "in --confirm-command --accept-messages --root C:/msys64"
    + when: (msys_version | replace('-', '')) not in msys_installed.stdout
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/win_common/tasks/remove-annoyances.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,39 @@
    +---
    +# sadly, HKCU will only impact the user we are connecting as with ansible
    +# so the those annoyances will only impact that user and not others
    +# currently I don't have a nice way of handling multiple users since Windows
    +# has no way to impersonate/become another user without its password
    +# also sadly, some of the settings need to be reapplied if you haven't
    +# interactively logged in as the target user before, like the news feed disabler
    +- name: remove annoyances
    + ansible.windows.win_regedit:
    + path: "{{ item.path }}"
    + name: "{{ item.name }}"
    + data: "{{ item.data }}"
    + type: "{{ item.type }}"
    + loop: "{{ remove_annoyances_regkeys }}"
    + loop_control:
    + label: "{{ item.description }}"
    +
    +# OneDrive seems to be added automatically installed in every new user so the
    +# following task only affects the user that it's being executed as
    +- name: uninstall onedrive
    + ansible.windows.win_package:
    + product_id: 'OneDriveSetup.exe'
    + state: absent
    +
    +- name: get installed default applications
    + ansible.windows.win_shell: Get-AppxPackage -AllUsers | Select-Object Name
    + changed_when: false
    + register: installed_appxpackages
    +
    +- name: get installed default provisioned applications
    + ansible.windows.win_shell: Get-AppxProvisionedPackage -Online | Select-Object DisplayName
    + changed_when: false
    + register: installed_appxprovisionedpackages
    +
    +- name: uninstall default applications
    + include_tasks: remove-appxpackage.yml
    + loop: "{{ unwanted_apps }}"
    + loop_control:
    + label: "{{ item }}"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/win_common/tasks/remove-appxpackage.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,32 @@
    +---
    +- name: "remove appxpackage {{ item }}"
    + ansible.windows.win_shell: "Get-AppxPackage {{ item }} -AllUsers | Remove-AppxPackage"
    + when: item in installed_appxpackages.stdout
    +
    +- name: "remove appxprovisionedpackage {{ item }}"
    + ansible.windows.win_shell: "Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -eq \"{{ item }}\"} | Remove-AppxProvisionedPackage -Online"
    + when: item in installed_appxprovisionedpackages.stdout
    + register: remove_appxprovisionedpackage_output
    + failed_when:
    + - remove_appxprovisionedpackage_output.rc != 0
    + - '"The system cannot find the path specified" not in remove_appxprovisionedpackage_output.stderr'
    +
    +# sometimes the first task in this file removes the appx package but the
    +# registry key for it does not get deleted. This causes the second task
    +# to fail since it finds the key, tries to delete the path associated with
    +# it and fails since it doesn't exist
    +# the following block takes care of this situation when it happens (haven't been
    +# able to figure out when this actually happens, so far it seems to be random)
    +- block:
    + - name: "get PackageName for {{ item }}"
    + ansible.windows.win_shell: "Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -eq \"{{ item }}\"} | Select-Object -ExpandProperty PackageName"
    + register: package_name
    +
    + - name: "remove remaining registry key for appxpackage {{ item }}"
    + ansible.windows.win_regedit:
    + path: 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\Applications\{{ package_name.stdout }}'
    + state: absent
    + delete_key: yes
    + when:
    + - remove_appxprovisionedpackage_output.rc is defined and remove_appxprovisionedpackage_output.rc != 0
    + - '"The system cannot find the path specified" in remove_appxprovisionedpackage_output.stderr'
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/roles/win_common/tasks/spice-guest-tools.yml Thu Nov 25 01:38:59 2021 -0600
    @@ -0,0 +1,46 @@
    +---
    +# we need to specify the path to the .pem file since this module is currently
    +# unable to automatically look for files under the `files` dir of the role
    +# its being executed from, other modules like win_copy do this properly and
    +# thus only the file name is needed
    +# the task runs only on localhost because the module does not run on Windows
    +- name: get thumbprint of Red Hat publisher certificate
    + become: no
    + community.crypto.x509_certificate_info:
    + path: "{{ role_path }}/files/redhat.pem"
    + register: redhat_publisher_certificate_info
    + run_once: yes
    + delegate_to: localhost
    +
    +- name: check if Red Hat publisher certificate is installed
    + community.windows.win_certificate_info:
    + store_location: LocalMachine
    + store_name: TrustedPublisher
    + thumbprint: "{{ redhat_publisher_certificate_info.fingerprints.sha1.replace(':', '') | upper }}"
    + register: matched_certificate
    +
    +- block:
    + - name: copy Red Hat publisher certificate
    + ansible.windows.win_copy:
    + src: redhat.pem
    + dest: '{{ ansible_env.TEMP }}'
    +
    + # mark the publisher of the spice installer (Red Hat in this case) as trusted
    + # without this, the cannot install the guest tools in an unattended fashion as
    + # we'll get a prompt asking to trust the publisher of the installer being
    + # executed otherwise, thus preventing the installation from completing
    + - name: setup Red Hat publisher certificate as trusted
    + ansible.windows.win_certificate_store:
    + path: '{{ ansible_env.TEMP }}\redhat.pem'
    + file_type: pem
    + store_location: LocalMachine
    + store_name: TrustedPublisher
    + state: present
    + when: not matched_certificate.exists
    +
    +- name: install spice Windows guest tools
    + ansible.windows.win_package:
    + path: "{{ spice_guest_tools_url }}"
    + state: present
    + product_id: "{{ spice_guest_tools_product_id }}"
    + arguments: /S