From ad0e160b8d38376726ed56d0d165b3def946234b Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:49:52 +0800 Subject: [PATCH 01/10] `azurerm_backup_protected_vm` - support recover soft deleted vm --- internal/features/defaults.go | 3 + internal/features/user_flags.go | 5 + internal/provider/features.go | 25 ++ internal/provider/features_test.go | 84 ++++++ .../backup_protected_vm_resource.go | 58 ++++- .../backup_protected_vm_resource_test.go | 246 +++++++++++++++++- .../docs/guides/features-block.html.markdown | 12 + 7 files changed, 423 insertions(+), 10 deletions(-) diff --git a/internal/features/defaults.go b/internal/features/defaults.go index 133d6a01a887..d529cf52fc53 100644 --- a/internal/features/defaults.go +++ b/internal/features/defaults.go @@ -40,6 +40,9 @@ func Default() UserFeatures { ResourceGroup: ResourceGroupFeatures{ PreventDeletionIfContainsResources: true, }, + RecoveryServicesVault: RecoveryServicesVault{ + RecoverSoftDeletedBackupProtected: true, + }, TemplateDeployment: TemplateDeploymentFeatures{ DeleteNestedItemsDuringDeletion: true, }, diff --git a/internal/features/user_flags.go b/internal/features/user_flags.go index a0b1cfe9f448..d5651bcf22d5 100644 --- a/internal/features/user_flags.go +++ b/internal/features/user_flags.go @@ -14,6 +14,7 @@ type UserFeatures struct { TemplateDeployment TemplateDeploymentFeatures LogAnalyticsWorkspace LogAnalyticsWorkspaceFeatures ResourceGroup ResourceGroupFeatures + RecoveryServicesVault RecoveryServicesVault ManagedDisk ManagedDiskFeatures Subscription SubscriptionFeatures } @@ -79,3 +80,7 @@ type AppConfigurationFeatures struct { type SubscriptionFeatures struct { PreventCancellationOnDestroy bool } + +type RecoveryServicesVault struct { + RecoverSoftDeletedBackupProtected bool +} diff --git a/internal/provider/features.go b/internal/provider/features.go index c6a416af55fb..7d0147077c70 100644 --- a/internal/provider/features.go +++ b/internal/provider/features.go @@ -255,6 +255,21 @@ func schemaFeatures(supportLegacyTestSuite bool) *pluginsdk.Schema { }, }, + "recovery_services_vault": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*schema.Schema{ + "recover_soft_deleted_backup_protected_vm": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + }, + }, + }, + "managed_disk": { Type: pluginsdk.TypeList, Optional: true, @@ -461,6 +476,16 @@ func expandFeatures(input []interface{}) features.UserFeatures { } } + if raw, ok := val["recovery_services_vault"]; ok { + items := raw.([]interface{}) + if len(items) > 0 && items[0] != nil { + appConfRaw := items[0].(map[string]interface{}) + if v, ok := appConfRaw["recover_soft_deleted_backup_protected_vm"]; ok { + featuresMap.RecoveryServicesVault.RecoverSoftDeletedBackupProtected = v.(bool) + } + } + } + if raw, ok := val["managed_disk"]; ok { items := raw.([]interface{}) if len(items) > 0 { diff --git a/internal/provider/features_test.go b/internal/provider/features_test.go index 2e35abf8a385..b85ef73c5f55 100644 --- a/internal/provider/features_test.go +++ b/internal/provider/features_test.go @@ -68,6 +68,9 @@ func TestExpandFeatures(t *testing.T) { ResourceGroup: features.ResourceGroupFeatures{ PreventDeletionIfContainsResources: true, }, + RecoveryServicesVault: features.RecoveryServicesVault{ + RecoverSoftDeletedBackupProtected: true, + }, Subscription: features.SubscriptionFeatures{ PreventCancellationOnDestroy: false, }, @@ -127,6 +130,11 @@ func TestExpandFeatures(t *testing.T) { "prevent_deletion_if_contains_resources": true, }, }, + "recovery_services_vault": []interface{}{ + map[string]interface{}{ + "recover_soft_deleted_backup_protected_vm": true, + }, + }, "subscription": []interface{}{ map[string]interface{}{ "prevent_cancellation_on_destroy": true, @@ -188,6 +196,9 @@ func TestExpandFeatures(t *testing.T) { ResourceGroup: features.ResourceGroupFeatures{ PreventDeletionIfContainsResources: true, }, + RecoveryServicesVault: features.RecoveryServicesVault{ + RecoverSoftDeletedBackupProtected: true, + }, Subscription: features.SubscriptionFeatures{ PreventCancellationOnDestroy: true, }, @@ -260,6 +271,11 @@ func TestExpandFeatures(t *testing.T) { "prevent_deletion_if_contains_resources": false, }, }, + "recovery_services_vault": []interface{}{ + map[string]interface{}{ + "recover_soft_deleted_backup_protected_vm": false, + }, + }, "subscription": []interface{}{ map[string]interface{}{ "prevent_cancellation_on_destroy": false, @@ -321,6 +337,9 @@ func TestExpandFeatures(t *testing.T) { ResourceGroup: features.ResourceGroupFeatures{ PreventDeletionIfContainsResources: false, }, + RecoveryServicesVault: features.RecoveryServicesVault{ + RecoverSoftDeletedBackupProtected: false, + }, Subscription: features.SubscriptionFeatures{ PreventCancellationOnDestroy: false, }, @@ -1151,6 +1170,71 @@ func TestExpandFeaturesResourceGroup(t *testing.T) { } } +func TestExpandFeaturesRecoveryServicesVault(t *testing.T) { + testData := []struct { + Name string + Input []interface{} + EnvVars map[string]interface{} + Expected features.UserFeatures + }{ + { + Name: "Empty Block", + Input: []interface{}{ + map[string]interface{}{ + "recovery_services_vault": []interface{}{}, + }, + }, + Expected: features.UserFeatures{ + RecoveryServicesVault: features.RecoveryServicesVault{ + RecoverSoftDeletedBackupProtected: true, + }, + }, + }, + { + Name: "Purge Soft Delete On Destroy and Recover Soft Deleted Protected VM Enabled", + Input: []interface{}{ + map[string]interface{}{ + "recovery_services_vault": []interface{}{ + map[string]interface{}{ + "recover_soft_deleted_backup_protected_vm": true, + }, + }, + }, + }, + Expected: features.UserFeatures{ + RecoveryServicesVault: features.RecoveryServicesVault{ + RecoverSoftDeletedBackupProtected: true, + }, + }, + }, + { + Name: "Purge Soft Delete On Destroy and Recover Soft Deleted Protected VM Disabled", + Input: []interface{}{ + map[string]interface{}{ + "recovery_services_vault": []interface{}{ + map[string]interface{}{ + "recover_soft_deleted_backup_protected_vm": false, + }, + }, + }, + }, + Expected: features.UserFeatures{ + RecoveryServicesVault: features.RecoveryServicesVault{ + RecoverSoftDeletedBackupProtected: false, + }, + }, + }, + } + + for _, testCase := range testData { + t.Logf("[DEBUG] Test Case: %q", testCase.Name) + result := expandFeatures(testCase.Input) + if !reflect.DeepEqual(result.RecoveryServicesVault, testCase.Expected.RecoveryServicesVault) { + t.Fatalf("Expected %+v but got %+v", testCase.Expected.RecoveryServicesVault, result.RecoveryServicesVault) + } + } +} + func TestExpandFeaturesManagedDisk(t *testing.T) { testData := []struct { Name string diff --git a/internal/services/recoveryservices/backup_protected_vm_resource.go b/internal/services/recoveryservices/backup_protected_vm_resource.go index 912bfd31d41d..a8faf42f94ac 100644 --- a/internal/services/recoveryservices/backup_protected_vm_resource.go +++ b/internal/services/recoveryservices/backup_protected_vm_resource.go @@ -96,15 +96,24 @@ func resourceRecoveryServicesBackupProtectedVMCreateUpdate(d *pluginsdk.Resource log.Printf("[DEBUG] Creating/updating Azure Backup Protected VM %s (resource group %q)", protectedItemName, resourceGroup) id := protecteditems.NewProtectedItemID(subscriptionId, resourceGroup, vaultName, "Azure", containerName, protectedItemName) - if d.IsNewResource() { - existing, err := client.Get(ctx, id, protecteditems.GetOperationOptions{}) - if err != nil { - if !response.WasNotFound(existing.HttpResponse) { - return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + + existing, err := client.Get(ctx, id, protecteditems.GetOperationOptions{}) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !response.WasNotFound(existing.HttpResponse) { + isSoftDeleted := false + if meta.(*clients.Client).Features.RecoveryServicesVault.RecoverSoftDeletedBackupProtected { + isSoftDeleted, err = resourceRecoveryServicesVaultBackupProtectedVMrecoverSoftDeleted(ctx, client, opClient, id, existing.Model) + if err != nil { + return fmt.Errorf("recovering soft deleted %s: %+v", id, err) } } - if !response.WasNotFound(existing.HttpResponse) { + if !isSoftDeleted && d.IsNewResource() { return tf.ImportAsExistsError("azurerm_backup_protected_vm", id.ID()) } } @@ -417,6 +426,43 @@ func expandDiskLunList(input []interface{}) []interface{} { return result } +func resourceRecoveryServicesVaultBackupProtectedVMrecoverSoftDeleted(ctx context.Context, client *protecteditems.ProtectedItemsClient, opClient *backup.ProtectedItemOperationResultsClient, id protecteditems.ProtectedItemId, model *protecteditems.ProtectedItemResource) (isSoftDeleted bool, err error) { + isSoftDeleted = false + if model == nil { + return isSoftDeleted, fmt.Errorf("model was nil") + } + + if model.Properties == nil { + return isSoftDeleted, fmt.Errorf("properties was nil") + } + + if prop, ok := model.Properties.(protecteditems.AzureIaaSComputeVMProtectedItem); ok { + if prop.IsScheduledForDeferredDelete != nil && *prop.IsScheduledForDeferredDelete { + isSoftDeleted = true + resp, err := client.CreateOrUpdate(ctx, id, protecteditems.ProtectedItemResource{ + Properties: &protecteditems.AzureIaaSComputeVMProtectedItem{ + IsRehydrate: pointer.To(true), + }, + }) + if err != nil { + return isSoftDeleted, fmt.Errorf("issuing request for %s: %+v", id, err) + } + + operationId, err := parseBackupOperationId(resp.HttpResponse) + if err != nil { + return isSoftDeleted, err + } + + if err = resourceRecoveryServicesBackupProtectedVMWaitForStateCreateUpdate(ctx, opClient, id, operationId); err != nil { + return isSoftDeleted, err + } + + } + } + + return isSoftDeleted, nil +} + func resourceRecoveryServicesBackupProtectedVMSchema() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "resource_group_name": commonschema.ResourceGroupName(), diff --git a/internal/services/recoveryservices/backup_protected_vm_resource_test.go b/internal/services/recoveryservices/backup_protected_vm_resource_test.go index d1d8c2e9e3fd..9e30c30ccb06 100644 --- a/internal/services/recoveryservices/backup_protected_vm_resource_test.go +++ b/internal/services/recoveryservices/backup_protected_vm_resource_test.go @@ -232,6 +232,41 @@ func TestAccBackupProtectedVm_protectionStopped(t *testing.T) { }) } +func TestAccBackupProtectedVm_recoverSoftDeletedVM(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_backup_protected_vm", "test") + r := BackupProtectedVmResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicWithSoftDelete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + ), + }, + data.ImportStep(), + { + Config: r.basicWithSoftDeleted(data), + }, + { + Config: r.basicWithSoftDelete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + ), + }, + data.ImportStep(), + { + // to disable soft delete feature + Config: r.basic(data), + }, + { + // vault cannot be deleted unless we unregister all backups + Config: r.base(data), + }, + }) +} + func (t BackupProtectedVmResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := protecteditems.ParseProtectedItemID(state.ID) if err != nil { @@ -248,10 +283,6 @@ func (t BackupProtectedVmResource) Exists(ctx context.Context, clients *clients. func (BackupProtectedVmResource) base(data acceptance.TestData) string { return fmt.Sprintf(` -provider "azurerm" { - features {} -} - resource "azurerm_resource_group" "test" { name = "acctestRG-backup-%d" location = "%s" @@ -365,6 +396,9 @@ resource "azurerm_virtual_machine" "test" { storage_uri = azurerm_storage_account.test.primary_blob_endpoint } + lifecycle { + ignore_changes = [tags, identity] + } } resource "azurerm_recovery_services_vault" "test" { @@ -473,8 +507,158 @@ resource "azurerm_backup_policy_vm" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomString, data.RandomInteger, data.RandomInteger) } +func (BackupProtectedVmResource) baseWithSoftDelete(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-backup-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "vnet" + location = azurerm_resource_group.test.location + address_space = ["10.0.0.0/16"] + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_subnet" "test" { + name = "acctest_subnet" + virtual_network_name = azurerm_virtual_network.test.name + resource_group_name = azurerm_resource_group.test.name + address_prefixes = ["10.0.10.0/24"] +} + +resource "azurerm_network_interface" "test" { + name = "acctest_nic" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + ip_configuration { + name = "acctestipconfig" + subnet_id = azurerm_subnet.test.id + private_ip_address_allocation = "Dynamic" + public_ip_address_id = azurerm_public_ip.test.id + } +} + +resource "azurerm_public_ip" "test" { + name = "acctest-ip" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + allocation_method = "Dynamic" + domain_name_label = "acctestip%d" +} + +resource "azurerm_storage_account" "test" { + name = "acctest%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_managed_disk" "test" { + name = "acctest-datadisk" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "1023" +} + +resource "azurerm_virtual_machine" "test" { + name = "acctestvm" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + vm_size = "Standard_D1_v2" + network_interface_ids = [azurerm_network_interface.test.id] + + delete_os_disk_on_termination = true + delete_data_disks_on_termination = true + + storage_image_reference { + publisher = "Canonical" + offer = "0001-com-ubuntu-server-jammy" + sku = "22_04-lts" + version = "latest" + } + + storage_os_disk { + name = "acctest-osdisk" + managed_disk_type = "Standard_LRS" + caching = "ReadWrite" + create_option = "FromImage" + } + + storage_data_disk { + name = "acctest-datadisk" + managed_disk_id = azurerm_managed_disk.test.id + managed_disk_type = "Standard_LRS" + disk_size_gb = azurerm_managed_disk.test.disk_size_gb + create_option = "Attach" + lun = 0 + } + + storage_data_disk { + name = "acctest-another-datadisk" + create_option = "Empty" + disk_size_gb = "1" + lun = 1 + managed_disk_type = "Standard_LRS" + } + + os_profile { + computer_name = "acctest" + admin_username = "vmadmin" + admin_password = "Password123!@#" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + boot_diagnostics { + enabled = true + storage_uri = azurerm_storage_account.test.primary_blob_endpoint + } + + lifecycle { + ignore_changes = [tags] + } +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "Standard" + + soft_delete_enabled = true +} + +resource "azurerm_backup_policy_vm" "test" { + name = "acctest-%d" + resource_group_name = azurerm_resource_group.test.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + + backup { + frequency = "Daily" + time = "23:00" + } + + retention_daily { + count = 10 + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomString, data.RandomInteger, data.RandomInteger) +} + func (r BackupProtectedVmResource) basic(data acceptance.TestData) string { return fmt.Sprintf(` +provider "azurerm" { + features {} +} + %s resource "azurerm_backup_protected_vm" "test" { @@ -490,6 +674,10 @@ resource "azurerm_backup_protected_vm" "test" { func (r BackupProtectedVmResource) updateDiskExclusion(data acceptance.TestData) string { return fmt.Sprintf(` +provider "azurerm" { + features {} +} + %s resource "azurerm_backup_protected_vm" "test" { @@ -572,6 +760,10 @@ resource "azurerm_managed_disk" "test" { // For update backup policy id test func (r BackupProtectedVmResource) withBasePolicy(data acceptance.TestData) string { return fmt.Sprintf(` +provider "azurerm" { + features {} +} + %s resource "azurerm_backup_policy_vm" "test_change_backup" { @@ -634,6 +826,10 @@ resource "azurerm_backup_protected_vm" "import" { func (r BackupProtectedVmResource) additionalVault(data acceptance.TestData) string { return fmt.Sprintf(` +provider "azurerm" { + features {} +} + %s resource "azurerm_resource_group" "test2" { @@ -722,6 +918,10 @@ resource "azurerm_backup_protected_vm" "test" { func (r BackupProtectedVmResource) protectionStopped(data acceptance.TestData) string { return fmt.Sprintf(` +provider "azurerm" { + features {} +} + %s resource "azurerm_backup_protected_vm" "test" { @@ -734,3 +934,41 @@ resource "azurerm_backup_protected_vm" "test" { } `, r.base(data)) } + +func (r BackupProtectedVmResource) basicWithSoftDelete(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + recovery_services_vault { + recover_soft_deleted_backup_protected_vm = true + } + } +} + +%s + +resource "azurerm_backup_protected_vm" "test" { + resource_group_name = azurerm_resource_group.test.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + source_vm_id = azurerm_virtual_machine.test.id + backup_policy_id = azurerm_backup_policy_vm.test.id + + include_disk_luns = [0] +} +`, r.baseWithSoftDelete(data)) +} + +func (r BackupProtectedVmResource) basicWithSoftDeleted(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + recovery_services_vault { + recover_soft_deleted_backup_protected_vm = true + } + } +} + +%s + +`, r.baseWithSoftDelete(data)) +} diff --git a/website/docs/guides/features-block.html.markdown b/website/docs/guides/features-block.html.markdown index 36ace635190a..dc2b2ec82e3a 100644 --- a/website/docs/guides/features-block.html.markdown +++ b/website/docs/guides/features-block.html.markdown @@ -62,6 +62,10 @@ provider "azurerm" { prevent_deletion_if_contains_resources = true } + recovery_services_vault { + recover_soft_deleted_backup_protected_vm = true + } + subscription { prevent_cancellation_on_destroy = false } @@ -105,6 +109,8 @@ The `features` block supports the following: * `resource_group` - (Optional) A `resource_group` block as defined below. +* `recovery_services_vault` - (Optional) A `recovery_services_vault` block as defined below. + * `template_deployment` - (Optional) A `template_deployment` block as defined below. * `virtual_machine` - (Optional) A `virtual_machine` block as defined below. @@ -189,6 +195,12 @@ The `resource_group` block supports the following: --- +The `recovery_services_vault` block supports the following: + +* `recover_soft_deleted_backup_protected_vm` - (Optional) Should the `azurerm_backup_protected_vm` resource recover a Soft-Deleted protected VM? Defaults to `true`. + +--- + The `subscription` block supports the following: * `prevent_cancellation_on_destroy` - (Optional) Should the `azurerm_subscription` resource prevent a subscription to be cancelled on destroy? Defaults to `false`. From 1176de944fe6386df3b447bd5fa6bbcb6f755d6e Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:51:24 +0800 Subject: [PATCH 02/10] update test case --- .../recoveryservices/backup_protected_vm_resource_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/services/recoveryservices/backup_protected_vm_resource_test.go b/internal/services/recoveryservices/backup_protected_vm_resource_test.go index 9e30c30ccb06..49276fd7b73a 100644 --- a/internal/services/recoveryservices/backup_protected_vm_resource_test.go +++ b/internal/services/recoveryservices/backup_protected_vm_resource_test.go @@ -396,9 +396,6 @@ resource "azurerm_virtual_machine" "test" { storage_uri = azurerm_storage_account.test.primary_blob_endpoint } - lifecycle { - ignore_changes = [tags, identity] - } } resource "azurerm_recovery_services_vault" "test" { From ef912823bb0c238934d2c3a26a73c1024bb8639c Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:21:37 +0800 Subject: [PATCH 03/10] update per comment --- internal/features/defaults.go | 2 +- internal/features/user_flags.go | 2 +- internal/provider/features.go | 6 +-- internal/provider/features_test.go | 16 ++++---- internal/provider/provider.go | 3 -- .../backup_protected_vm_resource.go | 38 +++++++++++-------- .../backup_protected_vm_resource_test.go | 30 ++++++--------- 7 files changed, 47 insertions(+), 50 deletions(-) diff --git a/internal/features/defaults.go b/internal/features/defaults.go index d529cf52fc53..cd40ef111b78 100644 --- a/internal/features/defaults.go +++ b/internal/features/defaults.go @@ -41,7 +41,7 @@ func Default() UserFeatures { PreventDeletionIfContainsResources: true, }, RecoveryServicesVault: RecoveryServicesVault{ - RecoverSoftDeletedBackupProtected: true, + RecoverSoftDeletedBackupProtectedVM: true, }, TemplateDeployment: TemplateDeploymentFeatures{ DeleteNestedItemsDuringDeletion: true, diff --git a/internal/features/user_flags.go b/internal/features/user_flags.go index d5651bcf22d5..282db739577c 100644 --- a/internal/features/user_flags.go +++ b/internal/features/user_flags.go @@ -82,5 +82,5 @@ type SubscriptionFeatures struct { } type RecoveryServicesVault struct { - RecoverSoftDeletedBackupProtected bool + RecoverSoftDeletedBackupProtectedVM bool } diff --git a/internal/provider/features.go b/internal/provider/features.go index 7d0147077c70..e5a0fffca1d8 100644 --- a/internal/provider/features.go +++ b/internal/provider/features.go @@ -15,7 +15,7 @@ func schemaFeatures(supportLegacyTestSuite bool) *pluginsdk.Schema { // NOTE: if there's only one nested field these want to be Required (since there's no point // specifying the block otherwise) - however for 2+ they should be optional featuresMap := map[string]*pluginsdk.Schema{ - //lintignore:XS003 + // lintignore:XS003 "api_management": { Type: pluginsdk.TypeList, Optional: true, @@ -189,7 +189,7 @@ func schemaFeatures(supportLegacyTestSuite bool) *pluginsdk.Schema { }, }, - //lintignore:XS003 + // lintignore:XS003 "virtual_machine": { Type: pluginsdk.TypeList, Optional: true, @@ -481,7 +481,7 @@ func expandFeatures(input []interface{}) features.UserFeatures { if len(items) > 0 && items[0] != nil { appConfRaw := items[0].(map[string]interface{}) if v, ok := appConfRaw["recover_soft_deleted_backup_protected_vm"]; ok { - featuresMap.RecoveryServicesVault.RecoverSoftDeletedBackupProtected = v.(bool) + featuresMap.RecoveryServicesVault.RecoverSoftDeletedBackupProtectedVM = v.(bool) } } } diff --git a/internal/provider/features_test.go b/internal/provider/features_test.go index b85ef73c5f55..a6af5b87ea7a 100644 --- a/internal/provider/features_test.go +++ b/internal/provider/features_test.go @@ -69,7 +69,7 @@ func TestExpandFeatures(t *testing.T) { PreventDeletionIfContainsResources: true, }, RecoveryServicesVault: features.RecoveryServicesVault{ - RecoverSoftDeletedBackupProtected: true, + RecoverSoftDeletedBackupProtectedVM: true, }, Subscription: features.SubscriptionFeatures{ PreventCancellationOnDestroy: false, @@ -197,7 +197,7 @@ func TestExpandFeatures(t *testing.T) { PreventDeletionIfContainsResources: true, }, RecoveryServicesVault: features.RecoveryServicesVault{ - RecoverSoftDeletedBackupProtected: true, + RecoverSoftDeletedBackupProtectedVM: true, }, Subscription: features.SubscriptionFeatures{ PreventCancellationOnDestroy: true, @@ -338,7 +338,7 @@ func TestExpandFeatures(t *testing.T) { PreventDeletionIfContainsResources: false, }, RecoveryServicesVault: features.RecoveryServicesVault{ - RecoverSoftDeletedBackupProtected: false, + RecoverSoftDeletedBackupProtectedVM: false, }, Subscription: features.SubscriptionFeatures{ PreventCancellationOnDestroy: false, @@ -1186,12 +1186,12 @@ func TestExpandFeaturesRecoveryServicesVault(t *testing.T) { }, Expected: features.UserFeatures{ RecoveryServicesVault: features.RecoveryServicesVault{ - RecoverSoftDeletedBackupProtected: true, + RecoverSoftDeletedBackupProtectedVM: true, }, }, }, { - Name: "Purge Soft Delete On Destroy and Recover Soft Deleted Protected VM Enabled", + Name: "Recover Soft Deleted Protected VM Enabled", Input: []interface{}{ map[string]interface{}{ "recovery_services_vault": []interface{}{ @@ -1203,12 +1203,12 @@ func TestExpandFeaturesRecoveryServicesVault(t *testing.T) { }, Expected: features.UserFeatures{ RecoveryServicesVault: features.RecoveryServicesVault{ - RecoverSoftDeletedBackupProtected: true, + RecoverSoftDeletedBackupProtectedVM: true, }, }, }, { - Name: "Purge Soft Delete On Destroy and Recover Soft Deleted Protected VM Disabled", + Name: "Recover Soft Deleted Protected VM Disabled", Input: []interface{}{ map[string]interface{}{ "recovery_services_vault": []interface{}{ @@ -1220,7 +1220,7 @@ func TestExpandFeaturesRecoveryServicesVault(t *testing.T) { }, Expected: features.UserFeatures{ RecoveryServicesVault: features.RecoveryServicesVault{ - RecoverSoftDeletedBackupProtected: false, + RecoverSoftDeletedBackupProtectedVM: false, }, }, }, diff --git a/internal/provider/provider.go b/internal/provider/provider.go index e669474b9ed2..9f85127147ac 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -515,7 +515,6 @@ func getOidcToken(d *schema.ResourceData) (*string, error) { if path := d.Get("oidc_token_file_path").(string); path != "" { fileTokenRaw, err := os.ReadFile(path) - if err != nil { return nil, fmt.Errorf("reading OIDC Token from file %q: %v", path, err) } @@ -537,7 +536,6 @@ func getClientId(d *schema.ResourceData) (*string, error) { if path := d.Get("client_id_file_path").(string); path != "" { fileClientIdRaw, err := os.ReadFile(path) - if err != nil { return nil, fmt.Errorf("reading Client ID from file %q: %v", path, err) } @@ -559,7 +557,6 @@ func getClientSecret(d *schema.ResourceData) (*string, error) { if path := d.Get("client_secret_file_path").(string); path != "" { fileSecretRaw, err := os.ReadFile(path) - if err != nil { return nil, fmt.Errorf("reading Client Secret from file %q: %v", path, err) } diff --git a/internal/services/recoveryservices/backup_protected_vm_resource.go b/internal/services/recoveryservices/backup_protected_vm_resource.go index a8faf42f94ac..e97a254109f5 100644 --- a/internal/services/recoveryservices/backup_protected_vm_resource.go +++ b/internal/services/recoveryservices/backup_protected_vm_resource.go @@ -97,25 +97,28 @@ func resourceRecoveryServicesBackupProtectedVMCreateUpdate(d *pluginsdk.Resource id := protecteditems.NewProtectedItemID(subscriptionId, resourceGroup, vaultName, "Azure", containerName, protectedItemName) - existing, err := client.Get(ctx, id, protecteditems.GetOperationOptions{}) - if err != nil { - if !response.WasNotFound(existing.HttpResponse) { - return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + if d.IsNewResource() { + existing, err := client.Get(ctx, id, protecteditems.GetOperationOptions{}) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } } - } - if !response.WasNotFound(existing.HttpResponse) { - isSoftDeleted := false - if meta.(*clients.Client).Features.RecoveryServicesVault.RecoverSoftDeletedBackupProtected { - isSoftDeleted, err = resourceRecoveryServicesVaultBackupProtectedVMrecoverSoftDeleted(ctx, client, opClient, id, existing.Model) - if err != nil { - return fmt.Errorf("recovering soft deleted %s: %+v", id, err) + if !response.WasNotFound(existing.HttpResponse) { + isSoftDeleted := false + if meta.(*clients.Client).Features.RecoveryServicesVault.RecoverSoftDeletedBackupProtectedVM { + isSoftDeleted, err = resourceRecoveryServicesVaultBackupProtectedVMRecoverSoftDeleted(ctx, client, opClient, id, existing.Model) + if err != nil { + return fmt.Errorf("recovering soft deleted %s: %+v", id, err) + } } - } - if !isSoftDeleted && d.IsNewResource() { - return tf.ImportAsExistsError("azurerm_backup_protected_vm", id.ID()) + if !isSoftDeleted { + return tf.ImportAsExistsError("azurerm_backup_protected_vm", id.ID()) + } } + } item := protecteditems.ProtectedItemResource{ @@ -209,6 +212,11 @@ func resourceRecoveryServicesBackupProtectedVMRead(d *pluginsdk.ResourceData, me if model := resp.Model; model != nil { if properties := model.Properties; properties != nil { if vm, ok := properties.(protecteditems.AzureIaaSComputeVMProtectedItem); ok { + + if vm.IsScheduledForDeferredDelete != nil && *vm.IsScheduledForDeferredDelete { + return fmt.Errorf("reading %s: the VM has been soft deleted, to recover it, specify the `recover_soft_deleted_backup_protected_vm` feature.", id) + } + d.Set("source_vm_id", vm.SourceResourceId) d.Set("protection_state", pointer.From(vm.ProtectionState)) @@ -426,7 +434,7 @@ func expandDiskLunList(input []interface{}) []interface{} { return result } -func resourceRecoveryServicesVaultBackupProtectedVMrecoverSoftDeleted(ctx context.Context, client *protecteditems.ProtectedItemsClient, opClient *backup.ProtectedItemOperationResultsClient, id protecteditems.ProtectedItemId, model *protecteditems.ProtectedItemResource) (isSoftDeleted bool, err error) { +func resourceRecoveryServicesVaultBackupProtectedVMRecoverSoftDeleted(ctx context.Context, client *protecteditems.ProtectedItemsClient, opClient *backup.ProtectedItemOperationResultsClient, id protecteditems.ProtectedItemId, model *protecteditems.ProtectedItemResource) (isSoftDeleted bool, err error) { isSoftDeleted = false if model == nil { return isSoftDeleted, fmt.Errorf("model was nil") diff --git a/internal/services/recoveryservices/backup_protected_vm_resource_test.go b/internal/services/recoveryservices/backup_protected_vm_resource_test.go index 49276fd7b73a..80b04e408874 100644 --- a/internal/services/recoveryservices/backup_protected_vm_resource_test.go +++ b/internal/services/recoveryservices/backup_protected_vm_resource_test.go @@ -238,7 +238,7 @@ func TestAccBackupProtectedVm_recoverSoftDeletedVM(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basicWithSoftDelete(data), + Config: r.basicWithSoftDelete(data, false), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("resource_group_name").Exists(), @@ -246,10 +246,10 @@ func TestAccBackupProtectedVm_recoverSoftDeletedVM(t *testing.T) { }, data.ImportStep(), { - Config: r.basicWithSoftDeleted(data), + Config: r.basicWithSoftDelete(data, true), }, { - Config: r.basicWithSoftDelete(data), + Config: r.basicWithSoftDelete(data, false), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("resource_group_name").Exists(), @@ -932,18 +932,8 @@ resource "azurerm_backup_protected_vm" "test" { `, r.base(data)) } -func (r BackupProtectedVmResource) basicWithSoftDelete(data acceptance.TestData) string { - return fmt.Sprintf(` -provider "azurerm" { - features { - recovery_services_vault { - recover_soft_deleted_backup_protected_vm = true - } - } -} - -%s - +func (r BackupProtectedVmResource) basicWithSoftDelete(data acceptance.TestData, deleted bool) string { + protectedVMBlock := ` resource "azurerm_backup_protected_vm" "test" { resource_group_name = azurerm_resource_group.test.name recovery_vault_name = azurerm_recovery_services_vault.test.name @@ -952,10 +942,11 @@ resource "azurerm_backup_protected_vm" "test" { include_disk_luns = [0] } -`, r.baseWithSoftDelete(data)) -} +` + if deleted { + protectedVMBlock = "" + } -func (r BackupProtectedVmResource) basicWithSoftDeleted(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features { @@ -967,5 +958,6 @@ provider "azurerm" { %s -`, r.baseWithSoftDelete(data)) +%s +`, r.baseWithSoftDelete(data), protectedVMBlock) } From e8c66feafbf9c415efa94a3594cf90a0cd74d54c Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:21:50 +0800 Subject: [PATCH 04/10] update per comments --- internal/provider/provider.go | 3 + .../backup_protected_vm_resource.go | 85 +++++++++++-------- 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 9f85127147ac..e669474b9ed2 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -515,6 +515,7 @@ func getOidcToken(d *schema.ResourceData) (*string, error) { if path := d.Get("oidc_token_file_path").(string); path != "" { fileTokenRaw, err := os.ReadFile(path) + if err != nil { return nil, fmt.Errorf("reading OIDC Token from file %q: %v", path, err) } @@ -536,6 +537,7 @@ func getClientId(d *schema.ResourceData) (*string, error) { if path := d.Get("client_id_file_path").(string); path != "" { fileClientIdRaw, err := os.ReadFile(path) + if err != nil { return nil, fmt.Errorf("reading Client ID from file %q: %v", path, err) } @@ -557,6 +559,7 @@ func getClientSecret(d *schema.ResourceData) (*string, error) { if path := d.Get("client_secret_file_path").(string); path != "" { fileSecretRaw, err := os.ReadFile(path) + if err != nil { return nil, fmt.Errorf("reading Client Secret from file %q: %v", path, err) } diff --git a/internal/services/recoveryservices/backup_protected_vm_resource.go b/internal/services/recoveryservices/backup_protected_vm_resource.go index e97a254109f5..02e40f8264a4 100644 --- a/internal/services/recoveryservices/backup_protected_vm_resource.go +++ b/internal/services/recoveryservices/backup_protected_vm_resource.go @@ -107,10 +107,20 @@ func resourceRecoveryServicesBackupProtectedVMCreateUpdate(d *pluginsdk.Resource if !response.WasNotFound(existing.HttpResponse) { isSoftDeleted := false - if meta.(*clients.Client).Features.RecoveryServicesVault.RecoverSoftDeletedBackupProtectedVM { - isSoftDeleted, err = resourceRecoveryServicesVaultBackupProtectedVMRecoverSoftDeleted(ctx, client, opClient, id, existing.Model) - if err != nil { - return fmt.Errorf("recovering soft deleted %s: %+v", id, err) + if existing.Model != nil && existing.Model.Properties != nil { + if prop, ok := existing.Model.Properties.(protecteditems.AzureIaaSComputeVMProtectedItem); ok { + isSoftDeleted = pointer.From(prop.IsScheduledForDeferredDelete) + } + } + + if isSoftDeleted { + if meta.(*clients.Client).Features.RecoveryServicesVault.RecoverSoftDeletedBackupProtectedVM { + err = resourceRecoveryServicesVaultBackupProtectedVMRecoverSoftDeleted(ctx, client, opClient, id) + if err != nil { + return fmt.Errorf("recovering soft deleted %s: %+v", id, err) + } + } else { + return fmt.Errorf(optedOutOfRecoveringSoftDeletedBackupProtectedVMFmt(parsedVmId.ID(), vaultName)) } } @@ -206,15 +216,12 @@ func resourceRecoveryServicesBackupProtectedVMRead(d *pluginsdk.ResourceData, me return fmt.Errorf("making Read request on %s: %+v", id, err) } - d.Set("resource_group_name", id.ResourceGroupName) - d.Set("recovery_vault_name", id.VaultName) - if model := resp.Model; model != nil { if properties := model.Properties; properties != nil { if vm, ok := properties.(protecteditems.AzureIaaSComputeVMProtectedItem); ok { - if vm.IsScheduledForDeferredDelete != nil && *vm.IsScheduledForDeferredDelete { - return fmt.Errorf("reading %s: the VM has been soft deleted, to recover it, specify the `recover_soft_deleted_backup_protected_vm` feature.", id) + d.SetId("") + return nil } d.Set("source_vm_id", vm.SourceResourceId) @@ -245,6 +252,9 @@ func resourceRecoveryServicesBackupProtectedVMRead(d *pluginsdk.ResourceData, me } } + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("recovery_vault_name", id.VaultName) + return nil } @@ -434,41 +444,44 @@ func expandDiskLunList(input []interface{}) []interface{} { return result } -func resourceRecoveryServicesVaultBackupProtectedVMRecoverSoftDeleted(ctx context.Context, client *protecteditems.ProtectedItemsClient, opClient *backup.ProtectedItemOperationResultsClient, id protecteditems.ProtectedItemId, model *protecteditems.ProtectedItemResource) (isSoftDeleted bool, err error) { - isSoftDeleted = false - if model == nil { - return isSoftDeleted, fmt.Errorf("model was nil") +func resourceRecoveryServicesVaultBackupProtectedVMRecoverSoftDeleted(ctx context.Context, client *protecteditems.ProtectedItemsClient, opClient *backup.ProtectedItemOperationResultsClient, id protecteditems.ProtectedItemId) (err error) { + resp, err := client.CreateOrUpdate(ctx, id, protecteditems.ProtectedItemResource{ + Properties: &protecteditems.AzureIaaSComputeVMProtectedItem{ + IsRehydrate: pointer.To(true), + }, + }, + ) + if err != nil { + return fmt.Errorf("issuing request for %s: %+v", id, err) + } + + operationId, err := parseBackupOperationId(resp.HttpResponse) + if err != nil { + return err } - if model.Properties == nil { - return isSoftDeleted, fmt.Errorf("properties was nil") + if err = resourceRecoveryServicesBackupProtectedVMWaitForStateCreateUpdate(ctx, opClient, id, operationId); err != nil { + return err } - if prop, ok := model.Properties.(protecteditems.AzureIaaSComputeVMProtectedItem); ok { - if prop.IsScheduledForDeferredDelete != nil && *prop.IsScheduledForDeferredDelete { - isSoftDeleted = true - resp, err := client.CreateOrUpdate(ctx, id, protecteditems.ProtectedItemResource{ - Properties: &protecteditems.AzureIaaSComputeVMProtectedItem{ - IsRehydrate: pointer.To(true), - }, - }) - if err != nil { - return isSoftDeleted, fmt.Errorf("issuing request for %s: %+v", id, err) - } + return nil +} - operationId, err := parseBackupOperationId(resp.HttpResponse) - if err != nil { - return isSoftDeleted, err - } +func optedOutOfRecoveringSoftDeletedBackupProtectedVMFmt(vmId string, vaultName string) string { + return fmt.Sprintf(` +An existing soft-deleted Backup Protected VM exists with the source VM %q in the recovery services +vault %q, however automatically recovering this Backup Protected VM has been disabled via the +"features" block. - if err = resourceRecoveryServicesBackupProtectedVMWaitForStateCreateUpdate(ctx, opClient, id, operationId); err != nil { - return isSoftDeleted, err - } +Terraform can automatically recover the soft-deleted Backup Protected VM when this behaviour is +enabled within the "features" block (located within the "provider" block) - more +information can be found here: - } - } +https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/features-block - return isSoftDeleted, nil +Alternatively you can manually recover this (e.g. using the Azure CLI) and then import +this into Terraform via "terraform import". +`, vmId, vaultName) } func resourceRecoveryServicesBackupProtectedVMSchema() map[string]*pluginsdk.Schema { From 72f5f7f21697c681ce2e41d43e73510ac9eac3bd Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:08:21 +0800 Subject: [PATCH 05/10] update per comment --- internal/provider/features.go | 4 ++-- internal/provider/features_test.go | 10 +++++----- ...site_recovery_replicated_vm_resource_test.go | 17 ++++++++--------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/internal/provider/features.go b/internal/provider/features.go index e5a0fffca1d8..14936527c5b7 100644 --- a/internal/provider/features.go +++ b/internal/provider/features.go @@ -255,7 +255,7 @@ func schemaFeatures(supportLegacyTestSuite bool) *pluginsdk.Schema { }, }, - "recovery_services_vault": { + "recovery_services_vaults": { Type: pluginsdk.TypeList, Optional: true, MaxItems: 1, @@ -476,7 +476,7 @@ func expandFeatures(input []interface{}) features.UserFeatures { } } - if raw, ok := val["recovery_services_vault"]; ok { + if raw, ok := val["recovery_services_vaults"]; ok { items := raw.([]interface{}) if len(items) > 0 && items[0] != nil { appConfRaw := items[0].(map[string]interface{}) diff --git a/internal/provider/features_test.go b/internal/provider/features_test.go index a6af5b87ea7a..7966482f61d9 100644 --- a/internal/provider/features_test.go +++ b/internal/provider/features_test.go @@ -130,7 +130,7 @@ func TestExpandFeatures(t *testing.T) { "prevent_deletion_if_contains_resources": true, }, }, - "recovery_services_vault": []interface{}{ + "recovery_services_vaults": []interface{}{ map[string]interface{}{ "recover_soft_deleted_backup_protected_vm": true, }, @@ -271,7 +271,7 @@ func TestExpandFeatures(t *testing.T) { "prevent_deletion_if_contains_resources": false, }, }, - "recovery_services_vault": []interface{}{ + "recovery_services_vaults": []interface{}{ map[string]interface{}{ "recover_soft_deleted_backup_protected_vm": false, }, @@ -1181,7 +1181,7 @@ func TestExpandFeaturesRecoveryServicesVault(t *testing.T) { Name: "Empty Block", Input: []interface{}{ map[string]interface{}{ - "recovery_services_vault": []interface{}{}, + "recovery_services_vaults": []interface{}{}, }, }, Expected: features.UserFeatures{ @@ -1194,7 +1194,7 @@ func TestExpandFeaturesRecoveryServicesVault(t *testing.T) { Name: "Recover Soft Deleted Protected VM Enabled", Input: []interface{}{ map[string]interface{}{ - "recovery_services_vault": []interface{}{ + "recovery_services_vaults": []interface{}{ map[string]interface{}{ "recover_soft_deleted_backup_protected_vm": true, }, @@ -1211,7 +1211,7 @@ func TestExpandFeaturesRecoveryServicesVault(t *testing.T) { Name: "Recover Soft Deleted Protected VM Disabled", Input: []interface{}{ map[string]interface{}{ - "recovery_services_vault": []interface{}{ + "recovery_services_vaults": []interface{}{ map[string]interface{}{ "recover_soft_deleted_backup_protected_vm": false, }, 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 8b20f6a2095e..41903b2800ad 100644 --- a/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go +++ b/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go @@ -361,9 +361,9 @@ resource "azurerm_virtual_machine" "test" { delete_os_disk_on_termination = true storage_image_reference { - publisher = "OpenLogic" - offer = "CentOS" - sku = "7.5" + publisher = "Canonical" + offer = "0001-com-ubuntu-server-focal" + sku = "20_04-lts" version = "latest" } @@ -1033,9 +1033,9 @@ resource "azurerm_virtual_machine" "test" { delete_os_disk_on_termination = true storage_image_reference { - publisher = "OpenLogic" - offer = "CentOS" - sku = "7.5" + publisher = "Canonical" + offer = "0001-com-ubuntu-server-focal" + sku = "20_04-lts" version = "latest" } @@ -1540,8 +1540,8 @@ resource "azurerm_virtual_machine" "test" { storage_image_reference { publisher = "Canonical" - offer = "0001-com-ubuntu-server-focal" - sku = "20_04-lts" + offer = "0001-com-ubuntu-server-jammy" + sku = "22_04-lts" version = "latest" } @@ -1934,7 +1934,6 @@ resource "azurerm_site_recovery_replicated_vm" "test" { ] } `, r.template(data), data.RandomInteger) - } func (r SiteRecoveryReplicatedVmResource) withAvailabilitySet(data acceptance.TestData) string { From 628d288417dca80d3727083594a471d1ba5f2347 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:22:27 +0800 Subject: [PATCH 06/10] format code --- internal/features/user_flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/features/user_flags.go b/internal/features/user_flags.go index 36ba2b75f9b3..f38c68a4caf9 100644 --- a/internal/features/user_flags.go +++ b/internal/features/user_flags.go @@ -14,7 +14,7 @@ type UserFeatures struct { TemplateDeployment TemplateDeploymentFeatures LogAnalyticsWorkspace LogAnalyticsWorkspaceFeatures ResourceGroup ResourceGroupFeatures - RecoveryServicesVault RecoveryServicesVault + RecoveryServicesVault RecoveryServicesVault ManagedDisk ManagedDiskFeatures Subscription SubscriptionFeatures PostgresqlFlexibleServer PostgresqlFlexibleServerFeatures From af7da92950767ffd31922bd60472b458bcca75d1 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:54:45 +0800 Subject: [PATCH 07/10] update test case --- .../recoveryservices/backup_protected_vm_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/recoveryservices/backup_protected_vm_resource_test.go b/internal/services/recoveryservices/backup_protected_vm_resource_test.go index 80b04e408874..43ded52eef8d 100644 --- a/internal/services/recoveryservices/backup_protected_vm_resource_test.go +++ b/internal/services/recoveryservices/backup_protected_vm_resource_test.go @@ -950,7 +950,7 @@ resource "azurerm_backup_protected_vm" "test" { return fmt.Sprintf(` provider "azurerm" { features { - recovery_services_vault { + recovery_services_vaults { recover_soft_deleted_backup_protected_vm = true } } From 753b2a2feff75f7c1db63eed2eb9ed90ac4619a8 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Fri, 22 Mar 2024 10:50:33 +0800 Subject: [PATCH 08/10] revert site recovery test file --- ...site_recovery_replicated_vm_resource_test.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) 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 41903b2800ad..8b20f6a2095e 100644 --- a/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go +++ b/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go @@ -361,9 +361,9 @@ resource "azurerm_virtual_machine" "test" { delete_os_disk_on_termination = true storage_image_reference { - publisher = "Canonical" - offer = "0001-com-ubuntu-server-focal" - sku = "20_04-lts" + publisher = "OpenLogic" + offer = "CentOS" + sku = "7.5" version = "latest" } @@ -1033,9 +1033,9 @@ resource "azurerm_virtual_machine" "test" { delete_os_disk_on_termination = true storage_image_reference { - publisher = "Canonical" - offer = "0001-com-ubuntu-server-focal" - sku = "20_04-lts" + publisher = "OpenLogic" + offer = "CentOS" + sku = "7.5" version = "latest" } @@ -1540,8 +1540,8 @@ resource "azurerm_virtual_machine" "test" { storage_image_reference { publisher = "Canonical" - offer = "0001-com-ubuntu-server-jammy" - sku = "22_04-lts" + offer = "0001-com-ubuntu-server-focal" + sku = "20_04-lts" version = "latest" } @@ -1934,6 +1934,7 @@ resource "azurerm_site_recovery_replicated_vm" "test" { ] } `, r.template(data), data.RandomInteger) + } func (r SiteRecoveryReplicatedVmResource) withAvailabilitySet(data acceptance.TestData) string { From 532986ab5e4498e59a82c4cf4301b97e8bcbab45 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:25:14 +0800 Subject: [PATCH 09/10] `recover_soft_deleted_backup_protected_vm` defaults to `false` --- internal/provider/features.go | 2 +- website/docs/guides/features-block.html.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/provider/features.go b/internal/provider/features.go index 445938ae4f1e..3a9fcebdbb46 100644 --- a/internal/provider/features.go +++ b/internal/provider/features.go @@ -269,7 +269,7 @@ func schemaFeatures(supportLegacyTestSuite bool) *pluginsdk.Schema { "recover_soft_deleted_backup_protected_vm": { Type: pluginsdk.TypeBool, Optional: true, - Default: true, + Default: false, }, }, }, diff --git a/website/docs/guides/features-block.html.markdown b/website/docs/guides/features-block.html.markdown index ecd8b21c52fa..9dee3d19d656 100644 --- a/website/docs/guides/features-block.html.markdown +++ b/website/docs/guides/features-block.html.markdown @@ -207,7 +207,7 @@ The `resource_group` block supports the following: The `recovery_services_vault` block supports the following: -* `recover_soft_deleted_backup_protected_vm` - (Optional) Should the `azurerm_backup_protected_vm` resource recover a Soft-Deleted protected VM? Defaults to `true`. +* `recover_soft_deleted_backup_protected_vm` - (Optional) Should the `azurerm_backup_protected_vm` resource recover a Soft-Deleted protected VM? Defaults to `false`. --- From 16cefafa28780be5efa57a699f05de05b03e9e4e Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Tue, 7 May 2024 13:41:59 +0800 Subject: [PATCH 10/10] fix test --- .../recoveryservices/backup_protected_vm_resource_test.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/internal/services/recoveryservices/backup_protected_vm_resource_test.go b/internal/services/recoveryservices/backup_protected_vm_resource_test.go index 6e457e7d9a75..bb284eb71668 100644 --- a/internal/services/recoveryservices/backup_protected_vm_resource_test.go +++ b/internal/services/recoveryservices/backup_protected_vm_resource_test.go @@ -794,10 +794,6 @@ resource "azurerm_backup_policy_vm" "test_change_backup" { // For update backup policy id test func (r BackupProtectedVmResource) linkFirstBackupPolicy(data acceptance.TestData) string { return fmt.Sprintf(` -provider "azurerm" { - features {} -} - %s resource "azurerm_backup_protected_vm" "test" { @@ -812,10 +808,6 @@ resource "azurerm_backup_protected_vm" "test" { // For update backup policy id test func (r BackupProtectedVmResource) linkSecondBackupPolicy(data acceptance.TestData) string { return fmt.Sprintf(` -provider "azurerm" { - features {} -} - %s resource "azurerm_backup_protected_vm" "test" {