Install SQL Server Management Studio on a Host Without Internet Access
Install SQL Server Management Studio on a Host Without Internet Access
This guide shows how to install, upgrade, and uninstall SQL Server Management Studio (SSMS) on a host with no internet access. It is for SSMS 21 and the versions after it.
SSMS 21 and the versions after it use a Visual Studio-style bootstrapper, vs_SSMS.exe. SSMS 20 and the versions before it use an MSI installer. The bootstrapper downloads its packages when you start it. An offline host cannot download these packages.
The solution has two parts. In Stage 1, you make an offline layout on a machine with internet access. You make a zip file of the layout and put it in a repository. In Stage 2, the offline host downloads the zip file and installs SSMS from it.
This guide uses the repository name ZipVault as an example. Use a repository that you have, for example JFrog Artifactory, Sonatype Nexus, or a file share. Change each example URL to the URL of your repository.
Do each step on a non-production host before you use a production host.
Contents
- Requirements
- Stage 1: Make the Offline Layout and Put It in a Repository
- Step 1: Make the Staging Folder
- Step 2: Find the Version and the Download URL
- Step 3: Download the Bootstrapper
- Step 4: Make the Offline Layout
- Step 4a: Add the Microsoft Code Signing Certificate
- Step 5: Make a Zip File of the Layout Folder
- Step 6: Get the SHA256 Checksum
- Step 7: Upload the Zip File to the Repository
- Step 8: Remove the Staging Folder
- Step 9: Record the Version and the Checksum
- Stage 2: Install or Upgrade SSMS on the Offline Host
- Step 1: Set the Values
- Step 2: Download the Zip File and Compare the Checksum
- Step 3: Extract the Zip File
- Step 4: Install the Certificates
- Step 5: Close SSMS
- Step 6: Select the Install Command
- Step 7: Start the Bootstrapper
- Step 8: Wait for the Visual Studio Installer to Complete
- Step 9: Make Sure That the Version Is Correct
- Step 10: Add the BI and Code Tools Workloads
- Step 11: Uninstall a Previous Major Version (Optional)
- Step 12: Remove the Temporary Files
- Log Files and Exit Codes
- Stage 3: Uninstall Fully
Requirements
The machine that makes the layout must have:
- Windows 11 or Windows Server 2022
- Internet access
- PowerShell 7 or a version after it
- Access to your repository
- Free disk space for the layout and for the zip file
The offline host must have:
- Windows 11, Windows Server 2025, Windows Server 2022, or Windows Server 2019, 64-bit. See the Microsoft system requirements for SSMS.
- Administrator rights
- Access to your repository
- The Microsoft Visual C++ v14 Redistributable, x86 and x64. An offline install must have it, as the Microsoft system requirements page shows.
- .NET Framework 4.8. SSMS 22 must have it. Setup installs it when it is missing, as the Microsoft system requirements page shows.
Stage 1: Make the Offline Layout and Put It in a Repository
Do Stage 1 one time on a machine with internet access. Do it again each time Microsoft releases a new SSMS version.
Do all steps of Stage 1 in PowerShell 7 or a version after it. To see your version, use the first command in the next code block. If Major is less than 7, start pwsh.
$PSVersionTable.PSVersion # If Major is less than 7, start pwsh.
pwsh
Step 1: Make the Staging Folder
$ProgressPreference = 'SilentlyContinue'
$env:SSMS_STAGE = 'C:\stage'
New-Item -ItemType Directory -Force -Path $env:SSMS_STAGE
Step 2: Find the Version and the Download URL
Open the Microsoft SSMS release history page:
https://learn.microsoft.com/en-us/ssms/release-history
Find the version to deploy on that page. For SSMS 22, use the Evergreen bootstrapper table. For SSMS 21 and the versions before it, use the Fixed version installers table. Record the version number and the direct download URL. Then set them:
$version = '22.10.2'
$bootstrapperUrl = 'https://aka.ms/ssms/22/release/vs_SSMS.exe'
$env:SSMS_LAYOUT = "$env:SSMS_STAGE\ssmsvnext-layout-$version"
Note: The URL changes with each release. The
aka.mslink in this example always gives the newest release.
Use only the URL from the release history page. For a previous version, use the direct link for that version.
Step 3: Download the Bootstrapper
Invoke-WebRequest -Uri $bootstrapperUrl -OutFile "$env:SSMS_STAGE\vs_SSMS.exe" *> $null
This step downloads only the bootstrapper. The file size is approximately 5 MB. The next step makes the full layout.
Step 4: Make the Offline Layout
& "$env:SSMS_STAGE\vs_SSMS.exe" --layout $env:SSMS_LAYOUT --lang en-US --quiet --wait
This step downloads the full offline layout into $env:SSMS_LAYOUT. The layout size is approximately 2.5 GB. The download time can be long on a slow connection.
The full layout path must be less than 80 characters long. The Create an offline installation of SSMS page gives this information.
Note: The bootstrapper opens a new window.
Wait for that window to close. Do not continue to Step 4a until the window is closed.
Step 4a: Add the Microsoft Code Signing Certificate
A host behind a firewall cannot validate the certificate chain of the Visual Studio Installer at install time. Stage 2 installs the certificate on the offline host.
Download the Microsoft Windows Code Signing PCA 2024 certificate. Add it to the certificates folder of the layout.
Invoke-WebRequest -Uri "https://www.microsoft.com/pkiops/certs/Microsoft%20Windows%20Code%20Signing%20PCA%202024.crt" `
-OutFile "$env:SSMS_LAYOUT\certificates\Microsoft Windows Code Signing PCA 2024.crt" *> $null
Note: This certificate has an expiration date.
If an install stops with a certificate error after the expiration date, find a new version in the Microsoft PKI repository. Then do this step again with the new file.
Step 5: Make a Zip File of the Layout Folder
Compress-Archive -Path "$env:SSMS_LAYOUT\*" -DestinationPath "$env:SSMS_STAGE\ssms-vnext-$version.zip" -Force *> $null
Example output: C:\stage\ssms-vnext-22.10.2.zip
Note: In a test,
Compress-Archivewith\*did not add hidden files.
Step 6: Get the SHA256 Checksum
Get-FileHash "$env:SSMS_STAGE\ssms-vnext-$version.zip" -Algorithm SHA256
Record the checksum. Stage 2 uses it.
Example output:
Algorithm Hash Path
--------- ---- ----
SHA256 A1B2C3D4E5F6... C:\stage\ssms-vnext-22.10.2.zip
Step 7: Upload the Zip File to the Repository
Upload the zip file to your repository. This example uses the repository name ZipVault. Change the URL to the URL of your repository.
$repoUrl = "https://zipvault.example.com/repository/sql-tools/ssms/ssms-vnext-$version.zip"
Invoke-WebRequest -Uri $repoUrl -Method Put -InFile "$env:SSMS_STAGE\ssms-vnext-$version.zip"
If your repository has a login, add the -Credential parameter or an authorization header to the command. If you use a file share, use Copy-Item to put the zip file there.
Step 8: Remove the Staging Folder
This step removes the staging folder from the machine that made the layout.
Remove-Item $env:SSMS_STAGE -Recurse -Force
Step 9: Record the Version and the Checksum
Record three values: the version, the SHA256 checksum from Step 6, and the repository URL from Step 7. Each person who installs SSMS must have all three values.
Stage 2: Install or Upgrade SSMS on the Offline Host
Do Stage 2 on each offline host. Do each step in the same PowerShell window. Open the window as Administrator. Each step uses the values that the previous steps set.
If you close the window, the values are lost. Open a new window as Administrator. Do Step 1 and Step 6 again. Then continue with the step where you stopped.
Stage 2 installs a new version or upgrades an installed version. Step 6 selects the correct command. If this version of SSMS is installed, do only Steps 1 to 3, Step 10, and Step 12.
Step 1: Set the Values
Use the version, the checksum, and the repository URL that you recorded in Stage 1.
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
$version = '22.10.2'
$majorVersion = ([version]$version).Major
$checksum = 'PASTE_THE_SHA256_HASH_FROM_STAGE_1_STEP_6'
$repoUrl = "https://zipvault.example.com/repository/sql-tools/ssms/ssms-vnext-$version.zip"
$zipPath = Join-Path $env:TEMP "ssms-vnext-$version.zip"
$extractPath = Join-Path $env:TEMP "ssms-vnext-$version"
Step 2: Download the Zip File and Compare the Checksum
Download the zip file from the repository. The file size is approximately 2.5 GB. Then compare its SHA256 checksum with the value that you recorded.
if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
Invoke-WebRequest -Uri $repoUrl -OutFile $zipPath
$actual = (Get-FileHash $zipPath -Algorithm SHA256).Hash
if ($actual -ne $checksum) { throw "Checksum mismatch. Expected $checksum but found $actual." }
If the checksums are different, stop. Download the file again.
Step 3: Extract the Zip File
Extract the zip file to a temporary folder. This step does not use the internet.
if (Test-Path $extractPath) { Remove-Item $extractPath -Recurse -Force }
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $extractPath)
Step 4: Install the Certificates
Install the certificates from the layout into the trust store of the host. A host behind a firewall must do this step, because it cannot get access to the Microsoft certificate servers.
Note: The checksum shows that the zip file did not change after Stage 1. It does not show who made the certificates.
Compare the SHA-1 thumbprint of each .cer file with the thumbprint on the Microsoft PKI repository page. The page lists Microsoft Root Certificate Authority 2010 and 2011. It does not list the .crt file.
$certPath = Join-Path $extractPath 'certificates'
if (Test-Path $certPath) {
Get-ChildItem $certPath -Filter '*.cer' | ForEach-Object {
certutil -addstore -f "Root" $_.FullName | Out-Null
}
Get-ChildItem $certPath -Filter '*.crt' | ForEach-Object {
certutil -addstore -f "CA" $_.FullName | Out-Null
}
}
Step 5: Close SSMS
Caution: Tell the users before you do this step. This command stops SSMS for all users on the host. SSMS does not keep work that you did not save.
Use this command to close SSMS on the host. If an SSMS session is open, the install stops with error 1003 or 8006.
Get-Process -Name 'Ssms' -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 5
Step 6: Select the Install Command
If the same major version is installed, for example 22.4 to 22.5, use update, an in-place upgrade. If no version is installed, or a different major version is installed, use the default install command. For example, use it for a new install, or for a change from 21.x to 22.x. A new install also adds the BI and Code Tools workloads. The update command cannot use --add.
$regPaths = @(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$sameMajorInstalled = foreach ($p in $regPaths) {
Get-ItemProperty $p -ErrorAction SilentlyContinue |
Where-Object { ($_.DisplayName -like '*SQL Server Management Studio*' -or
$_.DisplayName -like '*SSMS*') -and
$_.DisplayVersion -like "$majorVersion.*" }
}
$bootstrapVerb = if ($sameMajorInstalled) { 'update' } else { $null }
$bootstrapAddArgs = if (-not $bootstrapVerb) { '--add Microsoft.SqlServer.Workload.SSMS.BI --add Microsoft.SqlServer.Workload.SSMS.CodeTools' } else { $null }
$bootstrapArgs = (@($bootstrapVerb, $bootstrapAddArgs, '--noWeb', '--quiet', '--norestart') | Where-Object { $_ }) -join ' '
$bootstrapArgs # These are the arguments for Step 7.
Step 7: Start the Bootstrapper
Start the bootstrapper from the extracted layout. Do not add --wait.
The bootstrapper gives the work to setup.exe, the child process of the Visual Studio Installer. The bootstrapper can exit before setup.exe completes the install.
$bootstrapper = Join-Path $extractPath 'vs_SSMS.exe'
if (-not (Test-Path $bootstrapper)) { throw "vs_SSMS.exe not found in $extractPath" }
$proc = Start-Process -FilePath $bootstrapper -ArgumentList $bootstrapArgs -PassThru
Step 8: Wait for the Visual Studio Installer to Complete
Wait up to 2 minutes for the bootstrapper to stop. Then wait 30 seconds for setup.exe to start. Then wait for setup.exe to complete.
The code stops after 30 minutes. An SSMS install is 5 to 10 minutes long on most hosts.
$proc | Wait-Process -Timeout 120 -ErrorAction SilentlyContinue
Start-Sleep -Seconds 30
$timeoutSeconds = 1800
$elapsed = 0
$interval = 15
while ($elapsed -lt $timeoutSeconds) {
$vsSetup = Get-Process -Name 'setup' -ErrorAction SilentlyContinue |
Where-Object { $_.Path -like '*Microsoft Visual Studio\Installer*' }
if (-not $vsSetup) { break }
Start-Sleep -Seconds $interval
$elapsed += $interval
}
if ($elapsed -ge $timeoutSeconds) { throw "The install timed out after 30 minutes. Read the logs in $env:TEMP." }
"Installer exit code: $($proc.ExitCode)"
Note: The exit code in Step 8 is the exit code of the bootstrapper. Step 9 gives the result of the install.
Step 9: Make Sure That the Version Is Correct
Make sure that SSMS is in the registry or on disk. Make sure that the version is the target version or a version after it.
$installed = foreach ($p in $regPaths) {
Get-ItemProperty $p -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like '*SQL Server Management Studio*' -or
$_.DisplayName -like '*SSMS*' }
}
$ssmsExe = Get-Item 'C:\Program Files\Microsoft SQL Server Management Studio*\Release\Common7\IDE\Ssms.exe' `
-ErrorAction SilentlyContinue
if (-not $installed -and -not $ssmsExe) {
throw "SSMS is not in the registry and not on disk. Read the logs in $env:TEMP."
}
$detectedVersion = $null
if ($installed) {
$entry = $installed | Select-Object -First 1
if ($entry.DisplayVersion) { $detectedVersion = [version]$entry.DisplayVersion }
}
if (-not $detectedVersion -and $ssmsExe) {
$firstExe = $ssmsExe | Select-Object -First 1
$detectedVersion = [version]$firstExe.VersionInfo.ProductVersion
}
if ($detectedVersion -and $detectedVersion -lt [version]$version) {
throw "SSMS $detectedVersion is installed, but the target version $version was not reached. The install or the update can have been blocked, or can have failed."
}
"SSMS $detectedVersion is installed."
The installed build number is frequently more accurate than $version. For example, $version can be 22.8.0, and the installed build can be 22.8.12009.203. The code does not stop in this example.
Note: Step 9 uses the first SSMS entry in the registry. If the host has two major versions of SSMS, that entry can be the previous version. Then Step 9 can stop although the install is correct. To see all versions, use the entry list in Step 11.
Step 10: Add the BI and Code Tools Workloads
A new install adds these two workloads in Step 6. The update command cannot add them. A host can have this version without these two workloads. This step finds the workloads that the host does not have and adds them with a modify command.
Use the vs_SSMS.exe file from the layout for the modify command. Do not use the setup.exe file on the host. The setup.exe file has no source for a workload that the host did not install before.
$bootstrapper = Join-Path $extractPath 'vs_SSMS.exe'
$vswhereExe = 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe'
$requiredWorkloads = @('Microsoft.SqlServer.Workload.SSMS.BI', 'Microsoft.SqlServer.Workload.SSMS.CodeTools')
$missingWorkloads = @()
if (Test-Path $vswhereExe) {
foreach ($workload in $requiredWorkloads) {
$present = & $vswhereExe -all -products '*' -requires $workload -property installationPath 2>$null
if (-not $present) { $missingWorkloads += $workload }
}
} else {
"vswhere.exe not found. Cannot confirm the BI and Code Tools workloads."
}
if ($missingWorkloads.Count -gt 0) {
$channelId = "SSMS.$majorVersion.SSMS.Release"
$addArgs = ($missingWorkloads | ForEach-Object { "--add $_" }) -join ' '
$modifyProc = Start-Process -FilePath $bootstrapper `
-ArgumentList "modify --noWeb --productID Microsoft.VisualStudio.Product.SSMS --channelID $channelId $addArgs --quiet --norestart" `
-Wait -PassThru
"Added: $($missingWorkloads -join ', '). Exit code: $($modifyProc.ExitCode)"
}
Exit code 0 or 3010 shows that the workloads are added.
Note: Step 10 uses the Release channel,
SSMS.<major version>.SSMS.Release. This guide is for the Release channel.
Step 11: Uninstall a Previous Major Version (Optional)
Do this step only if a previous major version is installed. For example, SSMS 21 is on the host, and you install SSMS 22. Do not uninstall the version that you installed in Step 7.
For SSMS 21 and the versions after it, use the Visual Studio Installer. Change $oldMajor to the previous major version:
$oldMajor = 21
$setupExe = 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe'
Start-Process -FilePath $setupExe `
-ArgumentList "uninstall --installPath `"C:\Program Files\Microsoft SQL Server Management Studio $oldMajor\Release`" --quiet --norestart --noweb" `
-Wait
For SSMS 20 and the versions before it, use msiexec. First, show the entries. Find the GUID of the previous version:
foreach ($p in $regPaths) {
Get-ItemProperty $p -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like '*SQL Server Management Studio*' } |
Select-Object PSChildName, DisplayName, DisplayVersion
}
Then uninstall the previous version. Replace {GUID} with the PSChildName value:
Start-Process -FilePath 'msiexec' -ArgumentList '/x {GUID} /qn /norestart' -Wait
Step 12: Remove the Temporary Files
Remove-Item $zipPath -Force -ErrorAction SilentlyContinue
Remove-Item $extractPath -Recurse -Force -ErrorAction SilentlyContinue
Note: The installer records the path of the layout. After Step 12, a later Repair or Modify from the Visual Studio Installer can ask for the layout.
To change the host again, do Stage 2 again with the zip file.
Log Files and Exit Codes
Exit code 3010 shows that the install completed and a reboot is necessary. Reboot the host when you can.
The install writes log files to %TEMP%\dd_vs_SSMS_decompression_log.txt and %TEMP%\dd_vs_SSMS*. In the Microsoft documentation, other log files start with ssms_bootstrapper, ssms_client, and ssms_setup.
If the install stops with no error message and SSMS is not installed, open the newest dd_bootstrapper_*.log file in %TEMP%. The message Certificate is invalid shows a certificate problem. See Install certificates for an offline installation of SSMS.
For the full list of exit codes, see the Microsoft command-line parameters.
Stage 3: Uninstall Fully
Use Stage 3 only when you must uninstall SSMS fully from a host. Stage 3 removes all versions and all components.
Close SSMS first. Save the next script as Uninstall-SSMS.ps1. Start it in a PowerShell window that you opened as Administrator.
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
$setupExe = 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe'
$regHives = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
)
Write-Host 'Scanning for SSMS installations...'
$ssmsEntries = foreach ($hive in $regHives) {
if (Test-Path $hive) {
Get-ChildItem $hive -ErrorAction SilentlyContinue |
Get-ItemProperty -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like '*SQL Server Management Studio*' -and
$_.DisplayName -notlike '*Visual Studio Installer*' }
}
}
# Use the correct uninstall method for each entry, by major version.
$vsInstallerMajors = [System.Collections.Generic.HashSet[int]]::new()
$msiEntries = [System.Collections.Generic.List[object]]::new()
foreach ($entry in $ssmsEntries) {
$major = 0
if ($entry.DisplayVersion -match '^(\d+)') { $major = [int]$Matches[1] }
Write-Host "Found: '$($entry.DisplayName)' ($($entry.DisplayVersion))"
if ($major -ge 21) {
[void]$vsInstallerMajors.Add($major)
} else {
$msiEntries.Add($entry)
}
}
if ($vsInstallerMajors.Count -eq 0 -and $msiEntries.Count -eq 0) {
Write-Host 'No SSMS installations found. Nothing to uninstall.'
exit 0
}
# SSMS 21 and the versions after it: uninstall with the Visual Studio Installer, one call for each major version.
foreach ($major in $vsInstallerMajors) {
$installPath = "C:\Program Files\Microsoft SQL Server Management Studio $major\Release"
Write-Host "Uninstalling SSMS $major (Visual Studio Installer): $installPath..."
if (-not (Test-Path $setupExe)) {
Write-Host " Visual Studio Installer not found at '$setupExe'. Cannot uninstall SSMS $major."
continue
}
if (-not (Test-Path $installPath)) {
Write-Host " Install path not found: $installPath. It can already be removed."
continue
}
try {
$proc = Start-Process -FilePath $setupExe `
-ArgumentList "uninstall --installPath `"$installPath`" --quiet --norestart --noweb" `
-Wait -PassThru
if ($proc.ExitCode -eq 0 -or $proc.ExitCode -eq 3010) {
Write-Host " Uninstalled SSMS $major (exit code $($proc.ExitCode))"
} else {
Write-Host " setup.exe exited $($proc.ExitCode). Read the logs in $env:TEMP."
}
}
catch {
Write-Host " Failed to uninstall SSMS $major`: $($_.Exception.Message)"
}
}
# SSMS 20 and the versions before it: uninstall with msiexec.
foreach ($entry in $msiEntries) {
$guid = $entry.PSChildName
Write-Host "Uninstalling '$($entry.DisplayName)' ($($entry.DisplayVersion)) with msiexec..."
try {
$proc = Start-Process -FilePath 'msiexec' `
-ArgumentList "/x $guid /qn /norestart" `
-Wait -PassThru
# 0 = success, 3010 = success and a reboot is necessary, 1605 = not installed (the program is not on the host).
if ($proc.ExitCode -eq 0 -or $proc.ExitCode -eq 3010 -or $proc.ExitCode -eq 1605) {
Write-Host " Uninstalled: $($entry.DisplayName) (exit code $($proc.ExitCode))"
} else {
Write-Host " msiexec exited $($proc.ExitCode) for '$($entry.DisplayName)'. Read the event log."
}
}
catch {
Write-Host " Failed to uninstall '$($entry.DisplayName)'`: $($_.Exception.Message)"
}
}
# Remove empty SSMS folders from C:\Program Files.
Get-ChildItem 'C:\Program Files\Microsoft SQL Server Management Studio*' -Directory -ErrorAction SilentlyContinue |
Where-Object { (Get-ChildItem $_.FullName -Force -ErrorAction SilentlyContinue).Count -eq 0 } |
ForEach-Object {
Write-Host "Removing empty folder: $($_.FullName)"
Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue
}
Write-Host 'SSMS uninstall complete.'
The script does these steps:
- Examines the registry for SSMS entries.
- For an entry of SSMS 21 or a version after it, starts
setup.exe uninstall --installPathfor that major version. - For an entry of SSMS 20 or a version before it, starts
msiexec /x {GUID} /qn /norestart. - Continues with the next entry after an error in one removal.
- Removes empty SSMS folders from
C:\Program Files.
For the method with a user interface, see the Microsoft uninstall page. If a damaged install prevents the removal, the same page gives the InstallCleanup.exe tool. Use this tool only as the last step.
Comments
Post a Comment