Files
infra/ansible/roles/profile_workstation_host_windows/tasks/main.yml
2026-04-02 15:27:46 +02:00

234 lines
8.8 KiB
YAML

---
- name: Enable required Windows features for WSL
tags: [packages, services, wsl]
ansible.windows.win_optional_feature:
name:
- Microsoft-Windows-Subsystem-Linux
- VirtualMachinePlatform
state: present
include_parent: true
- name: Gather Windows host version details
tags: [packages]
ansible.windows.win_powershell:
script: |
$os = Get-ComputerInfo
$Ansible.Result = @{
product_name = $os.WindowsProductName
version = $os.WindowsVersion
build_number = [int]$os.OsBuildNumber
is_windows_11 = $os.WindowsProductName -like 'Windows 11*'
is_windows_10 = $os.WindowsProductName -like 'Windows 10*'
}
$Ansible.Changed = $false
register: windows_host_version_state
- name: Enable dark mode for Windows apps
tags: [packages]
ansible.windows.win_regedit:
path: HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize
name: AppsUseLightTheme
data: 0
type: dword
register: windows_apps_dark_mode_state
when: windows_enable_dark_theme | default(false)
- name: Enable dark mode for Windows system surfaces
tags: [packages]
ansible.windows.win_regedit:
path: HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize
name: SystemUsesLightTheme
data: 0
type: dword
register: windows_system_dark_mode_state
when: windows_enable_dark_theme | default(false)
- name: Hide Windows taskbar search box
tags: [packages]
ansible.windows.win_regedit:
path: HKCU:\Software\Microsoft\Windows\CurrentVersion\Search
name: SearchboxTaskbarMode
data: 0
type: dword
register: windows_taskbar_search_state
when: windows_hide_taskbar_search | default(false)
- name: Hide Windows 11 taskbar widgets
tags: [packages]
ansible.windows.win_regedit:
path: HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
name: TaskbarDa
data: 0
type: dword
register: windows_11_widgets_state
when:
- windows_hide_taskbar_widgets | default(false)
- windows_host_version_state.result.is_windows_11 | default(false)
- name: Hide Windows 10 news and interests taskbar widget
tags: [packages]
ansible.windows.win_regedit:
path: HKCU:\Software\Microsoft\Windows\CurrentVersion\Feeds
name: ShellFeedsTaskbarViewMode
data: 2
type: dword
register: windows_10_widgets_state
when:
- windows_hide_taskbar_widgets | default(false)
- windows_host_version_state.result.is_windows_10 | default(false)
- name: Note when Windows shell settings may require sign out
tags: [packages]
ansible.builtin.debug:
msg: >-
Windows shell appearance settings changed. Sign out and back in, or restart explorer.exe,
if taskbar or theme updates do not appear immediately.
changed_when: false
when: >-
(windows_apps_dark_mode_state is changed)
or (windows_system_dark_mode_state is changed)
or (windows_taskbar_search_state is changed)
or (windows_11_widgets_state is changed)
or (windows_10_widgets_state is changed)
- name: Ensure winget is available on Windows host
tags: [packages]
ansible.windows.win_powershell:
script: |
$winget = Get-Command winget.exe -ErrorAction SilentlyContinue
if ($null -eq $winget) {
throw 'winget.exe is not available on the Windows host. Install App Installer or rerun the bootstrap script.'
}
$Ansible.Changed = $false
- name: Ensure WSL 2 is the default backend
tags: [packages, services, wsl]
ansible.windows.win_powershell:
script: |
$status = & wsl --status 2>$null
if ($LASTEXITCODE -eq 0 -and $status -match 'Default Version:\s*2') {
$Ansible.Changed = $false
return
}
& wsl --set-default-version 2
if ($LASTEXITCODE -ne 0) {
throw 'Failed to set WSL default version to 2.'
}
$Ansible.Changed = $true
- name: Install Windows workstation applications with winget
tags: [packages]
ansible.windows.win_powershell:
script: |
$packageId = '{{ item.id }}'
$packageName = '{{ item.name | default(item.id) }}'
$packageScope = '{{ item.scope | default('' ) }}'
$installArgs = @(
'install'
'--id', $packageId
'--exact'
'--silent'
'--accept-package-agreements'
'--accept-source-agreements'
'--disable-interactivity'
)
if (-not [string]::IsNullOrWhiteSpace($packageScope)) {
$installArgs += @('--scope', $packageScope)
}
$installed = & winget list --id $packageId --exact --accept-source-agreements --disable-interactivity 2>$null
if ($LASTEXITCODE -eq 0 -and $installed -match [regex]::Escape($packageId)) {
$Ansible.Changed = $false
return
}
if ($packageScope -eq 'user') {
$interactiveUser = (Get-CimInstance Win32_ComputerSystem).UserName
if ([string]::IsNullOrWhiteSpace($interactiveUser)) {
throw "Failed to install $packageName with winget: no interactive Windows user session is available for a user-scoped install."
}
$taskName = "AnsibleWinget-$($packageId -replace '[^A-Za-z0-9.-]', '-')"
$taskScriptPath = Join-Path $env:TEMP "$taskName.ps1"
$stdoutPath = Join-Path $env:TEMP "$taskName.stdout.log"
$stderrPath = Join-Path $env:TEMP "$taskName.stderr.log"
$quotedArgs = ($installArgs | ForEach-Object { '"' + ($_ -replace '"', '""') + '"' }) -join ', '
$taskScript = @(
"`$installArgs = @($quotedArgs)",
"`$process = Start-Process -FilePath 'winget.exe' -ArgumentList `$installArgs -Wait -PassThru -NoNewWindow -RedirectStandardOutput '$stdoutPath' -RedirectStandardError '$stderrPath'",
"exit `$process.ExitCode"
) -join [Environment]::NewLine
Set-Content -Path $taskScriptPath -Value $taskScript -Encoding Ascii
try {
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$taskScriptPath`""
$principal = New-ScheduledTaskPrincipal -UserId $interactiveUser -LogonType InteractiveToken -RunLevel Limited
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -StartWhenAvailable
Register-ScheduledTask -TaskName $taskName -Action $action -Principal $principal -Settings $settings -Force | Out-Null
Start-ScheduledTask -TaskName $taskName
$deadline = (Get-Date).AddMinutes(10)
do {
Start-Sleep -Seconds 2
$task = Get-ScheduledTask -TaskName $taskName
$taskInfo = Get-ScheduledTaskInfo -TaskName $taskName
} while ((Get-Date) -lt $deadline -and ($task.State -ne 'Ready' -or $taskInfo.LastRunTime -eq [datetime]::MinValue))
if ($task.State -ne 'Ready' -or $taskInfo.LastRunTime -eq [datetime]::MinValue) {
throw "Failed to install $packageName with winget: timed out waiting for the user-scoped scheduled task to finish."
}
if ($taskInfo.LastTaskResult -ne 0) {
$stderr = if (Test-Path $stderrPath) { Get-Content -Path $stderrPath -Raw } else { '' }
$stdout = if (Test-Path $stdoutPath) { Get-Content -Path $stdoutPath -Raw } else { '' }
throw "Failed to install $packageName with winget. Exit code: $($taskInfo.LastTaskResult). Stdout: $stdout Stderr: $stderr"
}
}
finally {
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
Remove-Item -Path $taskScriptPath, $stdoutPath, $stderrPath -Force -ErrorAction SilentlyContinue
}
}
else {
& winget @installArgs
if ($LASTEXITCODE -ne 0) {
throw "Failed to install $packageName with winget"
}
}
$Ansible.Changed = $true
loop: "{{ windows_winget_packages | default([]) }}"
loop_control:
label: "{{ item.id }}"
- name: Install VS Code WSL extensions on Windows host
tags: [packages, vscode]
ansible.windows.win_powershell:
script: |
$extensionId = '{{ item }}'
$code = Get-Command code.cmd -ErrorAction SilentlyContinue
if ($null -eq $code) {
throw 'code.cmd is not available. Ensure Visual Studio Code is installed before managing extensions.'
}
$installedExtensions = & $code.Source --list-extensions
if ($installedExtensions -contains $extensionId) {
$Ansible.Changed = $false
return
}
& $code.Source --install-extension $extensionId --force
if ($LASTEXITCODE -ne 0) {
throw "Failed to install VS Code extension $extensionId"
}
$Ansible.Changed = $true
loop: "{{ windows_vscode_extensions | default([]) }}"
loop_control:
label: "{{ item }}"