diff --git a/internal/services/compute/managed_disk_resource.go b/internal/services/compute/managed_disk_resource.go index e0176bf13871..fed54e6e7105 100644 --- a/internal/services/compute/managed_disk_resource.go +++ b/internal/services/compute/managed_disk_resource.go @@ -181,6 +181,7 @@ func resourceManagedDisk() *pluginsdk.Resource { // https://github.com/Azure/azure-rest-api-specs/issues/8132 DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validate.DiskEncryptionSetID, + ConflictsWith: []string{"secure_vm_disk_encryption_set_id"}, }, "encryption_settings": encryptionSettingsSchema(), @@ -228,6 +229,25 @@ func resourceManagedDisk() *pluginsdk.Resource { ForceNew: true, }, + "secure_vm_disk_encryption_set_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validate.DiskEncryptionSetID, + ConflictsWith: []string{"disk_encryption_set_id"}, + }, + + "security_type": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.DiskSecurityTypesConfidentialVMVMGuestStateOnlyEncryptedWithPlatformKey), + string(compute.DiskSecurityTypesConfidentialVMDiskEncryptedWithPlatformKey), + string(compute.DiskSecurityTypesConfidentialVMDiskEncryptedWithCustomerKey), + }, false), + }, + "hyper_v_generation": { Type: pluginsdk.TypeString, Optional: true, @@ -436,6 +456,36 @@ func resourceManagedDiskCreate(d *pluginsdk.ResourceData, meta interface{}) erro } } + securityType := d.Get("security_type").(string) + secureVMDiskEncryptionId := d.Get("secure_vm_disk_encryption_set_id") + if securityType != "" { + if d.Get("trusted_launch_enabled").(bool) { + return fmt.Errorf("`security_type` cannot be specified when `trusted_launch_enabled` is set to `true`") + } + + switch createOption { + case compute.DiskCreateOptionFromImage: + case compute.DiskCreateOptionImport: + default: + return fmt.Errorf("`security_type` can only be specified when `create_option` is set to `FromImage` or `Import`") + } + + if compute.DiskSecurityTypesConfidentialVMDiskEncryptedWithCustomerKey == compute.DiskSecurityTypes(securityType) && secureVMDiskEncryptionId == "" { + return fmt.Errorf("`secure_vm_disk_encryption_set_id` must be specified when `security_type` is set to `ConfidentialVM_DiskEncryptedWithCustomerKey`") + } + + props.SecurityProfile = &compute.DiskSecurityProfile{ + SecurityType: compute.DiskSecurityTypes(securityType), + } + } + + if secureVMDiskEncryptionId != "" { + if compute.DiskSecurityTypesConfidentialVMDiskEncryptedWithCustomerKey != compute.DiskSecurityTypes(securityType) { + return fmt.Errorf("`secure_vm_disk_encryption_set_id` can only be specified when `security_type` is set to `ConfidentialVM_DiskEncryptedWithCustomerKey`") + } + props.SecurityProfile.SecureVMDiskEncryptionSetID = utils.String(secureVMDiskEncryptionId.(string)) + } + if d.Get("on_demand_bursting_enabled").(bool) { switch storageAccountType { case string(compute.StorageAccountTypesPremiumLRS): @@ -877,12 +927,22 @@ func resourceManagedDiskRead(d *pluginsdk.ResourceData, meta interface{}) error } trustedLaunchEnabled := false + securityType := "" + secureVMDiskEncryptionSetId := "" if securityProfile := props.SecurityProfile; securityProfile != nil { if securityProfile.SecurityType == compute.DiskSecurityTypesTrustedLaunch { trustedLaunchEnabled = true + } else { + securityType = string(securityProfile.SecurityType) + } + + if securityProfile.SecureVMDiskEncryptionSetID != nil { + secureVMDiskEncryptionSetId = *securityProfile.SecureVMDiskEncryptionSetID } } d.Set("trusted_launch_enabled", trustedLaunchEnabled) + d.Set("security_type", securityType) + d.Set("secure_vm_disk_encryption_set_id", secureVMDiskEncryptionSetId) onDemandBurstingEnabled := false if props.BurstingEnabled != nil { diff --git a/internal/services/compute/managed_disk_resource_test.go b/internal/services/compute/managed_disk_resource_test.go index cf5a1b4e4550..5e2544adeb82 100644 --- a/internal/services/compute/managed_disk_resource_test.go +++ b/internal/services/compute/managed_disk_resource_test.go @@ -544,6 +544,36 @@ func TestAccManagedDisk_create_withTrustedLaunchEnabled(t *testing.T) { }) } +func TestAccManagedDisk_create_withSecurityType(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_managed_disk", "test") + r := ManagedDiskResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.create_withSecurityType(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccManagedDisk_create_withSecureVMDiskEncryptionSetId(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_managed_disk", "test") + r := ManagedDiskResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.create_withSecureVMDiskEncryptionSetId(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccManagedDisk_update_withIOpsReadOnlyAndMBpsReadOnly(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_managed_disk", "test") r := ManagedDiskResource{} @@ -1756,6 +1786,158 @@ resource "azurerm_managed_disk" "test" { `, data.Locations.Primary, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } +func (ManagedDiskResource) create_withSecurityType(data acceptance.TestData) string { + // Confidential VM has limited region support + data.Locations.Primary = "northeurope" + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_platform_image" "test" { + location = "%s" + publisher = "Canonical" + offer = "0001-com-ubuntu-confidential-vm-focal" + sku = "20_04-lts-cvm" +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + + +resource "azurerm_managed_disk" "test" { + name = "acctestd-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + os_type = "Linux" + hyper_v_generation = "V2" + create_option = "FromImage" + image_reference_id = data.azurerm_platform_image.test.id + storage_account_type = "Standard_LRS" + + security_type = "ConfidentialVM_VMGuestStateOnlyEncryptedWithPlatformKey" +} +`, data.Locations.Primary, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (ManagedDiskResource) create_withSecureVMDiskEncryptionSetId(data acceptance.TestData) string { + // Confidential VM has limited region support + data.Locations.Primary = "northeurope" + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + recover_soft_deleted_key_vaults = false + purge_soft_delete_on_destroy = false + purge_soft_deleted_keys_on_destroy = false + } + } +} + +data "azurerm_client_config" "current" {} + +data "azurerm_platform_image" "test" { + location = "%[1]s" + publisher = "Canonical" + offer = "0001-com-ubuntu-confidential-vm-focal" + sku = "20_04-lts-cvm" +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[2]d" + location = "%[1]s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv%[3]s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "premium" + tenant_id = data.azurerm_client_config.current.tenant_id + enabled_for_disk_encryption = true + soft_delete_retention_days = 7 + purge_protection_enabled = true +} + +resource "azurerm_key_vault_access_policy" "service-principal" { + key_vault_id = azurerm_key_vault.test.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + key_permissions = [ + "Create", + "Delete", + "Get", + "Purge", + "Update", + ] + secret_permissions = [ + "Get", + "Delete", + "Set", + ] +} + +resource "azurerm_key_vault_key" "test" { + name = "examplekey" + key_vault_id = azurerm_key_vault.test.id + key_type = "RSA-HSM" + key_size = 2048 + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] + depends_on = [azurerm_key_vault_access_policy.service-principal] +} + +resource "azurerm_disk_encryption_set" "test" { + name = "acctestdes-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + key_vault_key_id = azurerm_key_vault_key.test.id + encryption_type = "ConfidentialVmEncryptedWithCustomerKey" + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_key_vault_access_policy" "disk-encryption" { + key_vault_id = azurerm_key_vault.test.id + key_permissions = [ + "Get", + "WrapKey", + "UnwrapKey", + ] + tenant_id = azurerm_disk_encryption_set.test.identity.0.tenant_id + object_id = azurerm_disk_encryption_set.test.identity.0.principal_id +} + +resource "azurerm_managed_disk" "test" { + name = "acctestd-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + os_type = "Linux" + hyper_v_generation = "V2" + create_option = "FromImage" + image_reference_id = data.azurerm_platform_image.test.id + storage_account_type = "Standard_LRS" + + security_type = "ConfidentialVM_DiskEncryptedWithCustomerKey" + secure_vm_disk_encryption_set_id = azurerm_disk_encryption_set.test.id + + depends_on = [ + azurerm_key_vault_access_policy.disk-encryption, + ] +} + +`, data.Locations.Primary, data.RandomInteger, data.RandomString) +} + func (ManagedDiskResource) create_withIOpsReadOnlyAndMBpsReadOnly(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/r/managed_disk.html.markdown b/website/docs/r/managed_disk.html.markdown index 34415e4fb1b8..a5ee4086193c 100644 --- a/website/docs/r/managed_disk.html.markdown +++ b/website/docs/r/managed_disk.html.markdown @@ -91,7 +91,7 @@ The following arguments are supported: --- -* `disk_encryption_set_id` - (Optional) The ID of a Disk Encryption Set which should be used to encrypt this Managed Disk. +* `disk_encryption_set_id` - (Optional) The ID of a Disk Encryption Set which should be used to encrypt this Managed Disk. Conflicts with `secure_vm_disk_encryption_set_id`. ~> **NOTE:** The Disk Encryption Set must have the `Reader` Role Assignment scoped on the Key Vault - in addition to an Access Policy to the Key Vault @@ -143,6 +143,16 @@ The following arguments are supported: -> **Note:** Trusted Launch can only be enabled when `create_option` is `FromImage` or `Import`. +* `security_type` - (Optional) Security Type of the Managed Disk when it is used for a Confidential VM. Possible values are `ConfidentialVM_VMGuestStateOnlyEncryptedWithPlatformKey`, `ConfidentialVM_DiskEncryptedWithPlatformKey` and `ConfidentialVM_DiskEncryptedWithCustomerKey`. Changing this forces a new resource to be created. + +~> **NOTE:** `security_type` cannot be specified when `trusted_launch_enabled` is set to true. + +~> **NOTE:** `secure_vm_disk_encryption_set_id` must be specified when `security_type` is set to `ConfidentialVM_DiskEncryptedWithCustomerKey`. + +* `secure_vm_disk_encryption_set_id` - (Optional) The ID of the Disk Encryption Set which should be used to Encrypt this OS Disk when the Virtual Machine is a Confidential VM. Conflicts with `disk_encryption_set_id`. Changing this forces a new resource to be created. + +~> **NOTE:** `secure_vm_disk_encryption_set_id` can only be specified when `security_type` is set to `ConfidentialVM_DiskEncryptedWithCustomerKey`. + * `on_demand_bursting_enabled` (Optional) Specifies if On-Demand Bursting is enabled for the Managed Disk. Defaults to `false`. -> **Note:** Credit-Based Bursting is enabled by default on all eligible disks. More information on [Credit-Based and On-Demand Bursting can be found in the documentation](https://docs.microsoft.com/azure/virtual-machines/disk-bursting#disk-level-bursting).