Provisionar chaves sempre criptografadas usando o PowerShell

Aplica-se a:SQL ServerBanco de Dados SQL do AzureInstância Gerenciada SQL do Azure

Este artigo fornece as etapas para provisionar chaves para Always Encrypted usando o módulo SqlServer PowerShell. Você pode usar o PowerShell para provisionar chaves Always Encrypted com e sem separação de funções, fornecendo controle sobre quem tem acesso às chaves de criptografia reais no armazenamento de chaves e quem tem acesso ao banco de dados.

Note

A Microsoft recomenda usar o PowerShell 7 ou versões posteriores ao executar scripts Always Encrypted PowerShell. O PowerShell 7 oferece suporte melhorado multiplataforma, melhor desempenho e a mais recente compatibilidade com o módulo SqlServer (v22+), que é necessário para muitos cenários Always Crypted.

Para obter uma visão geral do gerenciamento de chaves Always Encrypted, incluindo algumas recomendações de práticas recomendadas de alto nível, consulte Overview of key management for Always Encrypted. Para obter informações sobre como começar a usar o módulo SqlServer PowerShell para Always Encrypted, consulte Configure Always Encrypted using PowerShell.

Provisionamento de chaves sem separação de funções

O método de provisão de chaves descrito nesta secção não suporta a separação de papéis entre administradores de segurança e DBAs. Algumas das etapas nesta seção combinam operações em chaves físicas com operações em metadados de chave. Por isso, utilize este método de provisionamento das chaves se a sua organização utilizar o modelo DevOps, ou se a base de dados estiver alojada na cloud e o objetivo principal for restringir o acesso a dados sensíveis aos administradores da cloud (mas não aos DBAs locais). Não utilize este método se os potenciais adversários incluírem DBAs, ou se os DBAs não deverem ter acesso a dados sensíveis.

Antes de executar qualquer etapa que envolva o acesso a chaves de texto sem formatação ou ao armazenamento de chaves (identificado na coluna Acessos a chaves de texto sem formatação/armazenamento de chaves na tabela a seguir), verifique se o ambiente do PowerShell é executado em uma máquina segura diferente de um computador que hospeda seu banco de dados. Para obter mais informações, consulte Considerações de segurança para gerenciamento de chaves.

Tarefa Artigo Acessa chaves em texto claro/armazenamento de chaves Acessa a base de dados
Passo 1. Crie uma chave mestra de coluna num armazenamento de chaves.

Nota: O módulo SqlServer PowerShell não suporta esta etapa. Para realizar essa tarefa a partir de uma linha de comando, use as ferramentas específicas para o armazenamento de chaves selecionado.
Criar e armazenar chaves mestras de coluna para Always Encrypted Sim Não
Passo 2. Inicie um ambiente do PowerShell e importe o módulo SqlServer PowerShell. Configurar Always Encrypted usando o PowerShell Não Não
Passo 3. Conecte-se ao seu servidor e banco de dados. Conectar-se a um banco de dados Não Sim
Passo 4. Crie um objeto SqlColumnMasterKeySettings que contenha informações sobre o local da sua chave mestra de coluna. SqlColumnMasterKeySettings é um objeto que existe na memória (no PowerShell). Utilize o cmdlet específico para o seu armazenador de chaves. New-SqlAzureKeyVaultColumnMasterKeySettings

New-SqlCertificateStoreColumnMasterKeySettings

New-SqlCngColumnMasterKeySettings

New-SqlCspColumnMasterKeySettings
Não Não
Passo 5. Crie os metadados sobre a chave mestra da coluna em seu banco de dados.

Observação: Não verificamos a validade das chaves ou certificados usados para gerar a chave mestra da coluna.
[New-SqlColumnMasterKey](/powershell/sqlserver/sqlserver/vlatest/new-sqlcolumnmasterkey

Nota: por baixo das capas, o cmdlet emite a CREATE COLUMN MASTER KEY declaração para criar metadados-chave.
Não Sim
Passo 6. Autentique-se no Azure, se a sua chave mestra de coluna estiver armazenada no Cofre de Chaves do Azure. Connect-AzAccount Sim Não
Passo 7. Obtenha um token de acesso para os Cofres de Chaves do Azure, se a sua chave mestra de coluna estiver armazenada no Cofre de Chaves do Azure. Get-AzAccessToken Não Não
Passo 8. Gere uma nova chave de criptografia de coluna, criptografe-a com a chave mestra de coluna e crie metadados de chave de criptografia de coluna no banco de dados. New-SqlColumnEncryptionKey

Nota: Use uma variação do cmdlet que gera e criptografa internamente uma chave de criptografia de coluna.

Nota: Por baixo das capas, o cmdlet emite a CREATE COLUMN ENCRYPTION KEY declaração para criar metadados-chave.
Sim Sim

Repositório de Certificados do Windows sem separação de funções (exemplo)

Esse script é um exemplo de ponta a ponta para gerar uma chave mestra de coluna que é um certificado no Repositório de Certificados do Windows, gerar e criptografar uma chave de criptografia de coluna e criar metadados de chave em um banco de dados do SQL Server.

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

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

	[Parameter(Mandatory = $false)]
	[string]$CertificateSubject = "AlwaysEncryptedCert",

	[Parameter(Mandatory = $false)]
	[string]$CmkName = "CMK",

	[Parameter(Mandatory = $false)]
	[string]$CekName = "CEK"
)

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

Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop

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

