Search code examples
azureazure-keyvaultterraform-provider-azure

Azure VM Disk Encryption: Failed to configure bitlocker as expected. Exception: Invalid URI


Terraform is throwing following error while trying to enable the Disk Encryption for Azure VM

│ Error: Code=VMExtensionProvisioningError Message=VM has reported a failure when processing extension 'de-vm-conn-jb-1'. Error message: [2.2.0.45] Failed to configure bitlocker as expected. Exception: Invalid URI /subscriptions/xxxxxxxx-xx-xxx-573/resourceGroups/rg-connectivity-keyvault-centralus-001/providers/Microsoft.KeyVault/vaults/kv-conn-centralus-65/keys/des-key---/versions/xxxxx39e6,

Key vault module

data "azurerm_client_config" "current" {}

resource "azurerm_key_vault" "key_vault" {
  name                            = var.keyvault_name_override != "" ? var.keyvault_name_override : "kv-${var.app_or_service_name}-${var.subscription_type}-${var.instance_number}"
  location                        = var.location
  resource_group_name             = var.rg_name
  tenant_id                       = data.azurerm_client_config.current.tenant_id
  enabled_for_deployment          = var.enabled_for_deployment
  enabled_for_disk_encryption     = var.enabled_for_disk_encryption
  enabled_for_template_deployment = var.enabled_for_template_deployment
  enable_rbac_authorization       = var.enable_rbac_authorization
  purge_protection_enabled        = var.purge_protection_enabled
  soft_delete_retention_days      = var.soft_delete_retention_days
  sku_name                        = var.sku
  tags                            = var.tags
  public_network_access_enabled   = var.enable_public_network_access 

  network_acls {
    bypass = "AzureServices"
    default_action = "Deny"
  }   
}

resource "azurerm_key_vault_access_policy" "kv-access-policy" {
  count        = var.grant_access_to_service_principal == true ? 1 : 0
  key_vault_id = azurerm_key_vault.key_vault.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = data.azurerm_client_config.current.object_id

  certificate_permissions = [
    "Backup", "Create", "Delete", "DeleteIssuers", "Get", "GetIssuers", "Import", "List", "ListIssuers", "ManageContacts", "ManageIssuers", "Purge", "Recover", "Restore", "SetIssuers", "Update",
  ]

  key_permissions = [
    "Backup", "Create", "Decrypt", "Delete", "Encrypt", "Get", "Import", "List", "Purge", "Recover", "Restore", "Sign", "UnwrapKey", "Update", "Verify", "WrapKey", "Release", "Rotate", "GetRotationPolicy", "SetRotationPolicy",
  ]

  secret_permissions = [
    "Backup", "Delete", "Get", "List", "Purge", "Recover", "Restore", "Set",
  ]

  depends_on = [
    azurerm_key_vault.key_vault
  ]
}

resource "azurerm_key_vault_key" "vm-key" {
  count        = var.enabled_for_disk_encryption == true ? 1 : 0  
  name         = "des-key-${var.app_or_service_name}-${var.subscription_type}-${var.instance_number}"
  key_vault_id = azurerm_key_vault.key_vault.id
  key_type     = "RSA"
  key_size     = 2048

  depends_on = [
    azurerm_key_vault_access_policy.kv-access-policy
  ]

  key_opts = [
    "decrypt",
    "encrypt",
    "sign",
    "unwrapKey",
    "verify",
    "wrapKey",
  ]
}

resource "azurerm_disk_encryption_set" "en-set" {
  count               = var.enabled_for_disk_encryption == true ? 1 : 0    
  name                = "des_${var.app_or_service_name}_${var.subscription_type}_${var.instance_number}"
  resource_group_name = var.rg_name
  location            = var.location
  key_vault_key_id    = azurerm_key_vault_key.vm-key[0].id

  identity {
    type = "SystemAssigned"
  }

  depends_on = [
    azurerm_key_vault_key.vm-key
  ] 
}

resource "azurerm_key_vault_access_policy" "kv-access-policy-des" {
  count        = var.enabled_for_disk_encryption == true ? 1 : 0    
  key_vault_id = azurerm_key_vault.key_vault.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = azurerm_disk_encryption_set.en-set[0].identity.0.principal_id

  key_permissions = [
    "Get",
    "WrapKey",
    "UnwrapKey"
  ]

  depends_on = [
    azurerm_disk_encryption_set.en-set
  ]   
}

