# HG changeset patch
# Node ID 5d362f4c121c
# Parent  a272d577ac93
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -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.
 
diff --git a/collections/requirements.yml b/collections/requirements.yml
new file mode 100644
--- /dev/null
+++ b/collections/requirements.yml
@@ -0,0 +1,3 @@
+---
+collections:
+  - name: community.windows
diff --git a/roles/git/meta/main.yml b/roles/git/meta/main.yml
new file mode 100644
--- /dev/null
+++ b/roles/git/meta/main.yml
@@ -0,0 +1,4 @@
+---
+dependencies:
+  - role: win_common
+    when: ansible_facts['os_family'] == "Windows"
diff --git a/roles/git/tasks/main.yml b/roles/git/tasks/main.yml
--- a/roles/git/tasks/main.yml
+++ b/roles/git/tasks/main.yml
@@ -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"
diff --git a/roles/git/tasks/windows.yml b/roles/git/tasks/windows.yml
new file mode 100644
--- /dev/null
+++ b/roles/git/tasks/windows.yml
@@ -0,0 +1,6 @@
+---
+- include_role:
+    name: win_common
+    tasks_from: msys-install-package
+  vars:
+    package_name: git
diff --git a/roles/mercurial/meta/main.yml b/roles/mercurial/meta/main.yml
new file mode 100644
--- /dev/null
+++ b/roles/mercurial/meta/main.yml
@@ -0,0 +1,4 @@
+---
+dependencies:
+  - role: win_common
+    when: ansible_facts['os_family'] == "Windows"
diff --git a/roles/mercurial/tasks/main.yml b/roles/mercurial/tasks/main.yml
--- a/roles/mercurial/tasks/main.yml
+++ b/roles/mercurial/tasks/main.yml
@@ -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"
 
diff --git a/roles/mercurial/tasks/windows.yml b/roles/mercurial/tasks/windows.yml
new file mode 100644
--- /dev/null
+++ b/roles/mercurial/tasks/windows.yml
@@ -0,0 +1,6 @@
+---
+- include_role:
+    name: win_common
+    tasks_from: msys-install-package
+  vars:
+    package_name: mercurial
diff --git a/roles/meson/defaults/main.yml b/roles/meson/defaults/main.yml
--- a/roles/meson/defaults/main.yml
+++ b/roles/meson/defaults/main.yml
@@ -1,3 +1,2 @@
 ---
 meson_install_from_pip: false
-
diff --git a/roles/meson/meta/main.yml b/roles/meson/meta/main.yml
new file mode 100644
--- /dev/null
+++ b/roles/meson/meta/main.yml
@@ -0,0 +1,4 @@
+---
+dependencies:
+  - role: win_common
+    when: ansible_facts['os_family'] == "Windows"
diff --git a/roles/meson/tasks/main.yml b/roles/meson/tasks/main.yml
--- a/roles/meson/tasks/main.yml
+++ b/roles/meson/tasks/main.yml
@@ -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"
 
diff --git a/roles/meson/tasks/windows.yml b/roles/meson/tasks/windows.yml
new file mode 100644
--- /dev/null
+++ b/roles/meson/tasks/windows.yml
@@ -0,0 +1,6 @@
+---
+- include_role:
+    name: win_common
+    tasks_from: msys-install-package
+  vars:
+    package_name: meson
diff --git a/roles/openjdk/tasks/main.yml b/roles/openjdk/tasks/main.yml
--- a/roles/openjdk/tasks/main.yml
+++ b/roles/openjdk/tasks/main.yml
@@ -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"
diff --git a/roles/openjdk/tasks/windows.yml b/roles/openjdk/tasks/windows.yml
new file mode 100644
--- /dev/null
+++ b/roles/openjdk/tasks/windows.yml
@@ -0,0 +1,10 @@
+---
+- include_vars: windows.yml
+
+# TODO: Add product key so that it can check if the package is installed
+# without having to download the msi
+- name: install microsoft openjdk
+  ansible.windows.win_package:
+    path: "https://aka.ms/download-jdk/microsoft-jdk-{{ microsoft_openjdk_version }}-windows-x64.msi"
+    arguments: /passive
+    state: present
diff --git a/roles/openjdk/vars/windows.yml b/roles/openjdk/vars/windows.yml
new file mode 100644
--- /dev/null
+++ b/roles/openjdk/vars/windows.yml
@@ -0,0 +1,2 @@
+---
+microsoft_openjdk_version: 11.0.12.7.1
diff --git a/roles/teamcity/defaults/main.yml b/roles/teamcity/defaults/main.yml
--- a/roles/teamcity/defaults/main.yml
+++ b/roles/teamcity/defaults/main.yml
@@ -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"
diff --git a/roles/teamcity/meta/main.yaml b/roles/teamcity/meta/main.yaml
--- a/roles/teamcity/meta/main.yaml
+++ b/roles/teamcity/meta/main.yaml
@@ -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"
 
