From ebb2b5142236aaa0c1ca2457992be74b7b922eef Mon Sep 17 00:00:00 2001 From: Yichun Ma Date: Thu, 10 Mar 2022 16:13:05 +0800 Subject: [PATCH] `r\site_recovery_replicated_vm`: Add support for `target_disk_encryption_info` --- .../site_recovery_replicated_vm_resource.go | 131 +++++++++ ...te_recovery_replicated_vm_resource_test.go | 272 ++++++++++++++++++ .../site_recovery_replicated_vm.html.markdown | 26 ++ 3 files changed, 429 insertions(+) diff --git a/internal/services/recoveryservices/site_recovery_replicated_vm_resource.go b/internal/services/recoveryservices/site_recovery_replicated_vm_resource.go index 506fd23dbe70..89c77c13fdd0 100644 --- a/internal/services/recoveryservices/site_recovery_replicated_vm_resource.go +++ b/internal/services/recoveryservices/site_recovery_replicated_vm_resource.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/features" + keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/recoveryservices/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/recoveryservices/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -175,6 +176,60 @@ func resourceSiteRecoveryReplicatedVM() *pluginsdk.Resource { ValidateFunc: azure.ValidateResourceID, DiffSuppressFunc: suppress.CaseDifference, }, + + "target_disk_encryption_info": { + Type: pluginsdk.TypeList, + ConfigMode: pluginsdk.SchemaConfigModeAttr, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "disk_encryption_key": { + Type: pluginsdk.TypeList, + ConfigMode: pluginsdk.SchemaConfigModeAttr, + Required: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_url": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: keyVaultValidate.NestedItemId, + }, + + "vault_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: keyVaultValidate.VaultID, + }, + }, + }, + }, + "key_encryption_key": { + Type: pluginsdk.TypeList, + ConfigMode: pluginsdk.SchemaConfigModeAttr, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "key_url": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: keyVaultValidate.NestedItemId, + }, + + "vault_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: keyVaultValidate.VaultID, + }, + }, + }, + }, + }, + }, + }, }, }, }, @@ -273,6 +328,7 @@ func resourceSiteRecoveryReplicatedItemCreate(d *pluginsdk.ResourceData, meta in RecoveryReplicaDiskAccountType: &targetReplicaDiskType, RecoveryTargetDiskAccountType: &targetDiskType, RecoveryDiskEncryptionSetID: &targetEncryptionDiskSetID, + DiskEncryptionInfo: expandTargetDiskEncryptionInfo(diskInput["target_disk_encryption_info"].([]interface{})), }) } @@ -366,6 +422,7 @@ func resourceSiteRecoveryReplicatedItemUpdate(d *pluginsdk.ResourceData, meta in DiskID: &diskId, RecoveryReplicaDiskAccountType: &targetReplicaDiskType, RecoveryTargetDiskAccountType: &targetDiskType, + DiskEncryptionInfo: expandTargetDiskEncryptionInfo(diskInput["target_disk_encryption_info"].([]interface{})), }) } @@ -492,6 +549,8 @@ func resourceSiteRecoveryReplicatedItemRead(d *pluginsdk.ResourceData, meta inte } diskOutput["target_disk_encryption_set_id"] = recoveryEncryptionSetId + diskOutput["target_disk_encryption_info"] = flattenTargetDiskEncryptionInfo(disk) + disksOutput = append(disksOutput, diskOutput) } d.Set("managed_disk", pluginsdk.NewSet(resourceSiteRecoveryReplicatedVMDiskHash, disksOutput)) @@ -626,3 +685,75 @@ func waitForReplicationToBeHealthyRefreshFunc(d *pluginsdk.ResourceData, meta in return resp, *resp.Properties.ReplicationHealth, nil } } + +func expandTargetDiskEncryptionInfo(diskEncryptionInfoList []interface{}) *siterecovery.DiskEncryptionInfo { + if len(diskEncryptionInfoList) == 0 { + return &siterecovery.DiskEncryptionInfo{} + } + diskEncryptionInfoMap := diskEncryptionInfoList[0].(map[string]interface{}) + + dek := diskEncryptionInfoMap["disk_encryption_key"].([]interface{})[0].(map[string]interface{}) + diskEncryptionInfo := &siterecovery.DiskEncryptionInfo{ + DiskEncryptionKeyInfo: &siterecovery.DiskEncryptionKeyInfo{ + SecretIdentifier: utils.String(dek["secret_url"].(string)), + KeyVaultResourceArmID: utils.String(dek["vault_id"].(string)), + }, + } + + if keyEncryptionKey := diskEncryptionInfoMap["key_encryption_key"].([]interface{}); len(keyEncryptionKey) > 0 { + kek := keyEncryptionKey[0].(map[string]interface{}) + diskEncryptionInfo.KeyEncryptionKeyInfo = &siterecovery.KeyEncryptionKeyInfo{ + KeyIdentifier: utils.String(kek["key_url"].(string)), + KeyVaultResourceArmID: utils.String(kek["vault_id"].(string)), + } + } + + return diskEncryptionInfo +} + +func flattenTargetDiskEncryptionInfo(disk siterecovery.A2AProtectedManagedDiskDetails) []interface{} { + secretUrl := "" + dekVaultId := "" + keyUrl := "" + kekVaultId := "" + + if disk.SecretIdentifier != nil { + secretUrl = *disk.SecretIdentifier + } + if disk.DekKeyVaultArmID != nil { + dekVaultId = *disk.DekKeyVaultArmID + } + if disk.KeyIdentifier != nil { + keyUrl = *disk.KeyIdentifier + } + if disk.KekKeyVaultArmID != nil { + kekVaultId = *disk.KekKeyVaultArmID + } + + if secretUrl == "" && dekVaultId == "" && keyUrl == "" && kekVaultId == "" { + return []interface{}{} + } + + diskEncryptionKeys := make([]interface{}, 0) + if secretUrl != "" || dekVaultId != "" { + diskEncryptionKeys = append(diskEncryptionKeys, map[string]interface{}{ + "secret_url": secretUrl, + "vault_id": dekVaultId, + }) + } + + keyEncryptionKeys := make([]interface{}, 0) + if keyUrl != "" || kekVaultId != "" { + keyEncryptionKeys = append(keyEncryptionKeys, map[string]interface{}{ + "key_url": keyUrl, + "vault_id": kekVaultId, + }) + } + + return []interface{}{ + map[string]interface{}{ + "disk_encryption_key": diskEncryptionKeys, + "key_encryption_key": keyEncryptionKeys, + }, + } +} diff --git a/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go b/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go index d7dde80d7584..ce5712937b84 100644 --- a/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go +++ b/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go @@ -45,6 +45,21 @@ func TestAccSiteRecoveryReplicatedVm_des(t *testing.T) { }) } +func TestAccSiteRecoveryReplicatedVm_targetDiskEncryptionInfo(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_site_recovery_replicated_vm", "test") + r := SiteRecoveryReplicatedVmResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.targetDiskEncryptionInfo(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (SiteRecoveryReplicatedVmResource) basic(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -671,6 +686,263 @@ resource "azurerm_site_recovery_replicated_vm" "test" { `, data.RandomInteger, data.Locations.Primary, data.Locations.Secondary) } +func (SiteRecoveryReplicatedVmResource) targetDiskEncryptionInfo(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + purge_soft_delete_on_destroy = false + } + } +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-recovery-%[1]d-1" + location = "%[2]s" +} +resource "azurerm_resource_group" "test2" { + name = "acctestRG-recovery-%[1]d-2" + location = "%[3]s" +} + +resource "azurerm_key_vault" "test1" { + name = "acckv-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "premium" + enabled_for_disk_encryption = true + purge_protection_enabled = true +} + +resource "azurerm_key_vault_access_policy" "service-principal" { + key_vault_id = azurerm_key_vault.test1.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "create", + "delete", + "get", + "update", + ] + + secret_permissions = [ + "get", + "delete", + "set", + ] +} + +resource "azurerm_key_vault_key" "test1" { + name = "examplekey" + key_vault_id = azurerm_key_vault.test1.id + key_type = "RSA" + key_size = 3072 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] + + depends_on = [ + azurerm_key_vault_access_policy.service-principal + ] +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-vault-%[1]d" + location = azurerm_resource_group.test2.location + resource_group_name = azurerm_resource_group.test2.name + sku = "Standard" + soft_delete_enabled = false +} +resource "azurerm_site_recovery_fabric" "test1" { + resource_group_name = azurerm_resource_group.test2.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + name = "acctest-fabric1-%[1]d" + location = azurerm_resource_group.test.location +} +resource "azurerm_site_recovery_protection_container" "test1" { + resource_group_name = azurerm_resource_group.test2.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + recovery_fabric_name = azurerm_site_recovery_fabric.test1.name + name = "acctest-protection-cont1-%[1]d" +} +resource "azurerm_site_recovery_protection_container" "test2" { + resource_group_name = azurerm_resource_group.test2.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + recovery_fabric_name = azurerm_site_recovery_fabric.test1.name + name = "acctest-protection-cont2-t-%[1]d" +} +resource "azurerm_site_recovery_replication_policy" "test" { + resource_group_name = azurerm_resource_group.test2.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + name = "acctest-policy-%[1]d" + recovery_point_retention_in_minutes = 24 * 60 + application_consistent_snapshot_frequency_in_minutes = 4 * 60 +} +resource "azurerm_site_recovery_protection_container_mapping" "test" { + resource_group_name = azurerm_resource_group.test2.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + recovery_fabric_name = azurerm_site_recovery_fabric.test1.name + recovery_source_protection_container_name = azurerm_site_recovery_protection_container.test1.name + recovery_target_protection_container_id = azurerm_site_recovery_protection_container.test2.id + recovery_replication_policy_id = azurerm_site_recovery_replication_policy.test.id + name = "mapping-%[1]d" +} +resource "azurerm_virtual_network" "test1" { + name = "net-%[1]d" + resource_group_name = azurerm_resource_group.test.name + address_space = ["192.168.1.0/24"] + location = azurerm_site_recovery_fabric.test1.location +} +resource "azurerm_subnet" "test1" { + name = "snet-%[1]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test1.name + address_prefixes = ["192.168.1.0/24"] +} +resource "azurerm_network_interface" "test" { + name = "vm-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + ip_configuration { + name = "vm-%[1]d" + subnet_id = azurerm_subnet.test1.id + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_windows_virtual_machine" "vm" { + name = "acctvm%[4]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_D2s_v3" + admin_username = "adminuser" + admin_password = "P@ssw0rd1234!" + zone = "1" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2022-Datacenter" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } +} + +resource "azurerm_virtual_machine_extension" "test" { + name = "AzureDiskEncryption" + publisher = "Microsoft.Azure.Security" + type = "AzureDiskEncryption" + type_handler_version = "2.2" + auto_upgrade_minor_version = false + virtual_machine_id = azurerm_windows_virtual_machine.vm.id + + settings = <