output "keyvault_id" {
  value = azurerm_key_vault.key_vault.id
}

output "keyvault_uri" {
  value = azurerm_key_vault.key_vault.vault_uri
}

output "diskencryption_key_uri" {
  value = length(azurerm_key_vault_key.vm-key) > 0 ? azurerm_key_vault_key.vm-key[0].resource_id : null  
}

VM module : The output of the above key vault module is passed as a input to the VM module as mentioned below

  1. keyvault_uri is supplied as a KeyVaultURL
  2. keyvault_id is supplied as a KeyVaultResourceID
  3. diskencryption_key_uri is supplied as a KeyEncryptionKeyURL
  4. keyvault_id is supplied as a KekVaultResourceId

VM module is defined as highlighted below

# Computer name can be up to 15 characters
resource "azurerm_windows_virtual_machine" "Lz_VM" {
  name                        = "vm-${var.subscription_type}-${var.vm_type}-${var.instance_number}"
  resource_group_name         = var.resource_group_name
  location                    = var.location
  size                        = "Standard_B2S"
  admin_username              = var.username
  admin_password              = var.password
  provision_vm_agent          = true
  allow_extension_operations  = true
  network_interface_ids       = [azurerm_network_interface.vm-nic.id]
  encryption_at_host_enabled  = var.enabled_for_disk_encryption == true ? false : var.encryption_at_host_enabled

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2016-Datacenter"
    version   = "latest"
  }

  identity {
    type  = "SystemAssigned"
  }

  tags                = var.tags
}

# This extension is needed for other extensions
resource "azurerm_virtual_machine_extension" "daa-agent" {
  name                       = "DependencyAgentWindows"
  virtual_machine_id         = azurerm_windows_virtual_machine.Lz_VM.id
  publisher                  = "Microsoft.Azure.Monitoring.DependencyAgent"
  type                       = "DependencyAgentWindows"
  type_handler_version       = "9.10"
  automatic_upgrade_enabled  = true
  auto_upgrade_minor_version = true
}

# Add logging and monitoring extensions
resource "azurerm_virtual_machine_extension" "monitor-agent" {
  depends_on = [  azurerm_virtual_machine_extension.daa-agent  ]
  name                  = "AzureMonitorWindowsAgent"
  virtual_machine_id    = azurerm_windows_virtual_machine.Lz_VM.id
  publisher             = "Microsoft.Azure.Monitor"
  type                  = "AzureMonitorWindowsAgent"
  type_handler_version  =  "1.5"
  automatic_upgrade_enabled  = true
  auto_upgrade_minor_version = true
}

resource "azurerm_virtual_machine_extension" "omsagentwin" {
  name                       = "OmsAgentForWindows"
  virtual_machine_id         = azurerm_windows_virtual_machine.Lz_VM.id
  publisher                  = "Microsoft.EnterpriseCloud.Monitoring"
  type                       = "MicrosoftMonitoringAgent"
  type_handler_version       = "1.0"
  auto_upgrade_minor_version = true

  settings = <<SETTINGS
    {
      "workspaceId": "${var.log_analytics_workspaceid}",
      "azureResourceId": "${azurerm_windows_virtual_machine.Lz_VM.id}",
      "stopOnMultipleConnections": "false"
    }
  SETTINGS

  protected_settings = <<PROTECTED_SETTINGS
    {
    "workspaceKey": "${var.log_analytics_workspace_key}"
    }
  PROTECTED_SETTINGS
}
resource "azurerm_virtual_machine_extension" "gc" {
  name                       = "AzurePolicyforWindows"
  virtual_machine_id         = azurerm_windows_virtual_machine.Lz_VM.id
  publisher                  = "Microsoft.GuestConfiguration"
  type                       = "ConfigurationforWindows"
  type_handler_version       = "1.0"
  auto_upgrade_minor_version = true
}

resource "azurerm_virtual_machine_extension" "disk-encryption" {
  count                       = var.enabled_for_disk_encryption == true ? 1 : 0    
  name                        = "de-vm-${var.subscription_type}-${var.vm_type}-${var.instance_number}"
  virtual_machine_id          = azurerm_windows_virtual_machine.Lz_VM.id
  publisher                   = "Microsoft.Azure.Security"
  type                        = "AzureDiskEncryption"
  type_handler_version        = 2.2
  auto_upgrade_minor_version  = true

  settings = <<SETTINGS
    {
        "EncryptionOperation": "EnableEncryption",
        "KeyVaultURL": "${var.keyvaultURL}",
        "KeyVaultResourceId": "${var.keyvaultResourceId}",                   
        "KeyEncryptionKeyURL": "${var.keyResourceURL}",                   
        "KekVaultResourceId": "${var.keyvaultResourceId}",
        "KeyEncryptionAlgorithm": "RSA-OAEP",
        "VolumeType": "All"
    }
SETTINGS
}

