Skip to content

Commit dfbf316

Browse files
Uwe Jankeclaude
andcommitted
fix: report consistency – progress, dark theme, auto-open HTML, deadlock fix
- Write-Progress added to Get-sqmServerHardwareReport (per computer) and Export-sqmDatabaseDocumentation (per database) - Export-sqmDatabaseDocumentation: _HtmlHead CSS converted from light (#f0f4f8) to dark navy theme matching AlwaysOnHealthReport (#0f172a) - Get-sqmServerUtilization: replaced VS Code grey theme with consistent dark navy theme; Invoke-sqmOpenReport parameter corrected (-HtmlFile) - HTML auto-open fixed: AlwaysOnHealthReport and LoginAudit now pass -HtmlFile (preferred) instead of -TxtFile to Invoke-sqmOpenReport - Export-sqmAlwaysOnConfiguration: replaced direct Start-Process $txtFile with Invoke-sqmOpenReport for consistency - Get-sqmDeadlockReport: removed ErrorAction and null SqlCredential from splatting hashtable – fixes "Server not found" connection error Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 797ff3f commit dfbf316

7 files changed

Lines changed: 75 additions & 95 deletions

Public/Export-sqmAlwaysOnConfiguration.ps1

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,7 @@ ORDER BY ag.name, adc.database_name
280280
# CSV-Datei
281281
$configRows | Export-Csv -Path $csvFile -Encoding UTF8 -NoTypeInformation -Force
282282

283-
# Oeffne TXT-Datei wenn nicht -NoOpen
284-
if (-not $NoOpen -and $txtFile)
285-
{
286-
Start-Process $txtFile
287-
}
283+
Invoke-sqmOpenReport -TxtFile $txtFile -NoOpen:$NoOpen
288284

289285
Invoke-sqmLogging -Message "[$instance] AlwaysOn-Konfiguration exportiert: $txtFile" -FunctionName $functionName -Level "INFO"
290286
}

