Files
claudetools/clients/instrumental-music-center/scripts/imc1-sqlexpress-enum.ps1
Howard Enos f8c6b4b9ca sync: auto-sync from HOWARD-HOME at 2026-05-06 13:46:20
Author: Howard Enos
Machine: HOWARD-HOME
Timestamp: 2026-05-06 13:46:20
2026-05-06 13:46:23 -07:00

202 lines
11 KiB
PowerShell

$ErrorActionPreference = 'Continue'
$ProgressPreference = 'SilentlyContinue'
function Section($name) { "`n===== $name =====" }
Section 'STEP 1: Service + process view'
Get-Service 'MSSQL$SQLEXPRESS','SQLAgent$SQLEXPRESS','SQLBrowser','SQLWriter' -ErrorAction SilentlyContinue | Format-Table Name,Status,StartType -AutoSize
$svcCim = Get-CimInstance Win32_Service -Filter "Name='MSSQL$SQLEXPRESS'" -ErrorAction SilentlyContinue
$sqlexp_pid = $null
if ($svcCim) { $sqlexp_pid = [int]$svcCim.ProcessId }
"MSSQL`$SQLEXPRESS PID (live from CIM): $sqlexp_pid"
if ($sqlexp_pid) {
Get-Process -Id $sqlexp_pid -ErrorAction SilentlyContinue | Select-Object Id,StartTime,@{n='WSMB';e={[math]::Round($_.WorkingSet64/1MB,1)}},@{n='VMMB';e={[math]::Round($_.VirtualMemorySize64/1MB,1)}},@{n='PrivateMB';e={[math]::Round($_.PrivateMemorySize64/1MB,1)}},Handles,@{n='Threads';e={$_.Threads.Count}},CPU | Format-List
} else {
"No live PID for MSSQL`$SQLEXPRESS"
}
Section 'STEP 2: Registry — installed SQL bits'
Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' -ErrorAction SilentlyContinue | Select-Object * -ExcludeProperty PS* | Format-List
"--- Per-instance Setup keys ---"
Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\*\Setup' -ErrorAction SilentlyContinue |
Select-Object PSChildName, Edition, Version, PatchLevel, SqlProgramDir, SqlDataRoot, SQLBinRoot |
Format-Table -AutoSize -Wrap
Section 'STEP 3: Locate sqlcmd'
$sqlcmd = (Get-Command sqlcmd -ErrorAction SilentlyContinue).Source
if (-not $sqlcmd) {
foreach ($p in @(
'C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\sqlcmd.exe',
'C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\sqlcmd.exe',
'C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\sqlcmd.exe',
'C:\Program Files\Microsoft SQL Server\150\Tools\Binn\SQLCMD.EXE',
'C:\Program Files\Microsoft SQL Server\140\Tools\Binn\SQLCMD.EXE',
'C:\Program Files\Microsoft SQL Server\130\Tools\Binn\SQLCMD.EXE'
)) {
if (Test-Path $p) { $sqlcmd = $p; break }
}
}
"sqlcmd: $sqlcmd"
Section 'STEP 3a: @@VERSION / SERVERPROPERTY (Windows auth via .\SQLEXPRESS)'
$loginOk = $false
if ($sqlcmd) {
$verOut = & $sqlcmd -S '.\SQLEXPRESS' -E -d master -h-1 -W -l 5 -Q "SET NOCOUNT ON; SELECT @@VERSION; SELECT CAST(@@SERVERNAME AS varchar(200)) + '|' + CAST(SERVERPROPERTY('InstanceName') AS varchar(200)) + '|' + CAST(SERVERPROPERTY('Edition') AS varchar(200)) + '|' + CAST(SERVERPROPERTY('ProductVersion') AS varchar(200)) + '|' + CAST(SERVERPROPERTY('Collation') AS varchar(200));" 2>&1
$verOut
if ($LASTEXITCODE -eq 0 -and ($verOut -join "`n") -notmatch 'Login failed') { $loginOk = $true }
}
"loginOk=$loginOk"
Section 'STEP 3b: ERRORLOG location + tail (always run)'
$el = Get-ChildItem -Path 'C:\Program Files\Microsoft SQL Server\','C:\Program Files (x86)\Microsoft SQL Server\','S:\','D:\','E:\' -Recurse -Filter 'ERRORLOG' -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -like '*SQLEXPRESS*MSSQL\Log\ERRORLOG' -or $_.FullName -like '*MSSQL*SQLEXPRESS*Log*ERRORLOG*' }
$el | Select-Object FullName,LastWriteTime,Length | Format-Table -AutoSize
# Pick most-recently-written one for the tail
$primary = $el | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($primary) {
"--- ERRORLOG path: $($primary.FullName) (LastWriteTime: $($primary.LastWriteTime)) ---"
"--- Tail 400 lines ---"
Get-Content $primary.FullName -Tail 400 -ErrorAction SilentlyContinue
} else {
"No SQLEXPRESS ERRORLOG located"
}
Section 'STEP 4: sys.databases + sys.master_files (if loginOk)'
if ($loginOk -and $sqlcmd) {
& $sqlcmd -S '.\SQLEXPRESS' -E -d master -h-1 -W -l 5 -Q "SET NOCOUNT ON; SELECT CAST(name AS varchar(80)) + '|' + CAST(database_id AS varchar(10)) + '|' + state_desc + '|' + recovery_model_desc + '|' + CAST(create_date AS varchar(30)) + '|' + CAST(compatibility_level AS varchar(10)) FROM sys.databases ORDER BY database_id;" 2>&1
"--- master_files (user DBs only) ---"
& $sqlcmd -S '.\SQLEXPRESS' -E -d master -h-1 -W -l 5 -Q "SET NOCOUNT ON; SELECT CAST(DB_NAME(database_id) AS varchar(80)) + '|' + type_desc + '|' + CAST(name AS varchar(80)) + '|' + CAST(physical_name AS varchar(260)) + '|' + CAST(size*8/1024 AS varchar(20)) + 'MB' FROM sys.master_files WHERE database_id > 4 ORDER BY database_id, type;" 2>&1
} else {
"Skipped (login failed) — will use file-system fallback below"
}
Section 'STEP 4b: File-system DB enumeration (fallback / cross-check)'
# Pull SQLDataRoot from registry for SQLEXPRESS
$sqlexpReg = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\*\Setup' -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -match '^MSSQL\d+\.' } |
ForEach-Object {
$instKey = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$($_.PSChildName)"
$instName = (Get-ItemProperty -Path $instKey -ErrorAction SilentlyContinue).Name
[PSCustomObject]@{ Folder=$_.PSChildName; InstanceName=$instName; SqlDataRoot=$_.SqlDataRoot; SQLBinRoot=$_.SQLBinRoot }
}
$sqlexpReg | Format-Table -AutoSize -Wrap
$dataDirs = @()
$dataDirs += $sqlexpReg | Where-Object { $_.SqlDataRoot } | ForEach-Object { Join-Path $_.SqlDataRoot 'MSSQL\DATA' }
$dataDirs += 'C:\Program Files\Microsoft SQL Server\MSSQL*.SQLEXPRESS\MSSQL\DATA'
$dataDirs += 'C:\Program Files (x86)\Microsoft SQL Server\MSSQL*.SQLEXPRESS\MSSQL\DATA'
$dataDirs = $dataDirs | Where-Object { $_ } | Select-Object -Unique
"--- DATA dir candidates ---"
$dataDirs
"--- .mdf / .ldf / .ndf under SQLEXPRESS DATA dirs ---"
foreach ($dir in $dataDirs) {
Get-ChildItem -Path $dir -Include *.mdf,*.ldf,*.ndf -Recurse -ErrorAction SilentlyContinue |
Select-Object FullName,@{n='SizeMB';e={[math]::Round($_.Length/1MB,1)}},LastWriteTime |
Format-Table -AutoSize
}
Section 'STEP 5: Active sessions (if loginOk)'
if ($loginOk -and $sqlcmd) {
& $sqlcmd -S '.\SQLEXPRESS' -E -d master -h-1 -W -l 5 -Q "SET NOCOUNT ON; SELECT CAST(s.session_id AS varchar(10)) + '|' + ISNULL(CAST(s.login_name AS varchar(80)),'-') + '|' + ISNULL(CAST(s.host_name AS varchar(80)),'-') + '|' + ISNULL(CAST(s.program_name AS varchar(120)),'-') + '|' + ISNULL(CAST(s.client_interface_name AS varchar(60)),'-') + '|' + CAST(s.login_time AS varchar(30)) + '|' + CAST(s.last_request_end_time AS varchar(30)) + '|' + ISNULL(CAST(s.status AS varchar(20)),'-') + '|' + ISNULL(CAST(c.client_net_address AS varchar(50)),'-') + '|' + ISNULL(CAST(DB_NAME(s.database_id) AS varchar(80)),'-') FROM sys.dm_exec_sessions s LEFT JOIN sys.dm_exec_connections c ON s.session_id=c.session_id WHERE s.is_user_process=1 ORDER BY s.login_time;" 2>&1
} else {
"Skipped (login failed) — TCP step below provides remote IP evidence"
}
Section 'STEP 6: TCP — listening port + established connections for SQLEXPRESS PID'
if ($sqlexp_pid) {
"--- State summary ---"
Get-NetTCPConnection -OwningProcess $sqlexp_pid -ErrorAction SilentlyContinue | Group-Object State | Select-Object Name,Count | Format-Table -AutoSize
"--- Listening sockets ---"
Get-NetTCPConnection -OwningProcess $sqlexp_pid -State Listen -ErrorAction SilentlyContinue |
Select-Object LocalAddress,LocalPort | Format-Table -AutoSize
"--- Established connections (remote talking to SQLEXPRESS) ---"
$est = Get-NetTCPConnection -OwningProcess $sqlexp_pid -State Established -ErrorAction SilentlyContinue
if ($est) {
$est | Select-Object RemoteAddress,RemotePort,LocalAddress,LocalPort,CreationTime | Sort-Object RemoteAddress | Format-Table -AutoSize
"--- Reverse-resolve unique remote IPs ---"
$est.RemoteAddress | Sort-Object -Unique | ForEach-Object {
$ip = $_
$name = $null
try { $name = [System.Net.Dns]::GetHostEntry($ip).HostName } catch {}
[PSCustomObject]@{ RemoteIP=$ip; HostName=$name }
} | Format-Table -AutoSize
} else {
"(no established connections to SQLEXPRESS right now)"
}
"--- UDP (SQL Browser / instance discovery) for this PID ---"
Get-NetUDPEndpoint -OwningProcess $sqlexp_pid -ErrorAction SilentlyContinue |
Select-Object LocalAddress,LocalPort | Format-Table -AutoSize
} else {
"No PID — skipping TCP enumeration"
}
Section 'STEP 7: Installed apps (filtered)'
$apps = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*, HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName } |
Select-Object DisplayName, Publisher, InstallDate, DisplayVersion |
Sort-Object DisplayName -Unique
"--- Likely SQL-Express-using apps (AIM, Tri-Tech, MSP360, Cloudberry, Syncro, QuickBooks, Sage, Peachtree, Veeam, Backup, Music, POS, Retail, ScreenConnect) ---"
$apps | Where-Object { $_.DisplayName -match 'AIM|Tri-Tech|TriTech|MSP360|Cloudberry|Syncro|QuickBooks|Sage|Peachtree|Veeam|Backup|Music|POS|Retail|ScreenConnect|ConnectWise|Datto|N-able|Acronis|StorageCraft|ShadowProtect|Mozy|Carbonite|WSUS|RDS|Reporting Services|SSRS|Telerik|OpenAccess|Crystal' } |
Format-Table -AutoSize -Wrap
"--- Full SQL-related installs ---"
$apps | Where-Object { $_.DisplayName -match 'SQL|Express|Database' } | Format-Table -AutoSize -Wrap
"--- TOTAL apps installed: $($apps.Count) ---"
Section 'STEP 8: Connection-string grep — find apps with SQLEXPRESS in their config'
$paths = @('C:\ProgramData','C:\Program Files\','C:\Program Files (x86)\','C:\inetpub')
$hits = @()
foreach ($root in $paths) {
if (-not (Test-Path $root)) { continue }
try {
$files = Get-ChildItem -Path $root -Recurse -Include *.config,*.ini,*.xml,*.json,*.udl -ErrorAction SilentlyContinue -Force -File
foreach ($f in $files) {
try {
$m = Select-String -Path $f.FullName -Pattern 'SQLEXPRESS' -List -ErrorAction SilentlyContinue
if ($m) {
$hits += [PSCustomObject]@{ Path = $f.FullName; LastWriteTime = $f.LastWriteTime }
}
} catch {}
if ($hits.Count -ge 60) { break }
}
} catch {}
if ($hits.Count -ge 60) { break }
}
"--- Files referencing SQLEXPRESS (first 60) ---"
$hits | Format-Table -AutoSize -Wrap
# For top hits, show the actual matching line
"--- Sample matching lines (first 25 hits) ---"
foreach ($h in ($hits | Select-Object -First 25)) {
try {
$line = (Select-String -Path $h.Path -Pattern 'SQLEXPRESS' -ErrorAction SilentlyContinue | Select-Object -First 1).Line
if ($line) {
$line = $line.Trim()
if ($line.Length -gt 240) { $line = $line.Substring(0,240) + '...' }
"$($h.Path) :: $line"
}
} catch {}
}
Section 'STEP 9: Memory cap (read-only — sp_configure show only, NO RECONFIGURE)'
if ($loginOk -and $sqlcmd) {
& $sqlcmd -S '.\SQLEXPRESS' -E -d master -h-1 -W -l 5 -Q "SET NOCOUNT ON; SELECT CAST(name AS varchar(60)) + '|' + CAST(value AS varchar(20)) + '|' + CAST(value_in_use AS varchar(20)) + '|' + CAST(minimum AS varchar(20)) + '|' + CAST(maximum AS varchar(20)) FROM sys.configurations WHERE name IN ('max server memory (MB)','min server memory (MB)','show advanced options') ORDER BY name;" 2>&1
} else {
"Skipped (login failed) — sys.configurations needs auth"
}
Section 'DONE'
"Completed at: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss zzz')"