Rotieren von Always Encrypted-Schlüsseln mithilfe von PowerShell

Gilt für:SQL ServerAzure SQL-DatenbankAzure SQL Managed Instance

Dieser Artikel enthält die Schritte zur Rotation von Always Encrypted-Schlüsseln unter Verwendung des SqlServer PowerShell-Moduls. Informationen darüber, wie Sie mit dem SqlServer PowerShell-Modul für Always Encrypted beginnen können, finden Sie unter Konfigurieren von Always Encrypted mithilfe von PowerShell.

Hinweis

Microsoft empfiehlt die Verwendung von PowerShell 7 oder höher beim Ausführen von Always Encrypted PowerShell-Skripts. PowerShell 7 bietet eine verbesserte plattformübergreifende Unterstützung, bessere Leistung und die neueste Kompatibilität mit dem SqlServer-Modul (v22+), die für viele Always Encrypted-Szenarien erforderlich ist.

Das Drehen von Always Encrypted-Schlüsseln ist der Vorgang, einen vorhandenen Schlüssel durch einen neuen zu ersetzen. Möglicherweise müssen Sie einen Schlüssel drehen, wenn er kompromittiert ist, oder um die Richtlinien oder Compliancebestimmungen Ihrer Organisation einzuhalten, die eine regelmäßige Rotation von kryptografischen Schlüsseln erfordern.

Always Encrypted verwendet zwei Schlüsseltypen, es gibt also zwei allgemeine Schlüsselrotationsworkflows: das Drehen von Spaltenhauptschlüssel und das Drehen von Spaltenverschlüsselungsschlüssel.

  • Rotieren des Spaltenverschlüsselungsschlüssels – umfasst das Entschlüsseln von Daten, die mit dem vorhandenen Schlüssel verschlüsselt sind und das erneute Verschlüsseln der Daten mit einem neuen Spaltenverschlüsselungsschlüssel. Da die Drehung eines Spaltenverschlüsselungsschlüssels sowohl Zugriff auf die Schlüssel als auch auf die Datenbank erfordert, kann sie nur ohne Rollentrennung durchgeführt werden.
  • Rotation des Spaltenhauptschlüssels – umfasst das Entschlüsseln von Spaltenverschlüsselungsschlüsseln, die mit dem aktuellen Spaltenhauptschlüssel verschlüsselt sind, das erneute Verschlüsseln mit dem neuen Spaltenhauptschlüssel sowie das Aktualisieren der Metadaten für beide Schlüsseltypen. Die Rotation des Spaltenhauptschlüssels kann mit oder ohne Rollentrennung abgeschlossen werden (bei Verwendung des SqlServer PowerShell-Moduls).

Rotation eines Spaltenhauptschlüssels ohne Rollentrennung

Die in diesem Abschnitt beschriebene Methode zum Drehen eines Spaltenmasterschlüssels unterstützt keine Rollentrennung zwischen einem Sicherheitsadministrator und einem DBA. Einige der folgenden Schritte kombinieren Vorgänge für die physischen Schlüssel mit Vorgängen für Schlüsselmetadaten. Verwenden Sie diesen Workflow, wenn Ihre Organisation das DevOps-Modell verwendet oder wenn Ihre Datenbank in der Cloud gehostet wird, und das primäre Ziel besteht darin, Cloudadministratoren (aber nicht lokale DBAs) auf den Zugriff auf vertrauliche Daten zu beschränken. Verwenden Sie diese Methode nicht, wenn potenzielle Angreifer DBAs enthalten oder wenn DBAs keinen Zugriff auf vertrauliche Daten haben sollten.

Aufgabe Artikel Greift auf Klartextschlüssel/Schlüsselspeicher zu Greift auf Datenbank zu
Schritt 1. Erstellen Sie einen neuen Spaltenhauptschlüssel in einem Schlüsselspeicher.

Hinweis: Das PowerShell-Modul SqlServer unterstützt diesen Schritt nicht. Sie müssen Tools verwenden, die für Ihren Schlüsselspeicher spezifisch sind, um diese Aufgabe mithilfe der Befehlszeile durchzuführen. Wenn Sie Azure Key Vault als Schlüsselspeicher verwenden, wird die vom Kunden verwaltete Schlüsselrotation in Multimandantenumgebungen nicht unterstützt. Stellen Sie sicher, dass sich der neue kundenseitig verwaltete Schlüssel im selben Mandanten wie der vorhandene befindet.
Erstellen und Speichern von Spaltenhauptschlüsseln für Always Encrypted Ja Nein
Schritt 2. Starten Sie eine PowerShell-Umgebung, und importieren Sie das SqlServer-Modul. Importieren des SqlServer-Moduls Nein Nein
Schritt 3. Stellen Sie eine Verbindung mit Ihrem Server und Ihrer Datenbank her. Herstellen einer Verbindung mit einer Datenbank Nein Ja
Schritt 4. Erstellen Sie ein SqlColumnMasterKeySettings-Objekt, dass Informationen über den Speicherort Ihres neuen Spaltenhauptschlüssels enthält. SqlColumnMasterKeySettings ist ein Objekt, das im Arbeitsspeicher (in PowerShell) vorhanden ist. Verwenden Sie das Cmdlet, das für Ihren Schlüsselspeicher spezifisch ist, um es zu erstellen. New-SqlAzureKeyVaultColumnMasterKeySettings

New-SqlCertificateStoreColumnMasterKeySettings

New-SqlCngColumnMasterKeySettings

New-SqlCspColumnMasterKeySettings
Nein Nein
Schritt 5. Erstellen Sie die Metadaten über den neuen Spaltenhauptschlüssel in Ihrer Datenbank. New-SqlColumnMasterKey

