Skip to content

Commit

Permalink
azurerm_backup_protected_vm - support recover soft deleted vm
Browse files Browse the repository at this point in the history
  • Loading branch information
ziyeqf committed Dec 8, 2023
1 parent 3290839 commit ad0e160
Show file tree
Hide file tree
Showing 7 changed files with 423 additions and 10 deletions.
3 changes: 3 additions & 0 deletions internal/features/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ func Default() UserFeatures {
ResourceGroup: ResourceGroupFeatures{
PreventDeletionIfContainsResources: true,
},
RecoveryServicesVault: RecoveryServicesVault{
RecoverSoftDeletedBackupProtected: true,
},
TemplateDeployment: TemplateDeploymentFeatures{
DeleteNestedItemsDuringDeletion: true,
},
Expand Down
5 changes: 5 additions & 0 deletions internal/features/user_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type UserFeatures struct {
TemplateDeployment TemplateDeploymentFeatures
LogAnalyticsWorkspace LogAnalyticsWorkspaceFeatures
ResourceGroup ResourceGroupFeatures
RecoveryServicesVault RecoveryServicesVault
ManagedDisk ManagedDiskFeatures
Subscription SubscriptionFeatures
}
Expand Down Expand Up @@ -79,3 +80,7 @@ type AppConfigurationFeatures struct {
type SubscriptionFeatures struct {
PreventCancellationOnDestroy bool
}

type RecoveryServicesVault struct {
RecoverSoftDeletedBackupProtected bool
}
25 changes: 25 additions & 0 deletions internal/provider/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
84 changes: 84 additions & 0 deletions internal/provider/features_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ func TestExpandFeatures(t *testing.T) {
ResourceGroup: features.ResourceGroupFeatures{
PreventDeletionIfContainsResources: true,
},
RecoveryServicesVault: features.RecoveryServicesVault{
RecoverSoftDeletedBackupProtected: true,
},
Subscription: features.SubscriptionFeatures{
PreventCancellationOnDestroy: false,
},
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -188,6 +196,9 @@ func TestExpandFeatures(t *testing.T) {
ResourceGroup: features.ResourceGroupFeatures{
PreventDeletionIfContainsResources: true,
},
RecoveryServicesVault: features.RecoveryServicesVault{
RecoverSoftDeletedBackupProtected: true,
},
Subscription: features.SubscriptionFeatures{
PreventCancellationOnDestroy: true,
},
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -321,6 +337,9 @@ func TestExpandFeatures(t *testing.T) {
ResourceGroup: features.ResourceGroupFeatures{
PreventDeletionIfContainsResources: false,
},
RecoveryServicesVault: features.RecoveryServicesVault{
RecoverSoftDeletedBackupProtected: false,
},
Subscription: features.SubscriptionFeatures{
PreventCancellationOnDestroy: false,
},
Expand Down Expand Up @@ -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
Expand Down
58 changes: 52 additions & 6 deletions internal/services/recoveryservices/backup_protected_vm_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
}
Expand Down Expand Up @@ -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(),
Expand Down
Loading

0 comments on commit ad0e160

Please sign in to comment.