Skip to content

Commit 2a02d7c

Browse files
Uwe Jankeclaude
andcommitted
Fix dbatools parameter/cmdlet drift across 9 functions (1.6.3.0)
Found via a static dbatools-parameter audit over all functions, validated against a local SQL 2022. Mechanical parameter fixes: - Invoke-sqmRestoreDatabase: Get-DbaDefaultPath -Type Backup -> (Get-DbaDefaultPath).Backup - Test-sqmBackupIntegrity: Restore-DbaDatabase -FileListOnly -> Read-DbaBackupHeader -FileList - New-sqm{BackupMaintenance,OlaMaintenance,OlaSysDbBackup,OlaUsrDbBackup}Job: Set-DbaAgentJob -OperatorToEmail -> -EmailOperator - Invoke-sqmDeployScripts: Connect-DbaInstance -EnableException -> -ErrorAction Stop Redesigns (cmdlet does not exist in dbatools): - Invoke-sqmUpdateStatistics: Update-DbaDbStatistic does not exist; reimplemented via Invoke-DbaQuery with real UPDATE STATISTICS, target stats resolved from sys.stats/sys.dm_db_stats_properties so -OnlyModified/-Index/-Table/-Statistics/-SamplePercent work. Verified live (3 stats updated). - Invoke-sqmConfigRollback: Set-DbaService -StartMode does not exist; Get-DbaService returns SqlService CIM instances, so start mode is set via the SetStartMode(UInt32) CIM method (Auto=2/Manual=3/Disabled=4), working on PS 5.1 and 7. - Sync-sqmLoginsToAlwaysOn: Get-DbaAgentServiceAccount does not exist; agent service account now read from sys.dm_server_services (locale-robust LIKE '%Agent%') over the existing SQL connection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a438244 commit 2a02d7c

12 files changed

Lines changed: 140 additions & 43 deletions

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,31 @@
11
# sqmSQLTool — Changelog
22

3+
## [1.6.3.0] — 2026-06-22
4+
5+
### 🔧 Fixes — dbatools-Parameter-/Cmdlet-Drift (gefunden per statischem Audit gegen dbatools 2.8.1, validiert gegen lokalen SQL 2022)
6+
7+
Mechanische Parameter-Korrekturen:
8+
- **Invoke-sqmRestoreDatabase**: `Get-DbaDefaultPath -Type Backup``(Get-DbaDefaultPath …).Backup`
9+
(`-Type` existiert nicht; betraf den `-BackupBeforeRestore`-Pfad).
10+
- **Test-sqmBackupIntegrity**: `Restore-DbaDatabase -FileListOnly``Read-DbaBackupHeader -FileList`
11+
(Restore-DbaDatabase kennt kein `-FileListOnly`; der Verify-Pfad nutzte bereits korrekt `-VerifyOnly`).
12+
- **New-sqmBackupMaintenanceJob / New-sqmOlaMaintenanceJobs / New-sqmOlaSysDbBackupJob /
13+
New-sqmOlaUsrDbBackupJob**: `Set-DbaAgentJob -OperatorToEmail``-EmailOperator`.
14+
- **Invoke-sqmDeployScripts**: `Connect-DbaInstance -EnableException``-ErrorAction Stop`
15+
(Connect-DbaInstance hat kein `-EnableException`).
16+
17+
Redesigns (Cmdlet existiert gar nicht):
18+
- **Invoke-sqmUpdateStatistics**: nutzte `Update-DbaDbStatistic` — diesen Cmdlet gibt es nicht, die
19+
Funktion war wirkungslos. Neu implementiert über `Invoke-DbaQuery` mit echtem `UPDATE STATISTICS`;
20+
Zielstatistiken werden serverseitig aus `sys.stats`/`sys.dm_db_stats_properties` ermittelt, sodass
21+
`-OnlyModified`, `-Index`, `-Table`, `-Statistics` und `-SamplePercent` (FULLSCAN/SAMPLE) greifen.
22+
- **Invoke-sqmConfigRollback**: `Set-DbaService -StartMode` existiert nicht. dbatools' `Get-DbaService`
23+
liefert CIM-Instanzen der Klasse `SqlService`; der StartMode wird jetzt über deren CIM-Methode
24+
`SetStartMode(UInt32)` gesetzt (Automatic=2, Manual=3, Disabled=4). Funktioniert unter PS 5.1 und 7.
25+
- **Sync-sqmLoginsToAlwaysOn**: `Get-DbaAgentServiceAccount` existiert nicht. Das Agent-Dienstkonto
26+
kommt jetzt aus `sys.dm_server_services` (locale-robustes `LIKE '%Agent%'`) über die bestehende
27+
SQL-Verbindung.
28+
329
## [1.6.2.0] — 2026-06-22
430