Hinweis: Intern führt dieses Cmdlet die CREATE COLUMN MASTER KEY (Transact-SQL)-Anweisung aus, um Schlüsselmetadaten zu erstellen.
Nein Ja
Schritt 6. Authentifizieren Sie sich bei Azure, wenn Ihr aktueller oder neuer Spaltenhauptschlüssel in einem Schlüsseltresor oder einem verwalteten HSM im Azure Key Vault gespeichert ist. Connect-AzAccount Ja Nein
Schritt 7. Rufen Sie ein Zugriffstoken für Azure Key Vaults ab, wenn Ihr Spaltenhauptschlüssel in Azure Key Vault gespeichert ist. Get-AzAccessToken Nein Nein
Schritt 8: Starten Sie die Rotation, indem Sie jeden der Spaltenverschlüsselungsschlüssel verschlüsseln, der aktuell mit dem alten Spaltenhauptschlüssel geschützt ist, indem Sie den neuen Spaltenhauptschlüssel verwenden. Nach diesem Schritt ist jeder betroffene Spaltenverschlüsselungsschlüssel (der dem alten Spaltenhauptschlüssel zugeordnet ist, der rotiert wird) jeweils mit dem alten und dem neuen Spaltenhauptschlüssel verschlüsselt und besitzt zwei verschlüsselte Werte in den Datenbankmetadaten. Invoke-SqlColumnMasterKeyRotation Ja Ja
Schritt 9 Stimmen Sie mit den Administratoren aller Anwendungen ab, die verschlüsselte Spalten in der Datenbank abfragen (die mit dem alten Spaltenhauptschlüssel geschützt sind), sodass sie sicherstellen können, dass die Anwendungen über Zugriff auf den neuen Spaltenhauptschlüssel verfügen. Create and Store Column Master Keys (Always Encrypted) (Erstellen und Speichern von Spaltenhauptschlüsseln (Always Encrypted)) Ja Nein
Schritt 10. Schließen Sie die Rotation ab.

Hinweis: Stellen Sie vor der Ausführung dieses Schritts sicher, dass alle Anwendungen, die verschlüsselte Spalten abfragen, die mit dem alten Spaltenhauptschlüssel geschützt sind, so konfiguriert sind, dass sie den neuen Spaltenhauptschlüssel verwenden. Wenn Sie diesen Schritt vorzeitig ausführen, können einige der Anwendungen möglicherweise die Daten nicht verschlüsseln. Schließen Sie die Rotation ab, indem Sie die verschlüsselten Werte aus der Datenbank entfernen, die mit dem alten Spaltenhauptschlüssel generiert wurden. Dadurch wird die Zuordnung zwischen dem alten Spaltenhauptschlüssel und den von ihm geschützten Spaltenverschlüsselungsschlüsseln entfernt.
Complete-SqlColumnMasterKeyRotation Nein Ja
Schritt 11 Entfernen Sie die Metadaten aus dem alten Spaltenhauptschlüssel. Remove-SqlColumnMasterKey Nein Ja

Hinweis

Es wird dringend empfohlen, dass Sie den alten Spaltenhauptschlüssel nach der Rotation nicht dauerhaft löschen. Stattdessen sollten Sie den alten Spaltenhauptschlüssel im aktuellen Schlüsselspeicher oder an einem anderen sicheren Ort archivieren. Wenn Sie die Datenbank aus einer Sicherungsdatei auf einen Zeitpunkt vor der Konfiguration des neuen Spaltenhauptschlüssels wiederherstellen, benötigen Sie den alten Schlüssel für den Datenzugriff.

Rotieren eines Spaltenhauptschlüssels ohne Rollentrennung (Windows-Zertifikat-Beispiel)

Das folgende Skript stellt ein End-to-End-Beispiel dar, das einen vorhandenen Spaltenhauptschlüssel (CMK1) mit einem neuen Spaltenhauptschlüssel (CMK2) ersetzt.