if (-not $cert) {
	Write-Host "[AE] Certificate not found. Creating self-signed certificate."
	$cert = New-SelfSignedCertificate `
		-Subject $CertificateSubject `
		-CertStoreLocation Cert:CurrentUser\My `
		-KeyExportPolicy Exportable `
		-Type DocumentEncryptionCert `
		-KeyUsage DataEncipherment `
		-KeySpec KeyExchange
}

Write-Host "[AE] 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' database '$DatabaseName'. Verify instance name SQL2025, database existence, and local permissions."
	throw
}

Write-Host "[AE] Creating CMK settings from certificate thumbprint"
$cmkSettings = New-SqlCertificateStoreColumnMasterKeySettings -CertificateStoreLocation "CurrentUser" -Thumbprint $cert.Thumbprint

Write-Host "[AE] Ensuring CMK '$CmkName' exists"
$existingCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $CmkName }
if (-not $existingCmk) {
	New-SqlColumnMasterKey -Name $CmkName -InputObject $database -ColumnMasterKeySettings $cmkSettings | Out-Null
}

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

Write-Host "Completed successfully"

Azure Key Vault sem separação de funções (exemplo)

Esse script é um exemplo de ponta a ponta para provisionar e configurar um cofre de chaves no Cofre de Chaves do Azure, gerando uma chave mestra de coluna no cofre, gerando e criptografando uma chave de criptografia de coluna e criando metadados de chave em um banco de dados SQL do Azure.

param(
	[Parameter(Mandatory = $true)] [string]$SubscriptionId,
	[Parameter(Mandatory = $true)] [string]$ResourceGroupName,
	[Parameter(Mandatory = $true)] [string]$AzureLocation,
	[Parameter(Mandatory = $true)] [string]$KeyVaultName,
	[Parameter(Mandatory = $true)] [string]$KeyName,
	[Parameter(Mandatory = $true)] [string]$ServerName,
	[Parameter(Mandatory = $true)] [string]$DatabaseName,
	[string]$CmkName = "CMK",
	[string]$CekName = "CEK",
	[bool]$AssignRbacToCurrentPrincipal = $true
)

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

Import-Module Az.Accounts -ErrorAction Stop
Import-Module Az.Resources -ErrorAction Stop
Import-Module Az.KeyVault -ErrorAction Stop
Import-Module SqlServer -ErrorAction Stop

function Get-CurrentPrincipalObjectId {
	param([string]$AccountId)

	$userSignedIn = Get-AzADUser -SignedIn -ErrorAction SilentlyContinue
	if ($userSignedIn) { return $userSignedIn.Id }

	$user = Get-AzADUser -UserPrincipalName $AccountId -ErrorAction SilentlyContinue
	if ($user) { return $user.Id }

	$sp = Get-AzADServicePrincipal -DisplayName $AccountId -ErrorAction SilentlyContinue | Select-Object -First 1
	if ($sp) { return $sp.Id }

	throw "Could not resolve Microsoft Entra object id for account '$AccountId'."
}

