$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')"