The Scripted Version of Installing SSMS
Install and Update SQL Server Management Studio (SSMS) already covers the manual, click-through install via the Visual Studio Installer, the right walkthrough for a one-off install on your own workstation.
This script is the same job automated: silent install or update, detects what’s already there, picks the right method, and adds SSMS to PATH when it’s done. The one to reach for when you’re setting up more than one machine, or when “click through the installer” isn’t something you want to do by hand again.
Why a Scripted SSMS Install Matters
- SSMS 22+ and SSMS 17-20 use completely different installer frameworks, and you can’t upgrade across that boundary in place. This script detects which one is installed and tells you plainly when a manual uninstall has to happen first, rather than failing confusingly partway through.
- Silent by default, with a progress window available when you want to watch it. Fleet installs want silent, a one-off on your own machine might not.
- Adds SSMS to
PATHautomatically once installed, sossmslaunches it from any terminal without hunting for the install directory afterward.
When to Run This Script
- Standing up SSMS on a new machine as part of a broader environment setup
- Updating SSMS across several jump hosts or shared admin boxes without clicking through each one by hand
- Automating environment setup where a human clicking “Next” isn’t the workflow
Step 1: Are You Behind?
The status check from the SQL Server patching post covers SSMS too, one command, no elevation:
.\powershell\patching\patch-summary.ps1
SSMS
SQL Server Management Studio 22 v22.7.0 BEHIND -> 22.9.0
Scripted update: .\powershell\patching\ssms\install-ssms.ps1
Guide: https://sqldba.blog/dba-scripts-install-and-update-ssms-via-powershell/
A red BEHIND and the script to fix it, which is the rest of this page.
The Script
# install-ssms.ps1 - Install or update SQL Server Management Studio
#
# SSMS 22+ (default) : downloads vs_SSMS.exe from aka.ms/ssms/22/release/vs_SSMS.exe
# SSMS 17-20 (winget) : winget install Microsoft.SQLServerManagementStudio
#
# Cannot upgrade SSMS 17-20 in-place to SSMS 22 - run uninstall-ssms.ps1 first.
#
# Parameters:
# -Method download|winget : 'download' = SSMS 22 (default), 'winget' = SSMS 20
# -Url <url> : override download URL
# -DownloadDir <path> : folder to save installer (default: output-files\patches\ssms)
# -Passive : show VS Installer progress window instead of silent
# -UsePreview : use winget preview package (winget method only)
# -WhatIf : show what would run, no changes
#
# Examples:
# .\install-ssms.ps1 # install SSMS 22
# .\install-ssms.ps1 -Passive # install with progress window
# .\install-ssms.ps1 -Method winget # install SSMS 20
# .\install-ssms.ps1 -WhatIf # dry run
param(
[ValidateSet('winget', 'download')]
[string]$Method = 'download',
[string]$Url,
[string]$DownloadDir,
[switch]$UsePreview,
[switch]$Passive,
[switch]$WhatIf
)
$ErrorActionPreference = 'Stop'
# -- Admin check ---------------------------------------------------------------
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin -and -not $WhatIf) {
Write-Host 'ERROR: This script must be run as Administrator (or use -WhatIf to preview without installing).' -ForegroundColor Red; exit 1
}
# -- Logging -------------------------------------------------------------------
# When run from the repo the log goes to output-files\patches\ssms\.
# When run standalone (copied to any folder) it falls back to a logs\ subfolder
# next to the script. Handles empty $PSScriptRoot (copy-pasted into console).
$_scriptRoot = if ($PSScriptRoot) { $PSScriptRoot } else { $PWD.Path }
$_repoOutputDir = [System.IO.Path]::GetFullPath((Join-Path $_scriptRoot '..\..\..\output-files'))
$logDir = if (Test-Path $_repoOutputDir) {
Join-Path $_repoOutputDir 'patches\ssms'
} else {
Join-Path $_scriptRoot 'logs'
}
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
$logFile = Join-Path $logDir "ssms-install-$ts.log"
function Write-DbaLog {
param([string]$Msg, [string]$Color = 'White')
$line = "[$(Get-Date -Format 'HH:mm:ss')] $Msg"
Write-Host $line -ForegroundColor $Color
Add-Content -Path $logFile -Value $line
}
Write-DbaLog "SSMS install log - $ts" 'Cyan'
# -- Detect current SSMS version -----------------------------------------------
function Get-InstalledSsms {
$paths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
foreach ($p in $paths) {
$found = Get-ItemProperty $p -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like '*SQL Server Management Studio*' } |
Select-Object -First 1
if ($found) { return $found }
}
return $null
}
$current = Get-InstalledSsms
if ($current) {
$currentMajor = [int]($current.DisplayVersion -split '\.')[0]
Write-DbaLog "Installed : $($current.DisplayName) v$($current.DisplayVersion)" 'White'
if ($currentMajor -ge 22) {
Write-DbaLog " (SSMS 22+ - VS Installer framework)" 'DarkGray'
}
}
else {
Write-DbaLog 'SSMS not detected - will perform a fresh install.' 'Yellow'
$currentMajor = 0
}
# -- winget method -------------------------------------------------------------
if ($Method -eq 'winget') {
$winget = Get-Command winget.exe -ErrorAction SilentlyContinue
if (-not $winget) {
Write-DbaLog 'winget not available - switching to download method.' 'Yellow'
$Method = 'download'
}
else {
$packageId = if ($UsePreview) {
'Microsoft.SQLServerManagementStudio.Preview'
} else {
'Microsoft.SQLServerManagementStudio'
}
# Note: winget 'Microsoft.SQLServerManagementStudio' resolves to SSMS 20 (legacy stable).
# SSMS 22 is not in the winget source catalog - use -Method download for SSMS 22.
# Use 'install' for fresh installs, 'upgrade' when already present
$wingetVerb = if ($current) { 'upgrade' } else { 'install' }
Write-DbaLog "Method : winget $wingetVerb ($packageId)" 'Cyan'
if ($WhatIf) {
Write-DbaLog "WhatIf: winget $wingetVerb --id $packageId --silent --accept-source-agreements" 'Yellow'
return
}
Write-DbaLog "Running: winget $wingetVerb --id $packageId ..." 'Cyan'
$wingetArgs = @(
$wingetVerb,
'--id', $packageId,
'--silent',
'--accept-source-agreements'
)
$proc = Start-Process -FilePath $winget.Source -ArgumentList $wingetArgs `
-Wait -PassThru -NoNewWindow
switch ($proc.ExitCode) {
0 { Write-DbaLog "winget $wingetVerb completed." 'Green' }
-1978335212 {
# No package found in winget source
if ($current) {
Write-DbaLog 'winget could not find a newer package. If running SSMS 22+, use -Method download for VS Installer updates.' 'Yellow'
} else {
Write-DbaLog 'winget could not find the package. Use -Method download to install SSMS 22.' 'Yellow'
}
}
-1978335189 {
# Package found but no newer version in winget catalog
if ($currentMajor -ge 22) {
Write-DbaLog 'SSMS 22+ is not updated via winget. Use -Method download for in-place VS Installer updates.' 'Yellow'
} else {
Write-DbaLog 'SSMS is at the latest version available in the winget catalog.' 'Green'
}
}
default { Write-DbaLog "winget exited with code $($proc.ExitCode) - check output above." 'Yellow' }
}
}
}
# -- Download method -----------------------------------------------------------
if ($Method -eq 'download') {
# SSMS 22+ (VS Installer bootstrapper):
# URL : https://aka.ms/ssms/22/release/vs_SSMS.exe (always latest SSMS 22.x)
# Flags: --quiet --norestart --wait
# Ref : https://learn.microsoft.com/en-us/ssms/install/install
#
# SSMS 20 and below (legacy WiX installer):
# URL : https://aka.ms/ssmsfullsetup (resolves to latest SSMS 20.x)
# Flags: /install /quiet /norestart
$downloadUrl = if ($Url) { $Url } else { 'https://aka.ms/ssms/22/release/vs_SSMS.exe' }
$saveDir = if ($DownloadDir) { $DownloadDir } else { $logDir }
New-Item -ItemType Directory -Path $saveDir -Force | Out-Null
# Preserve the original filename so installer type can be detected from it
$installerFileName = Split-Path $downloadUrl -Leaf
if ($installerFileName -notmatch '\.exe$') { $installerFileName = "SSMS-Setup-$ts.exe" }
$installerPath = Join-Path $saveDir $installerFileName
# Detect installer type by filename:
# vs_SSMS.exe = VS Installer bootstrapper (SSMS 22+) -> uses -- style flags
# SSMS-Setup-ENU.exe = legacy WiX installer (SSMS 20 and below) -> uses / style flags
$isVsBootstrapper = $installerFileName -like 'vs_*.exe'
Write-DbaLog "Method : download" 'Cyan'
Write-DbaLog "URL : $downloadUrl"
Write-DbaLog "Installer : $installerFileName ($( if ($isVsBootstrapper) { 'VS bootstrapper - SSMS 22+' } else { 'legacy WiX - SSMS 20 and below' } ))" 'DarkGray'
# Cross-framework upgrade check:
# SSMS 20->22 cannot upgrade in-place - the installer framework changed entirely.
# SSMS 22->22.x can update in-place via the VS bootstrapper.
if ($current -and $currentMajor -lt 22 -and $isVsBootstrapper) {
# Covers SSMS 17, 18, 19, 20 - all use the legacy WiX installer framework.
# SSMS 22+ (VS Installer) cannot upgrade over the top of any of these.
Write-DbaLog ''
Write-DbaLog "SSMS $($current.DisplayName) v$($current.DisplayVersion) is installed (legacy WiX installer - versions 17-20)." 'Yellow'
Write-DbaLog "SSMS 22+ uses the VS Installer framework and cannot upgrade in-place over a legacy SSMS install." 'Yellow'
Write-DbaLog "Run .\uninstall-ssms.ps1 first, then re-run this script to install SSMS 22." 'Yellow'
exit 0
}
if ($current -and $currentMajor -ge 22 -and $isVsBootstrapper) {
Write-DbaLog "SSMS $($current.DisplayVersion) detected (VS Installer framework) - bootstrapper will perform an in-place update." 'DarkGray'
}
if ($WhatIf) {
$flags = if ($isVsBootstrapper) { '--quiet --norestart --wait' } else { '/install /quiet /norestart' }
Write-DbaLog "WhatIf: download '$downloadUrl' -> '$installerPath'" 'Yellow'
Write-DbaLog "WhatIf: Start-Process '$installerPath' $flags" 'Yellow'
return
}
Write-DbaLog 'Downloading SSMS installer...' 'Cyan'
try {
Start-BitsTransfer -Source $downloadUrl -Destination $installerPath -DisplayName "SSMS: $installerFileName"
$sizeMB = [math]::Round((Get-Item $installerPath).Length / 1MB, 1)
Write-DbaLog "Downloaded : $installerFileName ($sizeMB MB)" 'Green'
}
catch {
Write-DbaLog "ERROR: Download failed - $($_.Exception.Message)" 'Red'
exit 1
}
$installArgs = if ($isVsBootstrapper) {
$uiFlag = if ($Passive) { '--passive' } else { '--quiet' }
# --wait is required for the bootstrapper: vs_SSMS.exe launches the real VS Installer
# as a child process and exits immediately without it. --wait holds it open until done.
@($uiFlag, '--norestart', '--wait')
} else {
@('/install', '/quiet', '/norestart')
}
Write-DbaLog "Installing SSMS ($($installArgs -join ' '))..." 'Cyan'
$proc = Start-Process -FilePath $installerPath -ArgumentList $installArgs -PassThru `
-RedirectStandardOutput "$logFile.vs-stdout.txt" `
-RedirectStandardError "$logFile.vs-stderr.txt"
$sw = [System.Diagnostics.Stopwatch]::StartNew()
while (-not $proc.HasExited) {
Write-Host ("`r ... {0:mm\:ss} elapsed" -f $sw.Elapsed) -NoNewline -ForegroundColor DarkGray
Start-Sleep -Seconds 2
}
Write-Host ''
switch ($proc.ExitCode) {
0 { Write-DbaLog 'SSMS install completed.' 'Green' }
3010 { Write-DbaLog 'SSMS install completed - reboot required.' 'Yellow' }
default { Write-DbaLog "Installer exited with code $($proc.ExitCode) - verify manually." 'Yellow' }
}
}
# -- Verify new version --------------------------------------------------------
Write-DbaLog ''
Write-DbaLog 'Checking installed SSMS version...' 'Cyan'
$updated = Get-InstalledSsms
if ($updated) {
if ($current -and $updated.DisplayVersion -ne $current.DisplayVersion) {
Write-DbaLog "SSMS updated : v$($current.DisplayVersion) -> v$($updated.DisplayVersion)" 'Green'
}
else {
Write-DbaLog "SSMS version : v$($updated.DisplayVersion)" 'Green'
}
}
else {
Write-DbaLog 'Could not read SSMS version from registry - verify manually.' 'Yellow'
}
# -- Add SSMS to PATH ----------------------------------------------------------
$ssmsExe = $null
# SSMS 22+: use vswhere to get the install path
$vswhere = 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe'
if (Test-Path $vswhere) {
try {
$vsProducts = & $vswhere -all -products '*' -format json 2>&1 | ConvertFrom-Json
$ssmsProduct = $vsProducts |
Where-Object { $_.displayName -like '*SQL Server Management Studio*' } |
Select-Object -First 1
if ($ssmsProduct) {
$candidate = Join-Path $ssmsProduct.installationPath 'Common7\IDE\Ssms.exe'
if (Test-Path $candidate) { $ssmsExe = $candidate }
}
} catch { }
}
# Legacy SSMS (17-20): try registry InstallLocation then known default paths
if (-not $ssmsExe -and $updated -and $updated.InstallLocation) {
$candidate = Join-Path $updated.InstallLocation 'Common7\IDE\Ssms.exe'
if (Test-Path $candidate) { $ssmsExe = $candidate }
}
if (-not $ssmsExe) {
$knownPaths = @(
'C:\Program Files\Microsoft SQL Server Management Studio 22\Common7\IDE\Ssms.exe',
'C:\Program Files (x86)\Microsoft SQL Server Management Studio 20\Common7\IDE\Ssms.exe',
'C:\Program Files (x86)\Microsoft SQL Server Management Studio 19\Common7\IDE\Ssms.exe',
'C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\Ssms.exe'
)
$ssmsExe = $knownPaths | Where-Object { Test-Path $_ } | Select-Object -First 1
}
if ($ssmsExe) {
$ssmsDir = Split-Path $ssmsExe
$machinePath = [System.Environment]::GetEnvironmentVariable('PATH', 'Machine')
if ($machinePath -notlike "*$ssmsDir*") {
[System.Environment]::SetEnvironmentVariable('PATH', "$machinePath;$ssmsDir", 'Machine')
$env:PATH += ";$ssmsDir"
Write-DbaLog "PATH updated : added $ssmsDir" 'Green'
Write-DbaLog " Type 'ssms' to launch (current terminal already updated)." 'DarkGray'
}
else {
Write-DbaLog "PATH : 'ssms' command already available." 'DarkGray'
}
}
Write-DbaLog ''
Write-DbaLog 'Done.' 'Green'
Write-DbaLog "Log: $logFile"
Step 2: Dry Run
Run with -WhatIf first, it previews everything and changes nothing, no elevation needed:
[11:08:47] SSMS 22.7.0 detected (VS Installer framework) - bootstrapper will perform an in-place update.
[11:08:47] WhatIf: download 'https://aka.ms/ssms/22/release/vs_SSMS.exe' -> '...\output-files\patches\ssms\vs_SSMS.exe'
[11:08:47] WhatIf: Start-Process '...\vs_SSMS.exe' --quiet --norestart --wait
“Bootstrapper will perform an in-place update” is the good outcome: the installed SSMS is already on the same installer framework as the target, so no uninstall step is needed. If the script points you at uninstall-ssms.ps1 instead, that is not an error, it is the real SSMS 17-20 to 22 constraint being stated up front rather than failing halfway through.
Step 3: Update It
Same command without the switch. It downloads the current bootstrapper and runs the update silently; add -Passive if you want to watch the progress window. SQL Server itself is untouched, SSMS is client tooling, so there is no service restart and no downtime involved.
.\powershell\patching\ssms\install-ssms.ps1
[SCREENSHOT: the update run: detection, download, and the silent in-place update completing]
Step 4: Prove It
.\powershell\patching\patch-summary.ps1
[SCREENSHOT: patch-summary after the update, SSMS showing current in green]
And ssms launches from any terminal, the script adds it to PATH on the way out.
How To Run From The Repo
git clone https://github.com/peterwhyte-lgtm/dba-tools
cd dba-tools
.\Initialize-Environment.ps1
.\powershell\patching\ssms\install-ssms.ps1 -WhatIf
.\powershell\patching\ssms\install-ssms.ps1
.\powershell\patching\ssms\install-ssms.ps1 -Method winget
One catch on methods: winget resolves to SSMS 20, not 22; its catalog does not currently carry the new framework. For SSMS 22, the default download method is the one.
The scripts live in the repo:
Best Practices
- Run with
-WhatIffirst on any machine you’re not deeply familiar with, confirm which installer framework it’ll use before committing. - Use
-Passiveon your own workstation if you want to watch progress; leave it silent for anything scripted or unattended. - If a machine is still on SSMS 17-20 and you want SSMS 22, expect to run Uninstall SSMS via PowerShell first, this script will tell you so rather than failing silently.
- After install, a fresh terminal picks up the PATH update automatically; the current terminal gets it live too,
ssmsshould launch immediately without reopening anything.
Related Scripts
You may also find these scripts useful:
- SQL Server Installation and Patching (hub)
- Uninstall SSMS via PowerShell
- Install and Update SSMS (manual walkthrough)
- Patch SQL Server
- SSMS Complete Guide (every SSMS setup, configuration, and troubleshooting post on the site in one place)
- Installing Windows Terminal on Windows, a better console to run all of this from
- DBA Scripts: The Complete Guide, the map across every script on this site
Frequently Asked Questions
Should I use this script or the manual Visual Studio Installer walkthrough?
Use this script for repeatable installs, fleet setups, or anything you want automated. Use the manual walkthrough for a one-off install on your own workstation where clicking through a GUI once isn’t a burden.
Why does the script refuse to upgrade my SSMS 20 install straight to SSMS 22?
SSMS 22+ moved to the Visual Studio Installer framework entirely, replacing the legacy WiX-based installer used by SSMS 17 through 20. The two frameworks can’t upgrade over each other in place, an uninstall of the old one has to happen first.
Does updating SSMS affect SQL Server itself?
No. SSMS is client tooling; updating it touches nothing server-side, no services restart, and no maintenance window is needed. Update it as casually as a browser.
How do I know when a new SSMS version is out?
The patch-summary status check compares your installed SSMS against the latest release and prints a red BEHIND with this page’s link when an update is waiting, so a routine status run catches it without watching release notes.
Summary
Same job as the manual walkthrough, minus the clicking: detects what’s installed, picks download or winget, and stops you cold before a doomed SSMS 20-to-22 in-place upgrade instead of failing halfway through one.
Reach for it the moment “one more machine” turns into “several.” If a box is still on SSMS 17-20 and you need 22, run Uninstall SSMS via PowerShell first, this script will tell you so rather than pretending it can skip the step. Once it’s done, ssms launches from any terminal, no PATH hunting required.
This page covers one part of working with SSMS. The full reference ties the install, configuration and troubleshooting pieces together.
- SSMS Complete Guide, installing it, keeping it updated, and fixing it when it misbehaves.
- DBA Scripts, the SQL Server scripts you will actually run once SSMS is open.
More in this series: Install and Update SQL Server Management Studio (SSMS) · DBA Scripts: Uninstall SSMS via PowerShell · Open SSMS as a Different Domain User
Leave a Reply