diff --git a/roles/teamcity/tasks/agent.yml b/roles/teamcity/tasks/agent.yml
--- a/roles/teamcity/tasks/agent.yml
+++ b/roles/teamcity/tasks/agent.yml
@@ -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
diff --git a/roles/teamcity/tasks/main.yml b/roles/teamcity/tasks/main.yml
--- a/roles/teamcity/tasks/main.yml
+++ b/roles/teamcity/tasks/main.yml
@@ -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"
 
diff --git a/roles/teamcity/tasks/vars.yml b/roles/teamcity/tasks/vars.yml
deleted file mode 100644
--- a/roles/teamcity/tasks/vars.yml
+++ /dev/null
@@ -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/"
-
diff --git a/roles/teamcity/tasks/windows.yml b/roles/teamcity/tasks/windows.yml
new file mode 100644
--- /dev/null
+++ b/roles/teamcity/tasks/windows.yml
@@ -0,0 +1,121 @@
+---
+# 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
+
+# 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: 'C:\Users\{{ teamcity_user }}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\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
+      args:
+        chdir: "{{ teamcity_agent_install_dir }}"
+
+    - name: delete teamcity agent files
+      ansible.windows.win_file:
+        path: "{{ teamcity_agent_install_dir }}"
+        state: absent
+  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'
+
+- 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
+- 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 }}"
+    enabled: true
+    logon_type: interactive_token
+    #run_level: highest
+    restart_count: 10000000
+    execution_time_limit: PT0S
+    start_when_available: true
+    path: \TCAgents
+    actions:
+      - path: bin\agent.bat
+        arguments: start
+        working_directory: "{{ teamcity_agent_install_dir }}"
+    triggers:
+      - enabled: true
+        type: logon
+        user_id: "{{ teamcity_user }}"
+    username: "{{ teamcity_user }}"
+    password: "{{ teamcity_user_password }}"
+    update_password: no
diff --git a/roles/teamcity/templates/buildAgent.properties.j2 b/roles/teamcity/templates/buildAgent.properties.j2
new file mode 100644
--- /dev/null
+++ b/roles/teamcity/templates/buildAgent.properties.j2
@@ -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
diff --git a/roles/teamcity/vars/nix.yml b/roles/teamcity/vars/nix.yml
new file mode 100644
--- /dev/null
+++ b/roles/teamcity/vars/nix.yml
@@ -0,0 +1,5 @@
+---
+teamcity_group: teamcity
+teamcity_groups: []
+teamcity_home: /var/lib/teamcity/
+teamcity_agent_home: "{{ teamcity_home }}tc/"
diff --git a/roles/teamcity/vars/windows.yml b/roles/teamcity/vars/windows.yml
new file mode 100644
--- /dev/null
+++ b/roles/teamcity/vars/windows.yml
@@ -0,0 +1,5 @@
+---
+teamcity_agent_install_dir: C:\teamcity
+teamcity_user: teamcity
+teamcity_agent_temp_dir: '{{ ansible_env.TEMP }}\teamcity'
+teamcity_agent_force_reinstall: no
diff --git a/roles/win_common/defaults/main.yml b/roles/win_common/defaults/main.yml
new file mode 100644
--- /dev/null
+++ b/roles/win_common/defaults/main.yml
@@ -0,0 +1,144 @@
+---
+update_windows: yes
+msys_install_path: C:\msys64
+msys_force_reinstall: no
+msys2_version: 2021-07-25
+msys2_checksum: 7e055b71306e64192e2612f959f54ae99a5cf57186206ac702d113ef00ba35c0
+msys2_installer_path: "{{ ansible_env.TEMP }}\\msys2.exe"
+
+# remove/disable various annoyances from Windows
+remove_annoyances:
+  - 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: 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
+
+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"
diff --git a/roles/win_common/tasks/main.yml b/roles/win_common/tasks/main.yml
new file mode 100644
--- /dev/null
+++ b/roles/win_common/tasks/main.yml
@@ -0,0 +1,22 @@
+---
+- 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: msys.yml
diff --git a/roles/win_common/tasks/msys-install-package.yml b/roles/win_common/tasks/msys-install-package.yml
new file mode 100644
--- /dev/null
+++ b/roles/win_common/tasks/msys-install-package.yml
@@ -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"
diff --git a/roles/win_common/tasks/msys.yml b/roles/win_common/tasks/msys.yml
new file mode 100644
--- /dev/null
+++ b/roles/win_common/tasks/msys.yml
@@ -0,0 +1,31 @@
+---
+# Couldn't find a way to use the `creates_version` parameter of the win_package
+# module to check if the desired version of msys is already installed so we will
+# use a powershell snippet for this (`creates_version` checks a version flag in
+# the metadata of a .exe file and in the executables available in the msys
+# installation directory they all seem to use define this flag in a semver
+# fashion, e.g. 1.3.0, whereas the msys releases use the date as the version)
+- 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 {{ msys2_version }} installer"
+      ansible.windows.win_get_url:
+        url: "https://github.com/msys2/msys2-installer/releases/download/{{ msys2_version }}/msys2-x86_64-{{ msys2_version | replace('-', '') }}.exe"
+        dest: "{{ msys2_installer_path }}"
+        checksum: "{{ msys2_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: "{{ msys2_installer_path }}"
+        state: present
+        arguments: "in --confirm-command --accept-messages --root C:/msys64"
+  when: (msys2_version | replace('-', '')) not in msys_installed.stdout
diff --git a/roles/win_common/tasks/remove-annoyances.yml b/roles/win_common/tasks/remove-annoyances.yml
new file mode 100644
--- /dev/null
+++ b/roles/win_common/tasks/remove-annoyances.yml
@@ -0,0 +1,58 @@
+---
+# 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
+- name: remove annoyances
+  ansible.windows.win_regedit:
+    path: "{{ item.path }}"
+    name: "{{ item.name }}"
+    data: "{{ item.data }}"
+    type: "{{ item.type }}"
+  loop: "{{ remove_annoyances }}"
+  loop_control:
+    label: "{{ item.description }}"
+
+- name: ensure OneDrive process is stopped
+  ansible.windows.win_shell: Get-Process -Name OneDrive | Stop-Process -Force
+  register: onedrive_stop
+  changed_when: onedrive_stop.rc == 0
+  failed_when:
+    - onedrive_stop.rc == 1
+    - "'Cannot find a process with the name' not in onedrive_stop.stderr"
+
+# should be using win_package but it requires a product_id for `state: absent`
+# to work and I couldn't find a way to reliably get that value for now
+- name: uninstall onedrive
+  become: yes
+  become_user: SYSTEM
+  ansible.windows.win_shell: "{{ ansible_env.SystemRoot }}\\SysWOW64\\OneDriveSetup.exe /uninstall"
+  when: onedrive_stop.rc == 0
+
+- name: get installed default applications
+  ansible.windows.win_shell: Get-AppxPackage -AllUsers
+  changed_when: false
+  register: installed_appxpackages
+
+- name: get installed default provisioned applications
+  ansible.windows.win_shell: Get-AppxProvisionedPackage -Online
+  changed_when: false
+  register: installed_appxprovisionedpackages
+
+- name: uninstall default applications
+  block:
+    - ansible.windows.win_shell: "Get-AppxPackage {{ item }} -AllUsers | Remove-AppxPackage"
+      become: yes
+      become_user: SYSTEM
+      when: item in installed_appxpackages
+      loop: "{{ unwanted_apps }}"
+      loop_control:
+        label: "{{ item }}"
+
+    - ansible.windows.win_shell: "Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -eq \"{{ item }}\"} | Remove-AppxProvisionedPackage -Online"
+      become: yes
+      become_user: SYSTEM
+      when: item in installed_appxprovisionedpackages
+      loop: "{{ unwanted_apps }}"
+      loop_control:
+        label: "{{ item }}"
