Skip to content

Commit

Permalink
azurerm_backup_protected_vm: support protection_stopped
Browse files Browse the repository at this point in the history
  • Loading branch information
ziyeqf committed Feb 22, 2023
1 parent 46c208f commit 88fbbdb
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 9 deletions.
64 changes: 55 additions & 9 deletions internal/services/recoveryservices/backup_protected_vm_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ func resourceRecoveryServicesBackupProtectedVMCreateUpdate(d *pluginsdk.Resource
},
}

requireAdditionalupdate := false
if d.Get("protection_stopped").(bool) && d.IsNewResource() { // it only needs an additional update for new resource.
requireAdditionalupdate = true
}

if _, err = client.CreateOrUpdate(ctx, vaultName, resourceGroup, "Azure", containerName, protectedItemName, item); err != nil {
return fmt.Errorf("creating/updating Azure Backup Protected VM %q (Resource Group %q): %+v", protectedItemName, resourceGroup, err)
}
Expand All @@ -115,6 +120,36 @@ func resourceRecoveryServicesBackupProtectedVMCreateUpdate(d *pluginsdk.Resource
return err
}

if requireAdditionalupdate {
updateInput := backup.ProtectedItemResource{
Properties: &backup.AzureIaaSComputeVMProtectedItem{
ProtectionState: backup.ProtectionStateProtectionStopped,
SourceResourceID: utils.String(vmId),
},
}

resp, err := client.CreateOrUpdate(ctx, vaultName, resourceGroup, "Azure", containerName, protectedItemName, updateInput)
if err != nil {
return fmt.Errorf("creating/updating Azure Backup Protected VM %q (Resource Group %q): %+v", protectedItemName, resourceGroup, err)
}

locationURL, err := resp.Response.Location()
if err != nil || locationURL == nil {
return fmt.Errorf("creating/updating Azure Backup Protected VM %q (Resource Group %q): Location header missing or empty", protectedItemName, resourceGroup)
}

parsedLocation, err := azure.ParseAzureResourceID(handleAzureSdkForGoBug2824(locationURL.Path))
if err != nil {
return err
}

opState := resourceRecoveryServicesBackupProtectedVMOperationRefreshFunc(ctx, meta.(*clients.Client).RecoveryServices.BackupOperationResultsClient, d.Timeout(pluginsdk.TimeoutCreate), vaultName, resourceGroup, parsedLocation.Path["operationResults"])
if _, err := opState.WaitForStateContext(ctx); err != nil {
return fmt.Errorf("creating/updating Azure Backup Protected VM %q (Resource Group %q): %+v", protectedItemName, resourceGroup, err)
}

}

id := strings.Replace(*resp.ID, "Subscriptions", "subscriptions", 1) // This code is a workaround for this bug https://github.com/Azure/azure-sdk-for-go/issues/2824
d.SetId(id)

Expand Down Expand Up @@ -149,6 +184,7 @@ func resourceRecoveryServicesBackupProtectedVMRead(d *pluginsdk.ResourceData, me
if properties := resp.Properties; properties != nil {
if vm, ok := properties.AsAzureIaaSComputeVMProtectedItem(); ok {
d.Set("source_vm_id", vm.SourceResourceID)
d.Set("protection_stopped", vm.ProtectionState == backup.ProtectionStateProtectionStopped)

if v := vm.PolicyID; v != nil {
d.Set("backup_policy_id", strings.Replace(*v, "Subscriptions", "subscriptions", 1))
Expand Down Expand Up @@ -268,7 +304,18 @@ func resourceRecoveryServicesBackupProtectedVMWaitForDeletion(ctx context.Contex
}

// we should also wait for the operation to complete, or it will fail when creating a new backup vm with the same vm in different vault immediately.
opState := &pluginsdk.StateChangeConf{
opState := resourceRecoveryServicesBackupProtectedVMOperationRefreshFunc(ctx, opResultClient, d.Timeout(pluginsdk.TimeoutDelete), vaultName, resourceGroup, operationId)

_, err = opState.WaitForStateContext(ctx)
if err != nil {
return resp.(backup.ProtectedItemResource), fmt.Errorf("waiting for the Recovery Service Protected Item operation %q to be deleted (Resource Group %q): %+v", containerName, resourceGroup, err)
}

return resp.(backup.ProtectedItemResource), nil
}

func resourceRecoveryServicesBackupProtectedVMOperationRefreshFunc(ctx context.Context, opResultClient *backup.OperationResultsClient, timeout time.Duration, vaultName, resourceGroup, operationId string) pluginsdk.StateChangeConf {
return pluginsdk.StateChangeConf{
MinTimeout: 30 * time.Second,
Delay: 10 * time.Second,
Pending: []string{"202"},
Expand All @@ -281,15 +328,8 @@ func resourceRecoveryServicesBackupProtectedVMWaitForDeletion(ctx context.Contex
return resp, strconv.Itoa(resp.StatusCode), err
},

Timeout: d.Timeout(pluginsdk.TimeoutDelete),
Timeout: timeout,
}

_, err = opState.WaitForStateContext(ctx)
if err != nil {
return resp.(backup.ProtectedItemResource), fmt.Errorf("waiting for the Recovery Service Protected Item operation %q to be deleted (Resource Group %q): %+v", containerName, resourceGroup, err)
}

return resp.(backup.ProtectedItemResource), nil
}

func resourceRecoveryServicesBackupProtectedVMRefreshFunc(ctx context.Context, client *backup.ProtectedItemsClient, vaultName, resourceGroup, containerName, protectedItemName string) pluginsdk.StateRefreshFunc {
Expand Down Expand Up @@ -388,5 +428,11 @@ func resourceRecoveryServicesBackupProtectedVMSchema() map[string]*pluginsdk.Sch
ValidateFunc: validation.IntAtLeast(0),
},
},

"protection_stopped": {
Type: pluginsdk.TypeBool,
Optional: true,
Computed: true,
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,26 @@ func TestAccBackupProtectedVm_removeVM(t *testing.T) {
})
}

func TestAccBackupProtectedVm_protectionStopped(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_backup_protected_vm", "test")
r := BackupProtectedVmResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.protectionStopped(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("resource_group_name").Exists(),
),
},
data.ImportStep(),
{
// 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 := parse.ProtectedItemID(state.ID)
if err != nil {
Expand Down Expand Up @@ -687,3 +707,19 @@ resource "azurerm_backup_protected_vm" "test" {
}
`, r.additionalVault(data))
}

func (r BackupProtectedVmResource) protectionStopped(data acceptance.TestData) string {
return fmt.Sprintf(`
%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]
protection_stopped = true
}
`, r.base(data))
}
2 changes: 2 additions & 0 deletions website/docs/r/backup_protected_vm.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ This allows the source vm to be deleted without having to remove the backup.

* `include_disk_luns` - (Optional) A list of Disks' Logical Unit Numbers(LUN) to be included for VM Protection.

* `protection_stopped` - (Optional) Specifies whether to stop the protection. Defaults to `false`.

## Attributes Reference

The following attributes are exported:
Expand Down

0 comments on commit 88fbbdb

Please sign in to comment.