Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_managed_disk: Add support for Confidential VM #16908

Merged
merged 2 commits into from
May 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions internal/services/compute/managed_disk_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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 {
Expand Down
182 changes: 182 additions & 0 deletions internal/services/compute/managed_disk_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down Expand Up @@ -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" {
Expand Down
12 changes: 11 additions & 1 deletion website/docs/r/managed_disk.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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).
Expand Down