tests/Unit/Public/Certificates.Tests.ps1
|
#Requires -Modules Pester <# .SYNOPSIS Unit Tests fuer die Zertifikatsfunktionen (v1.9.179.0): Install-sqmCertificate (PFX-Syntax, Partner-Public-Key mit AUTHORIZATION statt Endpoint-Umstellung, Service-Broker-Endpoint, TLS-Bindung ueber Set-sqmSqlTlsCertificate), New-sqmSqlCertificate (Sicherung des neuen Zertifikats, kein Umbenennen, SERVICE_BROKER-Klausel), Set-sqmConfig -TrustServerCertificate und New-sqmCertificateRequest fuer benannte Instanzen. Invoke-DbaQuery wird gemockt; die erzeugten T-SQL-Befehle sind live gegen SQL Server 2022 geprueft. #> BeforeAll { . "$PSScriptRoot\..\..\..\tests\TestHelpers.ps1" Import-sqmTestModule $script:pwd = ConvertTo-SecureString "p'w" -AsPlainText -Force } AfterAll { if (Get-Module sqmSQLTool) { Remove-Module sqmSQLTool -Force } $env:SQMSQLTOOL_SKIP_AUTO_UPDATE = $null } Describe 'New-sqmCertImportSql' { It 'PFX: WITH FORMAT = PFX und PRIVATE KEY nur mit DECRYPTION BY PASSWORD' { $sql = InModuleScope sqmSQLTool -Parameters @{ P = $script:pwd } { New-sqmCertImportSql -CertificateName 'C1' -CertFile 'C:\c\a.pfx' -CertPassword $P -IsPfx $true -HasPrivKey $true } $sql | Should -Match "FROM FILE = N'C:\\c\\a\.pfx'" $sql | Should -Match "WITH FORMAT = 'PFX'" $sql | Should -Match "PRIVATE KEY \(DECRYPTION BY PASSWORD = N'p''w'\);" $sql | Should -Not -Match 'PRIVATE KEY \(FILE' } It 'CER + PVK: PRIVATE KEY (FILE, DECRYPTION BY PASSWORD), Anfuehrungszeichen maskiert' { $sql = InModuleScope sqmSQLTool -Parameters @{ P = $script:pwd } { New-sqmCertImportSql -CertificateName 'C1' -CertFile "C:\o'k\a.cer" -PrivateKeyFile 'C:\c\a.pvk' -CertPassword $P -IsPfx $false -HasPrivKey $true } $sql | Should -Match "FROM FILE = N'C:\\o''k\\a\.cer'" $sql | Should -Match "WITH PRIVATE KEY \(FILE = N'C:\\c\\a\.pvk', DECRYPTION BY PASSWORD = N'p''w'\);" } It 'Public Key mit AUTHORIZATION' { $sql = InModuleScope sqmSQLTool { New-sqmCertImportSql -CertificateName 'SQL01_AG' -CertFile 'C:\c\a.cer' -IsPfx $false -HasPrivKey $false -AuthorizationUser 'ag_user' } $sql | Should -Match '^CREATE CERTIFICATE \[SQL01_AG\] AUTHORIZATION \[ag_user\]' $sql | Should -Match "FROM FILE = N'C:\\c\\a\.cer';$" } } Describe 'Install-sqmCertificate' { BeforeEach { $script:cer = Join-Path $TestDrive 'part.cer'; Set-Content $script:cer 'x' $script:pvk = Join-Path $TestDrive 'part.pvk'; Set-Content $script:pvk 'x' $script:pfx = Join-Path $TestDrive 'srv.pfx'; Set-Content $script:pfx 'x' $script:major = 16; $script:dmk = 1; $script:userExists = $true Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } Mock -ModuleName sqmSQLTool Copy-sqmToCentralPath { } Mock -ModuleName sqmSQLTool Write-Host { } Mock -ModuleName sqmSQLTool Get-sqmCertFileInfo { [PSCustomObject]@{ Subject = 'CN=t'; Issuer = 'CN=t'; NotBefore = (Get-Date).AddDays(-1); NotAfter = (Get-Date).AddYears(1); Thumbprint = 'AABBCC' } } Mock -ModuleName sqmSQLTool Add-sqmCertToMachineStore { } Mock -ModuleName sqmSQLTool Set-sqmSqlTlsCertificate { [PSCustomObject]@{ PrivateKeyGranted = $true; ServiceAccount = 'NT Service\MSSQLSERVER' } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'ProductMajorVersion' } { [PSCustomObject]@{ MajorVersion = $script:major; HasDmk = $script:dmk } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'database_principals' } { if ($script:userExists) { [PSCustomObject]@{ name = 'ag_user' } } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'FROM sys.certificates WHERE name' } { } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'database_mirroring_endpoints' } { [PSCustomObject]@{ name = 'Hadr_endpoint' } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'service_broker_endpoints' } { [PSCustomObject]@{ name = 'Broker_EP' } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match '^CREATE CERTIFICATE|^ALTER ' } { } $script:out = Join-Path $TestDrive 'out' } It 'Partner-Public-Key: CREATE ... AUTHORIZATION, Endpoint wird NICHT umgestellt' { $r = Install-sqmCertificate -SqlInstance 'SQL02' -CertFile $script:cer -CertificateName 'SQL01_AG' -Purpose AlwaysOn -AuthorizationUser 'ag_user' -OutputPath $script:out -Confirm:$false $r.Success | Should -BeTrue $r.EndpointBound | Should -BeFalse $r.PartnerKeyOwner | Should -Be 'ag_user' Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 1 -Exactly -ParameterFilter { $Query -match 'CREATE CERTIFICATE \[SQL01_AG\] AUTHORIZATION \[ag_user\]' } Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 0 -Exactly -ParameterFilter { $Query -match 'ALTER ENDPOINT' } } It 'Partner-Public-Key ohne -AuthorizationUser: Import mit Warnung, kein ALTER ENDPOINT' { $r = Install-sqmCertificate -SqlInstance 'SQL02' -CertFile $script:cer -Purpose AlwaysOn -OutputPath $script:out -Confirm:$false -WarningAction SilentlyContinue $r.Warnings -join ' ' | Should -Match 'ALTER AUTHORIZATION ON CERTIFICATE' Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 0 -Exactly -ParameterFilter { $Query -match 'ALTER ENDPOINT' } } It 'Unbekannter -AuthorizationUser bricht vor dem Import ab' { $script:userExists = $false { Install-sqmCertificate -SqlInstance 'SQL02' -CertFile $script:cer -Purpose AlwaysOn -AuthorizationUser 'nope' -OutputPath $script:out -Confirm:$false -EnableException } | Should -Throw '*nope*existiert nicht*' Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 0 -Exactly -ParameterFilter { $Query -match '^CREATE CERTIFICATE' } } It 'Mit Private Key: lokaler AlwaysOn-Endpoint FOR DATABASE_MIRRORING' { $r = Install-sqmCertificate -SqlInstance 'SQL01' -CertFile $script:cer -PrivateKeyFile $script:pvk -CertPassword $script:pwd -CertificateName 'AG_NEW' -Purpose AlwaysOn -OutputPath $script:out -Confirm:$false $r.EndpointBound | Should -BeTrue Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 1 -Exactly -ParameterFilter { $Query -eq 'ALTER ENDPOINT [Hadr_endpoint] FOR DATABASE_MIRRORING (AUTHENTICATION = CERTIFICATE [AG_NEW]);' } } It 'Service Broker: Endpoint aus sys.service_broker_endpoints, FOR SERVICE_BROKER' { $r = Install-sqmCertificate -SqlInstance 'SQL01' -CertFile $script:cer -PrivateKeyFile $script:pvk -CertPassword $script:pwd -CertificateName 'SB_NEW' -Purpose ServiceBroker -OutputPath $script:out -Confirm:$false $r.EndpointBound | Should -BeTrue Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 1 -Exactly -ParameterFilter { $Query -eq 'ALTER ENDPOINT [Broker_EP] FOR SERVICE_BROKER (AUTHENTICATION = CERTIFICATE [SB_NEW]);' } } It 'PFX vor SQL Server 2022: klare Fehlermeldung, kein CREATE' { $script:major = 15 { Install-sqmCertificate -SqlInstance 'SQL01' -CertFile $script:pfx -CertPassword $script:pwd -Purpose UserDefined -OutputPath $script:out -Confirm:$false -EnableException } | Should -Throw '*erst ab SQL Server 2022*' Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 0 -Exactly -ParameterFilter { $Query -match '^CREATE CERTIFICATE' } } It 'PFX ab SQL Server 2022: CREATE mit FORMAT = PFX' { Install-sqmCertificate -SqlInstance 'SQL01' -CertFile $script:pfx -CertPassword $script:pwd -CertificateName 'P1' -OutputPath $script:out -Confirm:$false | Out-Null Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 1 -Exactly -ParameterFilter { $Query -match "WITH FORMAT = 'PFX'" } } It 'Private Key ohne Database Master Key: klare Fehlermeldung' { $script:dmk = 0 { Install-sqmCertificate -SqlInstance 'SQL01' -CertFile $script:cer -PrivateKeyFile $script:pvk -CertPassword $script:pwd -OutputPath $script:out -Confirm:$false -EnableException } | Should -Throw '*Database Master Key*' } It 'SSL: kein Import nach SQL Server, Bindung ueber Set-sqmSqlTlsCertificate' { $r = Install-sqmCertificate -SqlInstance 'SQL01\INST1' -CertFile $script:pfx -CertPassword $script:pwd -Purpose SSL -SetSqlServerSslCert -OutputPath $script:out -Confirm:$false -WarningAction SilentlyContinue $r.Success | Should -BeTrue $r.WindowsStoreImport | Should -BeTrue $r.SslCertSet | Should -BeTrue $r.SqlImported | Should -BeFalse Should -Invoke -ModuleName sqmSQLTool Set-sqmSqlTlsCertificate -Times 1 -Exactly -ParameterFilter { $SqlInstance -eq 'SQL01\INST1' -and $Thumbprint -eq 'AABBCC' } Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 0 -Exactly } It 'SSL: fehlendes Private-Key-Recht landet in den Warnungen' { Mock -ModuleName sqmSQLTool Set-sqmSqlTlsCertificate { [PSCustomObject]@{ PrivateKeyGranted = $false; ServiceAccount = 'NT Service\MSSQLSERVER' } } $r = Install-sqmCertificate -SqlInstance 'SQL01' -CertFile $script:pfx -CertPassword $script:pwd -Purpose SSL -SetSqlServerSslCert -OutputPath $script:out -Confirm:$false -WarningAction SilentlyContinue $r.Warnings -join ' ' | Should -Match 'Private Key' } } Describe 'New-sqmSqlCertificate' { BeforeEach { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } Mock -ModuleName sqmSQLTool Copy-sqmToCentralPath { } Mock -ModuleName sqmSQLTool Write-Host { } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'FROM sys.certificates c\s+WHERE c.name' } { [PSCustomObject]@{ CertificateName = 'SB_CERT'; subject = 'SB_CERT'; issuer_name = 'SB_CERT'; start_date = (Get-Date).AddYears(-1) expiry_date = (Get-Date).AddDays(10); thumbprint = [byte[]](1, 2, 3); PrivateKeyEncryption = 'ENCRYPTED_BY_MASTER_KEY'; HasPrivateKey = 1 } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'service_broker_endpoints' } { [PSCustomObject]@{ EndpointName = 'Broker_EP'; EndpointType = 'SERVICE_BROKER' } } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'dm_database_encryption_keys' } { } Mock -ModuleName sqmSQLTool Invoke-DbaQuery -ParameterFilter { $Query -match 'xp_create_subdir|^BACKUP CERTIFICATE|^CREATE CERTIFICATE|^ALTER ' } { } } It 'Sichert altes UND neues Zertifikat mit Private Key, legt Verzeichnis serverseitig an' { $r = New-sqmSqlCertificate -SqlInstance 'SQL01' -CertificateName 'SB_CERT' -BackupPath $TestDrive -BackupEncryptionPassword $script:pwd -Confirm:$false $r.Success | Should -BeTrue $r.NewPvkBackupFile | Should -Match 'SB_CERT_\d{4}\.pvk$' Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 1 -Exactly -ParameterFilter { $Query -match 'xp_create_subdir' } Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 1 -Exactly -ParameterFilter { $Query -match "^BACKUP CERTIFICATE \[SB_CERT\] .*SB_CERT_OLD\.pvk', ENCRYPTION BY PASSWORD = N'p''w'\)" } Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 1 -Exactly -ParameterFilter { $Query -match "^BACKUP CERTIFICATE \[SB_CERT_\d{4}\] .*WITH PRIVATE KEY" } } It 'Kein Umbenennen/Entfernen des alten Zertifikats, kein Endpoint-Wechsel ohne -BindEndpoint' { New-sqmSqlCertificate -SqlInstance 'SQL01' -CertificateName 'SB_CERT' -BackupPath $TestDrive -BackupEncryptionPassword $script:pwd -Confirm:$false | Out-Null Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 0 -Exactly -ParameterFilter { $Query -match 'ALTER CERTIFICATE|DROP CERTIFICATE|ALTER ENDPOINT' } } It '-BindEndpoint fuer Service Broker: FOR SERVICE_BROKER' { $r = New-sqmSqlCertificate -SqlInstance 'SQL01' -CertificateName 'SB_CERT' -NewCertificateName 'SB_NEW' -BindEndpoint -BackupPath $TestDrive -BackupEncryptionPassword $script:pwd -Confirm:$false $r.EndpointBound | Should -BeTrue Should -Invoke -ModuleName sqmSQLTool Invoke-DbaQuery -Times 1 -Exactly -ParameterFilter { $Query -match 'ALTER ENDPOINT \[Broker_EP\]\s+FOR SERVICE_BROKER \(AUTHENTICATION = CERTIFICATE \[SB_NEW\]\)' } } It 'Protokoll nennt keinen nicht existierenden Schalter mehr' { $r = New-sqmSqlCertificate -SqlInstance 'SQL01' -CertificateName 'SB_CERT' -BackupPath $TestDrive -BackupEncryptionPassword $script:pwd -Confirm:$false $txt = Get-Content $r.ProtocolFile -Raw $txt | Should -Not -Match 'ForAlwaysOn' $txt | Should -Match '-AuthorizationUser' } } Describe 'Set-sqmConfig -TrustServerCertificate' { BeforeAll { $script:ConfigFile = Join-Path $env:APPDATA 'MSSQLTools\config.json' $script:ConfigBackup = if (Test-Path $script:ConfigFile) { Get-Content $script:ConfigFile -Raw } else { $null } } AfterAll { if ($script:ConfigBackup) { Set-Content $script:ConfigFile -Value $script:ConfigBackup -Encoding UTF8 } elseif (Test-Path $script:ConfigFile) { Remove-Item $script:ConfigFile -Force } Set-DbatoolsConfig -FullName 'sql.connection.trustcert' -Value $true } It '$false: sofort wirksam fuer dbatools und gespeichert' { Set-sqmConfig -TrustServerCertificate $false Get-sqmConfig -Key 'TrustServerCertificate' | Should -BeFalse Get-DbatoolsConfigValue -FullName 'sql.connection.trustcert' | Should -BeFalse (Get-Content $script:ConfigFile -Raw | ConvertFrom-Json).TrustServerCertificate | Should -BeFalse } It '$true: wieder vertrauen' { Set-sqmConfig -TrustServerCertificate $true Get-DbatoolsConfigValue -FullName 'sql.connection.trustcert' | Should -BeTrue } } Describe 'New-sqmCertificateRequest' { It 'Benannte Instanz ohne -Subject: Subject aus dem Hostteil' { Mock -ModuleName sqmSQLTool Invoke-sqmLogging { } Mock -ModuleName sqmSQLTool Copy-sqmToCentralPath { } Mock -ModuleName sqmSQLTool Write-Host { } Mock -ModuleName sqmSQLTool Start-Process { [PSCustomObject]@{ ExitCode = 1 } } $r = New-sqmCertificateRequest -SqlInstance 'localhost\INST1' -OutputPath $TestDrive -Confirm:$false -WarningAction SilentlyContinue $r | Should -Not -BeNullOrEmpty $r.Subject | Should -Match '^CN=[^\\,]+,C=DE$' $r.SubjectAltNames | Should -Contain 'localhost' } } |