-
-
Save ScottHutchinson/b22339c3d3688da5c9b477281e258400 to your computer and use it in GitHub Desktop.
# 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" |
@ericmschmidt you just open up a browser and Sign in at https://marketplace.visualstudio.com with your Microsoft account before you run the script.
I thought I tried that but it didn't seem to have any affect. I'll try again. Thanks for the feedback.
You can workaround the login requirement by passing the cookies between requests by changing line 21 to:
$HTML = Invoke-WebRequest -Uri $Uri -UseBasicParsing -SessionVariable session
and line 35 to:
Invoke-WebRequest $href -OutFile $VsixLocation -WebSession $session
The first change creates a variable containing the session cookies and the second change passes those cookie values through to the second request. This is pretty much what the browser does when an anonymous user is downloading the vsix from the site.
@mcj-codificer, Thank you. I updated the script as you suggested.
Thank you for this! Do you know of any way I can merge this into a single script or if there is a way to push to multiple devices in an Azure environment?
There are a list of extensions that I'd like to be able to push to my dev team using a remote management tool or by uploading a script in Azure to push to specific devices.
Do I have to use Edge or Internet Explorer to sign in, or will any browser (Chrome/Firefox etc) work?
This is in regards to the comment thread about avoiding the rate limits. Thank you.
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?
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.
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.
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.
You say to avoid the Anonymous usage rate limits to login but I'm having trouble figuring out how to do that. Do you happen to have an example?