diff --git a/public/Start-DbaDbEncryption.ps1 b/public/Start-DbaDbEncryption.ps1 index 54f634e64b5..3556c2f0541 100644 --- a/public/Start-DbaDbEncryption.ps1 +++ b/public/Start-DbaDbEncryption.ps1 @@ -216,172 +216,171 @@ function Start-DbaDbEncryption { if (-not $Parallel) { # Sequential processing (original behavior) foreach ($db in $InputObject) { - try { - # Just in case they use inputobject + exclude - if ($db.Name -in $ExcludeDatabase) { continue } - $server = $db.Parent - # refresh in case we have a stale database - $null = $db.Refresh() - $null = $server.Refresh() - $servername = $server.Name - $dbname = $db.Name - - if ($db.EncryptionEnabled) { - Write-Message -Level Warning -Message "Database $($db.Name) on $($server.Name) is already encrypted" - continue - } - - # before doing anything, see if the master cert is in order - if ($EncryptorName) { - $mastercert = Get-DbaDbCertificate -SqlInstance $server -Database master | Where-Object Name -eq $EncryptorName - if (-not $mastercert -and $Force) { - $mastercert = New-DbaDbCertificate -SqlInstance $server -Database master -Name $EncryptorName + try { + # Just in case they use inputobject + exclude + if ($db.Name -in $ExcludeDatabase) { continue } + $server = $db.Parent + # refresh in case we have a stale database + $null = $db.Refresh() + $null = $server.Refresh() + $servername = $server.Name - $null = $server.Refresh() - $null = $server.Databases["master"].Refresh() + if ($db.EncryptionEnabled) { + Write-Message -Level Warning -Message "Database $($db.Name) on $($server.Name) is already encrypted" + continue } - } else { - $mastercert = Get-DbaDbCertificate -SqlInstance $server -Database master | Where-Object Name -NotMatch "##" - } - if ($EncryptorName -and -not $mastercert) { - Stop-Function -Message "EncryptorName specified but no matching certificate found on $($server.Name)" -Continue - } - - if ($mastercert.Count -gt 1) { - Stop-Function -Message "More than one certificate found on $($server.Name), please specify an EncryptorName" -Continue - } + # before doing anything, see if the master cert is in order + if ($EncryptorName) { + $mastercert = Get-DbaDbCertificate -SqlInstance $server -Database master | Where-Object Name -eq $EncryptorName + if (-not $mastercert -and $Force) { + $mastercert = New-DbaDbCertificate -SqlInstance $server -Database master -Name $EncryptorName - $stepCounter = 0 - Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Processing $($db.Name)" - } catch { - Stop-Function -Message "Failure" -ErrorRecord $_ -Continue - } + $null = $server.Refresh() + $null = $server.Databases["master"].Refresh() + } + } else { + $mastercert = Get-DbaDbCertificate -SqlInstance $server -Database master | Where-Object Name -NotMatch "##" + } - try { - # Ensure a database master key exists in the master database - Write-Message -Level Verbose -Message "Ensure a database master key exists in the master database for $($server.Name)" - Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Ensure a database master key exists in the master database for $($server.Name)" - $masterkey = Get-DbaDbMasterKey -SqlInstance $server -Database master - - if (-not $masterkey) { - Write-Message -Level Verbose -Message "master key not found, creating one" - $params = @{ - SqlInstance = $server - SecurePassword = $MasterKeySecurePassword - EnableException = $true + if ($EncryptorName -and -not $mastercert) { + Stop-Function -Message "EncryptorName specified but no matching certificate found on $($server.Name)" -Continue } - $masterkey = New-DbaServiceMasterKey @params - } - $null = $db.Refresh() - $null = $server.Refresh() - - $dbmasterkeytest = Get-DbaFile -SqlInstance $server -Path $BackupPath | Where-Object FileName -match "$servername-master" - if (-not $dbmasterkeytest) { - # has to be repeated in the event databases are piped in - $params = @{ - SqlInstance = $server - Database = "master" - Path = $BackupPath - EnableException = $true - SecurePassword = $BackupSecurePassword + if ($mastercert.Count -gt 1) { + Stop-Function -Message "More than one certificate found on $($server.Name), please specify an EncryptorName" -Continue } - $null = $server.Databases["master"].Refresh() - Write-Message -Level Verbose -Message "Backing up master key on $($server.Name)" - $null = Backup-DbaDbMasterKey @params + + $stepCounter = 0 + Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Processing $($db.Name)" + } catch { + Stop-Function -Message "Failure" -ErrorRecord $_ -Continue } - } catch { - Stop-Function -Message "Failure" -ErrorRecord $_ -Continue - } - try { - Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Processing EncryptorType for $($db.Name) on $($server.Name)" - if ($EncryptorType -eq "Certificate") { - if (-not $mastercert) { - Write-Message -Level Verbose -Message "master cert not found, creating one" + try { + # Ensure a database master key exists in the master database + Write-Message -Level Verbose -Message "Ensure a database master key exists in the master database for $($server.Name)" + Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Ensure a database master key exists in the master database for $($server.Name)" + $masterkey = Get-DbaDbMasterKey -SqlInstance $server -Database master + + if (-not $masterkey) { + Write-Message -Level Verbose -Message "master key not found, creating one" $params = @{ - SqlInstance = $server - Database = "master" - StartDate = $CertificateStartDate - ExpirationDate = $CertificateExpirationDate - ActiveForServiceBrokerDialog = $CertificateActiveForServiceBrokerDialog - EnableException = $true - } - if ($CertificateSubject) { - $params.Subject = $CertificateSubject + SqlInstance = $server + SecurePassword = $MasterKeySecurePassword + EnableException = $true } - $mastercert = New-DbaDbCertificate @params - } else { - Write-Message -Level Verbose -Message "master cert found on $($server.Name)" + $masterkey = New-DbaServiceMasterKey @params } $null = $db.Refresh() $null = $server.Refresh() - $mastercerttest = Get-DbaFile -SqlInstance $server -Path $BackupPath | Where-Object FileName -match "$($mastercert.Name).cer" - if (-not $mastercerttest) { - # Back up certificate - $null = $server.Databases["master"].Refresh() - $params = @{ - SqlInstance = $server - Database = "master" - Certificate = $mastercert.Name - Path = $BackupPath - EnableException = $true - EncryptionPassword = $BackupSecurePassword - } - Write-Message -Level Verbose -Message "Backing up master certificate on $($server.Name)" - $null = Backup-DbaDbCertificate @params - } - - if (-not $EncryptorName) { - Write-Message -Level Verbose -Message "Getting EncryptorName from master cert on $($server.Name)" - $EncryptorName = $mastercert.Name - } - } else { - $masterasym = Get-DbaDbAsymmetricKey -SqlInstance $server -Database master - - if (-not $masterasym) { - Write-Message -Level Verbose -Message "Asymmetric key not found, creating one for master on $($server.Name)" + $dbmasterkeytest = Get-DbaFile -SqlInstance $server -Path $BackupPath | Where-Object FileName -match "$servername-master" + if (-not $dbmasterkeytest) { + # has to be repeated in the event databases are piped in $params = @{ SqlInstance = $server Database = "master" + Path = $BackupPath EnableException = $true + SecurePassword = $BackupSecurePassword } - $masterasym = New-DbaDbAsymmetricKey @params - $null = $server.Refresh() $null = $server.Databases["master"].Refresh() - } else { - Write-Message -Level Verbose -Message "master asymmetric key found on $($server.Name)" + Write-Message -Level Verbose -Message "Backing up master key on $($server.Name)" + $null = Backup-DbaDbMasterKey @params } + } catch { + Stop-Function -Message "Failure" -ErrorRecord $_ -Continue + } - if (-not $EncryptorName) { - Write-Message -Level Verbose -Message "Getting EncryptorName from master asymmetric key" - $EncryptorName = $masterasym.Name + try { + Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Processing EncryptorType for $($db.Name) on $($server.Name)" + if ($EncryptorType -eq "Certificate") { + if (-not $mastercert) { + Write-Message -Level Verbose -Message "master cert not found, creating one" + $params = @{ + SqlInstance = $server + Database = "master" + StartDate = $CertificateStartDate + ExpirationDate = $CertificateExpirationDate + ActiveForServiceBrokerDialog = $CertificateActiveForServiceBrokerDialog + EnableException = $true + } + if ($CertificateSubject) { + $params.Subject = $CertificateSubject + } + $mastercert = New-DbaDbCertificate @params + } else { + Write-Message -Level Verbose -Message "master cert found on $($server.Name)" + } + + $null = $db.Refresh() + $null = $server.Refresh() + + $mastercerttest = Get-DbaFile -SqlInstance $server -Path $BackupPath | Where-Object FileName -match "$($mastercert.Name).cer" + if (-not $mastercerttest) { + # Back up certificate + $null = $server.Databases["master"].Refresh() + $params = @{ + SqlInstance = $server + Database = "master" + Certificate = $mastercert.Name + Path = $BackupPath + EnableException = $true + EncryptionPassword = $BackupSecurePassword + } + Write-Message -Level Verbose -Message "Backing up master certificate on $($server.Name)" + $null = Backup-DbaDbCertificate @params + } + + if (-not $EncryptorName) { + Write-Message -Level Verbose -Message "Getting EncryptorName from master cert on $($server.Name)" + $EncryptorName = $mastercert.Name + } + } else { + $masterasym = Get-DbaDbAsymmetricKey -SqlInstance $server -Database master + + if (-not $masterasym) { + Write-Message -Level Verbose -Message "Asymmetric key not found, creating one for master on $($server.Name)" + $params = @{ + SqlInstance = $server + Database = "master" + EnableException = $true + } + $masterasym = New-DbaDbAsymmetricKey @params + $null = $server.Refresh() + $null = $server.Databases["master"].Refresh() + } else { + Write-Message -Level Verbose -Message "master asymmetric key found on $($server.Name)" + } + + if (-not $EncryptorName) { + Write-Message -Level Verbose -Message "Getting EncryptorName from master asymmetric key" + $EncryptorName = $masterasym.Name + } } + } catch { + Stop-Function -Message "Failure" -ErrorRecord $_ -Continue } - } catch { - Stop-Function -Message "Failure" -ErrorRecord $_ -Continue - } - try { - # Create a database encryption key in the target database - # Enable database encryption on the target database - Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Creating database encryption key in $($db.Name) on $($server.Name)" - if ($db.HasDatabaseEncryptionKey) { - Write-Message -Level Verbose -Message "$($db.Name) on $($db.Parent.Name) already has a database encryption key" - } else { - Write-Message -Level Verbose -Message "Creating new encryption key for $($db.Name) on $($server.Name) with EncryptorName $EncryptorName" - $null = $db | New-DbaDbEncryptionKey -EncryptorName $EncryptorName -EnableException - } + try { + # Create a database encryption key in the target database + # Enable database encryption on the target database + Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Creating database encryption key in $($db.Name) on $($server.Name)" + if ($db.HasDatabaseEncryptionKey) { + Write-Message -Level Verbose -Message "$($db.Name) on $($db.Parent.Name) already has a database encryption key" + } else { + Write-Message -Level Verbose -Message "Creating new encryption key for $($db.Name) on $($server.Name) with EncryptorName $EncryptorName" + $null = $db | New-DbaDbEncryptionKey -EncryptorName $EncryptorName -EnableException + } - Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Enabling database encryption in $($db.Name) on $($server.Name)" - Write-Message -Level Verbose -Message "Enabling encryption for $($db.Name) on $($server.Name) using $EncryptorType $EncryptorName" - $db | Enable-DbaDbEncryption -EncryptorName $EncryptorName - } catch { - Stop-Function -Message "Failure" -ErrorRecord $_ -Continue - } + Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Enabling database encryption in $($db.Name) on $($server.Name)" + Write-Message -Level Verbose -Message "Enabling encryption for $($db.Name) on $($server.Name) using $EncryptorType $EncryptorName" + $db | Enable-DbaDbEncryption -EncryptorName $EncryptorName + } catch { + Stop-Function -Message "Failure" -ErrorRecord $_ -Continue + } } } else { # Parallel processing - group databases by instance and pre-create shared resources @@ -486,7 +485,23 @@ function Start-DbaDbEncryption { Stop-Function -Message "Failed to create shared resources on $servername" -ErrorRecord $_ -Continue } - # Step 3: Parallelize database encryption operations + # Step 3: Create a database encryption key in the target database if needed + # This has to be done before parallel processing as New-DbaDbEncryptionKey uses Get-DbaDatabase internally + # which uses the custom method .Query() that is not present in runspaces due to the way dbatools is loaded there. + foreach ($db in $InputObject) { + try { + if ($db.HasDatabaseEncryptionKey) { + Write-Message -Level Verbose -Message "$($db.Name) on $($db.Parent.Name) already has a database encryption key" + } else { + Write-Message -Level Verbose -Message "Creating new encryption key for $($db.Name) on $($server.Name) with EncryptorName $encryptorNameToUse" + $null = $db | New-DbaDbEncryptionKey -EncryptorName $encryptorNameToUse -EnableException + } + } catch { + Stop-Function -Message "Failure" -ErrorRecord $_ -Continue + } + } + + # Step 4: Parallelize database encryption operations $encryptionScript = { param ( $ServerName, @@ -510,13 +525,8 @@ function Start-DbaDbEncryption { throw "Database $DatabaseName not found on $ServerName" } - # Create encryption key if needed - if (-not $db.HasDatabaseEncryptionKey) { - $null = $db | New-DbaDbEncryptionKey -EncryptorName $EncryptorName -EnableException:$true -Confirm:$false - } - # Enable encryption - $result = $db | Enable-DbaDbEncryption -EncryptorName $EncryptorName -Confirm:$false + $result = $db | Enable-DbaDbEncryption -EncryptorName $EncryptorName -EnableException -Confirm:$false [PSCustomObject]@{ ComputerName = $server.ComputerName diff --git a/tests/Start-DbaDbEncryption.Tests.ps1 b/tests/Start-DbaDbEncryption.Tests.ps1 index b1fbdd19931..aa221965524 100644 --- a/tests/Start-DbaDbEncryption.Tests.ps1 +++ b/tests/Start-DbaDbEncryption.Tests.ps1 @@ -83,8 +83,8 @@ Describe $CommandName -Tag IntegrationTests { BackupSecurePassword = $passwd BackupPath = $backupPath } - $results = Start-DbaDbEncryption @splatEncryption -WarningVariable warn - $warn | Should -BeNullOrEmpty + $results = Start-DbaDbEncryption @splatEncryption + $WarnVar | Should -BeNullOrEmpty $results.Count | Should -Be 5 $results | Select-Object -First 1 -ExpandProperty EncryptionEnabled | Should -Be $true $results | Select-Object -First 1 -ExpandProperty DatabaseName | Should -Match "random" @@ -128,8 +128,9 @@ Describe $CommandName -Tag IntegrationTests { BackupPath = $parallelBackupPath Parallel = $true } - $results = Start-DbaDbEncryption @splatParallelEncryption -WarningVariable warn - $warn | Should -BeNullOrEmpty + # Warnings during parallel execution are not catched in $WarnVar as they are in different runspaces + $results = Start-DbaDbEncryption @splatParallelEncryption + $WarnVar | Should -BeNullOrEmpty $results.Count | Should -Be 3 foreach ($result in $results) { $result.EncryptionEnabled | Should -Be $true