Last active
October 22, 2025 14:22
-
-
Save bentman/638c478ae791598780c70749139e382f to your computer and use it in GitHub Desktop.
Install Dev Tools on Win Server 2022
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| Script to install Dev Tools on Windows Server (tested on 2022) | |
| .DESCRIPTION | |
| Installs the following from multiple resources: | |
| Microsoft.VCLibs *latest (GitHub via winget-cli deps) | |
| Microsoft.UI.Xaml *latest (GitHub API) | |
| winget-cli (dynamic version retrieval from api.github.com) | |
| Microsoft.WindowsTerminal (dynamic version retrieval from api.github.com) | |
| Microsoft pwsh.exe vCurrent (winget) | |
| Microsoft VSCode vCurrent (winget) | |
| Azure CLI vCurrent (PoSh/MSI) | |
| .NOTES | |
| Add-DevToMyWinServer.ps1 | |
| Version: 1.5 | |
| Creation Date: 2024-05-04 | |
| Updated: 2025-08-30 | |
| Author: https://github.com/bentman | |
| #> | |
| # Install NuGet (no-prompt) & set PSGallery trusted | |
| [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 | |
| Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | |
| Set-PSRepository -Name PSGallery -InstallationPolicy Trusted | |
| Push-Location ~\Downloads | |
| # Detect system architecture | |
| switch ([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture) { | |
| 'X64' { $arch = 'x64' } | |
| 'Arm64' { $arch = 'arm64'; $packageArch = 'arm' } # AppX packages use 'arm' for ARM64 | |
| 'X86' { $arch = 'x86' } | |
| default { throw "Unsupported architecture: $($_)" } | |
| } | |
| # Set package architecture (defaults to same as system arch for x64/x86) | |
| if (-not $packageArch) { $packageArch = $arch } | |
| Write-Host "Detected architecture: $arch (using '$packageArch' for package names)" | |
| # Install Microsoft.VCLibs | |
| $latestBase = 'https://github.com/microsoft/winget-cli/releases/latest/download' | |
| $depsZip = 'DesktopAppInstaller_Dependencies.zip' | |
| $depsOut = Join-Path $PWD $depsZip | |
| $depsDir = Join-Path $PWD 'wingetDeps' | |
| Invoke-WebRequest -Uri "$latestBase/$depsZip" -OutFile $depsOut -UseBasicParsing | |
| if (-not (Test-Path $depsDir)) { New-Item -Path $depsDir -ItemType Directory -Force } | |
| Expand-Archive -LiteralPath $depsOut -DestinationPath $depsDir -Force | |
| # Pick the right VCLibs appx by architecture | |
| $appxVCLibs = Get-ChildItem -Path (Join-Path $depsDir $packageArch) -Filter 'Microsoft.VCLibs*.appx' | Select-Object -First 1 | |
| if (-not $appxVCLibs) { throw "VCLibs package for $packageArch not found." } | |
| Write-Host "Installing VCLibs from $($appxVCLibs.FullName)" | |
| Add-AppxPackage -Path $appxVCLibs.FullName -ForceApplicationShutdown | |
| # Install Microsoft.UI.Xaml | |
| $apiUrl = 'https://api.github.com/repos/microsoft/microsoft-ui-xaml/releases/latest' | |
| $response = Invoke-WebRequest -Uri $apiUrl -UseBasicParsing | |
| $json = $response.Content | ConvertFrom-Json | |
| $tag = $json.tag_name # e.g. "v2.8.7" | |
| $appxName = "Microsoft.UI.Xaml.$($tag.TrimStart('v')).$packageArch.appx" | |
| $uiUrl = "https://github.com/microsoft/microsoft-ui-xaml/releases/download/$tag/$appxName" | |
| $uiOut = Join-Path $PWD $appxName | |
| Write-Host "Downloading Microsoft.UI.Xaml ($packageArch) from $uiUrl" | |
| Invoke-WebRequest -Uri $uiUrl -OutFile $uiOut -UseBasicParsing | |
| Write-Host "Installing Microsoft.UI.Xaml from $uiOut" | |
| Add-AppxPackage -Path $uiOut -ForceApplicationShutdown | |
| # Install WinGet (winget-cli) | |
| $winGetRepo = "https://api.github.com/repos/microsoft/winget-cli/releases/latest" | |
| $release = Invoke-WebRequest -Uri $winGetRepo -UseBasicParsing | ConvertFrom-Json | |
| $licXmlLink = $release.assets | Where-Object browser_download_url -Match '_License1.xml' | Select-Object -ExpandProperty browser_download_url | |
| $licXmlName = '_License1.xml' | |
| Invoke-WebRequest -Uri $licXmlLink -OutFile $licXmlName -UseBasicParsing | |
| Unblock-File .\$licXmlName | |
| $winGetLink = $release.assets | Where-Object browser_download_url -Match '.msixbundle' | Select-Object -ExpandProperty browser_download_url | |
| $winGetName = "winget.msixbundle" | |
| Invoke-WebRequest -Uri $winGetLink -OutFile $winGetName -UseBasicParsing | |
| Unblock-File .\$winGetName | |
| Add-AppxProvisionedPackage -Online -PackagePath .\$winGetName -LicensePath .\$licXmlName -Verbose | |
| # Install Windows Terminal | |
| $termRepo = "https://api.github.com/repos/microsoft/terminal/releases/latest" | |
| $termRel = Invoke-WebRequest -Uri $termRepo -UseBasicParsing | ConvertFrom-Json | |
| $termLink = $termRel.assets | Where-Object browser_download_url -Match '.msixbundle' | Select-Object -ExpandProperty browser_download_url | |
| $termName = 'WindowsTerminal.msixbundle' | |
| Invoke-WebRequest -Uri $termLink -OutFile .\$termName -Verbose | |
| Unblock-File .\$termName | |
| Add-AppPackage -Path .\$termName -Verbose | |
| Pop-Location | |
| ################################################ | |
| ### NOTE: This now requires shell restart!!! ### | |
| ################################################ | |
| # Search for available PowerShell versions (may prompt to accept terms) | |
| winget search Microsoft.PowerShell | |
| # Install PowerShell 7 from winget | |
| winget install --id Microsoft.Powershell --source winget | |
| # Search for available VS Code versions (may prompt to accept terms) | |
| winget search Microsoft.VisualStudioCode | |
| # Install VS Code from winget | |
| winget install --id Microsoft.VisualStudioCode --source winget | |
| # Search for available Azure CLI versions (may prompt to accept terms) | |
| winget search Microsoft.AzureCLI | |
| # Install Azure CLI from winget | |
| winget install --id Microsoft.AzureCLI --source winget | |
| # Update all installed packages | |
| winget update --all | |
| # Install OpenSSH Client and Server | |
| Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0 | |
| Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 | |
| # Start and configure SSH Server Service | |
| Get-Service sshd | Start-Service | |
| Set-Service -Name sshd -StartupType 'Automatic' | |
| # Configure SSH Server firewall rule | |
| $sshRule = Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | |
| if (-not $sshRule) { | |
| New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 | |
| } | |
| elseif (-not $sshRule.Enabled) { | |
| Set-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -Enabled True | |
| } | |
| # Set PowerShell 7 as default SSH shell (if available) | |
| $pwshPath = (Get-Command pwsh -ErrorAction SilentlyContinue).Source | |
| if ($pwshPath) { | |
| New-ItemProperty -Path 'HKLM:\SOFTWARE\OpenSSH' -Name DefaultShell -Value "`"$pwshPath`"" -PropertyType String -Force | |
| Write-Host "SSH default shell set to: $pwshPath" | |
| } | |
| else { | |
| # Fallback to Windows PowerShell 5.1 | |
| New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force | |
| Write-Host "SSH default shell set to: Windows PowerShell 5.1" | |
| } | |
| # Disable NLA on RDP | |
| Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name 'UserAuthentication' -Value 0 |
Author
> fail on Windows Server 2022
should upgrade to 2.8:
$MsftUi_Link = 'https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.x64.appx'
yup. looking for a way to do them all more 'dynamically' (aka - query the latest version to download). i'll update the static until then. thanks!
Author
Updated dynamic version retrieval...
winget-cli (api.github.com)
Microsoft.WindowsTerminal (api.github.com)
Author
Updated to v1.5
- System Architecture handling
- Compensates for the 'moving targets' (VCLibs & UI.Xaml)
-- VCLibs aka.ms download is 'version-locked', now getting directly from winget deps
-- Updated UI.Xaml to use dynamic version retrieval
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
VERBOSE: GET https://github.com/microsoft/terminal/releases/download/v1.19.11213.0/Microsoft.WindowsTerminal_1.19.11213.0_8wekyb3d8bbwe.msixbundle with 0-byte payload
VERBOSE: received 21550188-byte response of content type application/octet-stream
VERBOSE: Performing the operation "Deploy package" on target "D:\Microsoft.WindowsTerminal_1.19.11213.0_8wekyb3d8bbwe.msixbundle".
Add-AppPackage : Deployment failed with HRESULT: 0x80073CF3, Package failed updates, dependency or conflict validation.
Windows cannot install package Microsoft.WindowsTerminal_1.19.11213.0_x64__8wekyb3d8bbwe because this package depends on a framework that could not be found. Provide the framework "Microsoft.UI.Xaml.2.8" published by "CN=Microsof
t Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", with neutral or x64 processor architecture and minimum version 8.2305.5001.0, along with this package to install. The frameworks with name "Microsoft.UI.Xaml
.2.8" currently installed are: {}
fail on Windows Server 2022
should upgrade to 2.8:
$MsftUi_Link = 'https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.x64.appx'