Azure 磁盘加密示例脚本

适用于:✔️ Windows VM

本文提供了用于准备预加密 VHD 和其他任务的示例脚本。

注意

除非另有说明,否则所有脚本均引用最新的非 AAD 版本的 ADE。

Azure 磁盘加密的示例 PowerShell 脚本

  • 列出订阅中所有已加密的 VM

    可以使用此 PowerShell 脚本在订阅中存在的所有资源组中找到所有 ADE 加密的 VM 和扩展版本。

    另外,这些 cmdlet 将显示所有 ADE 加密的 VM(但不显示扩展版本):

    $osVolEncrypted = {(Get-AzVMDiskEncryptionStatus -ResourceGroupName $_.ResourceGroupName -VMName $_.Name).OsVolumeEncrypted}
    $dataVolEncrypted= {(Get-AzVMDiskEncryptionStatus -ResourceGroupName $_.ResourceGroupName -VMName $_.Name).DataVolumesEncrypted}
    Get-AzVm | Format-Table @{Label="MachineName"; Expression={$_.Name}}, @{Label="OsVolumeEncrypted"; Expression=$osVolEncrypted}, @{Label="DataVolumesEncrypted"; Expression=$dataVolEncrypted}
    
  • 列出订阅中所有已加密的 VMSS 实例

    可以使用此 PowerShell 脚本在订阅中存在的所有资源组中找到所有 ADE 加密的虚拟机规模集实例和扩展版本。

  • 列出 Key Vault 中用于加密 VM 的所有磁盘加密机密

Get-AzKeyVaultSecret -VaultName $KeyVaultName | where {$_.Tags.ContainsKey('DiskEncryptionKeyFileName')} | format-table @{Label="MachineName"; Expression={$_.Tags['MachineName']}}, @{Label="VolumeLetter"; Expression={$_.Tags['VolumeLetter']}}, @{Label="EncryptionKeyURL"; Expression={$_.Id}}

使用 Azure 磁盘加密先决条件 PowerShell 脚本

如果你已熟悉进行 Azure 磁盘加密的先决条件,则可以使用 Azure 磁盘加密先决条件 PowerShell 脚本。 有关此 PowerShell 脚本的用法示例,请参阅有关加密 VM 的快速入门。 可以删除脚本的某个部分中的注释(从第 211 行开始),以加密现有资源组中现有 VM 的所有磁盘。

下表显示了可在 PowerShell 脚本中使用的参数:

参数 说明 必需?
$resourceGroupName KeyVault 所属的资源组的名称。 如果不存在具有此名称的资源组,则会新建一个资源组。 True
$keyVaultName 要将加密密钥放到的 KeyVault 的名称。 如果不存在具有此名称的保管库,则会新建一个保管库。 True
$location KeyVault 的位置。 请确保 KeyVault 和要加密的 VM 位于同一位置。 使用 Get-AzLocation 获取位置列表。 True
$subscriptionId 要使用的 Azure 订阅的标识符。 可以使用 Get-AzSubscription 获取订阅 ID。 True
$aadAppName 用于将机密写入 KeyVault 的 Microsoft Entra 应用程序的名称。 如果该应用程序不存在,则会使用此名称创建新的应用程序。 如果此应用已存在,则将 aadClientSecret 参数传递给脚本。 False
$aadClientSecret 之前创建的 Microsoft Entra 应用程序的客户端密码。 False
$keyEncryptionKeyName KeyVault 中的可选密钥加密密钥的名称。 如果不存在具有此名称的密钥,则会新建一个密钥。 False

资源管理器模板

在不使用 Microsoft Entra 应用的情况下加密或解密 VM

使用 Microsoft Entra 应用加密或解密 VM(以前的版本)

准备预加密的 Windows VHD

以下部分介绍了必要操作,让你在 Azure IaaS 中准备将预加密的 Windows VHD 部署为加密 VHD。 使用该信息在 Azure Site Recovery 或 Azure 上准备和启动全新的 Windows VM (VHD)。 有关如何准备和上传 VHD 的详细信息,请参阅上传通用化 VHD 并使用它在 Azure 中创建新的 VM

更新组策略以允许使用非 TPM 保护 OS

在“本地计算机策略”>“计算机设置”>“管理模板”>“Windows 组件”下配置名为“BitLocker 驱动器加密”的 BitLocker 组策略设置。 如下图所示,将此设置更改为“操作系统驱动器” >“启动时需要附加身份验证” >“没有兼容的 TPM 时允许 BitLocker” :

