Skip to content

Instantly share code, notes, and snippets.

@ScottHutchinson
Last active September 14, 2024 17:01
Show Gist options
  • Save ScottHutchinson/b22339c3d3688da5c9b477281e258400 to your computer and use it in GitHub Desktop.
Save ScottHutchinson/b22339c3d3688da5c9b477281e258400 to your computer and use it in GitHub Desktop.
PowerShell scripts for batch installing Visual Studio extensions
# Based on http://nuts4.net/post/automated-download-and-installation-of-visual-studio-extensions-via-powershell
param([String] $PackageName)
$ErrorActionPreference = "Stop"
$baseProtocol = "https:"
$baseHostName = "marketplace.visualstudio.com"
$Uri = "$($baseProtocol)//$($baseHostName)/items?itemName=$($PackageName)"
$VsixLocation = "$($env:Temp)\$([guid]::NewGuid()).vsix"
$VSInstallDir = "C:\Program Files (x86)\Microsoft Visual Studio\Installer\resources\app\ServiceHub\Services\Microsoft.VisualStudio.Setup.Service"
if (-Not $VSInstallDir) {
Write-Error "Visual Studio InstallDir registry key missing"
Exit 1
}
Write-Host "Grabbing VSIX extension at $($Uri)"
$HTML = Invoke-WebRequest -Uri $Uri -UseBasicParsing -SessionVariable session
Write-Host "Attempting to download $($PackageName)..."
$anchor = $HTML.Links |
Where-Object { $_.class -eq 'install-button-container' } |
Select-Object -ExpandProperty href
if (-Not $anchor) {
Write-Error "Could not find download anchor tag on the Visual Studio Extensions page"
Exit 1
}
Write-Host "Anchor is $($anchor)"
$href = "$($baseProtocol)//$($baseHostName)$($anchor)"
Write-Host "Href is $($href)"
Invoke-WebRequest $href -OutFile $VsixLocation -WebSession $session
if (-Not (Test-Path $VsixLocation)) {
Write-Error "Downloaded VSIX file could not be located"
Exit 1
}
Write-Host "VSInstallDir is $($VSInstallDir)"
Write-Host "VsixLocation is $($VsixLocation)"
Write-Host "Installing $($PackageName)..."
Start-Process -Filepath "$($VSInstallDir)\VSIXInstaller" -ArgumentList "/q /a $($VsixLocation)" -Wait
Write-Host "Cleanup..."
rm $VsixLocation
Write-Host "Installation of $($PackageName) complete!"
# This script will download the latest version of each extension
# and install it in all supported versions of Visual Studio.
# It might take a few minutes to download and install each extension.
# To Run this Script:
# Optional: Sign in at https://marketplace.visualstudio.com to avoid being blocked
# due to Anonymous usage rate limits.
# Close Visual Studio before running this script.
# Run in PowerShell as Admin.
# Example: PS C:\Installers\Visual Studio\VSIX> ./install
# Get more Package Names from the Visual Studio Marketplace URL itemName parameter.
# Example: https://marketplace.visualstudio.com/items?itemName=TheDan.FindChangesetByComment
$DownloadAndInstall= $PSScriptRoot+"\install-vsix.ps1"
& $DownloadAndInstall -PackageName "SergeyVlasov.VisualCommander"
& $DownloadAndInstall -PackageName "MBulli.SmartCommandlineArguments"
& $DownloadAndInstall -PackageName "mayerwin.RenameVisualStudioWindowTitle"
& $DownloadAndInstall -PackageName "VisualCppDevLabs.CQuickFixes2017"
& $DownloadAndInstall -PackageName "TomasRestrepo.Viasfora"
& $DownloadAndInstall -PackageName "MadsKristensen.MarkdownEditor"
& $DownloadAndInstall -PackageName "PeterMacej.MultilineSearchandReplace"
& $DownloadAndInstall -PackageName "GitHub.GitHubExtensionforVisualStudio"
& $DownloadAndInstall -PackageName "TheDan.FindChangesetByComment"
& $DownloadAndInstall -PackageName "caphyon.ClangPowerTools"
@astrohart
Copy link

I note that on line 13 the script sets the $VSInstallDir variable to C:\Program Files (x86)\Microsoft Visual Studio\Installer\resources\app\ServiceHub\Services\Microsoft.VisualStudio.Setup.Service which is correct.

However, on line 16, if this variable refers to the pathname of a folder that does not exist on the computer, it says Visual Studio InstallDir registry key missing. I am wondering, is the error message inconsistent?

@GaTechThomas
Copy link

In VS 2022, the VSIXInstaller is by default located at:

C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\

If using the Developer PowerShell prompt, environment variable DevEnvDir can be used for that path.

@JohnstonJ
Copy link

This doesn't quite work (running an adapted version of this code):

==> default: Running provisioner: rust_analyzer_vs (shell)...
    default: Running: inline PowerShell script
    default: Where-Object : The property 'class' cannot be found on this object. Verify that the property exists.
    default: At C:\tmp\vagrant-shell.ps1:10 char:3
    default: +   Where-Object { $_.class -eq "install-button-container" } | `
    default: +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    default:     + CategoryInfo          : NotSpecified: (:) [Where-Object], PropertyNotFoundException
    default:     + FullyQualifiedErrorId : PropertyNotFoundStrict,Microsoft.PowerShell.Commands.WhereObjectCommand
    default:

The issue is that not all links on the page have a class. Changing the Where-Object part to:

Where-Object { $_.PSObject.Properties.Match('class').Count -and `
        $_.class -eq "install-button-container" }

seems to work.

@ScottHutchinson
Copy link
Author

Thanks for the fix. I haven't used these scripts for a couple years, since we only use two or three extensions now, and recently we have no Internet access on our developer machines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment