Get-InstalledVsVersion.ps1
|
function Invoke-VsWhere { [CmdletBinding()] param( [switch]$MSBuild ) if ($MSBuild) { $installerPath = Get-VsInstallerPath $vsWhere = if ($installerPath) { Join-Path $installerPath 'vswhere.exe' } if (-not $vsWhere -or -not (Test-Path -LiteralPath $vsWhere -PathType Leaf -ErrorAction Stop)) { throw 'Cannot discover Visual Studio MSBuild: vswhere.exe was not found in the Visual Studio Installer directory. Install or repair Visual Studio Installer.' } $json = Invoke-MSBuildVsWhere -Path $vsWhere if (-not $json) { throw 'Cannot discover Visual Studio MSBuild: vswhere.exe returned no JSON.' } $instances = ConvertFrom-Json -InputObject ($json -join "`n") -NoEnumerate -ErrorAction Stop if ($instances -isnot [array]) { throw 'Cannot discover Visual Studio MSBuild: vswhere.exe did not return a JSON array.' } foreach ($instance in $instances) { $installationVersion = $null if (-not $instance.installationPath -or -not [System.IO.Path]::IsPathFullyQualified([string]$instance.installationPath) -or -not [version]::TryParse([string]$instance.installationVersion, [ref]$installationVersion)) { throw 'Cannot discover Visual Studio MSBuild: vswhere.exe returned invalid installation metadata.' } $instance } return } $installerPath = Get-VsInstallerPath $vsWhere = if ($installerPath) { Join-Path $installerPath 'vswhere.exe' } if (-not $vsWhere -or -not (Test-Path -LiteralPath $vsWhere -PathType Leaf -ErrorAction Stop)) { throw 'Cannot discover Visual Studio developer environments: vswhere.exe was not found. Install or repair Visual Studio Installer.' } $json = Invoke-DeveloperVsWhere -Path $vsWhere if (-not $json) { throw 'Cannot discover Visual Studio developer environments: vswhere.exe returned no JSON.' } $instances = ConvertFrom-Json -InputObject ($json -join "`n") -NoEnumerate -ErrorAction Stop if ($instances -isnot [array]) { throw 'Cannot discover Visual Studio developer environments: vswhere.exe did not return a JSON array.' } $instances } function Invoke-DeveloperVsWhere { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Path ) $PSNativeCommandUseErrorActionPreference = $false $output = & $Path -products '*' -format json -utf8 2>&1 if ($LASTEXITCODE -ne 0) { throw "Cannot discover Visual Studio developer environments: vswhere.exe exited with code $LASTEXITCODE. $($output -join "`n")" } $output } function Get-VsDeveloperInstallation { [CmdletBinding()] param( [ValidateSet(2017, 2019, 2022, 2026)] [int]$Year ) $installations = foreach ($instance in @(Invoke-VsWhere -ErrorAction Stop)) { $version = $null if (-not [version]::TryParse([string]$instance.installationVersion, [ref]$version) -or -not $instance.installationPath -or -not [IO.Path]::IsPathFullyQualified([string]$instance.installationPath)) { throw 'Cannot discover Visual Studio developer environments: invalid installation metadata.' } $installedYear = ConvertTo-VsYear -InstallationVersion ([string]$instance.installationVersion) if (-not $installedYear -or ($Year -and $Year -ne $installedYear)) { continue } if ($instance.isComplete -ne $true -or $instance.isLaunchable -ne $true -or $instance.isPrerelease -eq $true) { continue } $scriptPath = Join-Path $instance.installationPath 'Common7' 'Tools' 'VsDevCmd.bat' if (-not (Test-Path -LiteralPath $scriptPath -PathType Leaf -ErrorAction Stop)) { continue } [pscustomobject]@{ Year = $installedYear Version = $version.ToString() InstallationPath = [IO.Path]::GetFullPath([string]$instance.installationPath) ScriptPath = $scriptPath } } $installations | Sort-Object -Property @( @{ Expression = { [version]$_.Version }; Descending = $true }, @{ Expression = { $_.InstallationPath }; Ascending = $true } ) } function ConvertTo-VsYear { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$InstallationVersion ) $majorText = ($InstallationVersion -split '\.')[0] $major = 0 if (-not [int]::TryParse($majorText, [ref]$major)) { return $null } switch ($major) { 15 { 2017 } 16 { 2019 } 17 { 2022 } 18 { 2026 } default { $null } } } function Get-InstalledVsVersion { <# .SYNOPSIS Lists installed Visual Studio years that can be loaded in PowerShell. .DESCRIPTION Uses vswhere.exe to enumerate complete, launchable release installations, including Build Tools, that contain Common7/Tools/VsDevCmd.bat. Returns sorted unique years for Visual Studio 2017, 2019, 2022, and 2026. No PSVSEnv or Set-VS provider command is required. Preview installations are excluded. Discovery errors terminate rather than hiding failures. The catalog_productLineVersion field is intentionally ignored because it can be inconsistent across Visual Studio products. .EXAMPLE Get-InstalledVsVersion Returns years such as 2022 or 2026 for eligible installed toolsets. #> [OutputType([int])] [CmdletBinding()] param() Assert-WindowsOnly -CommandName $MyInvocation.MyCommand.Name Get-VsDeveloperInstallation | Select-Object -ExpandProperty Year | Sort-Object -Unique } |