try {
	Write-Host "[AE] Signing in and selecting subscription"
	Connect-AzAccount | Out-Null
	$ctx = Set-AzContext -SubscriptionId $SubscriptionId

	Write-Host "[AE] Ensuring resource group exists"
	$resourceGroup = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue
	if (-not $resourceGroup) {
		$resourceGroup = New-AzResourceGroup -Name $ResourceGroupName -Location $AzureLocation
	}

	Write-Host "[AE] Ensuring key vault exists (RBAC mode)"
	$vault = Get-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -ErrorAction SilentlyContinue
	if (-not $vault) {
		$vault = New-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -Location $AzureLocation -EnableRbacAuthorization
	}

	if (-not $vault.EnableRbacAuthorization) {
		throw "Key Vault '$KeyVaultName' is not using RBAC authorization. Enable RBAC authorization on the vault before running this script."
	}

	if ($AssignRbacToCurrentPrincipal) {
		Write-Host "[AE] Ensuring RBAC role assignment"
		$principalSignInName = $ctx.Account.Id
		$roleName = "Key Vault Crypto Officer"
		$existingRole = Get-AzRoleAssignment -SignInName $principalSignInName -Scope $vault.ResourceId -RoleDefinitionName $roleName -ErrorAction SilentlyContinue
		if (-not $existingRole) {
			New-AzRoleAssignment -SignInName $principalSignInName -Scope $vault.ResourceId -RoleDefinitionName $roleName | Out-Null
		}
	}

	Write-Host "[AE] Ensuring column master key material exists in Key Vault"
	$akvKey = Get-AzKeyVaultKey -VaultName $KeyVaultName -Name $KeyName -ErrorAction SilentlyContinue
	if (-not $akvKey) {
		$akvKey = Add-AzKeyVaultKey -VaultName $KeyVaultName -Name $KeyName -Destination "Software"
	}

	Write-Host "[AE] Connecting to Azure SQL and creating metadata"
	$keyVaultAccessToken = (Get-AzAccessToken -ResourceUrl "https://vault.azure.net").Token
	$connStr = "Server=tcp:$ServerName.database.windows.net,1433;Database=$DatabaseName;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;Authentication=Active Directory Interactive"
	$database = Get-SqlDatabase -ConnectionString $connStr -Encrypt Mandatory
	$cmkSettings = New-SqlAzureKeyVaultColumnMasterKeySettings -KeyUrl $akvKey.Key.Kid

	$existingCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $CmkName }
	if (-not $existingCmk) {
		New-SqlColumnMasterKey -Name $CmkName -InputObject $database -ColumnMasterKeySettings $cmkSettings | Out-Null
	}

	$existingCek = Get-SqlColumnEncryptionKey -InputObject $database | Where-Object { $_.Name -eq $CekName }
	if (-not $existingCek) {
		New-SqlColumnEncryptionKey -Name $CekName -InputObject $database -ColumnMasterKey $CmkName -KeyVaultAccessToken $keyVaultAccessToken | Out-Null
	}

	Write-Host "Completed successfully"
}
catch {
	Write-Error "Script failed: $($_.Exception.Message)"
	throw
}

CNG/KSP sem separação de funções (exemplo)

O script abaixo é um exemplo de ponta a ponta para gerar uma chave mestra de coluna em um armazenamento de chaves que implementa CNG (Cryptography Next Generation API), gerar e criptografar uma chave de criptografia de coluna e criar metadados de chave em um banco de dados do SQL Server.

O exemplo usa o armazenamento de chaves que usa o Microsoft Software Key Storage Provider. Você pode optar por modificar o exemplo para usar outro armazenamento, como o módulo de segurança de hardware. Para isso, você precisará certificar-se de que o provedor de armazenamento de chaves (KSP) que implementa o CNG para seu dispositivo está instalado e corretamente em sua máquina. Você precisará substituir Microsoft Software Key Storage Provider pelo nome KSP do seu dispositivo.

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

	[Parameter(Mandatory = $true)]
	[string]$DatabaseName = "<database name>",

	[Parameter(Mandatory = $false)]
	[string]$CngKeyName = "AlwaysEncryptedKey",

	[Parameter(Mandatory = $false)]
	[string]$CmkName = "CMK",

	[Parameter(Mandatory = $false)]
	[string]$CekName = "CEK"
)

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

# Local key store provider and key settings.
$cngProviderName = "Microsoft Software Key Storage Provider"
$cngAlgorithmName = "RSA"
$cngKeySize = 2048

Import-Module SqlServer -ErrorAction Stop

Write-Host "[AE] Creating local CNG key '$CngKeyName'"
$cngProvider = New-Object System.Security.Cryptography.CngProvider($cngProviderName)
$cngKeyParams = New-Object System.Security.Cryptography.CngKeyCreationParameters
$cngKeyParams.Provider = $cngProvider
$cngKeyParams.KeyCreationOptions = [System.Security.Cryptography.CngKeyCreationOptions]::OverwriteExistingKey
$keySizeProperty = New-Object System.Security.Cryptography.CngProperty(
	"Length",
	[System.BitConverter]::GetBytes($cngKeySize),
	[System.Security.Cryptography.CngPropertyOptions]::None
)
$cngKeyParams.Parameters.Add($keySizeProperty)
$cngAlgorithm = New-Object System.Security.Cryptography.CngAlgorithm($cngAlgorithmName)
[System.Security.Cryptography.CngKey]::Create($cngAlgorithm, $CngKeyName, $cngKeyParams) | Out-Null