531
### 🔧 Fixes

Public/Invoke-sqmConfigRollback.ps1

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,15 @@ function Invoke-sqmConfigRollback
438438
{
439439
try
440440
{
441-
Set-DbaService -ComputerName $serverName -ServiceName $svcName `
442-
-StartMode $svcSnapshot.StartMode -ErrorAction Stop
441+
$svcObj = $currentServices | Where-Object { $_.ServiceName -eq $svcName } | Select-Object -First 1
442+
if (-not $svcObj) { throw "Service-Objekt '$svcName' nicht verfuegbar (Get-DbaService lieferte es nicht)." }
443+
# dbatools' Get-DbaService liefert CIM-Instanzen der Klasse SqlService (kein Set-DbaService-Cmdlet).
444+
# Den StartMode aendert deren CIM-Methode SetStartMode(UInt32). Mapping: Automatic=2, Manual=3, Disabled=4.
445+
$startModeMap = @{ 'Auto' = [uint32]2; 'Automatic' = [uint32]2; 'Manual' = [uint32]3; 'Disabled' = [uint32]4 }
446+
$desiredMode = "$($svcSnapshot.StartMode)".Trim()
447+
if (-not $startModeMap.ContainsKey($desiredMode)) { throw "Unbekannter StartMode '$desiredMode'." }
448+
$cimRet = Invoke-CimMethod -InputObject $svcObj -MethodName SetStartMode -Arguments @{ StartMode = $startModeMap[$desiredMode] } -ErrorAction Stop
449+
if ($cimRet.ReturnValue -ne 0) { throw "SetStartMode fuer '$svcName' lieferte ReturnValue $($cimRet.ReturnValue)." }
443450
Invoke-sqmLogging -Message "Service $svcName StartMode geaendert: $svcCurrentMode -> $($svcSnapshot.StartMode)" `
444451
-FunctionName $functionName -Level "INFO"
445452

Public/Invoke-sqmDeployScripts.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ function Invoke-sqmDeployScripts
377377

378378
try
379379
{
380-
$server = Connect-DbaInstance @connParams -EnableException
380+
$server = Connect-DbaInstance @connParams -ErrorAction Stop
381381
}
382382
catch
383383
{

Public/Invoke-sqmRestoreDatabase.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ function Invoke-sqmRestoreDatabase
303303
if ($BackupBeforeRestore -and $dbExists -and -not $isAGDatabase)
304304
{
305305
$backupFileName = "${DatabaseName}_preRestore_$(Get-Date -Format 'yyyyMMdd_HHmsqm').bak"
306-
$backupFileFull = Join-Path (Get-DbaDefaultPath -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Type Backup) $backupFileName
306+
$backupFileFull = Join-Path (Get-DbaDefaultPath -SqlInstance $SqlInstance -SqlCredential $SqlCredential).Backup $backupFileName
307307
$backupParams = @{
308308
SqlInstance = $SqlInstance
309309
SqlCredential = $SqlCredential

Public/Invoke-sqmUpdateStatistics.ps1

Lines changed: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,35 @@
66
Executes UPDATE STATISTICS with configurable options (scan percentage, only modified statistics, etc.).
77
Can be restricted to specific databases, tables, or statistics.
88
9+
Note: dbatools has no Update statistics cmdlet, so this runs UPDATE STATISTICS directly via
10+
Invoke-DbaQuery. The set of statistics to touch is determined from sys.stats /
11+
sys.dm_db_stats_properties so -OnlyModified, -Index and the Table/Statistics filters work
12+
server-side before anything is updated.
13+
914
.PARAMETER SqlInstance
1015
SQL Server instance (default: current computer name).
1116
1217
.PARAMETER SqlCredential
1318
PSCredential for the connection.
1419
1520
.PARAMETER Database
16-
Database name or wildcard pattern.
21+
Database name or wildcard pattern (PowerShell wildcards, e.g. '*' or 'Sales*'). System databases
22+
are excluded unless named explicitly. Default: '*' (all user databases).
1723
1824
.PARAMETER Table
19-
Table name or wildcard pattern.
25+
Table name or wildcard pattern. Default: '*'.
2026
2127
.PARAMETER Statistics
22-
Statistic name or wildcard pattern.
28+
Statistic name or wildcard pattern. Default: '*'.
2329
2430
.PARAMETER SamplePercent
25-
Percentage of rows used for the update (0 = full scan). Default: 0.
31+
Percentage of rows used for the update (0 = FULLSCAN). Default: 0.
2632
2733
.PARAMETER OnlyModified
28-
Only update statistics that have changed since the last update. Default: $true.
34+
Only update statistics that have changed since the last update (modification_counter > 0). Default: $true.
2935
3036
.PARAMETER Index
31-
Also update statistics associated with an index. Default: $true.
37+
Also update statistics backed by an index. When $false, only column statistics are updated. Default: $true.
3238
3339
.PARAMETER WhatIf
3440
Shows which statistics would be affected.
@@ -40,7 +46,7 @@
4046
Invoke-sqmUpdateStatistics -Database 'SalesDB' -SamplePercent 10
4147
4248
.NOTES
43-
Uses dbatools (Update-DbaDbStatistic).
49+
Uses dbatools (Get-DbaDatabase, Invoke-DbaQuery). Requires sysadmin/db_owner on the targets.
4450
#>
4551
function Invoke-sqmUpdateStatistics {
4652
[CmdletBinding(SupportsShouldProcess = $true)]
@@ -67,42 +73,94 @@ function Invoke-sqmUpdateStatistics {
6773

6874
begin {
6975
$functionName = $MyInvocation.MyCommand.Name
70-
if (-not (Get-Module -ListAvailable -Name dbatools)) {
71-
throw "dbatools-Modul nicht gefunden."
76+
if (-not $script:dbatoolsAvailable -and -not (Get-Module -ListAvailable -Name dbatools)) {
77+
throw "dbatools-Modul nicht gefunden. Bitte installieren: Install-Module dbatools"
7278
}
79+
80+
# PowerShell-Wildcards (* ?) -> SQL LIKE (% _)
81+
$tableLike = ($Table -replace '\*', '%') -replace '\?', '_'
82+
$statLike = ($Statistics -replace '\*', '%') -replace '\?', '_'
83+
84+
$connParams = @{ SqlInstance = $SqlInstance; ErrorAction = 'Stop' }
85+
if ($SqlCredential) { $connParams['SqlCredential'] = $SqlCredential }
86+
87+
Invoke-sqmLogging -Message "Starte $functionName auf $SqlInstance (Database='$Database', Table='$Table', Statistics='$Statistics', SamplePercent=$SamplePercent, OnlyModified=$OnlyModified, Index=$Index)" -FunctionName $functionName -Level "INFO"
7388
}
7489

7590
process {
7691
try {
77-
$params = @{
78-
SqlInstance = $SqlInstance
79-
SqlCredential = $SqlCredential
80-
Database = $Database
81-
Table = $Table
82-
Statistic = $Statistics
83-
SamplePercent = $SamplePercent
84-
OnlyModified = $OnlyModified
85-
Index = $Index
86-
EnableException = $EnableException
92+
# Zieldatenbanken aufloesen. System-DBs nur wenn explizit benannt (kein Wildcard, kein '*').
93+
$dbParams = $connParams.Clone()
94+
if ($Database -notmatch '[\*\?]') { $dbParams['Database'] = $Database } else { $dbParams['ExcludeSystem'] = $true }
95+
$targetDbs = @(Get-DbaDatabase @dbParams | Where-Object { $_.IsAccessible -and $_.Name -like $Database })
96+
97+
if ($targetDbs.Count -eq 0) {
98+
Invoke-sqmLogging -Message "Keine passenden Datenbanken fuer Muster '$Database' gefunden." -FunctionName $functionName -Level "WARNING"
99+
return
87100
}
88-
if ($PSCmdlet.ShouldProcess("UpdateStatistics on $SqlInstance", "Update")) {
89-
$result = Update-DbaDbStatistic @params
90-
$result | ForEach-Object {
91-
[PSCustomObject]@{
92-
Database = $_.Database
93-
Table = $_.Table
94-
Statistic = $_.Statistic
95-
Status = 'Success'
96-
Message = "Updated"
101+
102+
# WITH-Klausel: FULLSCAN oder SAMPLE n PERCENT
103+
$withClause = if ($SamplePercent -le 0) { 'FULLSCAN' } else { "SAMPLE $SamplePercent PERCENT" }
104+
105+
foreach ($db in $targetDbs) {
106+
$dbName = $db.Name
107+
108+
# Zu aktualisierende Statistiken serverseitig ermitteln.
109+
# auto_created/user_created = Spaltenstatistiken; alles andere (index-gestuetzt) nur wenn -Index.
110+
$selectQuery = @"
111+
SELECT sch.name AS SchemaName, t.name AS TableName, s.name AS StatName,
112+
CASE WHEN i.object_id IS NOT NULL THEN 1 ELSE 0 END AS IsIndexStat,
113+
sp.modification_counter AS ModCounter
114+
FROM sys.stats s
115+
JOIN sys.objects t ON s.object_id = t.object_id AND t.type = 'U' AND t.is_ms_shipped = 0
116+
JOIN sys.schemas sch ON t.schema_id = sch.schema_id
117+
LEFT JOIN sys.indexes i ON i.object_id = s.object_id AND i.name = s.name
118+
OUTER APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
119+
WHERE t.name LIKE @table AND s.name LIKE @stat
120+
"@
121+
$stats = @(Invoke-DbaQuery @connParams -Database $dbName -Query $selectQuery -SqlParameter @{ table = $tableLike; stat = $statLike })
122+
123+
foreach ($st in $stats) {
124+
if (-not $Index -and $st.IsIndexStat -eq 1) { continue }
125+
if ($OnlyModified -and (($st.ModCounter -eq $null) -or ($st.ModCounter -le 0))) { continue }
126+
127+
$tableEsc = "[$($st.SchemaName -replace '\]', ']]')].[$($st.TableName -replace '\]', ']]')]"
128+
$statEsc = "[$($st.StatName -replace '\]', ']]')]"
129+
$updateQuery = "UPDATE STATISTICS $tableEsc ($statEsc) WITH $withClause"
130+
$target = "$dbName : $($st.SchemaName).$($st.TableName).$($st.StatName)"
131+
132+
if ($PSCmdlet.ShouldProcess($target, "UPDATE STATISTICS WITH $withClause")) {
133+
try {
134+
Invoke-DbaQuery @connParams -Database $dbName -Query $updateQuery | Out-Null
135+
[PSCustomObject]@{
136+
SqlInstance = $SqlInstance
137+
Database = $dbName
138+
Table = "$($st.SchemaName).$($st.TableName)"
139+
Statistic = $st.StatName
140+
Status = 'Success'
141+
Message = "Updated WITH $withClause"
142+
}
143+
}
144+
catch {
145+
$errMsg = "Fehler bei UPDATE STATISTICS ($target): $($_.Exception.Message)"
146+
Invoke-sqmLogging -Message $errMsg -FunctionName $functionName -Level "ERROR"
147+
if ($EnableException) { throw }
148+
[PSCustomObject]@{
149+
SqlInstance = $SqlInstance
150+
Database = $dbName
151+
Table = "$($st.SchemaName).$($st.TableName)"
152+
Statistic = $st.StatName
153+
Status = 'Failed'
154+
Message = $errMsg
155+
}
156+
}
97157
}
98158
}
99-
} else {
100-
Write-Verbose "WhatIf: UpdateStatistics would be executed on $SqlInstance"
101159
}
102160
}
103161
catch {
104162
Invoke-sqmLogging -Message $_.Exception.Message -FunctionName $functionName -Level "ERROR"
105163
if ($EnableException) { throw }
106164
}
107165
}
108-
}
166+
}

Public/New-sqmBackupMaintenanceJob.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ Invoke-sqmUserDatabaseBackup -All -BackupType '$BackupType' -MailProfile '$MailP
407407
$op = Get-DbaAgentOperator @connParams -Operator $OperatorName -ErrorAction SilentlyContinue
408408
if ($op)
409409
{
410-
Set-DbaAgentJob @connParams -Job $JobName -OperatorToEmail $OperatorName -EmailLevel OnFailure -ErrorAction SilentlyContinue | Out-Null
410+
Set-DbaAgentJob @connParams -Job $JobName -EmailOperator $OperatorName -EmailLevel OnFailure -ErrorAction SilentlyContinue | Out-Null
411411
Invoke-sqmLogging -Message "Operator '$OperatorName' fuer Fehler-Benachrichtigung gesetzt." -FunctionName $functionName -Level "INFO"
412412
}
413413
else

Public/New-sqmOlaMaintenanceJobs.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ function New-sqmOlaMaintenanceJobs
319319
$op = Get-DbaAgentOperator @connParams -Operator $OperatorName -ErrorAction SilentlyContinue
320320
if ($op)
321321
{
322-
Set-DbaAgentJob @connParams -Job $Name -OperatorToEmail $OperatorName -EmailLevel OnFailure -ErrorAction SilentlyContinue | Out-Null
322+
Set-DbaAgentJob @connParams -Job $Name -EmailOperator $OperatorName -EmailLevel OnFailure -ErrorAction SilentlyContinue | Out-Null
323323
}
324324
else
325325
{

Public/New-sqmOlaSysDbBackupJob.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ EXECUTE master.dbo.DatabaseBackup
288288
$op = Get-DbaAgentOperator @connParams -Operator $OperatorName -ErrorAction SilentlyContinue
289289
if ($op)
290290
{
291-
Set-DbaAgentJob @connParams -Job $effJobName -OperatorToEmail $OperatorName -EmailLevel OnFailure -ErrorAction SilentlyContinue | Out-Null
291+
Set-DbaAgentJob @connParams -Job $effJobName -EmailOperator $OperatorName -EmailLevel OnFailure -ErrorAction SilentlyContinue | Out-Null
292292
}
293293
else
294294
{

Public/New-sqmOlaUsrDbBackupJob.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ EXECUTE master.dbo.DatabaseBackup
593593
$op = Get-DbaAgentOperator @connParams -Operator $OperatorName -ErrorAction SilentlyContinue
594594
if ($op)
595595
{
596-
Set-DbaAgentJob @connParams -Job $jobDef.JobName -OperatorToEmail $OperatorName -EmailLevel OnFailure -ErrorAction SilentlyContinue | Out-Null
596+
Set-DbaAgentJob @connParams -Job $jobDef.JobName -EmailOperator $OperatorName -EmailLevel OnFailure -ErrorAction SilentlyContinue | Out-Null
597597
}
598598
else
599599
{

Public/Sync-sqmLoginsToAlwaysOn.ps1

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,11 @@ ORDER BY drs.role ASC
396396
$agentAccount = $null
397397
try
398398
{
399-
$agentAccount = (Get-DbaAgentServiceAccount -SqlInstance $secondaryName -SqlCredential $dstCred).ServiceAccount
399+
# Get-DbaAgentServiceAccount existiert nicht. Das Agent-Dienstkonto liefert sys.dm_server_services
400+
# ueber die bestehende SQL-Verbindung (kein Windows/WMI noetig). servicename ist lokalisiert
401+
# ("SQL Server-Agent" auf DE), daher locale-robustes LIKE '%Agent%'.
402+
$agentSvcQuery = "SELECT TOP 1 service_account FROM sys.dm_server_services WHERE servicename LIKE '%Agent%'"
403+
$agentAccount = (Invoke-DbaQuery -SqlInstance $secondaryName -SqlCredential $dstCred -Query $agentSvcQuery -ErrorAction Stop).service_account
400404
Invoke-sqmLogging -Message "[$secondaryName] SafeForceMode: Auto-excluding Agent Account: $agentAccount" `
401405
-FunctionName $functionName -Level 'INFO'
402406
}

0 commit comments

Comments
 (0)