[CmdletBinding()]
param(
	[Parameter(Mandatory = $false)]
	[string]$ServerName = '<server name>',

	[Parameter(Mandatory = $false)]
	[ValidateNotNullOrEmpty()]
	[string]$DatabaseName = '<database name>',

	[Parameter(Mandatory = $false)]
	[string]$CertificateSubject = 'AlwaysEncryptedCertNew',

	[Parameter(Mandatory = $false)]
	[ValidateNotNullOrEmpty()]
	[string]$OldCmkName = 'CMK1',

	[Parameter(Mandatory = $false)]
	[ValidateNotNullOrEmpty()]
	[string]$NewCmkName = 'CMK2'
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop

Write-Host '[AE] Step 1: Creating a new self-signed certificate for the new CMK'
$cert = New-SelfSignedCertificate `
	-Subject $CertificateSubject `
	-CertStoreLocation 'Cert:CurrentUser\My' `
	-KeyExportPolicy Exportable `
	-Type DocumentEncryptionCert `
	-KeyUsage KeyEncipherment `
	-KeySpec KeyExchange `
	-KeyLength 2048
Write-Host "[AE] Certificate created with thumbprint: $($cert.Thumbprint)"

Write-Host "[AE] Step 2: Connecting to SQL Server '$ServerName' / Database '$DatabaseName'"
$connStr = "Server=$ServerName;Database=$DatabaseName;Integrated Security=True;Encrypt=True;TrustServerCertificate=True;Connection Timeout=30"

try {
	$database = Get-SqlDatabase -ConnectionString $connStr -ErrorAction Stop
}
catch {
	Write-Error "Failed to connect to '$ServerName' / '$DatabaseName'. Verify instance, database, and local permissions."
	throw
}

Write-Host "[AE] Step 3: Validating that old CMK '$OldCmkName' exists"
$oldCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $OldCmkName }
if (-not $oldCmk) {
	throw "Old CMK '$OldCmkName' does not exist. Cannot rotate."
}
Write-Host "[AE] Old CMK '$OldCmkName' found."

Write-Host "[AE] Step 4: Creating CMK settings for new certificate"
$newCmkSettings = New-SqlCertificateStoreColumnMasterKeySettings -CertificateStoreLocation 'CurrentUser' -Thumbprint $cert.Thumbprint

Write-Host "[AE] Step 5: Registering new CMK '$NewCmkName' in the database"
$newCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $NewCmkName }
if ($newCmk) {
	Write-Host "[AE] New CMK '$NewCmkName' already exists. Skipping creation."
}
else {
	New-SqlColumnMasterKey -Name $NewCmkName -InputObject $database -ColumnMasterKeySettings $newCmkSettings | Out-Null
	Write-Host "[AE] New CMK '$NewCmkName' registered."
}

Write-Host "[AE] Step 6: Initiating CMK rotation from '$OldCmkName' to '$NewCmkName'"
Write-Host "[AE] (This re-encrypts all associated CEKs under the new CMK...)"
Invoke-SqlColumnMasterKeyRotation `
	-SourceColumnMasterKeyName $OldCmkName `
	-TargetColumnMasterKeyName $NewCmkName `
	-InputObject $database
Write-Host "[AE] Rotation initiated."

Write-Host "[AE] Step 7: Completing the CMK rotation"
Complete-SqlColumnMasterKeyRotation `
	-SourceColumnMasterKeyName $OldCmkName `
	-InputObject $database
Write-Host "[AE] Rotation completed."

Write-Host "[AE] Step 8: Verifying CEKs are now under '$NewCmkName'"
$query = "SELECT name FROM sys.column_encryption_keys WHERE name = N'$($NewCmkName)'"
$rotatedCeks = Invoke-SqlCmd -ServerInstance $ServerName -Database $DatabaseName -Query $query -TrustServerCertificate  -ErrorAction SilentlyContinue
if ($rotatedCeks) {
	$cekCount = @($rotatedCeks).Count
	if ($cekCount -eq 0) { $cekCount = 1 }
	Write-Host "[AE] Verified: $cekCount CEK(s) now under '$NewCmkName'"
	@($rotatedCeks) | ForEach-Object { Write-Host "  - $($_.name)" }
}

Write-Host "[AE] Step 9: Removing old CMK metadata '$OldCmkName'"
Remove-SqlColumnMasterKey -Name $OldCmkName -InputObject $database
Write-Host "[AE] Old CMK '$OldCmkName' removed."

Write-Host '[AE] ========== Rotation Complete =========='
Write-Host "[AE] Old CMK: $OldCmkName (deleted)"
Write-Host "[AE] New CMK: $NewCmkName (active)"
Write-Host '[AE] All CEKs have been re-encrypted under the new CMK.'

Rotation eines Spaltenhauptschlüssels mit Rollentrennung

Der in diesem Abschnitt beschriebene Workflow der Rotation eines Spaltenhauptschlüssels stellt die Trennung zwischen einem Sicherheitsadministrator und einem DBA sicher.

Wichtig

Stellen Sie vor der Ausführung der Schritte, für die in der nachfolgenden Tabelle Greift auf Klartextschlüssel/Schlüsselspeicher zu=Ja (Schritte, die auf Nur-Text-Schlüssel oder Schlüsselspeicher zugreifen) gilt, sicher, dass die PowerShell-Umgebung auf einem sicheren Computer ausgeführt wird, der nicht der Computer ist, auf dem Ihre Datenbank gehostet wird. Weitere Informationen finden Sie im Abschnitt Security Considerations for Key Management(Überlegungen zur Sicherheit für die Schlüsselverwaltung).

Teil 1: DBA

Ein DBA ruft Metadaten über den Spaltenhauptschlüssel ab, der rotiert werden soll, und über die betroffenen Spaltenverschlüsselungsschlüssel, die dem aktuellen Spaltenhauptschlüssel zugeordnet sind. Der DBA gibt alle Informationen für den Sicherheitsadministrator frei.

Aufgabe Artikel Greift auf Klartextschlüssel/Schlüsselspeicher zu Greift auf Datenbank zu
Schritt 1. Starten Sie eine PowerShell-Umgebung, und importieren Sie das SqlServer-Modul. Importieren des SqlServer-Moduls Nein Keine
Schritt 2. Stellen Sie eine Verbindung mit Ihrem Server und Ihrer Datenbank her. Herstellen einer Verbindung mit einer Datenbank Nein Ja
Schritt 3. Rufen Sie die Metadaten über den alten Spaltenhauptschlüssel ab. Get-SqlColumnMasterKey Nein Ja
Schritt 4. Rufen Sie die Metadaten über Spaltenverschlüsselungsschlüssel ab, die vom alten Spaltenhauptschlüssel geschützt sind, einschließlich ihrer verschlüsselten Werte. Get-SqlColumnEncryptionKey Nein Ja
Schritt 5. Geben Sie den Speicherort des Spaltenhauptschlüssels (den Anbieternamen und einen Schlüsselpfad des Spaltenhauptschlüssels) sowie die verschlüsselten Werte der Spaltenverschlüsselungsschlüssel frei, die mit dem alten Spaltenhauptschlüssel geschützt sind. Beispiele hierzu finden Sie weiter unten. Nein Nein

Teil 2: Sicherheitsadministrator

Der Sicherheitsadministrator generiert einen neuen Spaltenhauptschlüssel, verschlüsselt den betroffenen Spaltenverschlüsselungsschlüssel erneut mit einem neuen Spaltenhauptschlüssel und gibt die Informationen über den neuen Spaltenhauptschlüssel sowie einen Satz neuer verschlüsselter Werte für die betroffenen Spaltenverschlüsselungsschlüssel für den DBA frei.

Aufgabe Artikel Greifen Sie auf Klartextschlüssel und Schlüsselspeicher zu Greift auf Datenbank zu
Schritt 1. Rufen Sie von Ihrem DBA den Speicherort des alten Spaltenhauptschlüssels sowie die verschlüsselten Werte der zugeordneten Spaltenverschlüsselungsschlüssel ab, die mit dem alten Spaltenhauptschlüssel geschützt sind. n/v
Beispiele hierzu finden Sie weiter unten.
Nein Nein
Schritt 2. Erstellen Sie einen neuen Spaltenhauptschlüssel in einem Schlüsselspeicher.

Hinweis: Das Modul SqlServer unterstützt diesen Schritt nicht. Sie müssen die Tools Verwenden, die für den Typ Ihres Schlüsselspeichers spezifisch sind, um diese Aufgabe mithilfe einer Befehlszeile durchzuführen. Wenn Sie Azure Key Vault als Schlüsselspeicher verwenden, wird die vom Kunden verwaltete Schlüsselrotation in Multimandantenumgebungen nicht unterstützt. Stellen Sie sicher, dass sich der neue kundenseitig verwaltete Schlüssel im selben Mandanten wie der vorhandene befindet.
Erstellen und Speichern von Spaltenhauptschlüsseln für Always Encrypted Ja Nein
Schritt 3. Starten Sie eine PowerShell-Umgebung, und importieren Sie das SqlServer-Modul. Importieren des SqlServer-Moduls Nein Nein
Schritt 4. Erstellen Sie ein SqlColumnMasterKeySettings-Objekt, dass Informationen über den Speicherort Ihres alten Spaltenhauptschlüssels enthält. SqlColumnMasterKeySettings ist ein Objekt, das im Arbeitsspeicher (in PowerShell) vorhanden ist. New-SqlColumnMasterKeySettings Nein Nein
Schritt 5. Erstellen Sie ein SqlColumnMasterKeySettings-Objekt, dass Informationen über den Speicherort Ihres neuen Spaltenhauptschlüssels enthält. SqlColumnMasterKeySettings ist ein Objekt, das im Arbeitsspeicher (in PowerShell) vorhanden ist. Verwenden Sie das Cmdlet, das für Ihren Schlüsselspeicher spezifisch ist, um es zu erstellen. New-SqlAzureKeyVaultColumnMasterKeySettings

New-SqlCertificateStoreColumnMasterKeySettings

New-SqlCngColumnMasterKeySettings

New-SqlCspColumnMasterKeySettings
Nein Nein
Schritt 6. Authentifizieren Sie sich bei Azure, wenn Ihr bisheriger (aktueller) oder neuer Spaltenhauptschlüssel in einem Schlüsseltresor oder einem verwalteten HSM im Azure Key Vault gespeichert ist. Connect-AzAccount Ja Nein
Schritt 7. Rufen Sie ein Zugriffstoken für Azure Key Vaults ab, wenn Ihr Spaltenhauptschlüssel in Azure Key Vault gespeichert ist. Get-AzAccessToken Nein Nein
Schritt 8: Verschlüsseln Sie jeden Wert des Spaltenverschlüsselungsschlüssels erneut, der aktuell mit dem alten Spaltenhauptschlüssel geschützt ist, indem Sie den neuen Spaltenhauptschlüssel verwenden. New-SqlColumnEncryptionKeyEncryptedValue

Hinweis: Übergeben Sie beim Aufrufen dieses Cmdlets die SqlColumnMasterKeySettings-Objekte jeweils für den alten und den neuen Spaltenhauptschlüssel zusammen mit einem Wert des Spaltenverschlüsselungsschlüssels, der erneut verschlüsselt werden soll.
Ja Nein
Schritt 9 Geben Sie den Speicherort des neuen Spaltenhauptschlüssels (den Anbieternamen und einen Schlüsselpfad des Spaltenhauptschlüssels) sowie den Satz der neu verschlüsselten Werte der Spaltenverschlüsselungsschlüssel für Ihren Datenbankadministrator frei . Beispiele hierzu finden Sie weiter unten. Nein Nein

Hinweis

Es wird dringend empfohlen, dass Sie den alten Spaltenhauptschlüssel nach der Rotation nicht dauerhaft löschen. Stattdessen sollten Sie den alten Spaltenhauptschlüssel im aktuellen Schlüsselspeicher oder an einem anderen sicheren Ort archivieren. Wenn Sie die Datenbank aus einer Sicherungsdatei auf einen Zeitpunkt vor der Konfiguration des neuen Spaltenhauptschlüssels wiederherstellen, benötigen Sie den alten Schlüssel für den Datenzugriff.

Teil 3: DBA

Der DBA erstellt Metadaten für den neuen Spaltenhauptschlüssel und aktualisiert die Metadaten der betroffenen Spaltenverschlüsselungsschlüssel, um den neuen Satz verschlüsselter Werte hinzuzufügen. In diesem Schritt koordiniert der DBA mit den Administratoren der Anwendungen, die Verschlüsselungsspalten abfragen. Diese stellen sicher, dass die Anwendung über Zugriff auf den neuen Spaltenhauptschlüssel verfügt. Sobald alle Anwendungen so eingerichtet sind, dass sie den neuen Spaltenhauptschlüssel verwenden können, entfernt der DBA den alten Satz der verschlüsselten Werte sowie die alten Spaltenhauptschlüssel-Metadaten.

Aufgabe Artikel Greifen Sie auf Klartextschlüssel und Schlüsselspeicher zu Greift auf Datenbank zu
Schritt 1. Rufen Sie von Ihrem Sicherheitsadministrator den Speicherort des neuen Spaltenhauptschlüssels sowie den neuen Satz von verschlüsselten Werten der entsprechenden Spaltenverschlüsselungsschlüssel ab, die mit dem alten Spaltenhauptschlüssel geschützt sind. Beispiele hierzu finden Sie weiter unten. Nein Nein
Schritt 2. Starten Sie eine PowerShell-Umgebung, und importieren Sie das SqlServer-Modul. Importieren des SqlServer-Moduls Nein Nein
Schritt 3. Stellen Sie eine Verbindung mit Ihrem Server und Ihrer Datenbank her. Herstellen einer Verbindung mit einer Datenbank Nein Ja
Schritt 4. Erstellen Sie ein SqlColumnMasterKeySettings-Objekt, dass Informationen über den Speicherort Ihres neuen Spaltenhauptschlüssels enthält. SqlColumnMasterKeySettings ist ein Objekt, das im Arbeitsspeicher (in PowerShell) vorhanden ist. New-SqlColumnMasterKeySettings Nein Nein
Schritt 5. Erstellen Sie die Metadaten über den neuen Spaltenhauptschlüssel in Ihrer Datenbank. New-SqlColumnMasterKey

Hinweis: Intern führt dieses Cmdlet die Anweisung CREATE COLUMN MASTER KEY (Transact-SQL) aus, um Schlüsselmetadaten zu erstellen.
Nein Ja
Schritt 6. Rufen Sie die Metadaten über Spaltenverschlüsselungsschlüssel ab, die vom alten Spaltenhauptschlüssel geschützt werden. Get-SqlColumnEncryptionKey Nein Ja
Schritt 7. Fügen Sie einen neuen verschlüsselten Wert (der mit dem neuen Spaltenhauptschlüssel erstellt wurde) zu den Metadaten für jeden betroffenen Spaltenverschlüsselungsschlüssel hinzu. Add-SqlColumnEncryptionKeyValue Nein Ja
Schritt 8: Stimmen Sie mit den Administratoren aller Anwendungen ab, die verschlüsselte Spalten in der Datenbank abfragen (die mit dem alten Spaltenhauptschlüssel geschützt sind), sodass sie sicherstellen können, dass die Anwendungen über Zugriff auf den neuen Spaltenhauptschlüssel verfügen. Erstellen und Speichern von Spaltenhauptschlüsseln (Always Encrypted) Nein Nein
Schritt 9 Schließen Sie die Rotation ab, indem Sie die verschlüsselten Werte, die mit dem alten Spaltenhauptschlüssel generiert wurden aus der Datenbank entfernen.

Hinweis: Stellen Sie vor der Ausführung dieses Schritts sicher, dass alle Anwendungen, die verschlüsselte Spalten abfragen, die mit dem alten Spaltenhauptschlüssel geschützt sind, so konfiguriert sind, dass sie den neuen Spaltenhauptschlüssel verwenden. Wenn Sie diesen Schritt vorzeitig ausführen, können einige der Anwendungen möglicherweise die Daten nicht verschlüsseln.

Durch diesen Schritt wird eine Zuordnung zwischen dem alten Spaltenhauptschlüssel und den von ihm geschützten Spaltenverschlüsselungsschlüsseln entfernt.
Complete-SqlColumnMasterKeyRotation

Alternativ können Sie den Wert Remove-SqlColumnEncryptionKeyValueverwenden.
Nein Ja
Schritt 10. Entfernen Sie die Metadaten des alten Spaltenhauptschlüssel aus der Datenbank. Remove-SqlColumnMasterKey Nein Ja

Rotieren eines Spaltenhauptschlüssels mit Rollentrennung (Windows-Zertifikat-Beispiel)

Das folgende Skript ist ein End-to-End-Beispiel zum Generieren eines neuen Spaltenhauptschlüssels, der im Windows-Zertifikatspeicher ein Zertifikat darstellt, und zum Rotieren eines vorhandenen (aktuellen) Spaltenhauptschlüssels, um diesen mit dem neuen Spaltenhauptschlüssel zu ersetzen. Das Skript nimmt an, dass die Zieldatenbank den Spaltenhauptschlüssel namens CMK1 (der rotiert werden soll) enthält, der einige Spaltenverschlüsselungsschlüssel verschlüsselt.

Teil 1: DBA

[CmdletBinding()]
param(
	[Parameter(Mandatory = $false)]
	[ValidateNotNullOrEmpty()]
	[string]$ServerName = '<server name>',

	[Parameter(Mandatory = $false)]
	[ValidateNotNullOrEmpty()]
	[string]$DatabaseName = '<database name>',

	[Parameter(Mandatory = $false)]
	[ValidateNotNullOrEmpty()]
	[string]$OldCmkName = 'CMK2',

	[Parameter(Mandatory = $false)]
	[ValidateNotNullOrEmpty()]
	[string]$OutputFolder = 'C:\temp'
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop

Write-Host "[CEK Export] Starting CMK and CEK data export"

# Validate output folder
if (-not (Test-Path -Path $OutputFolder -PathType Container)) {
	Write-Host "[CEK Export] Creating output folder: $OutputFolder"
	New-Item -Path $OutputFolder -ItemType Directory | Out-Null
}

# Connect to database
Write-Host "[CEK Export] Connecting to '$ServerName' / '$DatabaseName'"
$connStr = "Server=$ServerName;Database=$DatabaseName;Integrated Security=True;TrustServerCertificate=True;Connection Timeout=30"

try {
	$database = Get-SqlDatabase -ConnectionString $connStr -ErrorAction Stop
}
catch {
	Write-Error "Failed to connect to '$ServerName' / '$DatabaseName'."
	throw
}

# Retrieve old CMK
Write-Host "[CEK Export] Retrieving CMK '$OldCmkName'"
$oldCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $OldCmkName }
if (-not $oldCmk) {
	throw "CMK '$OldCmkName' not found in database '$DatabaseName'."
}

# Export CMK metadata using fixed text file name
$cmkFile = Join-Path $OutputFolder "oldcmkdata.txt"
Write-Host "[CEK Export] Exporting CMK metadata to: $cmkFile"
"CMKName|KeyStoreProviderName|KeyPath" | Set-Content -Path $cmkFile -Encoding UTF8
"$OldCmkName|$($oldCmk.KeyStoreProviderName)|$($oldCmk.KeyPath)" | Add-Content -Path $cmkFile -Encoding UTF8
Write-Host "[CEK Export]   ✓ CMK metadata exported"

# Discover and export CEKs using fixed text file name
Write-Host "[CEK Export] Discovering CEKs associated with '$OldCmkName'"
$ceks = Get-SqlColumnEncryptionKey -InputObject $database
$cekFile = Join-Path $OutputFolder "oldcekvalues.txt"
"CEKName|CEKEncryptedValue|HasMultipleEncryptedValues" | Set-Content -Path $cekFile -Encoding UTF8

$exportedCount = 0
$multiValueCount = 0

foreach ($cek in $ceks) {
	if (-not $cek.ColumnEncryptionKeyValues) {
		continue
	}

	# Check if this CEK has multiple encrypted values
	if ($cek.ColumnEncryptionKeyValues.Count -gt 1) {
		# CEK has multiple encrypted values - check if any reference the old CMK
		$refersToOldCmk = $cek.ColumnEncryptionKeyValues | Where-Object { $_.ColumnMasterKeyName -eq $OldCmkName }
		if ($refersToOldCmk) {
			Write-Warning "CEK '$($cek.Name)' has $($cek.ColumnEncryptionKeyValues.Count) encrypted values. One references '$OldCmkName'. This CEK cannot be rotated automatically."
			"$($cek.Name)|MULTIPLE_ENCRYPTED_VALUES|True" | Add-Content -Path $cekFile -Encoding UTF8
			$multiValueCount++
		}
	}
	else {
		# CEK has single encrypted value - check if it references the old CMK
		if ($cek.ColumnEncryptionKeyValues[0].ColumnMasterKeyName -eq $OldCmkName) {
			$encryptedValueHex = "0x" + -join ($cek.ColumnEncryptionKeyValues[0].EncryptedValue | ForEach-Object { $_.ToString("X2") })
			"$($cek.Name)|$encryptedValueHex|False" | Add-Content -Path $cekFile -Encoding UTF8
			$exportedCount++
		}
	}
}

Write-Host "[CEK Export]   ✓ CEK encrypted values exported"
Write-Host "[CEK Export]     - Exported: $exportedCount CEK(s)"
if ($multiValueCount -gt 0) {
	Write-Warning "      - Multi-valued CEKs (manual review needed): $multiValueCount"
}

Write-Host "[CEK Export] ===== Export Complete ====="
Write-Host "[CEK Export] CMK Metadata:   $cmkFile"
Write-Host "[CEK Export] CEK Values:     $cekFile"

Teil 2: Sicherheitsadministrator

[CmdletBinding()]
param(
    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$ShareFolder = 'C:\Temp\',

    [Parameter(Mandatory = $false)]
    [ValidateSet('CurrentUser', 'LocalMachine')]
    [string]$StoreLocation = 'CurrentUser',

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$CertificateSubject = 'AlwaysEncryptedCert'
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop

function Import-DelimitedTextFile {
    param(
        [Parameter(Mandatory = $true)] [string]$Path,
        [Parameter(Mandatory = $true)] [string[]]$RequiredColumns
    )

    if (-not (Test-Path -Path $Path -PathType Leaf)) {
        throw "Required file not found: $Path"
    }

    $raw = Get-Content -Path $Path -Raw
    if ([string]::IsNullOrWhiteSpace($raw)) {
        throw "File is empty: $Path"
    }

    $delimiter = if ($raw -match '\|') { '|' } else { ',' }
    $rows = @(Import-Csv -Path $Path -Delimiter $delimiter)
    if ($rows.Count -eq 0) {
        throw "No data rows found in file: $Path"
    }

    $first = $rows[0]
    $RequiredColumns | ForEach-Object {
        if (-not $first.PSObject.Properties[$_]) {
            throw "Missing required column '$_' in file: $Path"
        }
    }

    return $rows
}

if (-not (Test-Path -Path $ShareFolder -PathType Container)) {
    throw "Share folder does not exist: $ShareFolder"
}

$oldCmkDataFile = Join-Path $ShareFolder 'oldcmkdata.txt'
$oldCekValuesFile = Join-Path $ShareFolder 'oldcekvalues.txt'
$newCmkDataFile = Join-Path $ShareFolder 'newcmkdata.txt'
$newCekValuesFile = Join-Path $ShareFolder 'newcekvalues.txt'

Write-Host "[AE] Reading old CMK data from '$oldCmkDataFile'"
$oldCmkDataRows = Import-DelimitedTextFile -Path $oldCmkDataFile -RequiredColumns @('KeyStoreProviderName', 'KeyPath')
$oldCmkData = $oldCmkDataRows[0]

Write-Host "[AE] Reading old CEK values from '$oldCekValuesFile'"
$oldCekValues = Import-DelimitedTextFile -Path $oldCekValuesFile -RequiredColumns @('CEKName', 'CEKEncryptedValue')

Write-Host "[AE] Finding or creating certificate '$CertificateSubject' in $StoreLocation\\My"
$certPath = "Cert:$StoreLocation\My"
$cert = Get-ChildItem -Path $certPath |
    Where-Object { $_.Subject -eq "CN=$CertificateSubject" } |
    Sort-Object NotAfter -Descending |
    Select-Object -First 1

if (-not $cert) {
    $cert = New-SelfSignedCertificate `
        -Subject $CertificateSubject `
        -CertStoreLocation $certPath `
        -KeyExportPolicy Exportable `
        -Type DocumentEncryptionCert `
        -KeyUsage DataEncipherment `
        -KeySpec KeyExchange
}

Write-Host '[AE] Building CMK settings'
$oldCmkSettings = New-SqlColumnMasterKeySettings `
    -KeyStoreProviderName $oldCmkData.KeyStoreProviderName `
    -KeyPath $oldCmkData.KeyPath

$newCmkSettings = New-SqlCertificateStoreColumnMasterKeySettings `
    -CertificateStoreLocation $StoreLocation `
    -Thumbprint $cert.Thumbprint

Write-Host "[AE] Re-encrypting CEK values and writing '$newCekValuesFile'"
"CEKName|CEKEncryptedValue" | Set-Content -Path $newCekValuesFile -Encoding UTF8

$oldCekValues | ForEach-Object {
    $newValue = New-SqlColumnEncryptionKeyEncryptedValue `
        -TargetColumnMasterKeySettings $newCmkSettings `
        -ColumnMasterKeySettings $oldCmkSettings `
        -EncryptedValue $_.CEKEncryptedValue

    "$($_.CEKName)|$newValue" | Add-Content -Path $newCekValuesFile -Encoding UTF8
}

Write-Host "[AE] Writing new CMK data to '$newCmkDataFile'"
"KeyStoreProviderName|KeyPath" | Set-Content -Path $newCmkDataFile -Encoding UTF8
"$($newCmkSettings.KeyStoreProviderName)|$($newCmkSettings.KeyPath)" | Add-Content -Path $newCmkDataFile -Encoding UTF8

Write-Host '[AE] Completed successfully'
Write-Host "[AE] Output files: $newCmkDataFile , $newCekValuesFile"

Teil 3: DBA

[CmdletBinding()]
param(
    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$ServerName = '<server name>',

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$DatabaseName = '<database name>',

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$OldCmkName = 'CMK1',

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$NewCmkName = 'CMK2',

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$InputFolder = 'C:\temp'
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop

function Import-DelimitedTextFile {
    param(
        [Parameter(Mandatory = $true)] [string]$Path,
        [Parameter(Mandatory = $true)] [string[]]$RequiredColumns
    )

    if (-not (Test-Path -Path $Path -PathType Leaf)) {
        throw "Required file not found: $Path"
    }

    $raw = Get-Content -Path $Path -Raw
    if ([string]::IsNullOrWhiteSpace($raw)) {
        throw "File is empty: $Path"
    }

    $delimiter = if ($raw -match '\|') { '|' } else { ',' }
    $rows = @(Import-Csv -Path $Path -Delimiter $delimiter)
    if ($rows.Count -eq 0) {
        throw "No data rows found in file: $Path"
    }

    $first = $rows[0]
    $RequiredColumns | ForEach-Object {
        if (-not $first.PSObject.Properties[$_]) {
            throw "Missing required column '$_' in file: $Path"
        }
    }

    return $rows
}

if (-not (Test-Path -Path $InputFolder -PathType Container)) {
    throw "Input folder not found: $InputFolder"
}

$newCmkDataFile = Join-Path $InputFolder 'newcmkdata.txt'
$newCekValuesFile = Join-Path $InputFolder 'newcekvalues.txt'

Write-Host "[AE] Reading new CMK data from '$newCmkDataFile'"
$newCmkRows = Import-DelimitedTextFile -Path $newCmkDataFile -RequiredColumns @('KeyStoreProviderName', 'KeyPath')
$newCmkData = $newCmkRows[0]

Write-Host "[AE] Reading new CEK values from '$newCekValuesFile'"
$newCekValues = Import-DelimitedTextFile -Path $newCekValuesFile -RequiredColumns @('CEKName', 'CEKEncryptedValue')

Write-Host "[AE] Connecting to '$ServerName' / '$DatabaseName'"
$connStr = "Server=$ServerName;Database=$DatabaseName;Integrated Security=True;TrustServerCertificate=True;Connection Timeout=30"
$database = Get-SqlDatabase -ConnectionString $connStr -ErrorAction Stop

Write-Host "[AE] Ensuring target CMK '$NewCmkName' exists"
$newCmkSettings = New-SqlColumnMasterKeySettings -KeyStoreProviderName $newCmkData.KeyStoreProviderName -KeyPath $newCmkData.KeyPath
$existingNewCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $NewCmkName }
if (-not $existingNewCmk) {
    New-SqlColumnMasterKey -Name $NewCmkName -InputObject $database -ColumnMasterKeySettings $newCmkSettings | Out-Null
}

Write-Host "[AE] Adding new encrypted CEK values under '$NewCmkName'"
$ceks = Get-SqlColumnEncryptionKey -InputObject $database

$ceksToRotate = @(
    $ceks | Where-Object {
        $_.ColumnEncryptionKeyValues -and
        @($_.ColumnEncryptionKeyValues | Where-Object { $_.ColumnMasterKeyName -eq $OldCmkName }).Count -gt 0
    }
)

$ceksToRotate | ForEach-Object {
    $cek = $_
    if (@($cek.ColumnEncryptionKeyValues).Count -gt 1) {
        throw "CEK '$($cek.Name)' already has multiple encrypted values and still references '$OldCmkName'."
    }

    $newValueRow = @($newCekValues | Where-Object { $_.CEKName -eq $cek.Name }) | Select-Object -First 1
    if (-not $newValueRow) {
        throw "No new encrypted value found for CEK '$($cek.Name)' in file '$newCekValuesFile'."
    }

    Add-SqlColumnEncryptionKeyValue `
        -ColumnMasterKeyName $NewCmkName `
        -Name $cek.Name `
        -EncryptedValue $newValueRow.CEKEncryptedValue `
        -InputObject $database | Out-Null
}

Write-Host "[AE] Completing rotation for source CMK '$OldCmkName'"
Complete-SqlColumnMasterKeyRotation -SourceColumnMasterKeyName $OldCmkName -InputObject $database

Write-Host "[AE] Removing source CMK '$OldCmkName' metadata"
Remove-SqlColumnMasterKey -Name $OldCmkName -InputObject $database

Write-Host '[AE] Completed successfully'

Rotieren eines Spaltenverschlüsselungsschlüssels

Die Rotation eines Spaltenverschlüsselungsschlüssels umfasst das Entschlüsseln der Daten in allen Spalten, die mit dem zu rotierenden Schlüssel verschlüsselt wurden, und die Neuverschlüsselung der Daten mithilfe des neuen Spaltenverschlüsselungsschlüssels. Dieser Workflow für die Rotation benötigt Zugriff auf die Schlüssel und die Datenbank und kann deshalb nicht mit Rollentrennung ausgeführt werden. Die Rotation eines Spaltenverschlüsselungsschlüssels kann viel Zeit in Anspruch nehmen, wenn die Tabellen mit den Spalten, die mit dem zu rotierenden Schlüssel verschlüsselt wurden, groß sind. Daher muss Ihre Organisation eine Rotation der Spaltenverschlüsselungsschlüssel sorgfältig planen.

Sie können einen Spaltenverschlüsselungsschlüssel offline oder online rotieren. Die erste Methode ist wahrscheinlich schneller, aber Ihre Anwendungen können nicht in die betroffenen Tabellen schreiben. Der letztere Ansatz dauert wahrscheinlich länger, aber Sie können den Zeitraum einschränken, in dem die betroffenen Tabellen für Anwendungen nicht verfügbar sind. Weitere Details finden Sie unter Konfigurieren der Spaltenverschlüsselung mithilfe von Always Encrypted mit PowerShell und Set-SqlColumnEncryption.

Aufgabe Artikel Greift auf Klartextschlüssel/Schlüsselspeicher zu Greift auf Datenbank zu
Schritt 1. Starten Sie eine PowerShell-Umgebung, und importieren Sie das SqlServer-Modul. Importieren des SqlServer-Moduls Nein Nein
Schritt 2. Stellen Sie eine Verbindung mit Ihrem Server und Ihrer Datenbank her. Herstellen einer Verbindung mit einer Datenbank Nein Ja
Schritt 3. Authentifizieren Sie sich bei Azure, wenn Ihr Spaltenhauptschlüssel (der den zu rotierenden Spaltenverschlüsselungsschlüssel schützt) in einem Schlüsseltresor oder einem verwalteten HSM im Azure Key Vault gespeichert ist. Connect-AzAccount Ja Nein
Schritt 4. Rufen Sie ein Zugriffstoken für Azure Key Vaults ab, wenn Ihr Spaltenhauptschlüssel in Azure Key Vault gespeichert ist. Get-AzAccessToken Nein Nein
Schritt 5. Generieren Sie einen neuen Spaltenverschlüsselungsschlüssel, verschlüsseln Sie ihn mit dem Spaltenhauptschlüssel, und erstellen Sie Spaltenverschlüsselungsschlüssel-Metadaten in der Datenbank. New-SqlColumnEncryptionKey

Hinweis: Verwenden Sie eine Variation des Cmdlets, dass intern einen Spaltenverschlüsselungsschlüssel generiert und verschlüsselt.
Hinter den Kulissen führt dieses Cmdlet die CREATE COLUMN ENCRYPTION KEY (Transact-SQL)-Anweisung aus, um die Schlüsselmetadaten zu erstellen.
Ja Ja
Schritt 6. Suchen Sie alle Spalten, die mit dem alten Spaltenverschlüsselungsschlüssel verschlüsselt sind. Programmierungshandbuch für SQL Server Management Objects (SMO) Nein Ja
Schritt 7. Erstellen Sie ein SqlColumnEncryptionSettings -Objekt für jede betroffene Spalte. SqlColumnEncryptionSettings ist ein Objekt, das im Arbeitsspeicher (in PowerShell) vorhanden ist. Es gibt das Zielverschlüsselungsschema für eine Spalte an. In diesem Fall sollte das Objekt angeben, dass die betroffene Spalte mit dem neuen Spaltenverschlüsselungsschlüssel verschlüsselt werden soll. Neue-SqlSpaltenverschlüsselungseinstellungen Nein Nein
Schritt 8: Verschlüsseln Sie die Spalten, die in Schritt 5 angegeben sind, erneut mit dem neuen Verschlüsselungsschlüssel. Set-SqlColumnEncryption

Hinweis: Dieser Schritt kann lange dauern. Je nach gewähltem Ansatz (online oder offline) können Ihre Anwendungen während des gesamten Vorgangs oder nur teilweise nicht auf die Tabellen zugreifen.
Ja Ja
Schritt 9 Entfernen Sie die Metadaten für den alten Spaltenverschlüsselungsschlüssel. Remove-SqlColumnEncryptionKey Nein Ja

Beispiel – Rotieren eines Spaltenverschlüsselungsschlüssels

Das folgende Skript zeigt, wie ein Spaltenverschlüsselungsschlüssel rotiert wird. Das Skript nimmt an, dass die Zieldatenbank einige Spalten enthält, die mit einem Spaltenverschlüsselungsschlüssel namens CEK1 (der rotiert werden soll) verschlüsselt sind, der mithilfe eines Spaltenhauptschlüssels namens CMK1 geschützt ist (der Spaltenhauptschlüssel ist nicht in Azure Key Vault gespeichert).

[CmdletBinding()]
param(
    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$ServerName = '<server name>',

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$DatabaseName = '<database name>',

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$OldCekName = 'CEK1',

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$NewCekName = 'CEK2',

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$CmkName = 'CMK2',

    [Parameter(Mandatory = $false)]
    [ValidateRange(0, 3600)]
    [int]$MaxDowntimeInSeconds = 120,

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$LogFileDirectory = '.'
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop

if ($OldCekName -eq $NewCekName) {
    throw 'OldCekName and NewCekName must be different.'
}

if (-not (Test-Path -Path $LogFileDirectory -PathType Container)) {
    New-Item -Path $LogFileDirectory -ItemType Directory | Out-Null
}

Write-Host "[AE] Connecting to '$ServerName' / '$DatabaseName'"
$connStr = "Server=$ServerName;Database=$DatabaseName;Integrated Security=True;TrustServerCertificate=True;Connection Timeout=30"
$database = Get-SqlDatabase -ConnectionString $connStr -ErrorAction Stop

Write-Host "[AE] Ensuring CMK '$CmkName' exists"
$cmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $CmkName }
if (-not $cmk) {
    throw "Column master key '$CmkName' was not found."
}

Write-Host "[AE] Ensuring target CEK '$NewCekName' exists"
$existingNewCek = Get-SqlColumnEncryptionKey -InputObject $database | Where-Object { $_.Name -eq $NewCekName }
if (-not $existingNewCek) {
    New-SqlColumnEncryptionKey -Name $NewCekName -InputObject $database -ColumnMasterKey $CmkName | Out-Null
}

Write-Host "[AE] Discovering encrypted columns using '$OldCekName'"
$settings = @()
$tables = @($database.Tables)
$tables | ForEach-Object {
    $table = $_
    @($table.Columns) | ForEach-Object {
        $column = $_
        if ($column.IsEncrypted -and $column.ColumnEncryptionKeyName -eq $OldCekName) {
            $columnName = "{0}.{1}.{2}" -f $table.Schema, $table.Name, $column.Name
            $settings += New-SqlColumnEncryptionSettings -ColumnName $columnName -EncryptionType $column.EncryptionType -EncryptionKey $NewCekName
        }
    }
}

if ($settings.Count -eq 0) {
    Write-Warning "No encrypted columns found that reference '$OldCekName'. Nothing to rotate."
    return
}

Write-Host "[AE] Re-encrypting $($settings.Count) column(s) to '$NewCekName'"
Set-SqlColumnEncryption `
    -ColumnEncryptionSettings $settings `
    -InputObject $database `
    -UseOnlineApproach `
    -MaxDowntimeInSeconds $MaxDowntimeInSeconds `
    -LogFileDirectory $LogFileDirectory

Write-Host "[AE] Validating no columns still reference '$OldCekName'"
$stillUsingOld = $false
@($database.Tables) | ForEach-Object {
    @($_.Columns) | ForEach-Object {
        if ($_.IsEncrypted -and $_.ColumnEncryptionKeyName -eq $OldCekName) {
            $stillUsingOld = $true
        }
    }
}

if ($stillUsingOld) {
    throw "At least one encrypted column still references '$OldCekName'. Aborting CEK removal."
}

Write-Host "[AE] Removing old CEK '$OldCekName'"
Remove-SqlColumnEncryptionKey -Name $OldCekName -InputObject $database

Write-Host '[AE] Completed successfully'

Nächste Schritte

Siehe auch