Write-Host "[AE] Connecting to $ServerName / $DatabaseName"
$connStr = "Server=$ServerName;Database=$DatabaseName;Integrated Security=True;Encrypt=True;TrustServerCertificate=True"
$database = Get-SqlDatabase -ConnectionString $connStr

Write-Host "[AE] Preparing column master key settings"
$cmkSettings = New-SqlCngColumnMasterKeySettings -CngProviderName $cngProviderName -KeyName $CngKeyName

Write-Host "[AE] Ensuring CMK exists"
$existingCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $CmkName }
if (-not $existingCmk) {
	New-SqlColumnMasterKey -Name $CmkName -InputObject $database -ColumnMasterKeySettings $cmkSettings | Out-Null
}

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

Write-Host "Completed successfully"

Provisionamento de chaves com separação de funções

Esta seção fornece as etapas para configurar a criptografia em que os administradores de segurança não têm acesso ao banco de dados e os administradores de banco de dados não têm acesso ao armazenamento de chaves ou às chaves de texto sem formatação.

Administrador de segurança

Antes de executar qualquer etapa que envolva o acesso a chaves de texto sem formatação ou ao armazenamento de chaves (identificado na coluna Acessos a chaves de texto sem formatação/armazenamento de chaves na tabela a seguir), verifique se:

  • O ambiente do PowerShell é executado em uma máquina segura que é diferente de um computador que hospeda seu banco de dados.
  • Os DBAs em sua organização não têm acesso à máquina (o que derrotaria o propósito da separação de funções).

Para obter mais informações, consulte Considerações de segurança para gerenciamento de chaves.

Tarefa Artigo Acessa chaves em texto claro/armazenamento de chaves Acessa a base de dados
Passo 1. Crie uma chave mestra de coluna num armazenamento de chaves.

Nota: O módulo SqlServer não suporta esta etapa. Para realizar essa tarefa a partir de uma linha de comando, você precisa usar as ferramentas específicas do tipo de armazenamento de chaves.
Criar e armazenar chaves mestras de coluna para Always Encrypted Sim Não
Passo 2. Inicie uma sessão do PowerShell e importe o módulo SqlServer. Importar o módulo SqlServer Não Não
Passo 3. Crie um objeto SqlColumnMasterKeySettings que contenha informações sobre o local da sua chave mestra de coluna. SqlColumnMasterKeySettings é um objeto que existe na memória (no PowerShell). Utilize o cmdlet específico para o seu armazenador de chaves. New-SqlAzureKeyVaultColumnMasterKeySettings

New-SqlCertificateStoreColumnMasterKeySettings

New-SqlCngColumnMasterKeySettings

New-SqlCspColumnMasterKeySettings
Não Não
Passo 4. Autentique-se no Azure, se a sua chave mestra de coluna estiver armazenada no Cofre de Chaves do Azure. Connect-AzAccount Sim Não
Passo 5. Obtenha um token de acesso para os Cofres de Chaves do Azure, se a sua chave mestra de coluna estiver armazenada no Cofre de Chaves do Azure. Get-AzAccessToken Não Não
Passo 6. Gere uma chave de criptografia de coluna, criptografe-a com a chave mestra de coluna para produzir um valor criptografado da chave de criptografia de coluna. Novo-ValorCifradoDeChaveDeEncriptaçãoDeColunaSql Sim Não
Passo 7. Forneça o local da chave mestra de coluna (o nome do provedor e o caminho da chave mestra de coluna) e o valor criptografado da chave de encriptação de coluna para o DBA. Veja os exemplos no final do artigo. Não Não

Administrador de Bases de Dados (DBA)

Os DBAs usam as informações que recebem do administrador de segurança (etapa 7 acima) para criar e gerenciar os metadados de chave Always Encrypted no banco de dados.

Tarefa Artigo Acede a teclas de texto simples Acessa a base de dados
Passo 1. Obtenha a localização da chave-mestra da coluna e o valor encriptado da chave de encriptação da coluna do seu Administrador de Segurança. Veja os exemplos no final do artigo. Não Não
Passo 2. Inicie um ambiente PowerShell e importe o módulo SqlServer. Configurar Always Encrypted usando o PowerShell Não Não
Passo 3. Conecte-se ao seu servidor e a um banco de dados. Conectar-se a um banco de dados Não Sim
Passo 4. Crie um objeto SqlColumnMasterKeySettings que contenha informações sobre o local da sua chave mestra de coluna. SqlColumnMasterKeySettings é um objeto que existe na memória. New-SqlColumnMasterKeySettings Não Não
Passo 5. Crie os metadados sobre a chave mestra da coluna em seu banco de dados.