It works perfectly fine when the following variables are replaced with the actual vaules

"KeyVaultURL": "${var.keyvaultURL}",
"KeyVaultResourceId": "${var.keyvaultResourceId}",                   
"KeyEncryptionKeyURL": "${var.keyResourceURL}",                   
"KekVaultResourceId": "${var.keyvaultResourceId}",

Update: To set the OS_Disk Encryption

Output from Key vault

output "disk_encryption_key_set_id" {
  value = length(azurerm_disk_encryption_set.en-set) > 0 ? azurerm_disk_encryption_set.en-set[0].id : null
}

In VM module

variable "disk_encryption_key_set_id" {
  type    = string
  default = null
}

and used it like below

  os_disk {
    caching                 = "ReadWrite"
    storage_account_type    = "Standard_LRS"
    disk_encryption_set_id  = var.disk_encryption_key_set_id
  }

Is this fine?


Solution

  • This setting block worked for me with your code.

      settings = <<SETTINGS
        {
            "EncryptionOperation": "EnableEncryption",
            "KeyVaultURL": "${azurerm_key_vault.key_vault.vault_uri}",
            "KeyVaultResourceId": "${azurerm_key_vault.key_vault.id}",                   
            "KeyEncryptionKeyURL": "${azurerm_key_vault_key.vm-key[0].id}",         
            "KekVaultResourceId": "${azurerm_key_vault.key_vault.id}",  
            "KeyEncryptionAlgorithm": "RSA-OAEP",
            "VolumeType": "All"
        }
    SETTINGS
    

    As I had no module-referenced name could not decide myself but after having the following outputs in the key vault module, as per your naming schema.

    output "keyvault_id" {
      value = azurerm_key_vault.key_vault.id
    }
    output "keyvault_uri" {
      value = azurerm_key_vault.key_vault.vault_uri
    }
    output "diskencryption_key_uri" {
      value = azurerm_key_vault_key.vm-key[0].id. #<<----CHANGED--->>#
    }
    

    in your key_vault module it can be adjusted as follows:

        {
            "EncryptionOperation": "EnableEncryption",
            "KeyVaultURL": "${module.<module_name>.keyvault_uri}",
            "KeyVaultResourceId": "${module.<module_name>.keyvault_id}",                   
            "KeyEncryptionKeyURL": "${module.<module_name>.diskencryption_key_uri}",         
            "KekVaultResourceId": "${module.<module_name>.keyvault_id}",        
            "KeyEncryptionAlgorithm": "RSA-OAEP",
            "VolumeType": "All"
        }
    

    Referenced Successful Apply

    azurerm_windows_virtual_machine.Lz_VM: Modifications complete after 47s [id=/subscriptions/yoursubscriptionid/resourceGroups/rg-kv-stackoverflow/providers/Microsoft.Compute/virtualMachines/vm-stackoverflow-001]
    azurerm_virtual_machine_extension.disk-encryption[0]: Creating...
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [10s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [20s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [30s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [40s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [50s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [1m0s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [1m10s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [1m20s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [1m30s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [1m40s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [1m50s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Still creating... [2m0s elapsed]
    azurerm_virtual_machine_extension.disk-encryption[0]: Creation complete after 2m3s [id=/subscriptions/yoursubscriptionid/resourceGroups/rg-kv-stackoverflow/providers/Microsoft.Compute/virtualMachines/vm-stackoverflow-001/ext
    

    BUT

    I will suggest to use azurerm_disk_encryption_set even for os_disk encryption of virtual machine using the disk_encryption_set_id attribute in os_disk block.

    Example:

    resource "azurerm_windows_virtual_machine" "Lz_VM" {
    [.....]
    
      os_disk {
        caching              = "ReadWrite"
        storage_account_type = "Standard_LRS"
        disk_encryption_set_id = var.disk_encryption_set_id ## pass this from KV module, need one more output on KV module.
      }
    [....]
    

    I hope this helped.