Public/Export-sqmDatabaseDocumentation.ps1

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -172,45 +172,46 @@ function Export-sqmDatabaseDocumentation
172172
<title>$Title</title>
173173
<style>
174174
*{box-sizing:border-box;margin:0;padding:0}
175-
body{font-family:'Segoe UI',system-ui,sans-serif;background:#f0f4f8;color:#1e293b;font-size:13px}
176-
.page-header{background:#1a3a5c;color:#fff;padding:24px 32px}
177-
.page-header h1{font-size:1.5rem;font-weight:900;letter-spacing:-.03em}
178-
.page-header .sub{font-size:.82rem;opacity:.7;margin-top:4px}
179-
.toc{background:#fff;border-bottom:1px solid #dde4ee;padding:10px 32px;display:flex;gap:6px;flex-wrap:wrap}
180-
.toc a{font-size:.75rem;color:#2563a8;text-decoration:none;background:#eff6ff;border-radius:12px;padding:2px 10px}
181-
.toc a:hover{background:#dbeafe}
182-
.main{max-width:1200px;margin:0 auto;padding:28px 24px}
183-
.db-section{background:#fff;border:1px solid #dde4ee;border-radius:10px;margin-bottom:28px;overflow:hidden}
184-
.db-header{background:#1a3a5c;color:#fff;padding:14px 20px;display:flex;align-items:center;justify-content:space-between}
185-
.db-header h2{font-size:1rem;font-weight:800;letter-spacing:-.02em}
186-
.db-header .db-status{font-size:.7rem;font-weight:700;padding:2px 8px;border-radius:10px;background:rgba(255,255,255,.15)}
187-
.db-header .db-status.ok{background:#d1fae5;color:#064e3b}
188-
.db-header .db-status.warn{background:#fef3c7;color:#92400e}
189-
.db-header .db-status.crit{background:#fde8e8;color:#991b1b}
175+
body{font-family:'Segoe UI',system-ui,sans-serif;background:#0f172a;color:#e2e8f0;font-size:13px}
176+
.page-header{background:linear-gradient(160deg,#1e3a8a 0%,#2e5090 100%);padding:30px 32px;border-left:4px solid #3b82f6;margin-bottom:24px}
177+
.page-header h1{font-size:1.5rem;font-weight:900;color:#60a5fa;letter-spacing:-.03em}
178+
.page-header .sub{font-size:.82rem;color:#94a3b8;margin-top:6px}
179+
.toc{background:#0b1e3d;border:1px solid #1e3a5f;border-radius:8px;padding:12px 20px;margin:0 24px 24px;display:flex;gap:6px;flex-wrap:wrap}
180+
.toc a{font-size:.75rem;color:#7dd3fc;text-decoration:none;background:#1e3a5f;border-radius:12px;padding:2px 10px}
181+
.toc a:hover{background:#2d5a8c;color:#bae6fd}
182+
.main{max-width:1400px;margin:0 auto;padding:0 24px 40px}
183+
.db-section{background:#0b1e3d;border:1px solid #1e3a5f;border-radius:10px;margin-bottom:24px;overflow:hidden}
184+
.db-header{background:#1e3a8a;padding:14px 20px;display:flex;align-items:center;justify-content:space-between;border-bottom:2px solid #3b82f6}
185+
.db-header h2{font-size:1rem;font-weight:800;color:#60a5fa;letter-spacing:-.02em}
186+
.db-header .db-status{font-size:.7rem;font-weight:700;padding:2px 10px;border-radius:10px}
187+
.db-header .db-status.ok{background:rgba(52,211,153,.15);color:#34d399;border:1px solid #34d399}
188+
.db-header .db-status.warn{background:rgba(245,158,11,.15);color:#f59e0b;border:1px solid #f59e0b}
189+
.db-header .db-status.crit{background:rgba(239,68,68,.15);color:#ef4444;border:1px solid #ef4444}
190190
.db-body{padding:16px 20px}
191191
.section-block{margin-bottom:18px}
192-
.section-block h3{font-size:.78rem;font-weight:700;color:#2563a8;text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid #e2e8f0;padding-bottom:5px;margin-bottom:10px}
193-
.props-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:6px 16px}
192+
.section-block h3{font-size:.78rem;font-weight:700;color:#60a5fa;text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid #1e3a5f;padding-bottom:5px;margin-bottom:10px}
193+
.props-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px 16px}
194194
.prop{display:flex;flex-direction:column}
195195
.prop .lbl{font-size:.68rem;color:#64748b;font-weight:600;text-transform:uppercase;letter-spacing:.05em}
196-
.prop .val{font-size:.82rem;color:#1e293b;font-weight:500;margin-top:1px}
197-
.prop .val.warn{color:#92400e;font-weight:700}
198-
.prop .val.ok{color:#064e3b}
199-
table{width:100%;border-collapse:collapse;font-size:.78rem}
200-
table th{background:#f0f4f8;color:#1e293b;padding:5px 10px;text-align:left;font-weight:700;font-size:.72rem;text-transform:uppercase;letter-spacing:.04em;border-bottom:2px solid #dde4ee}
201-
table td{padding:5px 10px;border-bottom:1px solid #e8edf4;vertical-align:top}
202-
table tr:hover td{background:#f8fafc}
196+
.prop .val{font-size:.82rem;color:#e2e8f0;font-weight:500;margin-top:1px}
197+
.prop .val.warn{color:#f59e0b;font-weight:700}
198+
.prop .val.ok{color:#34d399}
199+
table{width:100%;border-collapse:collapse;font-size:.78rem;background:#071428;border-radius:6px;overflow:hidden}
200+
table th{background:#1e3a8a;color:#60a5fa;padding:7px 10px;text-align:left;font-weight:700;font-size:.72rem;text-transform:uppercase;letter-spacing:.04em;border-bottom:2px solid #3b82f6}
201+
table td{padding:6px 10px;border-bottom:1px solid #1e3a5f;color:#e2e8f0;vertical-align:top}
202+
table tr:hover td{background:#0f2744}
203203
.badge{display:inline-block;font-size:.65rem;font-weight:700;padding:1px 7px;border-radius:9px;text-transform:uppercase;letter-spacing:.04em}
204-
.badge-full{background:#d1fae5;color:#064e3b}
205-
.badge-simple{background:#f3e8ff;color:#6b21a8}
206-
.badge-bulk{background:#fef3c7;color:#92400e}
207-
.footer{text-align:center;font-size:.72rem;color:#94a3b8;padding:20px;border-top:1px solid #dde4ee}
204+
.badge-full{background:rgba(52,211,153,.15);color:#34d399;border:1px solid #34d399}
205+
.badge-simple{background:rgba(167,139,250,.15);color:#a78bfa;border:1px solid #a78bfa}
206+
.badge-bulk{background:rgba(245,158,11,.15);color:#f59e0b;border:1px solid #f59e0b}
207+
.footer{text-align:center;font-size:.72rem;color:#475569;padding:24px;border-top:1px solid #1e3a5f;margin-top:16px}
208+
.footer a{color:#5dade2}
208209
</style>
209210
</head>
210211
<body>
211212
<div class="page-header">
212213
<h1>&#128202; Datenbank-Dokumentation</h1>
213-
<div class="sub">Instanz: $Instance &nbsp;?&nbsp; Erstellt: $Timestamp</div>
214+
<div class="sub">Instanz: $Instance &nbsp;&bull;&nbsp; Erstellt: $Timestamp</div>
214215
</div>
215216
"@
216217
}
@@ -367,11 +368,19 @@ GROUP BY s.database_id;
367368

368369
# CSV-Zeilen-Liste
369370
$csvRows = [System.Collections.Generic.List[PSCustomObject]]::new()
370-
371+
371372
# ?? Pro Datenbank ????????????????????????????????????????????????
372-
foreach ($db in ($filteredDbs | Sort-Object Name))
373+
$sortedDbs = @($filteredDbs | Sort-Object Name)
374+
$totalDbs = $sortedDbs.Count
375+
$dbIndex = 0
376+
377+
foreach ($db in $sortedDbs)
373378
{
379+
$dbIndex++
374380
$dbName = $db.Name
381+
Write-Progress -Activity "Export-sqmDatabaseDocumentation [$instance]" `
382+
-Status "[$dbIndex/$totalDbs] $dbName" `
383+
-PercentComplete ([math]::Round($dbIndex / $totalDbs * 100))
375384
Invoke-sqmLogging -Message "[$instance] Dokumentiere Datenbank '$dbName' ..." -FunctionName $functionName -Level "INFO"
376385

377386
try

Public/Get-sqmAlwaysOnHealthReport.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ ORDER BY has.start_time;
381381
$htmlFile = $null
382382
}
383383

384-
Invoke-sqmOpenReport -TxtFile $txtFile -NoOpen:$NoOpen
384+
Invoke-sqmOpenReport -HtmlFile $htmlFile -TxtFile $txtFile -NoOpen:$NoOpen
385385

386386
Invoke-sqmLogging -Message "[$instance] Health-Bericht erstellt: $txtFile" -FunctionName $functionName -Level "INFO"
387387
}

Public/Get-sqmDeadlockReport.ps1

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,8 @@ function Get-sqmDeadlockReport
107107
{
108108
try
109109
{
110-
$connParams = @{
111-
SqlInstance = $SqlInstance
112-
SqlCredential = $SqlCredential
113-
Database = 'master'
114-
ErrorAction = 'Stop'
115-
}
110+
$connParams = @{ SqlInstance = $SqlInstance }
111+
if ($SqlCredential) { $connParams['SqlCredential'] = $SqlCredential }
116112

117113
# -----------------------------------------------------------------------
118114
# Deadlock-XML aus System Health Ring Buffer lesen
@@ -135,7 +131,7 @@ WHERE xdr.value('@timestamp', 'datetime2') >= '$($StartTime.ToString('yyyy-MM-dd
135131
ORDER BY EventTime DESC
136132
"@
137133

138-
$rawDeadlocks = Invoke-DbaQuery @connParams -Query $ringBufferQuery
134+
$rawDeadlocks = Invoke-DbaQuery @connParams -Database master -Query $ringBufferQuery -ErrorAction Stop
139135

140136
Invoke-sqmLogging -Message "$(@($rawDeadlocks).Count) Deadlock-Ereignis(se) im Ring Buffer gefunden." -FunctionName $functionName -Level "INFO"
141137

Public/Get-sqmServerHardwareReport.ps1

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,18 @@ function Get-sqmServerHardwareReport
122122

123123
process
124124
{
125+
$totalComputers = $ComputerName.Count
126+
$computerIndex = 0
127+
125128
foreach ($computer in $ComputerName)
126129
{
130+
$computerIndex++
127131
if ([string]::IsNullOrWhiteSpace($computer)) { $computer = $env:COMPUTERNAME }
128132

133+
Write-Progress -Activity 'Get-sqmServerHardwareReport' `
134+
-Status "[$computerIndex/$totalComputers] $computer – Sammle Hardware-Daten..." `
135+
-PercentComplete ([math]::Round($computerIndex / $totalComputers * 100))
136+
129137
try
130138
{
131139
Write-Host " [$computer] Sammle Hardware-Daten..." -ForegroundColor Gray

Public/Get-sqmServerUtilization.ps1

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ FROM sys.dm_exec_query_stats;
250250
# Open report
251251
if (-not $NoOpen)
252252
{
253-
Invoke-sqmOpenReport -ReportPath $htmlPath
253+
Invoke-sqmOpenReport -HtmlFile $htmlPath -NoOpen:$NoOpen
254254
}
255255

256256
Write-Verbose "Utilization data collection complete. Reports saved to: $OutputPath"
@@ -359,58 +359,28 @@ function Generate-UtilizationHtmlReport
359359
<meta charset='utf-8'>
360360
<title>SQL Server Utilization Report</title>
361361
<style>
362-
body {
363-
background-color: #1e1e1e;
364-
color: #d4d4d4;
365-
font-family: 'Consolas', 'Courier New', monospace;
366-
margin: 20px;
367-
}
368-
h1, h2 {
369-
color: #00c8e8;
370-
border-bottom: 2px solid #00c8e8;
371-
padding-bottom: 10px;
372-
}
373-
table {
374-
border-collapse: collapse;
375-
width: 100%;
376-
margin: 20px 0;
377-
background-color: #252526;
378-
}
379-
th {
380-
background-color: #00aae8;
381-
color: #1e1e1e;
382-
padding: 12px;
383-
text-align: left;
384-
font-weight: bold;
385-
}
386-
td {
387-
border: 1px solid #3e3e3e;
388-
padding: 10px;
389-
text-align: left;
390-
}
391-
tr:hover {
392-
background-color: #2d2d30;
393-
}
394-
.summary-table td {
395-
border: 1px solid #3e3e3e;
396-
}
397-
.summary-table td:first-child {
398-
font-weight: bold;
399-
width: 25%;
400-
}
401-
.footer {
402-
margin-top: 40px;
403-
padding-top: 20px;
404-
border-top: 1px solid #3e3e3e;
405-
font-size: 0.9em;
406-
color: #808080;
407-
}
362+
*{box-sizing:border-box;margin:0;padding:0}
363+
body{font-family:'Segoe UI',Arial,sans-serif;background:#0f172a;color:#e2e8f0;font-size:13px}
364+
.page-header{background:linear-gradient(160deg,#1e3a8a 0%,#2e5090 100%);padding:28px 32px;border-left:4px solid #3b82f6;margin-bottom:24px}
365+
.page-header h1{font-size:1.4rem;font-weight:700;color:#60a5fa;margin:0 0 4px 0}
366+
.page-header .sub{font-size:.82rem;color:#94a3b8}
367+
.wrap{padding:0 24px 40px}
368+
h2{font-size:1rem;font-weight:600;color:#60a5fa;margin:28px 0 12px;border-bottom:1px solid #1e3a5f;padding-bottom:6px}
369+
table{width:100%;border-collapse:collapse;background:#0b1e3d;border-radius:6px;overflow:hidden;margin-bottom:20px}
370+
th{background:#1e3a8a;color:#60a5fa;padding:9px 12px;text-align:left;font-weight:600;font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;border-bottom:2px solid #3b82f6}
371+
td{padding:8px 12px;border-bottom:1px solid #1e3a5f;color:#e2e8f0;vertical-align:top}
372+
tr:hover td{background:#0f2744}
373+
.summary-table td:first-child{color:#94a3b8;font-weight:600;width:28%}
374+
.footer{margin-top:32px;padding-top:16px;border-top:1px solid #1e3a5f;font-size:.75rem;color:#475569;text-align:center}
375+
.footer a{color:#5dade2;text-decoration:none}
408376
</style>
409377
</head>
410378
<body>
411-
<h1>SQL Server Utilization Report</h1>
412-
<h2>Instance: $SqlInstance</h2>
413-
<p>Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')</p>
379+
<div class='page-header'>
380+
<h1>SQL Server Utilization Report</h1>
381+
<div class='sub'>Instance: $SqlInstance &bull; Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')</div>
382+
</div>
383+
<div class='wrap'>
414384
415385
<h2>Summary Metrics</h2>
416386
<table class='summary-table'>
@@ -451,6 +421,7 @@ function Generate-UtilizationHtmlReport
451421
<p>$reference</p>
452422
<p>Sampling Period: $($Agg.StartTime.ToString('yyyy-MM-dd HH:mm:ss'))$($Agg.EndTime.ToString('HH:mm:ss')) ($duration sec, $($Agg.SampleCount) samples)</p>
453423
</div>
424+
</div>
454425
</body>
455426
</html>
456427
"@

Public/Invoke-sqmLoginAudit.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ WHERE sp.type IN ('S','U','G')
571571

572572
Copy-sqmToCentralPath -Path $txtFile, $csvFile, $htmlFile
573573

574-
Invoke-sqmOpenReport -TxtFile $txtFile -NoOpen:$NoOpen
574+
Invoke-sqmOpenReport -HtmlFile $htmlFile -TxtFile $txtFile -NoOpen:$NoOpen
575575

576576
Invoke-sqmLogging -Message "[$instance] Login-Audit-Bericht erstellt: $txtFile" -FunctionName $functionName -Level "INFO"
577577
}

0 commit comments

Comments
 (0)