Azure 磁碟加密範例指令碼

適用於:✔️ Windows VM

本文提供了對於準備預先加密 VHD 和其他工作的範例指令碼。

注意

除非另有說明,所有指令碼參考的都是 ADE 的最新且非 AAD 的版本。

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 加密的虛擬機器擴展集執行個體和延伸模組版本。

  • 列出所有用來加密金鑰保存庫中 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 要用來放置加密金鑰的金鑰保存庫名稱。 如果不存在此名稱的應用程式,將會以此名稱建立新的保存庫。 True
$location 金鑰保存庫的位置。 請確定金鑰保存庫和要加密的 VM 位於相同位置。 使用 Get-AzLocation 取得位置清單。 True
$subscriptionId 要使用的 Azure 訂用帳戶識別碼。 您可以使用 Get-AzSubscription 取得您的訂用帳戶識別碼。 True
$aadAppName 會用來將祕密寫入到 KeyVault 的 Microsoft Entra 應用程式名稱。 如果不存在此名稱的應用程式,將會以此名稱建立新的應用程式。 如果此應用程式已經存在,請將 aadClientSecret 參數傳遞至指令碼。 False
$aadClientSecret 稍早建立的 Microsoft Entra 應用程式用戶端密碼。 False
$keyEncryptionKeyName 金鑰保存庫中選用金鑰加密金鑰的名稱。 如果不存在此名稱的應用程式,將會以此名稱建立新的金鑰。 False

Resource Manager 範本

在沒有 Microsoft Entra 應用程式的情況下加密或解密 VM

使用 Microsoft Entra 應用程式加密或解密 VM (舊版)

準備已預先加密的 Windows VHD

下列各節是準備預先加密的 Windows VHD 以在 Azure IaaS 中部署為加密的 VHD 的必要項目。 使用該資訊以在 Azure Site Recovery 或 Azure 上準備並啟動全新的 Windows VM (VHD)。 如需有關如何準備和上傳 VHD 的詳細資訊,請參閱上傳一般化 VHD 並使用它在 Azure 中建立新的 VM

更新群組原則以對作業系統保護允許非 TPM

設定 BitLocker 磁碟機加密的 BitLocker 群組原則設定,其位於此路徑本機電腦原則>電腦設定>系統管理範本>Windows 元件。 將這個設定變更為作業系統磁碟機>啟動時需要其他驗證>在不含相容 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 磁碟區

若要壓縮作業系統磁碟分割並為 BitLocker 準備電腦,請視需要執行 bdehdcfg

bdehdcfg -target c: shrink -quiet

使用 BitLocker 保護作業系統磁碟區

使用 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 的祕密上傳至金鑰保存庫

先前取得的磁碟加密密碼必須上傳,做為金鑰保存庫中的密碼。 若要這樣做,您必須將設定的秘密權限和 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 cloud 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 字串編碼,然後上傳至金鑰保存庫。 此外,請確定在金鑰保存庫中建立密碼時會設定下列標籤。


 # 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 加密的磁碟加密密碼

將密碼上傳至金鑰保存庫之前,您可以選擇性地使用金鑰加密金鑰來加密密碼。 使用包裝 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.net")
    $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