tests/Unit/Public/Add-sqmDatabaseToAG.Tests.ps1
|
#Requires -Modules Pester <# .SYNOPSIS Unit Tests fuer Add-sqmDatabaseToAG, Schwerpunkt TDE-Behandlung. dbatools wird vollstaendig gemockt; getestet wird die Entscheidungslogik: Versionsschranke SQL 2019, Zertifikatspruefung ueber den Thumbprint, Verteilung an die Secondaries und die Zusicherung, dass bei einem Abbruch nichts veraendert wurde. #> # Hinweis zu den ParameterFiltern: ein Pester-Mock uebernimmt den Parameterblock des # Originals, deshalb kommt -SqlInstance als DbaInstanceParameter an und nicht als String. # Ein blosses $SqlInstance -eq 'SQL02' greift nicht - erst "$SqlInstance" -eq 'SQL02'. BeforeAll { . "$PSScriptRoot\..\..\..\tests\TestHelpers.ps1" Import-sqmTestModule $script:TestDir = New-TempTestDirectory $script:Pw = ConvertTo-SecureString 'Cert#Pwd!2026' -AsPlainText -Force } AfterAll { if ($script:TestDir -and (Test-Path $script:TestDir)) { Remove-Item $script:TestDir -Recurse -Force } if (Get-Module sqmSQLTool) { Remove-Module sqmSQLTool -Force } $env:MSSQLTOOLS_SKIP_AUTO_UPDATE = $null } Describe 'Add-sqmDatabaseToAG' { Context 'Parameter-Validierung' { It '<_> Parameter existiert' -ForEach @( 'SqlInstance', 'SqlCredential', 'AvailabilityGroup', 'Database', 'All', 'BackupPath', 'SyncTdeCertificate', 'TdeCertificateBackupPath', 'TdeCertificatePassword', 'TdeMasterKeyPassword', 'KeepTdeCertificateBackup', 'EnableException' ) { (Get-Command Add-sqmDatabaseToAG).Parameters.ContainsKey($_) | Should -Be $true } It 'Unterstuetzt ShouldProcess (WhatIf/Confirm)' { $cmd = Get-Command Add-sqmDatabaseToAG $cmd.Parameters.ContainsKey('WhatIf') | Should -Be $true $cmd.Parameters.ContainsKey('Confirm') | Should -Be $true } It 'Die TDE-Parameter gelten in beiden Parametersaetzen' { $cmd = Get-Command Add-sqmDatabaseToAG foreach ($setName in 'Specific', 'All') { $set = $cmd.ParameterSets | Where-Object { $_.Name -eq $setName } $set.Parameters.Name | Should -Contain 'SyncTdeCertificate' $set.Parameters.Name | Should -Contain 'TdeCertificatePassword' } } It '-SyncTdeCertificate ohne -TdeCertificateBackupPath wird abgelehnt' { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } { Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'DB1' ` -SyncTdeCertificate -TdeCertificatePassword $script:Pw -WhatIf } | Should -Throw '*TdeCertificateBackupPath*' } It '-SyncTdeCertificate ohne -TdeCertificatePassword wird abgelehnt' { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } { Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'DB1' ` -SyncTdeCertificate -TdeCertificateBackupPath $script:TestDir -WhatIf } | Should -Throw '*TdeCertificatePassword*' } It 'Ein nicht vorhandener Ablagepfad wird abgelehnt' { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } { Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'DB1' ` -SyncTdeCertificate -TdeCertificateBackupPath 'C:\gibt\es\nicht' ` -TdeCertificatePassword $script:Pw -WhatIf } | Should -Throw '*nicht gefunden*' } } Context 'Datenbank ohne TDE (unveraendertes Verhalten)' { BeforeAll { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { [PSCustomObject]@{ Name = 'AG1' } } Mock -ModuleName sqmSQLTool Get-DbaAgReplica { @([PSCustomObject]@{ Name = 'SQL01'; Role = 'Primary' }, [PSCustomObject]@{ Name = 'SQL02'; Role = 'Secondary' }) } Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'PlainDB'; RecoveryModel = 'Full'; IsAccessible = $true } } Mock -ModuleName sqmSQLTool Get-DbaAgDatabase { $null } Mock -ModuleName sqmSQLTool Remove-DbaDatabase { } Mock -ModuleName sqmSQLTool Add-DbaAgDatabase { } Mock -ModuleName sqmSQLTool Connect-DbaInstance { [PSCustomObject]@{ VersionMajor = 16 } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery { @() } } It 'Wird ganz normal zur AG hinzugefuegt' { $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'PlainDB' -Confirm:$false $r.Status | Should -Be 'Success' Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 1 } It 'Fragt die Zertifikate gar nicht erst ab' { Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'PlainDB' -Confirm:$false | Out-Null Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'WHERE CONVERT\(varchar\(100\), thumbprint, 1\)' } -Exactly 0 } } Context 'Automatische AG-Auswahl ohne -AvailabilityGroup' { BeforeAll { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } Mock -ModuleName sqmSQLTool Write-Host { } Mock -ModuleName sqmSQLTool Get-DbaAgReplica { @([PSCustomObject]@{ Name = 'SQL01'; Role = 'Primary' }, [PSCustomObject]@{ Name = 'SQL02'; Role = 'Secondary' }) } Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'PlainDB'; RecoveryModel = 'Full'; IsAccessible = $true } } Mock -ModuleName sqmSQLTool Get-DbaAgDatabase { $null } Mock -ModuleName sqmSQLTool Remove-DbaDatabase { } Mock -ModuleName sqmSQLTool Add-DbaAgDatabase { } Mock -ModuleName sqmSQLTool Invoke-DbaQuery { @() } } It 'Genau eine AG mit dieser Instanz als Primary wird ohne Rueckfrage verwendet' { # AG_SEC ist hier nur Secondary und kommt deshalb nicht in Frage Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { @([PSCustomObject]@{ Name = 'AG1'; LocalReplicaRole = 'Primary'; PrimaryReplica = 'SQL01' }, [PSCustomObject]@{ Name = 'AG_SEC'; LocalReplicaRole = 'Secondary'; PrimaryReplica = 'SQL03' }) } Mock -ModuleName sqmSQLTool Read-Host { throw 'darf nicht fragen' } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -Database 'PlainDB' -Confirm:$false $r.Status | Should -Be 'Success' Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -ParameterFilter { $AvailabilityGroup -eq 'AG1' } -Exactly 1 Should -Invoke -ModuleName sqmSQLTool Read-Host -Exactly 0 } It 'Mehrere AGs: fragt nach und nimmt die gewaehlte' { Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { @([PSCustomObject]@{ Name = 'AG_B'; LocalReplicaRole = 'Primary'; PrimaryReplica = 'SQL01' }, [PSCustomObject]@{ Name = 'AG_A'; LocalReplicaRole = 'Primary'; PrimaryReplica = 'SQL01' }) } Mock -ModuleName sqmSQLTool Test-sqmInteractiveSession { $true } # Erst ungueltig, dann 2 = AG_B (Liste ist nach Name sortiert) $script:answers = [System.Collections.Queue]::new(@('x', '2')) Mock -ModuleName sqmSQLTool Read-Host { $script:answers.Dequeue() } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -Database 'PlainDB' -Confirm:$false $r.Status | Should -Be 'Success' Should -Invoke -ModuleName sqmSQLTool Read-Host -Exactly 2 Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -ParameterFilter { $AvailabilityGroup -eq 'AG_B' } -Exactly 1 } It 'Mehrere AGs ohne interaktive Sitzung: bricht mit Namensliste ab, aendert nichts' { Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { @([PSCustomObject]@{ Name = 'AG_A'; LocalReplicaRole = 'Primary'; PrimaryReplica = 'SQL01' }, [PSCustomObject]@{ Name = 'AG_B'; LocalReplicaRole = 'Primary'; PrimaryReplica = 'SQL01' }) } Mock -ModuleName sqmSQLTool Test-sqmInteractiveSession { $false } Mock -ModuleName sqmSQLTool Read-Host { throw 'darf nicht fragen' } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -Database 'PlainDB' -Confirm:$false $r.Status | Should -Be 'GlobalError' $r.Message | Should -BeLike '*AG_A, AG_B*' Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 0 } It 'Leere Eingabe bricht ab' { Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { @([PSCustomObject]@{ Name = 'AG_A'; LocalReplicaRole = 'Primary'; PrimaryReplica = 'SQL01' }, [PSCustomObject]@{ Name = 'AG_B'; LocalReplicaRole = 'Primary'; PrimaryReplica = 'SQL01' }) } Mock -ModuleName sqmSQLTool Test-sqmInteractiveSession { $true } Mock -ModuleName sqmSQLTool Read-Host { '' } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -Database 'PlainDB' -Confirm:$false $r.Status | Should -Be 'GlobalError' Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 0 } It 'Instanz ist nirgends Primary: Fehler nennt den tatsaechlichen Primary' { Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { [PSCustomObject]@{ Name = 'AG1'; LocalReplicaRole = 'Secondary'; PrimaryReplica = 'SQL02' } } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -Database 'PlainDB' -Confirm:$false $r.Status | Should -Be 'GlobalError' $r.Message | Should -BeLike '*AG1 (Primary: SQL02)*' } } Context 'Recovery Full und FULL-Backup' { BeforeAll { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { [PSCustomObject]@{ Name = 'AG1' } } Mock -ModuleName sqmSQLTool Get-DbaAgReplica { @([PSCustomObject]@{ Name = 'SQL01'; Role = 'Primary' }, [PSCustomObject]@{ Name = 'SQL02'; Role = 'Secondary' }) } Mock -ModuleName sqmSQLTool Get-DbaAgDatabase { $null } Mock -ModuleName sqmSQLTool Add-DbaAgDatabase { } Mock -ModuleName sqmSQLTool Set-DbaDbRecoveryModel { } # Secondary hat eine alte Kopie, die fuer das Seeding geloescht wird Mock -ModuleName sqmSQLTool Remove-DbaDatabase { } } BeforeEach { Mock -ModuleName sqmSQLTool Invoke-DbaQuery { @() } Mock -ModuleName sqmSQLTool Backup-DbaDatabase { [PSCustomObject]@{ BackupComplete = $true; BackupPath = 'X:\Backup\DB_FULL.bak' } } } It 'SIMPLE: stellt auf Full um, sichert und fuegt danach hinzu' { Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'SimpleDB'; RecoveryModel = 'Simple'; IsAccessible = $true } } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'SimpleDB' -Confirm:$false $r.Status | Should -Be 'Success' $r.Message | Should -BeLike '*DB_FULL.bak*' Should -Invoke -ModuleName sqmSQLTool Set-DbaDbRecoveryModel -Exactly 1 Should -Invoke -ModuleName sqmSQLTool Backup-DbaDatabase -ParameterFilter { $Database -eq 'SimpleDB' -and $Type -eq 'Full' } -Exactly 1 Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 1 } It '-BackupPath wird an Backup-DbaDatabase durchgereicht' { Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'SimpleDB'; RecoveryModel = 'Simple'; IsAccessible = $true } } Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'SimpleDB' -BackupPath 'X:\AgSeed' -Confirm:$false | Out-Null Should -Invoke -ModuleName sqmSQLTool Backup-DbaDatabase -ParameterFilter { $Path -eq 'X:\AgSeed' } -Exactly 1 } It 'Backup schlaegt fehl: kein Drop auf dem Secondary, kein Add' { Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'SimpleDB'; RecoveryModel = 'Simple'; IsAccessible = $true } } Mock -ModuleName sqmSQLTool Backup-DbaDatabase { throw 'Platte voll' } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'SimpleDB' -Confirm:$false $r.Status | Should -Be 'BackupFailed' $r.Message | Should -BeLike '*Platte voll*' Should -Invoke -ModuleName sqmSQLTool Remove-DbaDatabase -Exactly 0 Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 0 } It 'Full ohne Log-Chain (last_log_backup_lsn NULL) wird ebenfalls gesichert' { Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'NoChainDB'; RecoveryModel = 'Full'; IsAccessible = $true } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'last_log_backup_lsn' } -MockWith { [PSCustomObject]@{ last_log_backup_lsn = [System.DBNull]::Value } } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'NoChainDB' -Confirm:$false $r.Status | Should -Be 'Success' Should -Invoke -ModuleName sqmSQLTool Set-DbaDbRecoveryModel -Exactly 0 Should -Invoke -ModuleName sqmSQLTool Backup-DbaDatabase -Exactly 1 } It 'Full mit bestehender Log-Chain wird nicht gesichert' { Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'ChainDB'; RecoveryModel = 'Full'; IsAccessible = $true } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'last_log_backup_lsn' } -MockWith { [PSCustomObject]@{ last_log_backup_lsn = [decimal]42000000012300001 } } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'ChainDB' -Confirm:$false $r.Status | Should -Be 'Success' Should -Invoke -ModuleName sqmSQLTool Backup-DbaDatabase -Exactly 0 } } Context 'TDE-Datenbank auf einem Replica unterhalb SQL 2019' { BeforeAll { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { [PSCustomObject]@{ Name = 'AG1' } } Mock -ModuleName sqmSQLTool Get-DbaAgReplica { @([PSCustomObject]@{ Name = 'SQL01'; Role = 'Primary' }, [PSCustomObject]@{ Name = 'SQL02'; Role = 'Secondary' }) } Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'PayrollDB'; RecoveryModel = 'Full'; IsAccessible = $true } } Mock -ModuleName sqmSQLTool Get-DbaAgDatabase { $null } Mock -ModuleName sqmSQLTool Remove-DbaDatabase { } Mock -ModuleName sqmSQLTool Add-DbaAgDatabase { } Mock -ModuleName sqmSQLTool Invoke-DbaQuery { @() } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'dm_database_encryption_keys' } -MockWith { [PSCustomObject]@{ DatabaseName = 'PayrollDB'; EncryptionState = 3; CertificateName = 'TDE_Cert'; EncryptorThumbprint = '0xAA11' } } # Primary SQL 2022, Secondary noch SQL 2016 Mock -ModuleName sqmSQLTool Connect-DbaInstance { [PSCustomObject]@{ VersionMajor = 16 } } Mock -ModuleName sqmSQLTool Connect-DbaInstance -ParameterFilter { "$SqlInstance" -eq 'SQL02' } -MockWith { [PSCustomObject]@{ VersionMajor = 13 } } } It 'Meldet TdeUnsupportedVersion und nennt das zu alte Replica' { $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'PayrollDB' -Confirm:$false $r.Status | Should -Be 'TdeUnsupportedVersion' $r.Message | Should -BeLike '*SQL02*' } It 'Veraendert dabei nichts: kein Drop, kein Add' { Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'PayrollDB' -Confirm:$false | Out-Null Should -Invoke -ModuleName sqmSQLTool Remove-DbaDatabase -Exactly 0 Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 0 } } Context 'TDE-Datenbank, Zertifikat fehlt auf dem Secondary' { BeforeAll { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { [PSCustomObject]@{ Name = 'AG1' } } Mock -ModuleName sqmSQLTool Get-DbaAgReplica { @([PSCustomObject]@{ Name = 'SQL01'; Role = 'Primary' }, [PSCustomObject]@{ Name = 'SQL02'; Role = 'Secondary' }) } Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'PayrollDB'; RecoveryModel = 'Full'; IsAccessible = $true } } Mock -ModuleName sqmSQLTool Get-DbaAgDatabase { $null } Mock -ModuleName sqmSQLTool Remove-DbaDatabase { } Mock -ModuleName sqmSQLTool Add-DbaAgDatabase { } Mock -ModuleName sqmSQLTool Connect-DbaInstance { [PSCustomObject]@{ VersionMajor = 16 } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery { @() } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'dm_database_encryption_keys' } -MockWith { [PSCustomObject]@{ DatabaseName = 'PayrollDB'; EncryptionState = 3; CertificateName = 'TDE_Cert'; EncryptorThumbprint = '0xAA11' } } } It 'Ohne -SyncTdeCertificate wird uebersprungen, mit Nennung des Knotens' { $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'PayrollDB' -Confirm:$false $r.Status | Should -Be 'TdeCertificateMissing' $r.Message | Should -BeLike '*SQL02*' Should -Invoke -ModuleName sqmSQLTool Remove-DbaDatabase -Exactly 0 Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 0 } It 'Mit -SyncTdeCertificate wird exportiert, der Hauptschluessel angelegt und das Zertifikat erzeugt' { $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'PayrollDB' ` -SyncTdeCertificate -TdeCertificateBackupPath $script:TestDir -TdeCertificatePassword $script:Pw -Confirm:$false $r.Status | Should -Be 'Success' Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'BACKUP CERTIFICATE' -and "$SqlInstance" -eq 'SQL01' } -Exactly 1 Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'CREATE MASTER KEY' -and "$SqlInstance" -eq 'SQL02' } -Exactly 1 Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'CREATE CERTIFICATE' -and "$SqlInstance" -eq 'SQL02' } -Exactly 1 Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 1 } It 'Unter -WhatIf wird nichts exportiert und nichts hinzugefuegt' { $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'PayrollDB' ` -SyncTdeCertificate -TdeCertificateBackupPath $script:TestDir -TdeCertificatePassword $script:Pw -WhatIf $r.Status | Should -Be 'TdeSyncSkipped' Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'BACKUP CERTIFICATE' } -Exactly 0 Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 0 } } Context 'TDE-Datenbank, Zertifikat auf allen Secondaries vorhanden' { BeforeAll { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { [PSCustomObject]@{ Name = 'AG1' } } Mock -ModuleName sqmSQLTool Get-DbaAgReplica { @([PSCustomObject]@{ Name = 'SQL01'; Role = 'Primary' }, [PSCustomObject]@{ Name = 'SQL02'; Role = 'Secondary' }) } Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'PayrollDB'; RecoveryModel = 'Full'; IsAccessible = $true } } Mock -ModuleName sqmSQLTool Get-DbaAgDatabase { $null } Mock -ModuleName sqmSQLTool Remove-DbaDatabase { } Mock -ModuleName sqmSQLTool Add-DbaAgDatabase { } Mock -ModuleName sqmSQLTool Connect-DbaInstance { [PSCustomObject]@{ VersionMajor = 15 } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery { @() } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'dm_database_encryption_keys' } -MockWith { [PSCustomObject]@{ DatabaseName = 'PayrollDB'; EncryptionState = 3; CertificateName = 'TDE_Cert'; EncryptorThumbprint = '0xAA11' } } # Thumbprint-Treffer auf dem Secondary, dort unter anderem Namen - das genuegt SQL Server Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'WHERE CONVERT\(varchar\(100\), thumbprint, 1\)' } -MockWith { [PSCustomObject]@{ name = 'TDE_Cert_Kopie' } } } It 'Wird hinzugefuegt, ohne etwas zu exportieren' { $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'PayrollDB' -Confirm:$false $r.Status | Should -Be 'Success' Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'BACKUP CERTIFICATE' } -Exactly 0 Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 1 } } Context 'Sonderfaelle der Verschluesselung' { BeforeAll { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } Mock -ModuleName sqmSQLTool Get-DbaAvailabilityGroup { [PSCustomObject]@{ Name = 'AG1' } } Mock -ModuleName sqmSQLTool Get-DbaAgReplica { @([PSCustomObject]@{ Name = 'SQL01'; Role = 'Primary' }, [PSCustomObject]@{ Name = 'SQL02'; Role = 'Secondary' }) } Mock -ModuleName sqmSQLTool Get-DbaDatabase { [PSCustomObject]@{ Name = 'PayrollDB'; RecoveryModel = 'Full'; IsAccessible = $true } } Mock -ModuleName sqmSQLTool Get-DbaAgDatabase { $null } Mock -ModuleName sqmSQLTool Remove-DbaDatabase { } Mock -ModuleName sqmSQLTool Add-DbaAgDatabase { } Mock -ModuleName sqmSQLTool Connect-DbaInstance { [PSCustomObject]@{ VersionMajor = 16 } } } It 'Asymmetrischer Schluessel (EKM) wird als solcher gemeldet, nicht als fehlendes Zertifikat' { Mock -ModuleName sqmSQLTool Invoke-DbaQuery { @() } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'dm_database_encryption_keys' } -MockWith { [PSCustomObject]@{ DatabaseName = 'PayrollDB'; EncryptionState = 3; CertificateName = [System.DBNull]::Value; EncryptorThumbprint = '0xBB22' } } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'PayrollDB' -Confirm:$false $r.Status | Should -Be 'TdeEncryptorNotCertificate' Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 0 } It 'Gleicher Name mit abweichendem Thumbprint wird nicht ueberschrieben' { Mock -ModuleName sqmSQLTool Invoke-DbaQuery { @() } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'dm_database_encryption_keys' } -MockWith { [PSCustomObject]@{ DatabaseName = 'PayrollDB'; EncryptionState = 3; CertificateName = 'TDE_Cert'; EncryptorThumbprint = '0xAA11' } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'WHERE name = N' } -MockWith { [PSCustomObject]@{ Thumb = '0xCC33' } } $r = Add-sqmDatabaseToAG -SqlInstance 'SQL01' -AvailabilityGroup 'AG1' -Database 'PayrollDB' ` -SyncTdeCertificate -TdeCertificateBackupPath $script:TestDir -TdeCertificatePassword $script:Pw -Confirm:$false $r.Status | Should -Be 'TdeCertificateNameConflict' $r.Message | Should -BeLike '*0xCC33*' Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'CREATE CERTIFICATE' } -Exactly 0 Should -Invoke -ModuleName sqmSQLTool Add-DbaAgDatabase -Exactly 0 } } } |