Microsoft Antimalware in Azure

安装 BitLocker 功能组件

对于 Windows Server 2012 或更高版本,请使用以下命令:

dism /online /Enable-Feature /all /FeatureName:BitLocker /quiet /norestart

对于 Windows Server 2008 R2,请使用以下命令:

ServerManagerCmd -install BitLockers

使用 bdehdcfg 为 BitLocker 准备 OS 卷

若要压缩 OS 分区并为 BitLocker 准备计算机,请根据需要执行 bdehdcfg

bdehdcfg -target c: shrink -quiet

使用 BitLocker 保护 OS 卷

使用 manage-bde 命令在使用外部密钥保护程序的引导卷上启用加密。 此外将外部密钥(.bek 文件)放在外部驱动器或卷上。 下次重启后,会在系统/引导卷上启用加密。

manage-bde -on %systemdrive% -sk [ExternalDriveOrVolume]
reboot

注意

使用独立的数据/资源 VHD 准备 VM,以使用 BitLocker 获取外部密钥。

将加密的 VHD 上传到 Azure 存储帐户

启用 BitLocker 加密后,需要将本地加密的 VHD 上传到存储帐户。

    Add-AzVhd [-Destination] <Uri> [-LocalFilePath] <FileInfo> [[-NumberOfUploaderThreads] <Int32> ] [[-BaseImageUriToPatch] <Uri> ] [[-OverWrite]] [ <CommonParameters>]

将预加密 VM 的机密上传到密钥保管库

必须将前面获取的磁盘加密机密作为机密上传到密钥保管库中。 为此,你必须向将上传机密的帐户授予 set secret 权限和 wrapkey 权限。

# Typically, account Id is the user principal name (in user@domain.com format)
$upn = (Get-AzureRmContext).Account.Id
Set-AzKeyVaultAccessPolicy -VaultName $kvname -UserPrincipalName $acctid -PermissionsToKeys wrapKey -PermissionsToSecrets set

# In local Shell, the account ID is a managed service identity, so specify the username directly
# $upn = "user@domain.com"
# Set-AzKeyVaultAccessPolicy -VaultName $kvname -UserPrincipalName $acctid -PermissionsToKeys wrapKey -PermissionsToSecrets set

# When running as a service principal, retrieve the service principal ID from the account ID, and set access policy to that
# $acctid = (Get-AzureRmContext).Account.Id
# $spoid = (Get-AzureRmADServicePrincipal -ServicePrincipalName $acctid).Id
# Set-AzKeyVaultAccessPolicy -VaultName $kvname -ObjectId $spoid -BypassObjectIdValidation -PermissionsToKeys wrapKey -PermissionsToSecrets set

未使用 KEK 加密的磁盘加密机密

若要在密钥保管库中设置机密,请使用 Set-AzKeyVaultSecret。 将密码编码为 base64 字符串,然后将其上传到密钥保管库。 此外,请确保在 Key Vault 中创建机密时设置以下标记。


 # This is the passphrase that was provided for encryption during the distribution installation
 $passphrase = "contoso-password"

 $tags = @{"DiskEncryptionKeyEncryptionAlgorithm" = "RSA-OAEP"; "DiskEncryptionKeyFileName" = "LinuxPassPhraseFileName"}
 $secretName = [guid]::NewGuid().ToString()
 $secretValue = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($passphrase))
 $secureSecretValue = ConvertTo-SecureString $secretValue -AsPlainText -Force

 $secret = Set-AzKeyVaultSecret -VaultName $KeyVaultName -Name $secretName -SecretValue $secureSecretValue -tags $tags
 $secretUrl = $secret.Id

在下一步中使用 $secretUrl 以便在不使用 KEK 的情况下附加 OS 磁盘

使用 KEK 加密的磁盘加密机密

