if (-not ($IsWindows -or $PSVersionTable.PSEdition -eq 'Desktop')) { return } $settingsPath = Join-Path $env:LOCALAPPDATA 'Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json' if (-not (Test-Path -LiteralPath $settingsPath)) { Write-Warning "Windows Terminal settings not found: $settingsPath" return } $PowerShellGuid = '{574e775e-4f2a-5b96-ac1e-a2962a402336}' $LegacyPowerShellGuid = '{61c54bbd-c2c6-5271-96e7-009a87ff44bf}' $changed = $false function Get-JsonPropertyValue { param( [Parameter(Mandatory = $true)] $Object, [Parameter(Mandatory = $true)] [string]$Name ) $Property = $Object.PSObject.Properties[$Name] if ($null -eq $Property) { return $null } return $Property.Value } function Test-JsonValueEqual { param( $Left, $Right ) if ($null -eq $Left -and $null -eq $Right) { return $true } if ($null -eq $Left -or $null -eq $Right) { return $false } if ( $Left -is [System.Collections.IEnumerable] -and -not ($Left -is [string]) ) { return (ConvertTo-Json $Left -Depth 100 -Compress) -eq (ConvertTo-Json $Right -Depth 100 -Compress) } return $Left -eq $Right } function Set-JsonPropertyValue { param( [Parameter(Mandatory = $true)] $Object, [Parameter(Mandatory = $true)] [string]$Name, $Value ) $Property = $Object.PSObject.Properties[$Name] if ($null -eq $Property) { $Object | Add-Member -NotePropertyName $Name -NotePropertyValue $Value $script:changed = $true return } if (-not (Test-JsonValueEqual -Left $Property.Value -Right $Value)) { $Property.Value = $Value $script:changed = $true } } function Test-IsPowerShell7Profile { param( [Parameter(Mandatory = $true)] $Profile ) $Source = [string](Get-JsonPropertyValue -Object $Profile -Name source) $Name = [string](Get-JsonPropertyValue -Object $Profile -Name name) $CommandLine = [string](Get-JsonPropertyValue -Object $Profile -Name commandline) if ($Source -eq 'Windows.Terminal.PowershellCore') { return $true } if ($CommandLine -match '(?i)(^|[\\/ ])pwsh(?:\.exe)?(?:\s|$)') { return $true } return $Name -match '^(PowerShell|PowerShell 7)$' } function Test-IsDisposablePowerShell7Profile { param( [Parameter(Mandatory = $true)] $Profile ) $Source = [string](Get-JsonPropertyValue -Object $Profile -Name source) $Name = [string](Get-JsonPropertyValue -Object $Profile -Name name) $CommandLine = [string](Get-JsonPropertyValue -Object $Profile -Name commandline) return ( $Name -match '^(PowerShell|PowerShell 7)$' -and ( $Source -eq 'Windows.Terminal.PowershellCore' -or $CommandLine -match '(?i)(^|[\\/ ])pwsh(?:\.exe)?(?:\s|$)' ) ) } try { $originalSettings = Get-Content -LiteralPath $settingsPath -Raw $settings = $originalSettings | ConvertFrom-Json } catch { Write-Warning "Failed to read Windows Terminal settings: $settingsPath" Write-Warning $_ return } if ($null -eq (Get-JsonPropertyValue -Object $settings -Name profiles)) { $settings | Add-Member -NotePropertyName profiles -NotePropertyValue ([pscustomobject]@{ defaults = [pscustomobject]@{} list = @() }) $changed = $true } if ($null -eq (Get-JsonPropertyValue -Object $settings.profiles -Name list)) { $settings.profiles | Add-Member -NotePropertyName list -NotePropertyValue @() $changed = $true } $profiles = @($settings.profiles.list) $powerShell7CandidateIndices = New-Object System.Collections.Generic.List[int] $legacyPowerShellIndex = $null for ($i = 0; $i -lt $profiles.Count; $i++) { $profile = $profiles[$i] $guid = [string](Get-JsonPropertyValue -Object $profile -Name guid) $source = [string](Get-JsonPropertyValue -Object $profile -Name source) $commandLine = [string](Get-JsonPropertyValue -Object $profile -Name commandline) if (Test-IsPowerShell7Profile -Profile $profile) { [void]$powerShell7CandidateIndices.Add($i) } if ( $guid -eq $LegacyPowerShellGuid -or $commandLine -match '(?i)WindowsPowerShell\\v1\.0\\powershell\.exe' ) { $legacyPowerShellIndex = $i } if ($source -eq 'Windows.Terminal.Azure') { Set-JsonPropertyValue -Object $profile -Name hidden -Value $true } } $powerShell7Index = $null foreach ($candidateIndex in $powerShell7CandidateIndices) { $candidate = $profiles[$candidateIndex] if ([string](Get-JsonPropertyValue -Object $candidate -Name source) -eq 'Windows.Terminal.PowershellCore') { $powerShell7Index = $candidateIndex break } } if ($null -eq $powerShell7Index -and $powerShell7CandidateIndices.Count -gt 0) { $powerShell7Index = $powerShell7CandidateIndices[0] } if ($null -eq $powerShell7Index) { $profiles += [pscustomobject]@{ commandline = 'pwsh.exe' guid = $PowerShellGuid hidden = $false name = 'PowerShell 7' startingDirectory = '%USERPROFILE%' } $powerShell7Index = $profiles.Count - 1 $changed = $true } $powerShell7Profile = $profiles[$powerShell7Index] if ([string]::IsNullOrWhiteSpace([string](Get-JsonPropertyValue -Object $powerShell7Profile -Name guid))) { Set-JsonPropertyValue -Object $powerShell7Profile -Name guid -Value $PowerShellGuid } if ($null -eq (Get-JsonPropertyValue -Object $powerShell7Profile -Name commandline) -and [string](Get-JsonPropertyValue -Object $powerShell7Profile -Name source) -ne 'Windows.Terminal.PowershellCore') { Set-JsonPropertyValue -Object $powerShell7Profile -Name commandline -Value 'pwsh.exe' } Set-JsonPropertyValue -Object $powerShell7Profile -Name name -Value 'PowerShell 7' Set-JsonPropertyValue -Object $powerShell7Profile -Name hidden -Value $false Set-JsonPropertyValue -Object $powerShell7Profile -Name startingDirectory -Value '%USERPROFILE%' Set-JsonPropertyValue -Object $settings -Name defaultProfile -Value ([string](Get-JsonPropertyValue -Object $powerShell7Profile -Name guid)) if ($null -ne $legacyPowerShellIndex) { $legacyPowerShellProfile = $profiles[$legacyPowerShellIndex] Set-JsonPropertyValue -Object $legacyPowerShellProfile -Name name -Value 'Windows PowerShell 5.1 (Legacy)' Set-JsonPropertyValue -Object $legacyPowerShellProfile -Name hidden -Value $false } $removedIndices = New-Object System.Collections.Generic.HashSet[int] foreach ($candidateIndex in $powerShell7CandidateIndices) { if ($candidateIndex -eq $powerShell7Index) { continue } if (Test-IsDisposablePowerShell7Profile -Profile $profiles[$candidateIndex]) { if ($removedIndices.Add($candidateIndex)) { $changed = $true } } } $orderedProfiles = New-Object System.Collections.Generic.List[object] $addedIndices = New-Object System.Collections.Generic.HashSet[int] function Add-ProfileByIndex { param( [int]$Index ) if ($Index -lt 0) { return } if ($removedIndices.Contains($Index)) { return } if ($addedIndices.Add($Index)) { $orderedProfiles.Add($profiles[$Index]) } } Add-ProfileByIndex -Index $powerShell7Index if ($null -ne $legacyPowerShellIndex) { Add-ProfileByIndex -Index $legacyPowerShellIndex } for ($i = 0; $i -lt $profiles.Count; $i++) { Add-ProfileByIndex -Index $i } $orderedProfilesArray = @($orderedProfiles.ToArray()) if ((ConvertTo-Json $profiles -Depth 100 -Compress) -ne (ConvertTo-Json $orderedProfilesArray -Depth 100 -Compress)) { $changed = $true } $settings.profiles.list = $orderedProfilesArray if (-not $changed) { Write-Host 'Windows Terminal settings already match the managed baseline.' return } $backupPath = '{0}.bak-{1}' -f $settingsPath, (Get-Date -Format 'yyyyMMdd-HHmmss') Copy-Item -LiteralPath $settingsPath -Destination $backupPath -Force $settings | ConvertTo-Json -Depth 100 | Set-Content -LiteralPath $settingsPath -Encoding utf8BOM Write-Host "Updated Windows Terminal settings: $settingsPath" Write-Host "Backup saved to: $backupPath"