Start Here
Bookend Checks for an Installation You Can Trust
Two scripts, same shape (PASS / WARN / FAIL, a summary verdict), sitting on either side of an install: pre-install-check.ps1 confirms the machine is actually ready before you run setup, post-install-validation.ps1 confirms the instance is actually configured sensibly after you’re done. Neither installs or changes anything, both are safe to run any time.
Why Checking Both Ends Matters
- A failed prerequisite caught before install is a two-minute fix. The same problem caught after install is a support call. Low disk space, a pending reboot, or an existing instance name collision are all things
setup.exewill either fail on or silently work around in a way you don’t want. - “The installer finished successfully” and “this instance is configured correctly” are different claims. Setup completing without an error says nothing about whether SA is disabled, whether TempDB has the right file count, or whether max server memory got left at its unbounded default.
- Both scripts are read-only. Run them as often as you like, on any machine, without needing to justify a change window first.
When to Run These Scripts
pre-install-check.ps1, before running SQL Server setup on any new machinepost-install-validation.ps1, immediately after any install, and again periodically on servers you’ve inherited- Either one when auditing a machine you didn’t build yourself, to see what state it’s actually in
pre-install-check.ps1
Checks admin elevation, OS version against the minimum build for the target SQL Server version, pending reboot state, RAM, CPU count, .NET Framework version, PowerShell version, free disk space on the planned install/data/log drives, TCP port 1433 availability, existing SQL Server instances, and the active Windows Firewall profile.
<#
.SYNOPSIS
Run pre-installation checks before deploying SQL Server.
.DESCRIPTION
Validates the machine is ready for SQL Server installation. Reports PASS / WARN / FAIL
for each check. Does not make any changes to the system.
.PARAMETER InstanceName
Target instance name to check for existing installs. Default: MSSQLSERVER.
.PARAMETER InstallDir
Planned SQL Server binary directory — checked for disk space.
.PARAMETER DataDir
Planned data directory — checked for disk space.
.PARAMETER LogDir
Planned log directory — checked for disk space.
.PARAMETER SqlVersion
SQL Server version being installed (2016|2017|2019|2022). Default: 2022.
.EXAMPLE
.\admin\installation\pre-install-check.ps1
.\admin\installation\pre-install-check.ps1 -SqlVersion 2019 -DataDir D:\SQLData
.NOTES
RiskLevel : SAFE - reads only; writes nothing to the machine or the instance
#>
param(
[string]$InstanceName = 'MSSQLSERVER',
[string]$InstallDir = 'C:\Program Files\Microsoft SQL Server',
[string]$DataDir = 'C:\SQLData',
[string]$LogDir = 'C:\SQLLogs',
[ValidateSet('2016','2017','2019','2022')]
[string]$SqlVersion = '2022'
)
$ErrorActionPreference = 'SilentlyContinue'
$pass = 0; $warn = 0; $fail = 0
$results = [System.Collections.Generic.List[PSObject]]::new()
function Add-Check {
param([string]$Category, [string]$Check, [string]$Status, [string]$Detail)
$color = switch ($Status) { 'PASS'{'Green'} 'WARN'{'Yellow'} 'FAIL'{'Red'} default{'White'} }
$results.Add([PSCustomObject]@{ Category=$Category; Check=$Check; Status=$Status; Detail=$Detail })
Write-Host (" [{0,-4}] {1,-38} {2}" -f $Status, $Check, $Detail) -ForegroundColor $color
switch ($Status) { 'PASS'{$script:pass++} 'WARN'{$script:warn++} 'FAIL'{$script:fail++} }
}
Write-Host ""
Write-Host " SQL Server $SqlVersion Pre-Install Checks" -ForegroundColor Cyan
Write-Host (" " + [string]::new('-',60)) -ForegroundColor DarkCyan
Write-Host ""
# ── Admin elevation ───────────────────────────────────────────────────────────
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator)
Add-Check 'System' 'Running as Administrator' `
$(if ($isAdmin) {'PASS'} else {'FAIL'}) `
$(if ($isAdmin) {'Elevated'} else {'Re-run as Administrator'})
# ── OS version ────────────────────────────────────────────────────────────────
$os = Get-CimInstance Win32_OperatingSystem
$osBuild = [int]$os.BuildNumber
$osName = $os.Caption
$minBuild = switch ($SqlVersion) {
'2022' { 17763 } # Windows Server 2019
'2019' { 14393 } # Windows Server 2016
'2017' { 14393 } # Windows Server 2016
'2016' { 9600 } # Windows Server 2012 R2
}
$osStatus = if ($osBuild -ge $minBuild) {'PASS'} else {'FAIL'}
Add-Check 'System' 'OS version' $osStatus "$osName (Build $osBuild)"
# ── Pending reboot ────────────────────────────────────────────────────────────
$pendingReboot = $false
$rebootKeys = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending',
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired',
'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations'
)
foreach ($key in $rebootKeys) {
if (Test-Path $key) { $pendingReboot = $true; break }
}
Add-Check 'System' 'No pending reboot' `
$(if ($pendingReboot) {'WARN'} else {'PASS'}) `
$(if ($pendingReboot) {'Pending reboot detected — install may fail'} else {'Clean'})
# ── RAM ───────────────────────────────────────────────────────────────────────
$ramGB = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 1)
$ramStatus = if ($ramGB -ge 8) {'PASS'} elseif ($ramGB -ge 4) {'WARN'} else {'FAIL'}
Add-Check 'Hardware' 'RAM' $ramStatus "$ramGB GB $(if ($ramGB -lt 8) {'(8 GB recommended)'})"
# ── CPU count ─────────────────────────────────────────────────────────────────
$cpus = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors
Add-Check 'Hardware' 'Logical CPUs' 'PASS' "$cpus processors"
# ── .NET Framework ────────────────────────────────────────────────────────────
$netKey = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -ErrorAction SilentlyContinue
$netVer = $netKey.Release
$netStatus = if ($netVer -ge 461808) {'PASS'} elseif ($netVer -ge 394802) {'WARN'} else {'FAIL'}
$netLabel = if ($netVer -ge 528040) {'4.8+'} elseif ($netVer -ge 461808) {'4.7.2'} elseif ($netVer -ge 394802) {'4.6.2'} else {"$netVer (4.7.2+ required)"}
Add-Check 'Prerequisites' '.NET Framework' $netStatus $netLabel
# ── PowerShell version ────────────────────────────────────────────────────────
$psVer = $PSVersionTable.PSVersion
$psStatus = if ($psVer.Major -ge 5) {'PASS'} else {'WARN'}
Add-Check 'Prerequisites' 'PowerShell version' $psStatus "$($psVer.Major).$($psVer.Minor)"
# ── Disk space ────────────────────────────────────────────────────────────────
$diskChecks = @(
@{ Path=$InstallDir; Label='Install dir'; MinGB=20 },
@{ Path=$DataDir; Label='Data dir'; MinGB=50 },
@{ Path=$LogDir; Label='Log dir'; MinGB=20 }
)
foreach ($dc in $diskChecks) {
$drive = Split-Path -Qualifier $dc.Path
$psDrive = Get-PSDrive ($drive.TrimEnd(':')) -ErrorAction SilentlyContinue
if ($psDrive) {
$freeGB = [math]::Round($psDrive.Free / 1GB, 1)
$status = if ($freeGB -ge $dc.MinGB) {'PASS'} elseif ($freeGB -ge ($dc.MinGB / 2)) {'WARN'} else {'FAIL'}
Add-Check 'Disk' $dc.Label $status "$freeGB GB free on $drive (need $($dc.MinGB) GB)"
} else {
Add-Check 'Disk' $dc.Label 'WARN' "Drive $drive not found — will be created"
}
}
# ── TCP port 1433 availability ────────────────────────────────────────────────
$port1433InUse = $false
try {
$tcpConn = Get-NetTCPConnection -LocalPort 1433 -State Listen -ErrorAction SilentlyContinue
if ($tcpConn) { $port1433InUse = $true }
} catch {
Write-Error $_
throw
}
Add-Check 'Network' 'TCP port 1433' `
$(if ($port1433InUse) {'WARN'} else {'PASS'}) `
$(if ($port1433InUse) {'Already in use — check existing SQL instance'} else {'Available'})
# ── Existing SQL Server instances ─────────────────────────────────────────────
$regInstances = @()
$regPath = 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL'
if (Test-Path $regPath) {
$regInstances = Get-ItemProperty $regPath |
Get-Member -MemberType NoteProperty |
Where-Object { $_.Name -notmatch '^PS' } |
Select-Object -ExpandProperty Name
}
if ($regInstances.Count -eq 0) {
Add-Check 'SQL Server' 'Existing instances' 'PASS' 'None found'
} elseif ($regInstances -contains $InstanceName) {
Add-Check 'SQL Server' 'Existing instances' 'FAIL' "Instance '$InstanceName' already exists: $($regInstances -join ', ')"
} else {
Add-Check 'SQL Server' 'Existing instances' 'WARN' "Other instances present: $($regInstances -join ', ')"
}
# ── Windows Firewall ──────────────────────────────────────────────────────────
try {
$fwProfile = (Get-NetFirewallProfile -ErrorAction Stop | Where-Object Enabled -eq $true | Select-Object -First 1).Name
if ($fwProfile) {
Add-Check 'Network' 'Windows Firewall' 'WARN' "Active profile: $fwProfile — ensure port 1433 rule is added post-install"
} else {
Add-Check 'Network' 'Windows Firewall' 'PASS' 'No active firewall profiles'
}
} catch {
Add-Check 'Network' 'Windows Firewall' 'WARN' 'Could not check firewall state'
}
# ── Summary ───────────────────────────────────────────────────────────────────
Write-Host ""
Write-Host (" " + [string]::new('=',60)) -ForegroundColor DarkCyan
$summaryColor = if ($fail -gt 0) {'Red'} elseif ($warn -gt 0) {'Yellow'} else {'Green'}
$verdict = if ($fail -gt 0) {'NOT READY — fix FAIL items before proceeding'} `
elseif ($warn -gt 0) {'READY WITH WARNINGS — review WARN items'} `
else {'READY — all checks passed'}
Write-Host " $verdict" -ForegroundColor $summaryColor
Write-Host " PASS: $pass WARN: $warn FAIL: $fail" -ForegroundColor $summaryColor
Write-Host ""
Real Output — Run Against This Lab Machine
This machine already has SQL Server installed, which is exactly the kind of situation this script is meant to catch:
SQL Server 2022 Pre-Install Checks
------------------------------------------------------------
[FAIL] Running as Administrator Re-run as Administrator
[PASS] OS version Microsoft Windows 11 Home (Build 26200)
[PASS] No pending reboot Clean
[WARN] RAM 7.8 GB (8 GB recommended)
[PASS] Logical CPUs 8 processors
[PASS] .NET Framework 4.8+
[PASS] PowerShell version 7.6
[FAIL] Install dir 8.6 GB free on C: (need 20 GB)
[FAIL] Data dir 8.6 GB free on C: (need 50 GB)
[FAIL] Log dir 8.6 GB free on C: (need 20 GB)
[WARN] TCP port 1433 Already in use — check existing SQL instance
[FAIL] Existing instances Instance 'MSSQLSERVER' already exists: MSSQLSERVER
[WARN] Windows Firewall Active profile: Domain — ensure port 1433 rule is added post-install
============================================================
NOT READY — fix FAIL items before proceeding
PASS: 5 WARN: 3 FAIL: 5
Every FAIL here is genuine and correctly identified: this machine already runs a default instance, has well under the recommended free disk space, and the shell wasn’t elevated for this run. That’s exactly the signal a pre-install check should give.
post-install-validation.ps1
Connects to a SQL Server instance and checks service status, connectivity, version, key sp_configure settings, TempDB file count, TCP/IP connectivity, SA account status, and authentication mode.
<#
.SYNOPSIS
Validate a SQL Server installation is configured correctly.
.DESCRIPTION
Connects to a SQL Server instance and checks services, connectivity, version,
configuration settings, TempDB layout, and security posture.
Outputs PASS / WARN / FAIL per check with a summary.
.PARAMETER ServerInstance
Instance to validate. Default: . (local default instance).
.PARAMETER InstanceName
Windows service name suffix (e.g. MSSQLSERVER or SQL2022). Default: MSSQLSERVER.
.PARAMETER ExpectedMaxMemoryGB
Expected max server memory setting. If 0, validates it is not at the unlimited default.
.PARAMETER ExpectedMaxDOP
Expected MaxDOP. If 0, validates it is not at default (0).
.EXAMPLE
.\admin\installation\post-install-validation.ps1
.\admin\installation\post-install-validation.ps1 -ServerInstance PROD01\SQL2022 -InstanceName SQL2022
.NOTES
RiskLevel : SAFE - read-only queries; changes no configuration
#>
param(
[string]$ServerInstance = '.',
[string]$InstanceName = 'MSSQLSERVER',
[int]$ExpectedMaxMemoryGB = 0,
[int]$ExpectedMaxDOP = 0
)
$ErrorActionPreference = 'SilentlyContinue'
if ($ServerInstance -eq '.' -and $env:DBASCRIPTS_SERVER) { $ServerInstance = $env:DBASCRIPTS_SERVER }
$pass = 0; $warn = 0; $fail = 0
function Add-Check {
param([string]$Category, [string]$Check, [string]$Status, [string]$Detail)
$color = switch ($Status) { 'PASS'{'Green'} 'WARN'{'Yellow'} 'FAIL'{'Red'} default{'White'} }
Write-Host (" [{0,-4}] {1,-40} {2}" -f $Status, $Check, $Detail) -ForegroundColor $color
switch ($Status) { 'PASS'{$script:pass++} 'WARN'{$script:warn++} 'FAIL'{$script:fail++} }
}
Write-Host ""
Write-Host " Post-Install Validation — $ServerInstance" -ForegroundColor Cyan
Write-Host (" " + [string]::new('-', 62)) -ForegroundColor DarkCyan
Write-Host ""
# ── Services ──────────────────────────────────────────────────────────────────
$svcName = if ($InstanceName -eq 'MSSQLSERVER') { 'MSSQLSERVER' } else { "MSSQL`$$InstanceName" }
$agtName = if ($InstanceName -eq 'MSSQLSERVER') { 'SQLSERVERAGENT' } else { "SQLAgent`$$InstanceName" }
$sqlSvc = Get-Service -Name $svcName -ErrorAction SilentlyContinue
$agtSvc = Get-Service -Name $agtName -ErrorAction SilentlyContinue
Add-Check 'Services' 'SQL Server service running' `
$(if ($sqlSvc -and $sqlSvc.Status -eq 'Running') {'PASS'} else {'FAIL'}) `
$(if ($sqlSvc) { $sqlSvc.Status } else { "Service '$svcName' not found" })
Add-Check 'Services' 'SQL Agent service running' `
$(if ($agtSvc -and $agtSvc.Status -eq 'Running') {'PASS'} elseif ($agtSvc) {'WARN'} else {'WARN'}) `
$(if ($agtSvc) { $agtSvc.Status } else { "Service '$agtName' not found" })
Add-Check 'Services' 'SQL Agent startup type' `
$(if ($agtSvc -and $agtSvc.StartType -eq 'Automatic') {'PASS'} else {'WARN'}) `
$(if ($agtSvc) { $agtSvc.StartType } else { 'unknown' })
# ── Connectivity ──────────────────────────────────────────────────────────────
$connected = $false
try {
$versionRow = Invoke-Sqlcmd -ServerInstance $ServerInstance `
-Query "SELECT @@VERSION AS v, SERVERPROPERTY('ProductVersion') AS pv, SERVERPROPERTY('ProductLevel') AS pl" `
-QueryTimeout 15 -TrustServerCertificate -ErrorAction Stop
$connected = $true
Add-Check 'Connectivity' 'SQL Server connection' 'PASS' "v$($versionRow.pv) $($versionRow.pl)"
} catch {
Add-Check 'Connectivity' 'SQL Server connection' 'FAIL' $_.Exception.Message
}
if ($connected) {
# ── Configuration ─────────────────────────────────────────────────────────────
$configs = Invoke-Sqlcmd -ServerInstance $ServerInstance `
-Query "SELECT name, value_in_use FROM sys.configurations WHERE name IN ('max server memory (MB)','max degree of parallelism','cost threshold for parallelism','backup compression default','optimize for ad hoc workloads','remote admin connections')" `
-TrustServerCertificate -ErrorAction SilentlyContinue
$cfg = @{}
foreach ($row in $configs) { $cfg[$row.name] = [int]$row.value_in_use }
$maxMemMB = $cfg['max server memory (MB)']
if ($ExpectedMaxMemoryGB -gt 0) {
$expectedMB = $ExpectedMaxMemoryGB * 1024
Add-Check 'Config' 'Max server memory' `
$(if ($maxMemMB -eq $expectedMB) {'PASS'} else {'WARN'}) `
"$($maxMemMB)MB $(if ($maxMemMB -eq $expectedMB) {'matches expected'} else {"(expected $($expectedMB)MB)"})"
} else {
Add-Check 'Config' 'Max server memory' `
$(if ($maxMemMB -lt 2147483647) {'PASS'} else {'WARN'}) `
"$($maxMemMB)MB $(if ($maxMemMB -ge 2147483647) {'— not configured (SQL default = unlimited)'})"
}
$maxdop = $cfg['max degree of parallelism']
if ($ExpectedMaxDOP -gt 0) {
Add-Check 'Config' 'MaxDOP' `
$(if ($maxdop -eq $ExpectedMaxDOP) {'PASS'} else {'WARN'}) `
"$maxdop $(if ($maxdop -ne $ExpectedMaxDOP) {"(expected $ExpectedMaxDOP)"})"
} else {
Add-Check 'Config' 'MaxDOP' `
$(if ($maxdop -gt 0) {'PASS'} else {'WARN'}) `
$(if ($maxdop -eq 0) {'0 — not configured (unlimited parallelism)'} else { $maxdop })
}
$ctp = $cfg['cost threshold for parallelism']
Add-Check 'Config' 'Cost threshold for parallelism' `
$(if ($ctp -ge 25) {'PASS'} elseif ($ctp -ge 5) {'WARN'} else {'WARN'}) `
"$ctp $(if ($ctp -le 5) {'— SQL default; 25-50 recommended'})"
Add-Check 'Config' 'Backup compression' `
$(if ($cfg['backup compression default'] -eq 1) {'PASS'} else {'WARN'}) `
$(if ($cfg['backup compression default'] -eq 1) {'Enabled'} else {'Disabled — enable for smaller backups'})
Add-Check 'Config' 'Optimize for ad hoc workloads' `
$(if ($cfg['optimize for ad hoc workloads'] -eq 1) {'PASS'} else {'WARN'}) `
$(if ($cfg['optimize for ad hoc workloads'] -eq 1) {'Enabled'} else {'Disabled — enable to reduce plan cache bloat'})
# ── TempDB ────────────────────────────────────────────────────────────────────
$tempdbFiles = Invoke-Sqlcmd -ServerInstance $ServerInstance `
-Query "SELECT COUNT(*) AS n FROM tempdb.sys.database_files WHERE type = 0" `
-TrustServerCertificate -ErrorAction SilentlyContinue
$fileCount = [int]$tempdbFiles.n
$logicalCPUs = (Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue).NumberOfLogicalProcessors
$recommendedFiles = [math]::Min($logicalCPUs, 8)
Add-Check 'TempDB' 'TempDB data file count' `
$(if ($fileCount -ge $recommendedFiles) {'PASS'} elseif ($fileCount -ge 2) {'WARN'} else {'WARN'}) `
"$fileCount files (recommended: $recommendedFiles for this CPU count)"
# ── Network ───────────────────────────────────────────────────────────────────
$tcpCheck = Invoke-Sqlcmd -ServerInstance $ServerInstance `
-Query "SELECT local_tcp_port FROM sys.dm_exec_connections WHERE session_id = @@SPID" `
-TrustServerCertificate -ErrorAction SilentlyContinue
$tcpPort = $tcpCheck.local_tcp_port
Add-Check 'Network' 'TCP/IP connection' `
$(if ($tcpPort) {'PASS'} else {'WARN'}) `
$(if ($tcpPort) {"Port $tcpPort"} else {'Could not confirm TCP port'})
# ── Security ──────────────────────────────────────────────────────────────────
$saRow = Invoke-Sqlcmd -ServerInstance $ServerInstance `
-Query "SELECT is_disabled FROM sys.server_principals WHERE name = 'sa'" `
-TrustServerCertificate -ErrorAction SilentlyContinue
if ($saRow) {
Add-Check 'Security' 'SA account disabled' `
$(if ($saRow.is_disabled -eq 1) {'PASS'} else {'WARN'}) `
$(if ($saRow.is_disabled -eq 1) {'SA is disabled'} else {'SA is ENABLED — disable if not needed'})
}
$authMode = Invoke-Sqlcmd -ServerInstance $ServerInstance `
-Query "SELECT SERVERPROPERTY('IsIntegratedSecurityOnly') AS WinOnly" `
-TrustServerCertificate -ErrorAction SilentlyContinue
Add-Check 'Security' 'Authentication mode' 'PASS' `
$(if ($authMode.WinOnly -eq 1) {'Windows auth only'} else {'Mixed mode (Windows + SQL)'})
} else {
Write-Host " Cannot connect — skipping configuration, TempDB, network, and security checks." -ForegroundColor Yellow
}
# ── Summary ───────────────────────────────────────────────────────────────────
Write-Host ""
Write-Host (" " + [string]::new('=', 62)) -ForegroundColor DarkCyan
$summaryColor = if ($fail -gt 0) {'Red'} elseif ($warn -gt 0) {'Yellow'} else {'Green'}
$verdict = if ($fail -gt 0) {'ISSUES FOUND — review FAIL items'} `
elseif ($warn -gt 0) {'PASSED WITH WARNINGS — review WARN items'} `
else {'ALL CHECKS PASSED'}
Write-Host " $verdict" -ForegroundColor $summaryColor
Write-Host " PASS: $pass WARN: $warn FAIL: $fail" -ForegroundColor $summaryColor
Write-Host ""
Real Output — Run Against This Lab Machine
Post-Install Validation — .
--------------------------------------------------------------
[PASS] SQL Server service running Running
[WARN] SQL Agent service running Stopped
[WARN] SQL Agent startup type Manual
[PASS] SQL Server connection v17.0.4045.5 RTM
[PASS] Max server memory 1600MB
[PASS] MaxDOP 8
[WARN] Cost threshold for parallelism 5 — SQL default; 25-50 recommended
[WARN] Backup compression Disabled — enable for smaller backups
[WARN] Optimize for ad hoc workloads Disabled — enable to reduce plan cache bloat
[PASS] TempDB data file count 8 files (recommended: 8 for this CPU count)
[PASS] TCP/IP connection Port 1433
[WARN] SA account disabled SA is ENABLED — disable if not needed
[PASS] Authentication mode Mixed mode (Windows + SQL)
==============================================================
PASSED WITH WARNINGS — review WARN items
PASS: 7 WARN: 6 FAIL: 0
Six genuine WARN findings on a real, working instance, not a manufactured example. Every one of these is fixable with Configure SQL Server, which applies exactly this class of setting.
How To Run From The Repo
git clone https://github.com/peterwhyte-lgtm/dba-tools
cd dba-tools
.\Initialize-Environment.ps1
.\powershell\installation\pre-install-check.ps1
.\powershell\installation\post-install-validation.ps1
These scripts live in the repo at:
Understanding the Results
- A FAIL on
pre-install-check.ps1means don’t proceed yet, not “proceed carefully.” Disk space and existing-instance collisions are the two most likely to actually break a setup run partway through. - A WARN on
post-install-validation.ps1is a configuration choice, not a failure. SA enabled, for example, is fine on a locked-down lab box and a real problem on anything internet-facing, judge each WARN in context. - Re-run
post-install-validation.ps1periodically, not just once. Settings drift, someone re-enables SA for a one-off task and forgets to turn it back off, Agent gets stopped during troubleshooting and never restarted.
Best Practices
- Run
pre-install-check.ps1before every new install, even on a machine you think you know, disk space and pending reboots change silently. - Treat every FAIL from
pre-install-check.ps1as a blocker; treat WARNs as judgment calls to make deliberately, not skip past. - Run
post-install-validation.ps1right after setup finishes, then again a few weeks later once real workload has run against the instance. - Feed WARN findings from
post-install-validation.ps1straight into Configure SQL Server rather than fixing them by hand onesp_configureat a time.
Microsoft’s reference covers sp_configure, sys.dm_exec_connections and sys.configurations in full.
Related Scripts
You may also find these scripts useful:
- SQL Server Installation and Patching (hub)
- Install and Configure SQL Server
- Uninstall SQL Server
- Get Instance Configuration Snapshot
- DBA Scripts: Patch SQL Server
- DBA Scripts: The Complete Guide, the map across every script on this site
Frequently Asked Questions
Does pre-install-check.ps1 need to run on the target machine, or can it check remotely?
It reads local registry keys, drive free space, and OS info directly, so it needs to run on the machine that will host the new instance, not from a remote workstation.
Why does post-install-validation.ps1 flag SA being enabled as a WARN instead of a FAIL?
Because it’s a legitimate configuration in some environments (isolated lab boxes, certain legacy application requirements) and a real risk in others. The script surfaces it for a judgment call rather than assuming it’s always wrong.
Summary
Two read-only bookend checks, one before an install, one after. Run against a real, already-installed lab machine, pre-install-check.ps1 correctly flagged five genuine blockers (disk space, an existing instance, non-elevated shell) and post-install-validation.ps1 found six genuine configuration gaps on the working instance, every one of them fixable with the companion Configure SQL Server script.
Leave a Reply