将机密上传到 Key Vault 之前,可根据需要使用密钥加密密钥对其进行加密。 先使用包装 API 加密使用密钥加密密钥的机密。 此包装操作的输出是 base64 URL 编码的字符串,可以使用 Set-AzKeyVaultSecret cmdlet 将其作为机密上传。

    # This is the passphrase that was provided for encryption during the distribution installation
    $passphrase = "contoso-password"

    Add-AzKeyVaultKey -VaultName $KeyVaultName -Name "keyencryptionkey" -Destination Software
    $KeyEncryptionKey = Get-AzKeyVaultKey -VaultName $KeyVault.OriginalVault.Name -Name "keyencryptionkey"

    $apiversion = "2015-06-01"

    ##############################
    # Get Auth URI
    ##############################

    $uri = $KeyVault.VaultUri + "/keys"
    $headers = @{}

    $response = try { Invoke-RestMethod -Method GET -Uri $uri -Headers $headers } catch { $_.Exception.Response }

    $authHeader = $response.Headers["www-authenticate"]
    $authUri = [regex]::match($authHeader, 'authorization="(.*?)"').Groups[1].Value

    Write-Host "Got Auth URI successfully"

    ##############################
    # Get Auth Token
    ##############################

    $uri = $authUri + "/oauth2/token"
    $body = "grant_type=client_credentials"
    $body += "&client_id=" + $AadClientId
    $body += "&client_secret=" + [Uri]::EscapeDataString($AadClientSecret)
    $body += "&resource=" + [Uri]::EscapeDataString("https://vault.azure.cn")
    $headers = @{}

    $response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers -Body $body

    $access_token = $response.access_token

    Write-Host "Got Auth Token successfully"

    ##############################
    # Get KEK info
    ##############################

    $uri = $KeyEncryptionKey.Id + "?api-version=" + $apiversion
    $headers = @{"Authorization" = "Bearer " + $access_token}

    $response = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers

    $keyid = $response.key.kid

    Write-Host "Got KEK info successfully"

    ##############################
    # Encrypt passphrase using KEK
    ##############################

    $passphraseB64 = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($Passphrase))
    $uri = $keyid + "/encrypt?api-version=" + $apiversion
    $headers = @{"Authorization" = "Bearer " + $access_token; "Content-Type" = "application/json"}
    $bodyObj = @{"alg" = "RSA-OAEP"; "value" = $passphraseB64}
    $body = $bodyObj | ConvertTo-Json

    $response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers -Body $body

    $wrappedSecret = $response.value

    Write-Host "Encrypted passphrase successfully"

    ##############################
    # Store secret
    ##############################

    $secretName = [guid]::NewGuid().ToString()
    $uri = $KeyVault.VaultUri + "/secrets/" + $secretName + "?api-version=" + $apiversion
    $secretAttributes = @{"enabled" = $true}
    $secretTags = @{"DiskEncryptionKeyEncryptionAlgorithm" = "RSA-OAEP"; "DiskEncryptionKeyFileName" = "LinuxPassPhraseFileName"}
    $headers = @{"Authorization" = "Bearer " + $access_token; "Content-Type" = "application/json"}
    $bodyObj = @{"value" = $wrappedSecret; "attributes" = $secretAttributes; "tags" = $secretTags}
    $body = $bodyObj | ConvertTo-Json

    $response = Invoke-RestMethod -Method PUT -Uri $uri -Headers $headers -Body $body

    Write-Host "Stored secret successfully"

    $secretUrl = $response.id

将在下一步中使用 $KeyEncryptionKey$secretUrl 以便在使用 KEK 的情况下附加 OS 磁盘

附加 OS 磁盘时指定机密 URL

不使用 KEK

附加 OS 磁盘时,需要传递 $secretUrl。 该 URL 是在“不使用 KEK 对磁盘加密机密进行加密”部分中生成的。

    Set-AzVMOSDisk `
            -VM $VirtualMachine `
            -Name $OSDiskName `
            -SourceImageUri $VhdUri `
            -VhdUri $OSDiskUri `
            -Windows `
            -CreateOption FromImage `
            -DiskEncryptionKeyVaultId $KeyVault.ResourceId `
            -DiskEncryptionKeyUrl $SecretUrl

使用 KEK

附加 OS 磁盘时,传递 $KeyEncryptionKey$secretUrl。 该 URL 是在“使用 KEK 加密的磁盘加密机密”部分生成的。

    Set-AzVMOSDisk `
            -VM $VirtualMachine `
            -Name $OSDiskName `
            -SourceImageUri $CopiedTemplateBlobUri `
            -VhdUri $OSDiskUri `
            -Windows `
            -CreateOption FromImage `
            -DiskEncryptionKeyVaultId $KeyVault.ResourceId `
            -DiskEncryptionKeyUrl $SecretUrl `
            -KeyEncryptionKeyVaultId $KeyVault.ResourceId `
            -KeyEncryptionKeyURL $KeyEncryptionKey.Id