diff --git a/src/windows-dev-config/dev-config.winget b/src/windows-dev-config/dev-config.winget index 46a1027..c36e8ee 100644 --- a/src/windows-dev-config/dev-config.winget +++ b/src/windows-dev-config/dev-config.winget @@ -11,38 +11,38 @@ resources: # --------------------------------------------------------------------------- # Phase 0 — Elevation check (runs on initial invoke AND post-reboot resume) # --------------------------------------------------------------------------- - -- name: ElevationCheck - type: Microsoft.DSC.Transitional/WindowsPowerShellScript - properties: - getScript: | - $id = [Security.Principal.WindowsIdentity]::GetCurrent() - $p = [Security.Principal.WindowsPrincipal] $id - return @{ elevated = $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } - testScript: | - $id = [Security.Principal.WindowsIdentity]::GetCurrent() - $p = [Security.Principal.WindowsPrincipal] $id - return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - setScript: | - $configFile = "${WinGetConfigRoot}\dev-config.winget" - - if (-not (Test-Path $configFile)) { - $hint = "winget configure --file `"$configFile`" --accept-configuration-agreements --disable-interactivity" - throw "ElevationCheck: not elevated and cannot locate config file to re-launch automatically. Please re-run in an elevated terminal: $hint" - } - - Write-Host "ElevationCheck: not elevated — re-launching winget configure as Administrator..." - - $wingetArgs = @( - 'configure', - '--file', "`"$configFile`"", - '--accept-configuration-agreements', - '--disable-interactivity', - '--wait' - ) - Start-Process winget -ArgumentList $wingetArgs -Verb RunAs - - throw "ElevationCheck: re-launched elevated successfully. This unelevated session is now complete — check the elevated window for results." +# +# - name: ElevationCheck +# type: Microsoft.DSC.Transitional/WindowsPowerShellScript +# properties: +# getScript: | +# $id = [Security.Principal.WindowsIdentity]::GetCurrent() +# $p = [Security.Principal.WindowsPrincipal] $id +# return @{ elevated = $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } +# testScript: | +# $id = [Security.Principal.WindowsIdentity]::GetCurrent() +# $p = [Security.Principal.WindowsPrincipal] $id +# return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +# setScript: | +# $configFile = "${WinGetConfigRoot}\dev-config.winget" +# +# if (-not (Test-Path $configFile)) { +# $hint = "winget configure --file `"$configFile`" --accept-configuration-agreements --disable-interactivity" +# throw "ElevationCheck: not elevated and cannot locate config file to re-launch automatically. Please re-run in an elevated terminal: $hint" +# } +# +# Write-Host "ElevationCheck: not elevated — re-launching winget configure as Administrator..." +# +# $wingetArgs = @( +# 'configure', +# '--file', "`"$configFile`"", +# '--accept-configuration-agreements', +# '--disable-interactivity', +# '--wait' +# ) +# Start-Process winget -ArgumentList $wingetArgs -Verb RunAs +# +# throw "ElevationCheck: re-launched elevated successfully. This unelevated session is now complete — check the elevated window for results." # ============================================================================= # Terminal and PowerShell 7 @@ -50,8 +50,6 @@ resources: - type: Microsoft.WinGet/Package name: Terminal - dependsOn: - - ElevationCheck properties: id: Microsoft.WindowsTerminal source: winget @@ -61,8 +59,6 @@ resources: - type: Microsoft.WinGet/Package name: PowerShell - dependsOn: - - Terminal properties: id: Microsoft.PowerShell source: winget @@ -90,10 +86,9 @@ resources: $system = Get-ItemPropertyValue $regPath -Name SystemUsesLightTheme -EA SilentlyContinue return ($apps -eq 0 -and $system -eq 0) setScript: | - # Apply the .theme file to update shell chrome, wallpaper, and sound scheme - Start-Process -FilePath "C:\Windows\Resources\Themes\dark.theme" - Start-Sleep -Seconds 2 - Stop-Process -Name SystemSettings -Force -ErrorAction SilentlyContinue + $regPath = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize' + Set-ItemProperty -Path $regPath -Name "AppsUseLightTheme" -Value 0 + Set-ItemProperty -Path $regPath -Name "SystemUsesLightTheme" -Value 0 metadata: description: Sets dark theme @@ -196,6 +191,7 @@ resources: - type: Microsoft.DSC.Transitional/PowerShellScript name: SetCascadiaNfAsDefault dependsOn: + - PowerShell - InstallCascadiaCodeNerdFonts properties: getScript: | @@ -318,8 +314,6 @@ resources: - type: Microsoft.Windows/Registry name: Sudo - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Sudo valueName: Enabled @@ -327,13 +321,12 @@ resources: DWord: 3 metadata: description: Enable Sudo in inline mode + securityContext: elevated # Developer Mode: AllowDevelopmentWithoutDevLicense=1 (replaces # Microsoft.Windows.Settings/WindowsSettings DeveloperMode:true) - type: Microsoft.Windows/Registry name: DeveloperMode - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock valueName: AllowDevelopmentWithoutDevLicense @@ -341,12 +334,11 @@ resources: DWord: 1 metadata: description: Enable Developer Mode (sideload + dev features) + securityContext: elevated # Long path support (replaces Microsoft.Windows.Developer/EnableLongPathSupport) - type: Microsoft.Windows/Registry name: LongPaths - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SYSTEM\CurrentControlSet\Control\FileSystem valueName: LongPathsEnabled @@ -354,12 +346,11 @@ resources: DWord: 1 metadata: description: Enable Win32 long path support + securityContext: elevated # Remote Desktop (replaces Microsoft.Windows.Developer/EnableRemoteDesktop) - type: Microsoft.Windows/Registry name: RemoteDesktop - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server valueName: fDenyTSConnections @@ -367,6 +358,8 @@ resources: DWord: 0 metadata: description: Enable Remote Desktop (firewall rule still needs separate enable) + securityContext: elevated + # ============================================================================= # File Explorer and Desktop # ============================================================================= @@ -387,8 +380,6 @@ resources: # FileExtensions:Show) - type: Microsoft.Windows/Registry name: ShowFileExtensions - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: HideFileExt @@ -396,13 +387,12 @@ resources: DWord: 0 metadata: description: Show file extensions in Explorer + securityContext: elevated # Show hidden files (replaces Microsoft.Windows.Developer/WindowsExplorer # HiddenFiles:Show) - type: Microsoft.Windows/Registry name: ShowHiddenFiles - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: Hidden @@ -410,11 +400,10 @@ resources: DWord: 1 metadata: description: Show hidden files in Explorer - + securityContext: elevated + - type: Microsoft.Windows/Registry name: FullPathTitlebar - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: FullPathAddress @@ -422,11 +411,10 @@ resources: DWord: 1 metadata: description: Show full path in Explorer titlebar + securityContext: elevated - type: Microsoft.Windows/Registry name: OpenThisPC - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: LaunchTo @@ -434,11 +422,10 @@ resources: DWord: 1 metadata: description: Open File Explorer to This PC + securityContext: elevated - type: Microsoft.Windows/Registry name: FrequentFolders - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: ShowFrequent @@ -446,11 +433,10 @@ resources: DWord: 0 metadata: description: Disable frequent folders in Quick Access + securityContext: elevated - type: Microsoft.Windows/Registry name: FrequentFiles - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer valueName: ShowRecent @@ -458,11 +444,10 @@ resources: DWord: 0 metadata: description: Disable frequent files in Quick Access + securityContext: elevated - type: Microsoft.Windows/Registry name: RecommendedFiles - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer valueName: ShowCloudFilesInQuickAccess @@ -470,11 +455,10 @@ resources: DWord: 0 metadata: description: Disable recommended/cloud files in Quick Access + securityContext: elevated - type: Microsoft.Windows/Registry name: GitCodeFolders - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: NavPaneShowVersionControl @@ -482,11 +466,10 @@ resources: DWord: 1 metadata: description: Enable Git integration in File Explorer + securityContext: elevated - type: Microsoft.Windows/Registry name: TipsOff - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: ShowSyncProviderNotifications @@ -494,6 +477,7 @@ resources: DWord: 0 metadata: description: Disable sync provider notifications (tips) + securityContext: elevated # ============================================================================= # Notifications and Lock Screen @@ -501,13 +485,14 @@ resources: - type: Microsoft.Windows/Registry name: DoNotDisturb - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings valueName: NOC_GLOBAL_SETTING_TOASTS_ENABLED valueData: DWord: 0 + metadata: + description: Enable Do Not Disturb (disable all notifications) + securityContext: elevated # ============================================================================= # Taskbar @@ -517,8 +502,6 @@ resources: # WidgetsButton:Hide). 0 = hidden. - type: Microsoft.Windows/Registry name: TaskbarHideWidgets - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: TaskbarDa @@ -526,26 +509,29 @@ resources: DWord: 0 metadata: description: Hide Widgets button on the taskbar + securityContext: elevated - type: Microsoft.Windows/Registry name: BluetoothOff - dependsOn: - - ElevationCheck properties: keyPath: HKCU\Control Panel\Bluetooth valueName: Notification Area Icon valueData: DWord: 0 + metadata: + description: Hide Bluetooth icon in taskbar notification area + securityContext: elevated - type: Microsoft.Windows/Registry name: EndTask - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: TaskbarEndTask valueData: DWord: 1 + metadata: + description: Enable "End Task" on right-click of taskbar icons + securityContext: elevated # ============================================================================= # Start and Search @@ -553,8 +539,6 @@ resources: - type: Microsoft.Windows/Registry name: WebSearchOff - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Policies\Microsoft\Windows\Explorer valueName: DisableSearchBoxSuggestions @@ -562,11 +546,10 @@ resources: DWord: 1 metadata: description: Disable web search in Start/Search + securityContext: elevated - type: Microsoft.Windows/Registry name: SearchHightlightOff - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\SearchSettings valueName: IsDynamicSearchBoxEnabled @@ -574,11 +557,10 @@ resources: DWord: 0 metadata: description: Disable Show search highlights + securityContext: elevated - type: Microsoft.Windows/Registry name: StartRecommendations - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: Start_IrisRecommendations @@ -586,6 +568,7 @@ resources: DWord: 0 metadata: description: Disable Start menu recommendations + securityContext: elevated # ============================================================================= # Services and Features @@ -593,8 +576,6 @@ resources: - type: Microsoft.Windows/Registry name: WidgetServiceOff - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SOFTWARE\Policies\Microsoft\Dsh valueName: AllowNewsAndInterests @@ -602,6 +583,7 @@ resources: DWord: 0 metadata: description: Disable Widget service + securityContext: elevated # ============================================================================= @@ -612,8 +594,6 @@ resources: - type: Microsoft.Windows/Registry name: EdgeNewTab - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SOFTWARE\Policies\Microsoft\Edge valueName: NewTabPageLocation @@ -621,11 +601,10 @@ resources: String: about:blank metadata: description: Set Edge new tab to blank + securityContext: elevated - type: Microsoft.Windows/Registry name: EdgeOOBE - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SOFTWARE\Policies\Microsoft\Edge valueName: HideFirstRunExperience @@ -633,101 +612,7 @@ resources: DWord: 1 metadata: description: Disable Edge first-run experience - -# --------------------------------------------------------------------------- -# WSL Phase 2 — Enable WSL optional components via wsl --install -# (enables Virtual Machine Platform; reboot required) -# --------------------------------------------------------------------------- - -- name: InstallWslComponents - type: Microsoft.DSC.Transitional/WindowsPowerShellScript - dependsOn: - - ElevationCheck - properties: - getScript: | - $svc = Get-CimInstance -ClassName Win32_Service -Filter "Name='vmcompute'" - return @{ vmcomputePresent = [bool]$svc } - testScript: | - # If vmcompute is present, VMP is active — components already installed. - $svc = Get-CimInstance -ClassName Win32_Service -Filter "Name='vmcompute'" - return [bool]$svc - setScript: | - Write-Host "InstallWslComponents: running wsl --install --no-distribution..." - wsl --install --no-distribution - if ($LASTEXITCODE -ne 0) { - throw "InstallWslComponents: wsl --install --no-distribution failed with exit code $LASTEXITCODE" - } - -# --------------------------------------------------------------------------- -# WSL Phase 3 — Reboot so VMP takes effect -# --------------------------------------------------------------------------- - -- name: RebootForVmp - type: Microsoft.DSC.Transitional/WindowsPowerShellScript - dependsOn: - - InstallWslComponents - properties: - getScript: | - # Get-CimInstance returns $null without error when the service doesn't - # exist (unlike Get-Service, which sets HadErrors at the hosting layer - # even with -ErrorAction Ignore or try/catch). - $svc = Get-CimInstance -ClassName Win32_Service -Filter "Name='vmcompute'" - return @{ vmcomputePresent = [bool]$svc } - testScript: | - # vmcompute (Hyper-V Host Compute Service) is registered once Virtual - # Machine Platform is active post-reboot. Presence alone is sufficient - # — no need to check if it is running. - $svc = Get-CimInstance -ClassName Win32_Service -Filter "Name='vmcompute'" - return [bool]$svc - setScript: | - # ${WinGetConfigRoot} is set by winget configure to the directory - # containing the config file being applied. - $configFile = "${WinGetConfigRoot}\dev-config.winget" - $resumeCmd = "winget configure --file `"$configFile`" --accept-configuration-agreements" - - # RunOnce entries are deleted by Windows automatically before they - # are executed, so no cleanup step is needed in this config. - $runOncePath = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' - Set-ItemProperty -Path $runOncePath -Name 'DSCConfigureResume' -Value $resumeCmd -Force - - Write-Host "RebootForVmp: registered RunOnce resume key." - Write-Host " HKCU:\...\RunOnce\DSCConfigureResume = $resumeCmd" - Write-Host "RebootForVmp: rebooting now to activate Virtual Machine Platform..." - Restart-Computer -Force - - # Restart-Computer -Force returns immediately after signalling the OS - # to reboot — it does not block until the machine goes down. Without - # an explicit failure here DSC would consider this resource succeeded - # and attempt to run InstallUbuntu before the reboot happens. - # Throwing forces DSC to mark the current run as failed; the RunOnce - # key handles resuming on the next login. - Start-Sleep -Seconds 30 # give the OS time to initiate shutdown - throw "Reboot initiated to activate Virtual Machine Platform. DSC will resume via RunOnce on next login." - -# --------------------------------------------------------------------------- -# WSL Phase 4 — Install default Ubuntu distro (VMP now active post-reboot) -# --------------------------------------------------------------------------- - -- name: InstallUbuntu - type: Microsoft.DSC.Transitional/WindowsPowerShellScript - dependsOn: - - RebootForVmp - properties: - getScript: | - # WSL tracks installed distros as subkeys under Lxss. - $distros = Get-ChildItem 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss' -ErrorAction Ignore - return @{ distroCount = ($distros | Measure-Object).Count } - testScript: | - # Skip if any distro is already registered. - $distros = Get-ChildItem 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss' -ErrorAction Ignore - return [bool]$distros - setScript: | - Write-Host "InstallUbuntu: running wsl --install..." - wsl --install -d Ubuntu --no-launch - if ($LASTEXITCODE -ne 0) { - throw "InstallUbuntu: wsl --install failed with exit code $LASTEXITCODE" - } - + securityContext: elevated # ============================================================================= # Software Installs @@ -735,33 +620,30 @@ resources: - type: Microsoft.WinGet/Package name: Git - dependsOn: - - ElevationCheck - - InstallUbuntu properties: id: Git.Git source: winget useLatest: true metadata: description: Install Git + securityContext: elevated - type: Microsoft.WinGet/Package name: GitHubCLI dependsOn: - Git - - InstallUbuntu properties: id: GitHub.Cli source: winget useLatest: true metadata: description: Install GitHub CLI + securityContext: elevated - type: Microsoft.WinGet/Package name: GitHubCopilot dependsOn: - Git - - InstallUbuntu properties: id: GitHub.Copilot source: winget @@ -771,8 +653,6 @@ resources: - type: Microsoft.WinGet/Package name: VSCode - dependsOn: - - InstallUbuntu properties: id: Microsoft.VisualStudioCode source: winget @@ -782,19 +662,16 @@ resources: - type: Microsoft.WinGet/Package name: DotnetSdk - dependsOn: - - InstallUbuntu properties: id: Microsoft.dotnet.SDK.10 source: winget useLatest: true metadata: description: Install dotnet SDK + securityContext: elevated - type: Microsoft.WinGet/Package name: Python - dependsOn: - - InstallUbuntu properties: id: Python.Python.3.13 source: winget @@ -804,41 +681,36 @@ resources: - type: Microsoft.WinGet/Package name: UV - dependsOn: - - InstallUbuntu properties: id: astral-sh.uv source: winget useLatest: true metadata: description: Install UV (Python tool) + securityContext: elevated - type: Microsoft.WinGet/Package name: NodeJS - dependsOn: - - InstallUbuntu properties: id: OpenJS.NodeJS.LTS source: winget useLatest: true metadata: description: Install Node.js 24 LTS + securityContext: elevated - type: Microsoft.WinGet/Package name: nvmForNode - dependsOn: - - InstallUbuntu properties: id: CoreyButler.NVMforWindows source: winget useLatest: true metadata: description: Install NVM + securityContext: elevated - type: Microsoft.WinGet/Package name: OhMyPosh - dependsOn: - - InstallUbuntu properties: id: JanDeDobbeleer.OhMyPosh source: winget @@ -848,8 +720,6 @@ resources: - type: Microsoft.WinGet/Package name: winappCli - dependsOn: - - InstallUbuntu properties: id: Microsoft.winappcli source: winget @@ -859,8 +729,6 @@ resources: - type: Microsoft.WinGet/Package name: PowerToys - dependsOn: - - InstallUbuntu properties: id: Microsoft.PowerToys source: winget @@ -902,7 +770,9 @@ resources: - type: Microsoft.DSC.Transitional/PowerShellScript name: GitHubCopilotProfile dependsOn: + - PowerShell - GitHubCopilot + - Terminal properties: getScript: | $fragmentPath = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows Terminal\Fragments\DevConfig\github-copilot.fragment.json' @@ -955,6 +825,7 @@ resources: - type: Microsoft.DSC.Transitional/PowerShellScript name: InstallWinUITemplates dependsOn: + - PowerShell - DotnetSdk properties: getScript: | @@ -970,6 +841,7 @@ resources: - type: Microsoft.DSC.Transitional/PowerShellScript name: AddWinSkillsMarketplace dependsOn: + - PowerShell - GitHubCopilot properties: getScript: | @@ -985,6 +857,7 @@ resources: - type: Microsoft.DSC.Transitional/PowerShellScript name: InstallWinUIPlugin dependsOn: + - PowerShell - AddWinSkillsMarketplace properties: getScript: | @@ -996,3 +869,36 @@ resources: copilot plugin install winui@win-dev-skills metadata: description: Install the WinUI Copilot plugin from win-dev-skills + +# --------------------------------------------------------------------------- +# Enable WSL optional components via wsl --install +# (enables Virtual Machine Platform; reboot required) +# --------------------------------------------------------------------------- + +- name: InstallWslComponents + type: Microsoft.DSC.Transitional/PowerShellScript + dependsOn: + - PowerShell + properties: + getScript: | + $hasWsl = [bool](Get-Command "wsl.exe" -ErrorAction SilentlyContinue) + return @{ Result = "WSL binary found on system: $hasWsl" } + testScript: | + # Return true only if wsl.exe exists and is functional + if (Get-Command "wsl.exe" -ErrorAction SilentlyContinue) { + $process = Start-Process -FilePath "cmd.exe" -ArgumentList "/c wsl --status >nul 2>&1" -Wait -PassThru + return [bool]($process.ExitCode -eq 0) + } + return $false + setScript: | + Write-Host "InstallWslComponents: running wsl --install..." + $process = Start-Process -FilePath "cmd.exe" -ArgumentList "/c wsl --install >nul 2>&1" -Wait -PassThru + if ($process.ExitCode -eq 3010 -or $process.ExitCode -eq 1641) { + Write-Host "WSL installed successfully, but a system reboot is required." + } + elseif ($process.ExitCode -ne 0) { + throw "InstallWslComponents: wsl --install failed with exit code $($process.ExitCode)" + } + metadata: + description: Install WSL optional components (Virtual Machine Platform, etc.) + securityContext: elevated diff --git a/windows-dev-config/dev-config.winget b/windows-dev-config/dev-config.winget index 9ed8217..c36e8ee 100644 --- a/windows-dev-config/dev-config.winget +++ b/windows-dev-config/dev-config.winget @@ -11,47 +11,54 @@ resources: # --------------------------------------------------------------------------- # Phase 0 — Elevation check (runs on initial invoke AND post-reboot resume) # --------------------------------------------------------------------------- +# +# - name: ElevationCheck +# type: Microsoft.DSC.Transitional/WindowsPowerShellScript +# properties: +# getScript: | +# $id = [Security.Principal.WindowsIdentity]::GetCurrent() +# $p = [Security.Principal.WindowsPrincipal] $id +# return @{ elevated = $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } +# testScript: | +# $id = [Security.Principal.WindowsIdentity]::GetCurrent() +# $p = [Security.Principal.WindowsPrincipal] $id +# return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +# setScript: | +# $configFile = "${WinGetConfigRoot}\dev-config.winget" +# +# if (-not (Test-Path $configFile)) { +# $hint = "winget configure --file `"$configFile`" --accept-configuration-agreements --disable-interactivity" +# throw "ElevationCheck: not elevated and cannot locate config file to re-launch automatically. Please re-run in an elevated terminal: $hint" +# } +# +# Write-Host "ElevationCheck: not elevated — re-launching winget configure as Administrator..." +# +# $wingetArgs = @( +# 'configure', +# '--file', "`"$configFile`"", +# '--accept-configuration-agreements', +# '--disable-interactivity', +# '--wait' +# ) +# Start-Process winget -ArgumentList $wingetArgs -Verb RunAs +# +# throw "ElevationCheck: re-launched elevated successfully. This unelevated session is now complete — check the elevated window for results." + +# ============================================================================= +# Terminal and PowerShell 7 +# ============================================================================= -- name: ElevationCheck - type: Microsoft.DSC.Transitional/WindowsPowerShellScript +- type: Microsoft.WinGet/Package + name: Terminal properties: - getScript: | - $id = [Security.Principal.WindowsIdentity]::GetCurrent() - $p = [Security.Principal.WindowsPrincipal] $id - return @{ elevated = $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } - testScript: | - $id = [Security.Principal.WindowsIdentity]::GetCurrent() - $p = [Security.Principal.WindowsPrincipal] $id - return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - setScript: | - $configFile = "${WinGetConfigRoot}\dev-config.winget" - - if (-not (Test-Path $configFile)) { - $hint = "winget configure --file `"$configFile`" --accept-configuration-agreements --disable-interactivity" - throw "ElevationCheck: not elevated and cannot locate config file to re-launch automatically. Please re-run in an elevated terminal: $hint" - } - - Write-Host "ElevationCheck: not elevated — re-launching winget configure as Administrator..." - - $wingetArgs = @( - 'configure', - '--file', "`"$configFile`"", - '--accept-configuration-agreements', - '--disable-interactivity', - '--wait' - ) - Start-Process winget -ArgumentList $wingetArgs -Verb RunAs - - throw "ElevationCheck: re-launched elevated successfully. This unelevated session is now complete — check the elevated window for results." - -# ============================================================================= -# PowerShell 7 -# ============================================================================= + id: Microsoft.WindowsTerminal + source: winget + useLatest: true + metadata: + description: Install Windows Terminal - type: Microsoft.WinGet/Package name: PowerShell - dependsOn: - - ElevationCheck properties: id: Microsoft.PowerShell source: winget @@ -79,10 +86,9 @@ resources: $system = Get-ItemPropertyValue $regPath -Name SystemUsesLightTheme -EA SilentlyContinue return ($apps -eq 0 -and $system -eq 0) setScript: | - # Apply the .theme file to update shell chrome, wallpaper, and sound scheme - Start-Process -FilePath "C:\Windows\Resources\Themes\dark.theme" - Start-Sleep -Seconds 2 - Stop-Process -Name SystemSettings -Force -ErrorAction SilentlyContinue + $regPath = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize' + Set-ItemProperty -Path $regPath -Name "AppsUseLightTheme" -Value 0 + Set-ItemProperty -Path $regPath -Name "SystemUsesLightTheme" -Value 0 metadata: description: Sets dark theme @@ -178,10 +184,14 @@ resources: # ============================================================================= # Set Cascadia Mono NF as default Windows Terminal font +# NOTE: This cannot use a fragment. Fragments support adding profiles and color +# schemes, but not profiles.defaults (which applies settings across all profiles). +# Direct settings.json modification is the only available approach here. # ============================================================================= - type: Microsoft.DSC.Transitional/PowerShellScript name: SetCascadiaNfAsDefault dependsOn: + - PowerShell - InstallCascadiaCodeNerdFonts properties: getScript: | @@ -238,6 +248,9 @@ resources: # ============================================================================= # Set PowerShell 7 as the default Windows Terminal profile +# NOTE: This cannot use a fragment. Fragments support adding profiles and color +# schemes, but not the top-level defaultProfile setting in settings.json. +# Direct settings.json modification is the only available approach here. # ============================================================================= - type: Microsoft.DSC.Transitional/PowerShellScript name: ps7default @@ -301,8 +314,6 @@ resources: - type: Microsoft.Windows/Registry name: Sudo - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Sudo valueName: Enabled @@ -310,13 +321,12 @@ resources: DWord: 3 metadata: description: Enable Sudo in inline mode + securityContext: elevated # Developer Mode: AllowDevelopmentWithoutDevLicense=1 (replaces # Microsoft.Windows.Settings/WindowsSettings DeveloperMode:true) - type: Microsoft.Windows/Registry name: DeveloperMode - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock valueName: AllowDevelopmentWithoutDevLicense @@ -324,12 +334,11 @@ resources: DWord: 1 metadata: description: Enable Developer Mode (sideload + dev features) + securityContext: elevated # Long path support (replaces Microsoft.Windows.Developer/EnableLongPathSupport) - type: Microsoft.Windows/Registry name: LongPaths - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SYSTEM\CurrentControlSet\Control\FileSystem valueName: LongPathsEnabled @@ -337,12 +346,11 @@ resources: DWord: 1 metadata: description: Enable Win32 long path support + securityContext: elevated # Remote Desktop (replaces Microsoft.Windows.Developer/EnableRemoteDesktop) - type: Microsoft.Windows/Registry name: RemoteDesktop - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server valueName: fDenyTSConnections @@ -350,6 +358,8 @@ resources: DWord: 0 metadata: description: Enable Remote Desktop (firewall rule still needs separate enable) + securityContext: elevated + # ============================================================================= # File Explorer and Desktop # ============================================================================= @@ -370,8 +380,6 @@ resources: # FileExtensions:Show) - type: Microsoft.Windows/Registry name: ShowFileExtensions - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: HideFileExt @@ -379,13 +387,12 @@ resources: DWord: 0 metadata: description: Show file extensions in Explorer + securityContext: elevated # Show hidden files (replaces Microsoft.Windows.Developer/WindowsExplorer # HiddenFiles:Show) - type: Microsoft.Windows/Registry name: ShowHiddenFiles - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: Hidden @@ -393,11 +400,10 @@ resources: DWord: 1 metadata: description: Show hidden files in Explorer - + securityContext: elevated + - type: Microsoft.Windows/Registry name: FullPathTitlebar - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: FullPathAddress @@ -405,11 +411,10 @@ resources: DWord: 1 metadata: description: Show full path in Explorer titlebar + securityContext: elevated - type: Microsoft.Windows/Registry name: OpenThisPC - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: LaunchTo @@ -417,11 +422,10 @@ resources: DWord: 1 metadata: description: Open File Explorer to This PC + securityContext: elevated - type: Microsoft.Windows/Registry name: FrequentFolders - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: ShowFrequent @@ -429,11 +433,10 @@ resources: DWord: 0 metadata: description: Disable frequent folders in Quick Access + securityContext: elevated - type: Microsoft.Windows/Registry name: FrequentFiles - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer valueName: ShowRecent @@ -441,11 +444,10 @@ resources: DWord: 0 metadata: description: Disable frequent files in Quick Access + securityContext: elevated - type: Microsoft.Windows/Registry name: RecommendedFiles - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer valueName: ShowCloudFilesInQuickAccess @@ -453,11 +455,10 @@ resources: DWord: 0 metadata: description: Disable recommended/cloud files in Quick Access + securityContext: elevated - type: Microsoft.Windows/Registry name: GitCodeFolders - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: NavPaneShowVersionControl @@ -465,11 +466,10 @@ resources: DWord: 1 metadata: description: Enable Git integration in File Explorer + securityContext: elevated - type: Microsoft.Windows/Registry name: TipsOff - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: ShowSyncProviderNotifications @@ -477,6 +477,7 @@ resources: DWord: 0 metadata: description: Disable sync provider notifications (tips) + securityContext: elevated # ============================================================================= # Notifications and Lock Screen @@ -484,13 +485,14 @@ resources: - type: Microsoft.Windows/Registry name: DoNotDisturb - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings valueName: NOC_GLOBAL_SETTING_TOASTS_ENABLED valueData: DWord: 0 + metadata: + description: Enable Do Not Disturb (disable all notifications) + securityContext: elevated # ============================================================================= # Taskbar @@ -500,8 +502,6 @@ resources: # WidgetsButton:Hide). 0 = hidden. - type: Microsoft.Windows/Registry name: TaskbarHideWidgets - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: TaskbarDa @@ -509,26 +509,29 @@ resources: DWord: 0 metadata: description: Hide Widgets button on the taskbar + securityContext: elevated - type: Microsoft.Windows/Registry name: BluetoothOff - dependsOn: - - ElevationCheck properties: keyPath: HKCU\Control Panel\Bluetooth valueName: Notification Area Icon valueData: DWord: 0 + metadata: + description: Hide Bluetooth icon in taskbar notification area + securityContext: elevated - type: Microsoft.Windows/Registry name: EndTask - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced valueName: TaskbarEndTask valueData: DWord: 1 + metadata: + description: Enable "End Task" on right-click of taskbar icons + securityContext: elevated # ============================================================================= # Start and Search @@ -536,8 +539,6 @@ resources: - type: Microsoft.Windows/Registry name: WebSearchOff - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Policies\Microsoft\Windows\Explorer valueName: DisableSearchBoxSuggestions @@ -545,11 +546,10 @@ resources: DWord: 1 metadata: description: Disable web search in Start/Search + securityContext: elevated - type: Microsoft.Windows/Registry name: SearchHightlightOff - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\SearchSettings valueName: IsDynamicSearchBoxEnabled @@ -557,18 +557,18 @@ resources: DWord: 0 metadata: description: Disable Show search highlights + securityContext: elevated - type: Microsoft.Windows/Registry name: StartRecommendations - dependsOn: - - ElevationCheck properties: keyPath: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced - valueName: Start_Layout + valueName: Start_IrisRecommendations valueData: - DWord: 1 + DWord: 0 metadata: description: Disable Start menu recommendations + securityContext: elevated # ============================================================================= # Services and Features @@ -576,8 +576,6 @@ resources: - type: Microsoft.Windows/Registry name: WidgetServiceOff - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SOFTWARE\Policies\Microsoft\Dsh valueName: AllowNewsAndInterests @@ -585,6 +583,7 @@ resources: DWord: 0 metadata: description: Disable Widget service + securityContext: elevated # ============================================================================= @@ -595,8 +594,6 @@ resources: - type: Microsoft.Windows/Registry name: EdgeNewTab - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SOFTWARE\Policies\Microsoft\Edge valueName: NewTabPageLocation @@ -604,11 +601,10 @@ resources: String: about:blank metadata: description: Set Edge new tab to blank + securityContext: elevated - type: Microsoft.Windows/Registry name: EdgeOOBE - dependsOn: - - ElevationCheck properties: keyPath: HKLM\SOFTWARE\Policies\Microsoft\Edge valueName: HideFirstRunExperience @@ -616,101 +612,7 @@ resources: DWord: 1 metadata: description: Disable Edge first-run experience - -# --------------------------------------------------------------------------- -# WSL Phase 2 — Enable WSL optional components via wsl --install -# (enables Virtual Machine Platform; reboot required) -# --------------------------------------------------------------------------- - -- name: InstallWslComponents - type: Microsoft.DSC.Transitional/WindowsPowerShellScript - dependsOn: - - ElevationCheck - properties: - getScript: | - $svc = Get-CimInstance -ClassName Win32_Service -Filter "Name='vmcompute'" - return @{ vmcomputePresent = [bool]$svc } - testScript: | - # If vmcompute is present, VMP is active — components already installed. - $svc = Get-CimInstance -ClassName Win32_Service -Filter "Name='vmcompute'" - return [bool]$svc - setScript: | - Write-Host "InstallWslComponents: running wsl --install --no-distribution..." - wsl --install --no-distribution - if ($LASTEXITCODE -ne 0) { - throw "InstallWslComponents: wsl --install --no-distribution failed with exit code $LASTEXITCODE" - } - -# --------------------------------------------------------------------------- -# WSL Phase 3 — Reboot so VMP takes effect -# --------------------------------------------------------------------------- - -- name: RebootForVmp - type: Microsoft.DSC.Transitional/WindowsPowerShellScript - dependsOn: - - InstallWslComponents - properties: - getScript: | - # Get-CimInstance returns $null without error when the service doesn't - # exist (unlike Get-Service, which sets HadErrors at the hosting layer - # even with -ErrorAction Ignore or try/catch). - $svc = Get-CimInstance -ClassName Win32_Service -Filter "Name='vmcompute'" - return @{ vmcomputePresent = [bool]$svc } - testScript: | - # vmcompute (Hyper-V Host Compute Service) is registered once Virtual - # Machine Platform is active post-reboot. Presence alone is sufficient - # — no need to check if it is running. - $svc = Get-CimInstance -ClassName Win32_Service -Filter "Name='vmcompute'" - return [bool]$svc - setScript: | - # ${WinGetConfigRoot} is set by winget configure to the directory - # containing the config file being applied. - $configFile = "${WinGetConfigRoot}\dev-config.winget" - $resumeCmd = "winget configure --file `"$configFile`" --accept-configuration-agreements" - - # RunOnce entries are deleted by Windows automatically before they - # are executed, so no cleanup step is needed in this config. - $runOncePath = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' - Set-ItemProperty -Path $runOncePath -Name 'DSCConfigureResume' -Value $resumeCmd -Force - - Write-Host "RebootForVmp: registered RunOnce resume key." - Write-Host " HKCU:\...\RunOnce\DSCConfigureResume = $resumeCmd" - Write-Host "RebootForVmp: rebooting now to activate Virtual Machine Platform..." - Restart-Computer -Force - - # Restart-Computer -Force returns immediately after signalling the OS - # to reboot — it does not block until the machine goes down. Without - # an explicit failure here DSC would consider this resource succeeded - # and attempt to run InstallUbuntu before the reboot happens. - # Throwing forces DSC to mark the current run as failed; the RunOnce - # key handles resuming on the next login. - Start-Sleep -Seconds 30 # give the OS time to initiate shutdown - throw "Reboot initiated to activate Virtual Machine Platform. DSC will resume via RunOnce on next login." - -# --------------------------------------------------------------------------- -# WSL Phase 4 — Install default Ubuntu distro (VMP now active post-reboot) -# --------------------------------------------------------------------------- - -- name: InstallUbuntu - type: Microsoft.DSC.Transitional/WindowsPowerShellScript - dependsOn: - - RebootForVmp - properties: - getScript: | - # WSL tracks installed distros as subkeys under Lxss. - $distros = Get-ChildItem 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss' -ErrorAction Ignore - return @{ distroCount = ($distros | Measure-Object).Count } - testScript: | - # Skip if any distro is already registered. - $distros = Get-ChildItem 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss' -ErrorAction Ignore - return [bool]$distros - setScript: | - Write-Host "InstallUbuntu: running wsl --install..." - wsl --install -d Ubuntu --no-launch - if ($LASTEXITCODE -ne 0) { - throw "InstallUbuntu: wsl --install failed with exit code $LASTEXITCODE" - } - + securityContext: elevated # ============================================================================= # Software Installs @@ -718,33 +620,30 @@ resources: - type: Microsoft.WinGet/Package name: Git - dependsOn: - - ElevationCheck - - InstallUbuntu properties: id: Git.Git source: winget useLatest: true metadata: description: Install Git + securityContext: elevated - type: Microsoft.WinGet/Package name: GitHubCLI dependsOn: - Git - - InstallUbuntu properties: id: GitHub.Cli source: winget useLatest: true metadata: description: Install GitHub CLI + securityContext: elevated - type: Microsoft.WinGet/Package name: GitHubCopilot dependsOn: - Git - - InstallUbuntu properties: id: GitHub.Copilot source: winget @@ -754,8 +653,6 @@ resources: - type: Microsoft.WinGet/Package name: VSCode - dependsOn: - - InstallUbuntu properties: id: Microsoft.VisualStudioCode source: winget @@ -765,19 +662,16 @@ resources: - type: Microsoft.WinGet/Package name: DotnetSdk - dependsOn: - - InstallUbuntu properties: id: Microsoft.dotnet.SDK.10 source: winget useLatest: true metadata: description: Install dotnet SDK + securityContext: elevated - type: Microsoft.WinGet/Package name: Python - dependsOn: - - InstallUbuntu properties: id: Python.Python.3.13 source: winget @@ -787,41 +681,36 @@ resources: - type: Microsoft.WinGet/Package name: UV - dependsOn: - - InstallUbuntu properties: id: astral-sh.uv source: winget useLatest: true metadata: description: Install UV (Python tool) + securityContext: elevated - type: Microsoft.WinGet/Package name: NodeJS - dependsOn: - - InstallUbuntu properties: id: OpenJS.NodeJS.LTS source: winget useLatest: true metadata: description: Install Node.js 24 LTS + securityContext: elevated - type: Microsoft.WinGet/Package name: nvmForNode - dependsOn: - - InstallUbuntu properties: id: CoreyButler.NVMforWindows source: winget useLatest: true metadata: description: Install NVM + securityContext: elevated - type: Microsoft.WinGet/Package name: OhMyPosh - dependsOn: - - InstallUbuntu properties: id: JanDeDobbeleer.OhMyPosh source: winget @@ -831,8 +720,6 @@ resources: - type: Microsoft.WinGet/Package name: winappCli - dependsOn: - - InstallUbuntu properties: id: Microsoft.winappcli source: winget @@ -842,8 +729,6 @@ resources: - type: Microsoft.WinGet/Package name: PowerToys - dependsOn: - - InstallUbuntu properties: id: Microsoft.PowerToys source: winget @@ -879,92 +764,58 @@ resources: # ============================================================================= # Add GitHub Copilot profile to Windows Terminal +# Uses a fragment file so settings.json is never touched directly. +# Fragment file: %LOCALAPPDATA%\Microsoft\Windows Terminal\Fragments\DevConfig\github-copilot.fragment.json # ============================================================================= - type: Microsoft.DSC.Transitional/PowerShellScript name: GitHubCopilotProfile dependsOn: + - PowerShell - GitHubCopilot + - Terminal properties: getScript: | - $guid = '{b1a4d2c8-6f3e-4a7b-9e2d-1c8f5a3b7d91}' - $settingsPath = @( - "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json", - "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\LocalState\settings.json", - "$env:LOCALAPPDATA\Microsoft\Windows Terminal\settings.json" - ) | Where-Object { Test-Path $_ } | Select-Object -First 1 - if (-not $settingsPath) { return @{ profilePresent = $false } } - $raw = Get-Content $settingsPath -Raw - $clean = [regex]::Replace($raw, '/\*[\s\S]*?\*/', '') - $clean = [regex]::Replace($clean, '(?m)^\s*//.*$', '') - $settings = $clean | ConvertFrom-Json - $exists = [bool]($settings.profiles.list | Where-Object { $_.guid -eq $guid }) - return @{ profilePresent = $exists } + $fragmentPath = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows Terminal\Fragments\DevConfig\github-copilot.fragment.json' + return @{ fragmentPresent = (Test-Path $fragmentPath) } testScript: | - $guid = '{b1a4d2c8-6f3e-4a7b-9e2d-1c8f5a3b7d91}' - $settingsPath = @( - "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json", - "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\LocalState\settings.json", - "$env:LOCALAPPDATA\Microsoft\Windows Terminal\settings.json" - ) | Where-Object { Test-Path $_ } | Select-Object -First 1 - if (-not $settingsPath) { return $true } - $raw = Get-Content $settingsPath -Raw - $clean = [regex]::Replace($raw, '/\*[\s\S]*?\*/', '') - $clean = [regex]::Replace($clean, '(?m)^\s*//.*$', '') - $settings = $clean | ConvertFrom-Json - return [bool]($settings.profiles.list | Where-Object { $_.guid -eq $guid }) + $fragmentPath = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows Terminal\Fragments\DevConfig\github-copilot.fragment.json' + return (Test-Path $fragmentPath) setScript: | - $guid = '{b1a4d2c8-6f3e-4a7b-9e2d-1c8f5a3b7d91}' - $iconUrl = 'https://github.githubassets.com/favicons/favicon-dark.png' - $iconDir = Join-Path $env:LOCALAPPDATA 'WindowsTerminal\icons' - $iconPath = Join-Path $iconDir 'github-copilot.png' - $commandline = 'pwsh.exe -NoExit -Command "copilot"' + $fragmentsDir = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows Terminal\Fragments\DevConfig' + New-Item -ItemType Directory -Path $fragmentsDir -Force | Out-Null + + # Download icon alongside the fragment file so the relative path "copilot.png" resolves correctly. + $iconPath = Join-Path $fragmentsDir 'copilot.png' + Invoke-WebRequest -Uri 'https://github.githubassets.com/favicons/favicon-dark.png' -OutFile $iconPath -UseBasicParsing + + $fragment = @{ + profiles = @( + @{ + guid = '{b1a4d2c8-6f3e-4a7b-9e2d-1c8f5a3b7d91}' + name = 'GitHub Copilot' + commandline = 'pwsh.exe -NoExit -Command "copilot"' + icon = 'copilot.png' + startingDirectory = '%USERPROFILE%' + hidden = $false + tabTitle = 'Copilot' + } + ) + } - # 1) Download & cache the icon locally - New-Item -ItemType Directory -Path $iconDir -Force | Out-Null - Invoke-WebRequest -Uri $iconUrl -OutFile $iconPath -UseBasicParsing + $fragmentFile = Join-Path $fragmentsDir 'github-copilot.fragment.json' + $fragment | ConvertTo-Json -Depth 8 | Out-File -FilePath $fragmentFile -Encoding Utf8 - # 2) Locate settings.json (Store, Preview, or unpackaged install) - $settingsPath = @( + # Touch settings.json to trigger WT's hot-reload (re-scans Fragments\*.json). + @( "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json", "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\LocalState\settings.json", "$env:LOCALAPPDATA\Microsoft\Windows Terminal\settings.json" - ) | Where-Object { Test-Path $_ } | Select-Object -First 1 - if (-not $settingsPath) { throw 'Windows Terminal settings.json not found.' } - - # 3) Back up before editing - Copy-Item $settingsPath "$settingsPath.bak" -Force - - # 4) Load JSON (strip // and /* */ comments so ConvertFrom-Json is happy) - $raw = Get-Content $settingsPath -Raw - $clean = [regex]::Replace($raw, '/\*[\s\S]*?\*/', '') - $clean = [regex]::Replace($clean, '(?m)^\s*//.*$', '') - $settings = $clean | ConvertFrom-Json - - # 5) Build the new profile - $newProfile = [pscustomobject]@{ - name = 'GitHub Copilot' - guid = $guid - commandline = $commandline - icon = $iconPath - startingDirectory = '%USERPROFILE%' - hidden = $false - tabTitle = 'Copilot' + ) | Where-Object { Test-Path $_ } | ForEach-Object { + try { (Get-Item -LiteralPath $_).LastWriteTime = Get-Date } catch {} } - # 6) Ensure profiles.list exists, then add or replace by guid - if (-not $settings.profiles) { $settings | Add-Member profiles ([pscustomobject]@{ list = @() }) -Force } - if (-not $settings.profiles.list) { $settings.profiles | Add-Member list @() -Force } - - $list = @($settings.profiles.list | Where-Object { $_.guid -ne $guid }) - $list += $newProfile - $settings.profiles.list = $list - - # 7) Save - $settings | ConvertTo-Json -Depth 64 | Set-Content -Path $settingsPath -Encoding UTF8 - - Write-Host "Added 'GitHub Copilot' profile to $settingsPath" -ForegroundColor Green - Write-Host "Icon cached at $iconPath" - Write-Host "Backup saved at $settingsPath.bak" + Write-Host "GitHub Copilot profile fragment written to $fragmentFile" -ForegroundColor Green + Write-Host "Open Windows Terminal: the 'GitHub Copilot' profile is available in the dropdown." -ForegroundColor Cyan metadata: description: Create Github Copilot Profile @@ -974,6 +825,7 @@ resources: - type: Microsoft.DSC.Transitional/PowerShellScript name: InstallWinUITemplates dependsOn: + - PowerShell - DotnetSdk properties: getScript: | @@ -989,6 +841,7 @@ resources: - type: Microsoft.DSC.Transitional/PowerShellScript name: AddWinSkillsMarketplace dependsOn: + - PowerShell - GitHubCopilot properties: getScript: | @@ -1004,6 +857,7 @@ resources: - type: Microsoft.DSC.Transitional/PowerShellScript name: InstallWinUIPlugin dependsOn: + - PowerShell - AddWinSkillsMarketplace properties: getScript: | @@ -1015,3 +869,36 @@ resources: copilot plugin install winui@win-dev-skills metadata: description: Install the WinUI Copilot plugin from win-dev-skills + +# --------------------------------------------------------------------------- +# Enable WSL optional components via wsl --install +# (enables Virtual Machine Platform; reboot required) +# --------------------------------------------------------------------------- + +- name: InstallWslComponents + type: Microsoft.DSC.Transitional/PowerShellScript + dependsOn: + - PowerShell + properties: + getScript: | + $hasWsl = [bool](Get-Command "wsl.exe" -ErrorAction SilentlyContinue) + return @{ Result = "WSL binary found on system: $hasWsl" } + testScript: | + # Return true only if wsl.exe exists and is functional + if (Get-Command "wsl.exe" -ErrorAction SilentlyContinue) { + $process = Start-Process -FilePath "cmd.exe" -ArgumentList "/c wsl --status >nul 2>&1" -Wait -PassThru + return [bool]($process.ExitCode -eq 0) + } + return $false + setScript: | + Write-Host "InstallWslComponents: running wsl --install..." + $process = Start-Process -FilePath "cmd.exe" -ArgumentList "/c wsl --install >nul 2>&1" -Wait -PassThru + if ($process.ExitCode -eq 3010 -or $process.ExitCode -eq 1641) { + Write-Host "WSL installed successfully, but a system reboot is required." + } + elseif ($process.ExitCode -ne 0) { + throw "InstallWslComponents: wsl --install failed with exit code $($process.ExitCode)" + } + metadata: + description: Install WSL optional components (Virtual Machine Platform, etc.) + securityContext: elevated