Observação: Não verificamos a validade das chaves ou certificados usados para gerar a chave mestra da coluna.
New-SqlColumnMasterKey
Nota: nos bastidores, o cmdlet emite a instrução CREATE COLUMN MASTER KEY (Transact-SQL) para criar metadados da chave mestra de coluna.
Não Sim
Passo 6. Crie os metadados da chave de criptografia de coluna no banco de dados. New-SqlColumnEncryptionKey
Nota: Os DBAs usam uma variação do cmdlet que cria apenas metadados para a chave de encriptação da coluna.
Nos bastidores, o cmdlet emite a instrução CREATE COLUMN ENCRYPTION KEY (Transact-SQL) para criar metadados da chave de encriptação de colunas.
Não Sim

Repositório de Certificados do Windows com separação de funções (exemplo)

Administrador de segurança

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

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

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

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

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

	[Parameter(Mandatory = $false)]
	[string]$ExportKeyDataPath = 'C:\temp\keydata.txt'
)

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

Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop

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

if (-not $cert) {
	Write-Host '[AE] Certificate not found. Creating a new self-signed certificate.'
	$cert = New-SelfSignedCertificate `
		-Subject $CertificateSubject `
		-CertStoreLocation 'Cert:CurrentUser\My' `
		-KeyExportPolicy Exportable `
		-Type DocumentEncryptionCert `
		-KeyUsage DataEncipherment `
		-KeySpec KeyExchange
}

Write-Host "[AE] 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 name, database, and local permissions."
	throw
}

Write-Host '[AE] Building CMK settings from certificate'
$cmkSettings = New-SqlCertificateStoreColumnMasterKeySettings -CertificateStoreLocation 'CurrentUser' -Thumbprint $cert.Thumbprint

Write-Host "[AE] Ensuring CMK '$CmkName' exists"
$existingCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $CmkName }
if (-not $existingCmk) {
	New-SqlColumnMasterKey -Name $CmkName -InputObject $database -ColumnMasterKeySettings $cmkSettings | Out-Null
}

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

if ($ExportKeyDataPath) {
	Write-Host "[AE] Exporting key metadata to '$ExportKeyDataPath'"
	$encryptedValue = New-SqlColumnEncryptionKeyEncryptedValue -TargetColumnMasterKeySettings $cmkSettings
	"KeyStoreProviderName,KeyPath,EncryptedValue" | Set-Content -Path $ExportKeyDataPath -Encoding UTF8
	"$($cmkSettings.KeyStoreProviderName),$($cmkSettings.KeyPath),$encryptedValue" | Add-Content -Path $ExportKeyDataPath -Encoding UTF8
}

Write-Host 'Completed successfully'

Administrador de Bases de Dados (DBA)

[CmdletBinding()]
param(
	[Parameter(Mandatory = $false)]
	[string]$ServerName = 'localhost\SQL2025',

	[Parameter(Mandatory = $false)]
	[ValidateNotNullOrEmpty()]
	[string]$DatabaseName = 'AdventureWorks2025',

	[Parameter(Mandatory = $false)]
	[ValidateNotNullOrEmpty()]
	[string]$KeyDataFile = 'C:\temp\keydata.txt',

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

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

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

Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop

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

Write-Host "[AE] Loading key metadata from '$KeyDataFile'"
$keyData = Import-Csv -Path $KeyDataFile
if (-not $keyData) {
	throw "Key data file '$KeyDataFile' is empty."
}

$keyDataRow = $keyData | Select-Object -First 1
if (-not $keyDataRow.KeyStoreProviderName -or -not $keyDataRow.KeyPath -or -not $keyDataRow.EncryptedValue) {
	throw "Key data file must include non-empty columns: KeyStoreProviderName, KeyPath, EncryptedValue."
}

Write-Host "[AE] 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 name, database, and local permissions."
	throw
}

Write-Host "[AE] Building CMK settings for provider '$($keyDataRow.KeyStoreProviderName)'"
$cmkSettings = New-SqlColumnMasterKeySettings -KeyStoreProviderName $keyDataRow.KeyStoreProviderName -KeyPath $keyDataRow.KeyPath

Write-Host "[AE] Ensuring CMK '$CmkName' exists"
$existingCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $CmkName }
if (-not $existingCmk) {
	New-SqlColumnMasterKey -Name $CmkName -InputObject $database -ColumnMasterKeySettings $cmkSettings | Out-Null
}

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

Write-Host 'Completed successfully'