diff --git a/azurerm/resource_arm_automation_module.go b/azurerm/resource_arm_automation_module.go index ef3efb7cb7a4..5dc4d27be4dc 100644 --- a/azurerm/resource_arm_automation_module.go +++ b/azurerm/resource_arm_automation_module.go @@ -142,7 +142,6 @@ func resourceArmAutomationModuleCreateUpdate(d *schema.ResourceData, meta interf Target: []string{ string(automation.ModuleProvisioningStateSucceeded), }, - Timeout: 30 * time.Minute, MinTimeout: 30 * time.Second, Refresh: func() (interface{}, string, error) { resp, err2 := client.Get(ctx, resGroup, accName, name) @@ -157,6 +156,15 @@ func resourceArmAutomationModuleCreateUpdate(d *schema.ResourceData, meta interf return resp, "Unknown", nil }, } + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 30 * time.Minute + } _, err := stateConf.WaitForState() if err != nil { diff --git a/azurerm/resource_arm_container_group.go b/azurerm/resource_arm_container_group.go index c1f01d75ea5c..4a63c5d51a07 100644 --- a/azurerm/resource_arm_container_group.go +++ b/azurerm/resource_arm_container_group.go @@ -658,10 +658,16 @@ func resourceArmContainerGroupDelete(d *schema.ResourceData, meta interface{}) e Pending: []string{"Attached"}, Target: []string{"Detached"}, Refresh: containerGroupEnsureDetachedFromNetworkProfileRefreshFunc(ctx, networkProfileClient, networkProfileResourceGroup, networkProfileName, resourceGroup, name), - Timeout: 10 * time.Minute, MinTimeout: 15 * time.Second, ContinuousTargetOccurence: 5, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 10 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Container Group %q (Resource Group %q) to finish deleting: %s", name, resourceGroup, err) } diff --git a/azurerm/resource_arm_container_service.go b/azurerm/resource_arm_container_service.go index f768acc6620a..53d647ee5bbb 100644 --- a/azurerm/resource_arm_container_service.go +++ b/azurerm/resource_arm_container_service.go @@ -272,9 +272,19 @@ func resourceArmContainerServiceCreateUpdate(d *schema.ResourceData, meta interf Pending: []string{"Updating", "Creating"}, Target: []string{"Succeeded"}, Refresh: containerServiceStateRefreshFunc(client, resGroup, name), - Timeout: 30 * time.Minute, MinTimeout: 15 * time.Second, } + + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 30 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Container Service (%s) to become available: %s", d.Get("name"), err) } diff --git a/azurerm/resource_arm_cosmosdb_account.go b/azurerm/resource_arm_cosmosdb_account.go index 86d20ea736ff..a8059c9454ff 100644 --- a/azurerm/resource_arm_cosmosdb_account.go +++ b/azurerm/resource_arm_cosmosdb_account.go @@ -391,7 +391,7 @@ func resourceArmCosmosDbAccountCreate(d *schema.ResourceData, meta interface{}) } } - resp, err := resourceArmCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account) + resp, err := resourceArmCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account, d) if err != nil { return fmt.Errorf("Error creating CosmosDB Account %q (Resource Group %q): %+v", name, resourceGroup, err) } @@ -496,7 +496,7 @@ func resourceArmCosmosDbAccountUpdate(d *schema.ResourceData, meta interface{}) Tags: tags.Expand(t), } - if _, err = resourceArmCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account); err != nil { + if _, err = resourceArmCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account, d); err != nil { return fmt.Errorf("Error updating CosmosDB Account %q properties (Resource Group %q): %+v", name, resourceGroup, err) } @@ -532,14 +532,14 @@ func resourceArmCosmosDbAccountUpdate(d *schema.ResourceData, meta interface{}) } account.DatabaseAccountCreateUpdateProperties.Locations = &locationsUnchanged - if _, err = resourceArmCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account); err != nil { + if _, err = resourceArmCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account, d); err != nil { return fmt.Errorf("Error removing CosmosDB Account %q renamed locations (Resource Group %q): %+v", name, resourceGroup, err) } } //add any new/renamed locations account.DatabaseAccountCreateUpdateProperties.Locations = &newLocations - upsertResponse, err := resourceArmCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account) + upsertResponse, err := resourceArmCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account, d) if err != nil { return fmt.Errorf("Error updating CosmosDB Account %q locations (Resource Group %q): %+v", name, resourceGroup, err) } @@ -741,7 +741,6 @@ func resourceArmCosmosDbAccountDelete(d *schema.ResourceData, meta interface{}) stateConf := &resource.StateChangeConf{ Pending: []string{"Deleting"}, Target: []string{"NotFound"}, - Timeout: 180 * time.Minute, MinTimeout: 30 * time.Second, Refresh: func() (interface{}, string, error) { resp, err2 := client.Get(ctx, resourceGroup, name) @@ -755,6 +754,12 @@ func resourceArmCosmosDbAccountDelete(d *schema.ResourceData, meta interface{}) return resp, "Deleting", nil }, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 180 * time.Minute + } if _, err = stateConf.WaitForState(); err != nil { return fmt.Errorf("Waiting forCosmosDB Account %q to delete (Resource Group %q): %+v", name, resourceGroup, err) } @@ -762,7 +767,7 @@ func resourceArmCosmosDbAccountDelete(d *schema.ResourceData, meta interface{}) return nil } -func resourceArmCosmosDbAccountApiUpsert(client *documentdb.DatabaseAccountsClient, ctx context.Context, resourceGroup string, name string, account documentdb.DatabaseAccountCreateUpdateParameters) (*documentdb.DatabaseAccount, error) { +func resourceArmCosmosDbAccountApiUpsert(client *documentdb.DatabaseAccountsClient, ctx context.Context, resourceGroup string, name string, account documentdb.DatabaseAccountCreateUpdateParameters, d *schema.ResourceData) (*documentdb.DatabaseAccount, error) { future, err := client.CreateOrUpdate(ctx, resourceGroup, name, account) if err != nil { return nil, fmt.Errorf("Error creating/updating CosmosDB Account %q (Resource Group %q): %+v", name, resourceGroup, err) @@ -776,7 +781,6 @@ func resourceArmCosmosDbAccountApiUpsert(client *documentdb.DatabaseAccountsClie stateConf := &resource.StateChangeConf{ Pending: []string{"Creating", "Updating", "Deleting"}, Target: []string{"Succeeded"}, - Timeout: 180 * time.Minute, MinTimeout: 30 * time.Second, Delay: 30 * time.Second, // required because it takes some time before the 'creating' location shows up Refresh: func() (interface{}, string, error) { @@ -796,6 +800,16 @@ func resourceArmCosmosDbAccountApiUpsert(client *documentdb.DatabaseAccountsClie }, } + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 180 * time.Minute + } + resp, err := stateConf.WaitForState() if err != nil { return nil, fmt.Errorf("Error waiting for the CosmosDB Account %q (Resource Group %q) to provision: %+v", name, resourceGroup, err) diff --git a/azurerm/resource_arm_eventhub_namespace.go b/azurerm/resource_arm_eventhub_namespace.go index bb2c3cdbae92..dc596cdf0a5b 100644 --- a/azurerm/resource_arm_eventhub_namespace.go +++ b/azurerm/resource_arm_eventhub_namespace.go @@ -360,18 +360,24 @@ func resourceArmEventHubNamespaceDelete(d *schema.ResourceData, meta interface{} return fmt.Errorf("Error issuing delete request of EventHub Namespace %q (Resource Group %q): %+v", name, resGroup, err) } - return waitForEventHubNamespaceToBeDeleted(ctx, client, resGroup, name) + return waitForEventHubNamespaceToBeDeleted(ctx, client, resGroup, name, d) } -func waitForEventHubNamespaceToBeDeleted(ctx context.Context, client *eventhub.NamespacesClient, resourceGroup, name string) error { +func waitForEventHubNamespaceToBeDeleted(ctx context.Context, client *eventhub.NamespacesClient, resourceGroup, name string, d *schema.ResourceData) error { // we can't use the Waiter here since the API returns a 200 once it's deleted which is considered a polling status code.. log.Printf("[DEBUG] Waiting for EventHub Namespace (%q in Resource Group %q) to be deleted", name, resourceGroup) stateConf := &resource.StateChangeConf{ Pending: []string{"200"}, Target: []string{"404"}, Refresh: eventHubNamespaceStateStatusCodeRefreshFunc(ctx, client, resourceGroup, name), - Timeout: 40 * time.Minute, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 40 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for EventHub NameSpace (%q in Resource Group %q) to be deleted: %+v", name, resourceGroup, err) } diff --git a/azurerm/resource_arm_eventhub_namespace_disaster_recovery_config.go b/azurerm/resource_arm_eventhub_namespace_disaster_recovery_config.go index cfe0076f344e..421ae3481065 100644 --- a/azurerm/resource_arm_eventhub_namespace_disaster_recovery_config.go +++ b/azurerm/resource_arm_eventhub_namespace_disaster_recovery_config.go @@ -106,7 +106,7 @@ func resourceArmEventHubNamespaceDisasterRecoveryConfigCreate(d *schema.Resource return fmt.Errorf("Error creating/updating EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) } - if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name); err != nil { + if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name, d.Timeout(schema.TimeoutCreate)); err != nil { return fmt.Errorf("Error waiting for replication to complete for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) } @@ -145,7 +145,7 @@ func resourceArmEventHubNamespaceDisasterRecoveryConfigUpdate(d *schema.Resource return fmt.Errorf("Error issuing break pairing request for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) } - if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name); err != nil { + if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name, d.Timeout(schema.TimeoutUpdate)); err != nil { return fmt.Errorf("Error waiting for break pairing request to complete for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) } } @@ -164,7 +164,7 @@ func resourceArmEventHubNamespaceDisasterRecoveryConfigUpdate(d *schema.Resource return fmt.Errorf("Error creating/updating EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) } - if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name); err != nil { + if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name, d.Timeout(schema.TimeoutUpdate)); err != nil { return fmt.Errorf("Error waiting for replication to complete for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) } @@ -228,7 +228,7 @@ func resourceArmEventHubNamespaceDisasterRecoveryConfigDelete(d *schema.Resource return fmt.Errorf("Error breaking pairing for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) } - if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name); err != nil { + if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name, d.Timeout(schema.TimeoutDelete)); err != nil { return fmt.Errorf("Error waiting for break pairing request to complete for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) } @@ -240,7 +240,6 @@ func resourceArmEventHubNamespaceDisasterRecoveryConfigDelete(d *schema.Resource deleteWait := &resource.StateChangeConf{ Pending: []string{"200"}, Target: []string{"404"}, - Timeout: 30 * time.Minute, MinTimeout: 30 * time.Second, Refresh: func() (interface{}, string, error) { resp, err := client.Get(ctx, resourceGroup, namespaceName, name) @@ -255,6 +254,13 @@ func resourceArmEventHubNamespaceDisasterRecoveryConfigDelete(d *schema.Resource return resp, strconv.Itoa(resp.StatusCode), nil }, } + + if features.SupportsCustomTimeouts() { + deleteWait.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + deleteWait.Timeout = 30 * time.Minute + } + if _, err := deleteWait.WaitForState(); err != nil { return fmt.Errorf("Error waiting the deletion of EventHub Namespace Disaster Recovery Configs %q deletion (Namespace %q / Resource Group %q): %v", name, namespaceName, resourceGroup, err) } @@ -264,7 +270,6 @@ func resourceArmEventHubNamespaceDisasterRecoveryConfigDelete(d *schema.Resource nameFreeWait := &resource.StateChangeConf{ Pending: []string{"NameInUse"}, Target: []string{"None"}, - Timeout: 30 * time.Minute, MinTimeout: 30 * time.Second, Refresh: func() (interface{}, string, error) { resp, err := client.CheckNameAvailability(ctx, resourceGroup, namespaceName, eventhub.CheckNameAvailabilityParameter{Name: utils.String(name)}) @@ -275,6 +280,13 @@ func resourceArmEventHubNamespaceDisasterRecoveryConfigDelete(d *schema.Resource return resp, string(resp.Reason), nil }, } + + if features.SupportsCustomTimeouts() { + nameFreeWait.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + nameFreeWait.Timeout = 30 * time.Minute + } + if _, err := nameFreeWait.WaitForState(); err != nil { return fmt.Errorf("Error waiting the the EventHub Namespace Disaster Recovery Configs %q name to be available (Namespace %q / Resource Group %q): %v", name, namespaceName, resourceGroup, err) } @@ -282,12 +294,10 @@ func resourceArmEventHubNamespaceDisasterRecoveryConfigDelete(d *schema.Resource return nil } -func resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx context.Context, client *eventhub.DisasterRecoveryConfigsClient, resourceGroup, namespaceName, name string) error { +func resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx context.Context, client *eventhub.DisasterRecoveryConfigsClient, resourceGroup, namespaceName, name string, timeout time.Duration) error { stateConf := &resource.StateChangeConf{ - Pending: []string{string(eventhub.Accepted)}, - Target: []string{string(eventhub.Succeeded)}, - // Delay: 15 * time.Second, - Timeout: 30 * time.Minute, + Pending: []string{string(eventhub.Accepted)}, + Target: []string{string(eventhub.Succeeded)}, MinTimeout: 30 * time.Second, Refresh: func() (interface{}, string, error) { read, err := client.Get(ctx, resourceGroup, namespaceName, name) @@ -306,6 +316,12 @@ func resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx context. }, } + if features.SupportsCustomTimeouts() { + stateConf.Timeout = timeout + } else { + stateConf.Timeout = 30 * time.Minute + } + _, err := stateConf.WaitForState() return err } diff --git a/azurerm/resource_arm_iot_dps.go b/azurerm/resource_arm_iot_dps.go index 75864d3312b2..25740c2db5cb 100644 --- a/azurerm/resource_arm_iot_dps.go +++ b/azurerm/resource_arm_iot_dps.go @@ -258,18 +258,24 @@ func resourceArmIotDPSDelete(d *schema.ResourceData, meta interface{}) error { } } - return waitForIotDPSToBeDeleted(ctx, client, resourceGroup, name) + return waitForIotDPSToBeDeleted(ctx, client, resourceGroup, name, d) } -func waitForIotDPSToBeDeleted(ctx context.Context, client *iothub.IotDpsResourceClient, resourceGroup, name string) error { +func waitForIotDPSToBeDeleted(ctx context.Context, client *iothub.IotDpsResourceClient, resourceGroup, name string, d *schema.ResourceData) error { // we can't use the Waiter here since the API returns a 404 once it's deleted which is considered a polling status code.. log.Printf("[DEBUG] Waiting for IoT Device Provisioning Service %q (Resource Group %q) to be deleted", name, resourceGroup) stateConf := &resource.StateChangeConf{ Pending: []string{"200"}, Target: []string{"404"}, Refresh: iotdpsStateStatusCodeRefreshFunc(ctx, client, resourceGroup, name), - Timeout: 40 * time.Minute, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 40 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for IoT Device Provisioning Service %q (Resource Group %q) to be deleted: %+v", name, resourceGroup, err) } diff --git a/azurerm/resource_arm_iothub.go b/azurerm/resource_arm_iothub.go index 6fbf9d9baea1..9c612580b246 100644 --- a/azurerm/resource_arm_iothub.go +++ b/azurerm/resource_arm_iothub.go @@ -635,18 +635,24 @@ func resourceArmIotHubDelete(d *schema.ResourceData, meta interface{}) error { return err } - return waitForIotHubToBeDeleted(ctx, client, resourceGroup, name) + return waitForIotHubToBeDeleted(ctx, client, resourceGroup, name, d) } -func waitForIotHubToBeDeleted(ctx context.Context, client *devices.IotHubResourceClient, resourceGroup, name string) error { +func waitForIotHubToBeDeleted(ctx context.Context, client *devices.IotHubResourceClient, resourceGroup, name string, d *schema.ResourceData) error { // we can't use the Waiter here since the API returns a 404 once it's deleted which is considered a polling status code.. log.Printf("[DEBUG] Waiting for IotHub (%q in Resource Group %q) to be deleted", name, resourceGroup) stateConf := &resource.StateChangeConf{ Pending: []string{"200"}, Target: []string{"404"}, Refresh: iothubStateStatusCodeRefreshFunc(ctx, client, resourceGroup, name), - Timeout: 40 * time.Minute, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 40 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for IotHub (%q in Resource Group %q) to be deleted: %+v", name, resourceGroup, err) } diff --git a/azurerm/resource_arm_key_vault.go b/azurerm/resource_arm_key_vault.go index 4d6328aa2065..2e71e5ed7ac2 100644 --- a/azurerm/resource_arm_key_vault.go +++ b/azurerm/resource_arm_key_vault.go @@ -317,12 +317,17 @@ func resourceArmKeyVaultCreateUpdate(d *schema.ResourceData, meta interface{}) e Pending: []string{"pending"}, Target: []string{"available"}, Refresh: keyVaultRefreshFunc(*vault), - Timeout: 30 * time.Minute, Delay: 30 * time.Second, PollInterval: 10 * time.Second, ContinuousTargetOccurence: 10, } + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = 30 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Key Vault %q (Resource Group %q) to become available: %s", name, resourceGroup, err) } diff --git a/azurerm/resource_arm_key_vault_certificate.go b/azurerm/resource_arm_key_vault_certificate.go index c44e5bb505f2..90955ac2944e 100644 --- a/azurerm/resource_arm_key_vault_certificate.go +++ b/azurerm/resource_arm_key_vault_certificate.go @@ -56,9 +56,8 @@ func resourceArmKeyVaultCertificate() *schema.Resource { }, Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(30 * time.Minute), + Create: schema.DefaultTimeout(60 * time.Minute), Read: schema.DefaultTimeout(5 * time.Minute), - Update: schema.DefaultTimeout(30 * time.Minute), Delete: schema.DefaultTimeout(30 * time.Minute), }, @@ -421,9 +420,15 @@ func resourceArmKeyVaultCertificateCreate(d *schema.ResourceData, meta interface Pending: []string{"Provisioning"}, Target: []string{"Ready"}, Refresh: keyVaultCertificateCreationRefreshFunc(ctx, client, keyVaultBaseUrl, name), - Timeout: 60 * time.Minute, MinTimeout: 15 * time.Second, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = 60 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Certificate %q in Vault %q to become available: %s", name, keyVaultBaseUrl, err) } diff --git a/azurerm/resource_arm_mariadb_virtual_network_rule.go b/azurerm/resource_arm_mariadb_virtual_network_rule.go index dad11d794ae1..34f6bac1f296 100644 --- a/azurerm/resource_arm_mariadb_virtual_network_rule.go +++ b/azurerm/resource_arm_mariadb_virtual_network_rule.go @@ -141,12 +141,21 @@ func resourceArmMariaDbVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, me stateConf := &resource.StateChangeConf{ Pending: []string{"Initializing", "InProgress", "Unknown", "ResponseNotFound"}, Target: []string{"Ready"}, - Refresh: MariaDbVirtualNetworkStateStatusCodeRefreshFunc(ctx, client, resourceGroup, serverName, name), - Timeout: 30 * time.Minute, + Refresh: mariaDbVirtualNetworkStateStatusCodeRefreshFunc(ctx, client, resourceGroup, serverName, name), MinTimeout: 1 * time.Minute, ContinuousTargetOccurence: 5, } + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 30 * time.Minute + } + if _, err = stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for MariaDb Virtual Network Rule %q (MariaDb Server: %q, Resource Group: %q) to be created or updated: %+v", name, serverName, resourceGroup, err) } @@ -229,7 +238,7 @@ func resourceArmMariaDbVirtualNetworkRuleDelete(d *schema.ResourceData, meta int return nil } -func MariaDbVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client *mariadb.VirtualNetworkRulesClient, resourceGroup string, serverName string, name string) resource.StateRefreshFunc { +func mariaDbVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client *mariadb.VirtualNetworkRulesClient, resourceGroup string, serverName string, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { resp, err := client.Get(ctx, resourceGroup, serverName, name) diff --git a/azurerm/resource_arm_monitor_diagnostic_setting.go b/azurerm/resource_arm_monitor_diagnostic_setting.go index 2d6c051fd812..f2f9209d6156 100644 --- a/azurerm/resource_arm_monitor_diagnostic_setting.go +++ b/azurerm/resource_arm_monitor_diagnostic_setting.go @@ -34,7 +34,7 @@ func resourceArmMonitorDiagnosticSetting() *schema.Resource { Create: schema.DefaultTimeout(30 * time.Minute), Read: schema.DefaultTimeout(5 * time.Minute), Update: schema.DefaultTimeout(30 * time.Minute), - Delete: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), }, Schema: map[string]*schema.Schema{ @@ -350,10 +350,16 @@ func resourceArmMonitorDiagnosticSettingDelete(d *schema.ResourceData, meta inte Pending: []string{"Exists"}, Target: []string{"NotFound"}, Refresh: monitorDiagnosticSettingDeletedRefreshFunc(ctx, client, targetResourceId, id.name), - Timeout: 60 * time.Minute, MinTimeout: 15 * time.Second, ContinuousTargetOccurence: 5, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 60 * time.Minute + } + if _, err = stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Monitor Diagnostic Setting %q for Resource %q to become available: %s", id.name, id.resourceID, err) } diff --git a/azurerm/resource_arm_mysql_virtual_network_rule.go b/azurerm/resource_arm_mysql_virtual_network_rule.go index 238c737f336c..7679dda87579 100644 --- a/azurerm/resource_arm_mysql_virtual_network_rule.go +++ b/azurerm/resource_arm_mysql_virtual_network_rule.go @@ -142,11 +142,20 @@ func resourceArmMySqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta Pending: []string{"Initializing", "InProgress", "Unknown", "ResponseNotFound"}, Target: []string{"Ready"}, Refresh: mySQLVirtualNetworkStateStatusCodeRefreshFunc(ctx, client, resourceGroup, serverName, name), - Timeout: 30 * time.Minute, MinTimeout: 1 * time.Minute, ContinuousTargetOccurence: 5, } + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 30 * time.Minute + } + if _, err = stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) to be created or updated: %+v", name, serverName, resourceGroup, err) } diff --git a/azurerm/resource_arm_notification_hub.go b/azurerm/resource_arm_notification_hub.go index 3e70b6623463..1201796bbfb2 100644 --- a/azurerm/resource_arm_notification_hub.go +++ b/azurerm/resource_arm_notification_hub.go @@ -194,10 +194,20 @@ func resourceArmNotificationHubCreateUpdate(d *schema.ResourceData, meta interfa Pending: []string{"404"}, Target: []string{"200"}, Refresh: notificationHubStateRefreshFunc(ctx, client, resourceGroup, namespaceName, name), - Timeout: 10 * time.Minute, MinTimeout: 15 * time.Second, ContinuousTargetOccurence: 10, } + + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 10 * time.Minute + } + if _, err2 := stateConf.WaitForState(); err2 != nil { return fmt.Errorf("Error waiting for Notification Hub %q to become available: %s", name, err2) } diff --git a/azurerm/resource_arm_notification_hub_namespace.go b/azurerm/resource_arm_notification_hub_namespace.go index b34e48c76d11..d3a70606fcff 100644 --- a/azurerm/resource_arm_notification_hub_namespace.go +++ b/azurerm/resource_arm_notification_hub_namespace.go @@ -178,10 +178,20 @@ func resourceArmNotificationHubNamespaceCreateUpdate(d *schema.ResourceData, met Pending: []string{"404"}, Target: []string{"200"}, Refresh: notificationHubNamespaceStateRefreshFunc(ctx, client, resourceGroup, name), - Timeout: 10 * time.Minute, MinTimeout: 15 * time.Second, ContinuousTargetOccurence: 10, } + + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 10 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Notification Hub %q (Resource Group %q) to finish replicating: %s", name, resourceGroup, err) } @@ -276,7 +286,11 @@ func resourceArmNotificationHubNamespaceDelete(d *schema.ResourceData, meta inte Pending: []string{"200", "202"}, Target: []string{"404"}, Refresh: notificationHubNamespaceDeleteStateRefreshFunc(ctx, client, resourceGroup, name), - Timeout: 10 * time.Minute, + } + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 10 * time.Minute } if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Notification Hub %q (Resource Group %q) to be deleted: %s", name, resourceGroup, err) diff --git a/azurerm/resource_arm_policy_assignment.go b/azurerm/resource_arm_policy_assignment.go index 582474603991..d1971c54452a 100644 --- a/azurerm/resource_arm_policy_assignment.go +++ b/azurerm/resource_arm_policy_assignment.go @@ -181,11 +181,20 @@ func resourceArmPolicyAssignmentCreateUpdate(d *schema.ResourceData, meta interf Pending: []string{"404"}, Target: []string{"200"}, Refresh: policyAssignmentRefreshFunc(ctx, client, scope, name), - Timeout: 5 * time.Minute, MinTimeout: 10 * time.Second, ContinuousTargetOccurence: 10, } + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 5 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Policy Assignment %q to become available: %s", name, err) } diff --git a/azurerm/resource_arm_policy_definition.go b/azurerm/resource_arm_policy_definition.go index e4d4f819dcf4..3c73714a2eda 100644 --- a/azurerm/resource_arm_policy_definition.go +++ b/azurerm/resource_arm_policy_definition.go @@ -186,10 +186,20 @@ func resourceArmPolicyDefinitionCreateUpdate(d *schema.ResourceData, meta interf Pending: []string{"404"}, Target: []string{"200"}, Refresh: policyDefinitionRefreshFunc(ctx, client, name, managementGroupID), - Timeout: 5 * time.Minute, MinTimeout: 10 * time.Second, ContinuousTargetOccurence: 10, } + + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 5 * time.Minute + } + if _, err = stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Policy Definition %q to become available: %s", name, err) } diff --git a/azurerm/resource_arm_policy_set_definition.go b/azurerm/resource_arm_policy_set_definition.go index ab49d857ecf7..ca69161ba22f 100644 --- a/azurerm/resource_arm_policy_set_definition.go +++ b/azurerm/resource_arm_policy_set_definition.go @@ -193,11 +193,20 @@ func resourceArmPolicySetDefinitionCreateUpdate(d *schema.ResourceData, meta int Pending: []string{"404"}, Target: []string{"200"}, Refresh: policySetDefinitionRefreshFunc(ctx, client, name, managementGroupID), - Timeout: 5 * time.Minute, MinTimeout: 10 * time.Second, ContinuousTargetOccurence: 10, } + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 5 * time.Minute + } + if _, err = stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Policy Set Definition %q to become available: %s", name, err) } diff --git a/azurerm/resource_arm_postgresql_virtual_network_rule.go b/azurerm/resource_arm_postgresql_virtual_network_rule.go index 333e8a46787b..257dfb82da71 100644 --- a/azurerm/resource_arm_postgresql_virtual_network_rule.go +++ b/azurerm/resource_arm_postgresql_virtual_network_rule.go @@ -112,11 +112,20 @@ func resourceArmPostgreSQLVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, Pending: []string{"Initializing", "InProgress", "Unknown", "ResponseNotFound"}, Target: []string{"Ready"}, Refresh: postgreSQLVirtualNetworkStateStatusCodeRefreshFunc(ctx, client, resourceGroup, serverName, name), - Timeout: 10 * time.Minute, MinTimeout: 1 * time.Minute, ContinuousTargetOccurence: 5, } + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 10 * time.Minute + } + if _, err = stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for PostgreSQL Virtual Network Rule %q (PostgreSQL Server: %q, Resource Group: %q) to be created or updated: %+v", name, serverName, resourceGroup, err) } diff --git a/azurerm/resource_arm_private_dns_zone_virtual_network_link.go b/azurerm/resource_arm_private_dns_zone_virtual_network_link.go index cf4e0bc85c17..d065dfe3d31d 100644 --- a/azurerm/resource_arm_private_dns_zone_virtual_network_link.go +++ b/azurerm/resource_arm_private_dns_zone_virtual_network_link.go @@ -215,12 +215,17 @@ func resourceArmPrivateDnsZoneVirtualNetworkLinkDelete(d *schema.ResourceData, m log.Printf("[DEBUG] Virtual Network Link %q (Private DNS Zone %q / Resource Group %q) still exists", name, dnsZoneName, resGroup) return "Available", "Available", nil }, - Timeout: 30 * time.Minute, Delay: 30 * time.Second, PollInterval: 10 * time.Second, ContinuousTargetOccurence: 10, } + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 30 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("error waiting for deletion of Virtual Network Link %q (Private DNS Zone %q / Resource Group %q): %+v", name, dnsZoneName, resGroup, err) } diff --git a/azurerm/resource_arm_recovery_services_protected_vm.go b/azurerm/resource_arm_recovery_services_protected_vm.go index 1d6a9279d629..6675e7f6c83c 100644 --- a/azurerm/resource_arm_recovery_services_protected_vm.go +++ b/azurerm/resource_arm_recovery_services_protected_vm.go @@ -121,7 +121,7 @@ func resourceArmRecoveryServicesProtectedVmCreateUpdate(d *schema.ResourceData, return fmt.Errorf("Error creating/updating Recovery Service Protected VM %q (Resource Group %q): %+v", protectedItemName, resourceGroup, err) } - resp, err := resourceArmRecoveryServicesProtectedVmWaitForState(client, ctx, true, vaultName, resourceGroup, containerName, protectedItemName, policyId, d.IsNewResource()) + resp, err := resourceArmRecoveryServicesProtectedVmWaitForStateCreateUpdate(ctx, client, vaultName, resourceGroup, containerName, protectedItemName, policyId, d) if err != nil { return err } @@ -199,60 +199,92 @@ func resourceArmRecoveryServicesProtectedVmDelete(d *schema.ResourceData, meta i } } - if _, err := resourceArmRecoveryServicesProtectedVmWaitForState(client, ctx, false, vaultName, resourceGroup, containerName, protectedItemName, "", false); err != nil { + if _, err := resourceArmRecoveryServicesProtectedVmWaitForDeletion(ctx, client, vaultName, resourceGroup, containerName, protectedItemName, "", d); err != nil { return err } return nil } -func resourceArmRecoveryServicesProtectedVmWaitForState(client *backup.ProtectedItemsClient, ctx context.Context, found bool, vaultName, resourceGroup, containerName, protectedItemName string, policyId string, newResource bool) (backup.ProtectedItemResource, error) { +func resourceArmRecoveryServicesProtectedVmWaitForStateCreateUpdate(ctx context.Context, client *backup.ProtectedItemsClient, vaultName, resourceGroup, containerName, protectedItemName string, policyId string, d *schema.ResourceData) (backup.ProtectedItemResource, error) { state := &resource.StateChangeConf{ - Timeout: 30 * time.Minute, MinTimeout: 30 * time.Second, Delay: 10 * time.Second, - Refresh: func() (interface{}, string, error) { - resp, err := client.Get(ctx, vaultName, resourceGroup, "Azure", containerName, protectedItemName, "") - if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return resp, "NotFound", nil - } + Pending: []string{"NotFound"}, + Target: []string{"Found"}, + Refresh: resourceArmRecoveryServicesProtectedVmRefreshFunc(ctx, client, vaultName, resourceGroup, containerName, protectedItemName, policyId, true), + } - return resp, "Error", fmt.Errorf("Error making Read request on Recovery Service Protected VM %q (Resource Group %q): %+v", protectedItemName, resourceGroup, err) - } else if !newResource && policyId != "" { - if properties := resp.Properties; properties != nil { - if vm, ok := properties.AsAzureIaaSComputeVMProtectedItem(); ok { - if v := vm.PolicyID; v != nil { - if strings.Replace(*v, "Subscriptions", "subscriptions", 1) != policyId { - return resp, "NotFound", nil - } - } else { - return resp, "Error", fmt.Errorf("Error reading policy ID attribute nil on Recovery Service Protected VM %q (Resource Group %q)", protectedItemName, resourceGroup) - } - } else { - return resp, "Error", fmt.Errorf("Error reading properties on Recovery Service Protected VM %q (Resource Group %q)", protectedItemName, resourceGroup) - } - } else { - return resp, "Error", fmt.Errorf("Error reading properties on empty Recovery Service Protected VM %q (Resource Group %q)", protectedItemName, resourceGroup) - } - } - return resp, "Found", nil - }, + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + state.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + state.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + state.Timeout = 30 * time.Minute + } + + resp, err := state.WaitForState() + if err != nil { + i, _ := resp.(backup.ProtectedItemResource) + return i, fmt.Errorf("Error waiting for the Recovery Service Protected VM %q to be true (Resource Group %q) to provision: %+v", protectedItemName, resourceGroup, err) + } + + return resp.(backup.ProtectedItemResource), nil +} + +func resourceArmRecoveryServicesProtectedVmWaitForDeletion(ctx context.Context, client *backup.ProtectedItemsClient, vaultName, resourceGroup, containerName, protectedItemName string, policyId string, d *schema.ResourceData) (backup.ProtectedItemResource, error) { + state := &resource.StateChangeConf{ + MinTimeout: 30 * time.Second, + Delay: 10 * time.Second, + Pending: []string{"Found"}, + Target: []string{"NotFound"}, + Refresh: resourceArmRecoveryServicesProtectedVmRefreshFunc(ctx, client, vaultName, resourceGroup, containerName, protectedItemName, policyId, false), } - if found { - state.Pending = []string{"NotFound"} - state.Target = []string{"Found"} + if features.SupportsCustomTimeouts() { + state.Timeout = d.Timeout(schema.TimeoutDelete) } else { - state.Pending = []string{"Found"} - state.Target = []string{"NotFound"} + state.Timeout = 30 * time.Minute } resp, err := state.WaitForState() if err != nil { i, _ := resp.(backup.ProtectedItemResource) - return i, fmt.Errorf("Error waiting for the Recovery Service Protected VM %q to be %t (Resource Group %q) to provision: %+v", protectedItemName, found, resourceGroup, err) + return i, fmt.Errorf("Error waiting for the Recovery Service Protected VM %q to be false (Resource Group %q) to provision: %+v", protectedItemName, resourceGroup, err) } return resp.(backup.ProtectedItemResource), nil } + +func resourceArmRecoveryServicesProtectedVmRefreshFunc(ctx context.Context, client *backup.ProtectedItemsClient, vaultName, resourceGroup, containerName, protectedItemName string, policyId string, newResource bool) resource.StateRefreshFunc { + // TODO: split this into two functions + return func() (interface{}, string, error) { + resp, err := client.Get(ctx, vaultName, resourceGroup, "Azure", containerName, protectedItemName, "") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return resp, "NotFound", nil + } + + return resp, "Error", fmt.Errorf("Error making Read request on Recovery Service Protected VM %q (Resource Group %q): %+v", protectedItemName, resourceGroup, err) + } else if !newResource && policyId != "" { + if properties := resp.Properties; properties != nil { + if vm, ok := properties.AsAzureIaaSComputeVMProtectedItem(); ok { + if v := vm.PolicyID; v != nil { + if strings.Replace(*v, "Subscriptions", "subscriptions", 1) != policyId { + return resp, "NotFound", nil + } + } else { + return resp, "Error", fmt.Errorf("Error reading policy ID attribute nil on Recovery Service Protected VM %q (Resource Group %q)", protectedItemName, resourceGroup) + } + } else { + return resp, "Error", fmt.Errorf("Error reading properties on Recovery Service Protected VM %q (Resource Group %q)", protectedItemName, resourceGroup) + } + } else { + return resp, "Error", fmt.Errorf("Error reading properties on empty Recovery Service Protected VM %q (Resource Group %q)", protectedItemName, resourceGroup) + } + } + return resp, "Found", nil + } +} diff --git a/azurerm/resource_arm_recovery_services_protection_policy_vm.go b/azurerm/resource_arm_recovery_services_protection_policy_vm.go index c697368b84a6..e0705f614001 100644 --- a/azurerm/resource_arm_recovery_services_protection_policy_vm.go +++ b/azurerm/resource_arm_recovery_services_protection_policy_vm.go @@ -334,7 +334,7 @@ func resourceArmRecoveryServicesProtectionPolicyVmCreateUpdate(d *schema.Resourc return fmt.Errorf("Error creating/updating Recovery Service Protection Policy %q (Resource Group %q): %+v", policyName, resourceGroup, err) } - resp, err := resourceArmRecoveryServicesProtectionPolicyWaitForState(client, ctx, true, vaultName, resourceGroup, policyName) + resp, err := resourceArmRecoveryServicesProtectionPolicyWaitForUpdate(ctx, client, vaultName, resourceGroup, policyName, d) if err != nil { return err } @@ -445,7 +445,7 @@ func resourceArmRecoveryServicesProtectionPolicyVmDelete(d *schema.ResourceData, } } - if _, err := resourceArmRecoveryServicesProtectionPolicyWaitForState(client, ctx, false, vaultName, resourceGroup, policyName); err != nil { + if _, err := resourceArmRecoveryServicesProtectionPolicyWaitForDeletion(ctx, client, vaultName, resourceGroup, policyName, d); err != nil { return err } @@ -705,37 +705,67 @@ func flattenArmRecoveryServicesProtectionPolicyRetentionWeeklyFormat(retention * return weekdays, weeks } -func resourceArmRecoveryServicesProtectionPolicyWaitForState(client *backup.ProtectionPoliciesClient, ctx context.Context, found bool, vaultName, resourceGroup, policyName string) (backup.ProtectionPolicyResource, error) { +func resourceArmRecoveryServicesProtectionPolicyWaitForUpdate(ctx context.Context, client *backup.ProtectionPoliciesClient, vaultName, resourceGroup, policyName string, d *schema.ResourceData) (backup.ProtectionPolicyResource, error) { state := &resource.StateChangeConf{ - Timeout: 30 * time.Minute, MinTimeout: 30 * time.Second, Delay: 10 * time.Second, - Refresh: func() (interface{}, string, error) { - resp, err := client.Get(ctx, vaultName, resourceGroup, policyName) - if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return resp, "NotFound", nil - } + Pending: []string{"NotFound"}, + Target: []string{"Found"}, + Refresh: resourceArmRecoveryServicesProtectionPolicyRefreshFunc(ctx, client, vaultName, resourceGroup, policyName), + } - return resp, "Error", fmt.Errorf("Error making Read request on Recovery Service Protection Policy %q (Resource Group %q): %+v", policyName, resourceGroup, err) - } + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + state.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + state.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + state.Timeout = 30 * time.Minute + } - return resp, "Found", nil - }, + resp, err := state.WaitForState() + if err != nil { + return resp.(backup.ProtectionPolicyResource), fmt.Errorf("Error waiting for the Recovery Service Protection Policy %q to be true (Resource Group %q) to provision: %+v", policyName, resourceGroup, err) + } + + return resp.(backup.ProtectionPolicyResource), nil +} + +func resourceArmRecoveryServicesProtectionPolicyWaitForDeletion(ctx context.Context, client *backup.ProtectionPoliciesClient, vaultName, resourceGroup, policyName string, d *schema.ResourceData) (backup.ProtectionPolicyResource, error) { + state := &resource.StateChangeConf{ + MinTimeout: 30 * time.Second, + Delay: 10 * time.Second, + Pending: []string{"Found"}, + Target: []string{"NotFound"}, + Refresh: resourceArmRecoveryServicesProtectionPolicyRefreshFunc(ctx, client, vaultName, resourceGroup, policyName), } - if found { - state.Pending = []string{"NotFound"} - state.Target = []string{"Found"} + if features.SupportsCustomTimeouts() { + state.Timeout = d.Timeout(schema.TimeoutDelete) } else { - state.Pending = []string{"Found"} - state.Target = []string{"NotFound"} + state.Timeout = 30 * time.Minute } resp, err := state.WaitForState() if err != nil { - return resp.(backup.ProtectionPolicyResource), fmt.Errorf("Error waiting for the Recovery Service Protection Policy %q to be %t (Resource Group %q) to provision: %+v", policyName, found, resourceGroup, err) + return resp.(backup.ProtectionPolicyResource), fmt.Errorf("Error waiting for the Recovery Service Protection Policy %q to be false (Resource Group %q) to provision: %+v", policyName, resourceGroup, err) } return resp.(backup.ProtectionPolicyResource), nil } + +func resourceArmRecoveryServicesProtectionPolicyRefreshFunc(ctx context.Context, client *backup.ProtectionPoliciesClient, vaultName, resourceGroup, policyName string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := client.Get(ctx, vaultName, resourceGroup, policyName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return resp, "NotFound", nil + } + + return resp, "Error", fmt.Errorf("Error making Read request on Recovery Service Protection Policy %q (Resource Group %q): %+v", policyName, resourceGroup, err) + } + + return resp, "Found", nil + } +} diff --git a/azurerm/resource_arm_redis_cache.go b/azurerm/resource_arm_redis_cache.go index 7457f43e3891..31b1a59e012b 100644 --- a/azurerm/resource_arm_redis_cache.go +++ b/azurerm/resource_arm_redis_cache.go @@ -368,9 +368,15 @@ func resourceArmRedisCacheCreate(d *schema.ResourceData, meta interface{}) error Pending: []string{"Scaling", "Updating", "Creating"}, Target: []string{"Succeeded"}, Refresh: redisStateRefreshFunc(ctx, client, resGroup, name), - Timeout: 60 * time.Minute, MinTimeout: 15 * time.Second, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = 60 * time.Minute + } + if _, err = stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Redis Cache (%s) to become available: %s", d.Get("name"), err) } @@ -451,9 +457,15 @@ func resourceArmRedisCacheUpdate(d *schema.ResourceData, meta interface{}) error Pending: []string{"Scaling", "Updating", "Creating"}, Target: []string{"Succeeded"}, Refresh: redisStateRefreshFunc(ctx, client, resGroup, name), - Timeout: 60 * time.Minute, MinTimeout: 15 * time.Second, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } else { + stateConf.Timeout = 60 * time.Minute + } + if _, err = stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Redis Instance (%s) to become available: %s", d.Get("name"), err) } diff --git a/azurerm/resource_arm_relay_hybrid_connection.go b/azurerm/resource_arm_relay_hybrid_connection.go index 790fe9ffb011..bc120af68b19 100644 --- a/azurerm/resource_arm_relay_hybrid_connection.go +++ b/azurerm/resource_arm_relay_hybrid_connection.go @@ -12,6 +12,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -105,7 +106,7 @@ func resourceArmHybridConnectionCreateUpdate(d *schema.ResourceData, meta interf func resourceArmHybridConnectionRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).Relay.HybridConnectionsClient - ctx, cancel := timeouts.ForCreateUpdate(meta.(*ArmClient).StopContext, d) + ctx, cancel := timeouts.ForRead(meta.(*ArmClient).StopContext, d) defer cancel() id, err := azure.ParseAzureResourceID(d.Id()) @@ -140,7 +141,7 @@ func resourceArmHybridConnectionRead(d *schema.ResourceData, meta interface{}) e func resourceArmHybridConnectionDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).Relay.HybridConnectionsClient - ctx, cancel := timeouts.ForCreateUpdate(meta.(*ArmClient).StopContext, d) + ctx, cancel := timeouts.ForDelete(meta.(*ArmClient).StopContext, d) defer cancel() id, err := azure.ParseAzureResourceID(d.Id()) @@ -166,10 +167,15 @@ func resourceArmHybridConnectionDelete(d *schema.ResourceData, meta interface{}) Pending: []string{"Pending"}, Target: []string{"Deleted"}, Refresh: hybridConnectionDeleteRefreshFunc(ctx, client, resourceGroup, relayNamespace, name), - Timeout: 30 * time.Minute, MinTimeout: 15 * time.Second, } + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 30 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Relay Hybrid Connection %q (Namespace %q Resource Group %q) to be deleted: %s", name, relayNamespace, resourceGroup, err) } diff --git a/azurerm/resource_arm_relay_namespace.go b/azurerm/resource_arm_relay_namespace.go index ea10523404b7..40bb8a93891b 100644 --- a/azurerm/resource_arm_relay_namespace.go +++ b/azurerm/resource_arm_relay_namespace.go @@ -34,7 +34,7 @@ func resourceArmRelayNamespace() *schema.Resource { Create: schema.DefaultTimeout(30 * time.Minute), Read: schema.DefaultTimeout(5 * time.Minute), Update: schema.DefaultTimeout(30 * time.Minute), - Delete: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), }, Schema: map[string]*schema.Schema{ @@ -278,9 +278,15 @@ func resourceArmRelayNamespaceDelete(d *schema.ResourceData, meta interface{}) e Pending: []string{"Pending"}, Target: []string{"Deleted"}, Refresh: relayNamespaceDeleteRefreshFunc(ctx, client, resourceGroup, name), - Timeout: 60 * time.Minute, MinTimeout: 15 * time.Second, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 60 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Relay Namespace %q (Resource Group %q) to be deleted: %s", name, resourceGroup, err) } diff --git a/azurerm/resource_arm_security_center_workspace.go b/azurerm/resource_arm_security_center_workspace.go index ce2c0c125a54..b2d72b96e437 100644 --- a/azurerm/resource_arm_security_center_workspace.go +++ b/azurerm/resource_arm_security_center_workspace.go @@ -110,7 +110,6 @@ func resourceArmSecurityCenterWorkspaceCreateUpdate(d *schema.ResourceData, meta stateConf := &resource.StateChangeConf{ Pending: []string{"Waiting"}, Target: []string{"Populated"}, - Timeout: 30 * time.Minute, MinTimeout: 30 * time.Second, Refresh: func() (interface{}, string, error) { resp, err2 := client.Get(ctx, name) @@ -128,6 +127,16 @@ func resourceArmSecurityCenterWorkspaceCreateUpdate(d *schema.ResourceData, meta }, } + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 30 * time.Minute + } + resp, err := stateConf.WaitForState() if err != nil { return fmt.Errorf("Error waiting: %+v", err) diff --git a/azurerm/resource_arm_sql_virtual_network_rule.go b/azurerm/resource_arm_sql_virtual_network_rule.go index 11e75b7df2e4..8103759caa36 100644 --- a/azurerm/resource_arm_sql_virtual_network_rule.go +++ b/azurerm/resource_arm_sql_virtual_network_rule.go @@ -108,11 +108,20 @@ func resourceArmSqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta i Pending: []string{"Initializing", "InProgress", "Unknown", "ResponseNotFound"}, Target: []string{"Ready"}, Refresh: sqlVirtualNetworkStateStatusCodeRefreshFunc(ctx, client, resourceGroup, serverName, name), - Timeout: 10 * time.Minute, MinTimeout: 1 * time.Minute, ContinuousTargetOccurence: 5, } + if features.SupportsCustomTimeouts() { + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(schema.TimeoutUpdate) + } + } else { + stateConf.Timeout = 10 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for SQL Virtual Network Rule %q (SQL Server: %q, Resource Group: %q) to be created or updated: %+v", name, serverName, resourceGroup, err) } diff --git a/azurerm/resource_arm_storage_share_directory.go b/azurerm/resource_arm_storage_share_directory.go index 164a8e5c36fa..56990e910a97 100644 --- a/azurerm/resource_arm_storage_share_directory.go +++ b/azurerm/resource_arm_storage_share_directory.go @@ -109,11 +109,16 @@ func resourceArmStorageShareDirectoryCreate(d *schema.ResourceData, meta interfa Pending: []string{"404"}, Target: []string{"200"}, Refresh: storageShareDirectoryRefreshFunc(ctx, client, accountName, shareName, directoryName), - Timeout: 5 * time.Minute, MinTimeout: 10 * time.Second, ContinuousTargetOccurence: 5, } + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutCreate) + } else { + stateConf.Timeout = 5 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Directory %q (File Share %q / Account %q) to become available: %s", directoryName, shareName, accountName, err) } diff --git a/azurerm/resource_arm_template_deployment.go b/azurerm/resource_arm_template_deployment.go index e5a814d33765..ba479dd28639 100644 --- a/azurerm/resource_arm_template_deployment.go +++ b/azurerm/resource_arm_template_deployment.go @@ -281,7 +281,7 @@ func resourceArmTemplateDeploymentDelete(d *schema.ResourceData, meta interface{ return err } - return waitForTemplateDeploymentToBeDeleted(ctx, client, resourceGroup, name) + return waitForTemplateDeploymentToBeDeleted(ctx, client, resourceGroup, name, d) } // TODO: move this out into the new `helpers` structure @@ -314,15 +314,21 @@ func normalizeJson(jsonString interface{}) string { return string(b[:]) } -func waitForTemplateDeploymentToBeDeleted(ctx context.Context, client *resources.DeploymentsClient, resourceGroup, name string) error { +func waitForTemplateDeploymentToBeDeleted(ctx context.Context, client *resources.DeploymentsClient, resourceGroup, name string, d *schema.ResourceData) error { // we can't use the Waiter here since the API returns a 200 once it's deleted which is considered a polling status code.. log.Printf("[DEBUG] Waiting for Template Deployment (%q in Resource Group %q) to be deleted", name, resourceGroup) stateConf := &resource.StateChangeConf{ Pending: []string{"200"}, Target: []string{"404"}, Refresh: templateDeploymentStateStatusCodeRefreshFunc(ctx, client, resourceGroup, name), - Timeout: 40 * time.Minute, } + + if features.SupportsCustomTimeouts() { + stateConf.Timeout = d.Timeout(schema.TimeoutDelete) + } else { + stateConf.Timeout = 40 * time.Minute + } + if _, err := stateConf.WaitForState(); err != nil { return fmt.Errorf("Error waiting for Template Deployment (%q in Resource Group %q) to be deleted: %+v", name, resourceGroup, err) } diff --git a/azurerm/resource_arm_template_deployment_test.go b/azurerm/resource_arm_template_deployment_test.go index f2ef39f3d70f..b381ac3a9dd3 100644 --- a/azurerm/resource_arm_template_deployment_test.go +++ b/azurerm/resource_arm_template_deployment_test.go @@ -2,9 +2,11 @@ package azurerm import ( "fmt" + "log" "net/http" "regexp" "testing" + "time" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" @@ -227,7 +229,20 @@ func testCheckAzureRMTemplateDeploymentDisappears(resourceName string) resource. return fmt.Errorf("Failed deleting Deployment %q (Resource Group %q): %+v", deploymentName, resourceGroup, err) } - return waitForTemplateDeploymentToBeDeleted(ctx, client, resourceGroup, deploymentName) + // we can't use the Waiter here since the API returns a 200 once it's deleted which is considered a polling status code.. + log.Printf("[DEBUG] Waiting for Template Deployment (%q in Resource Group %q) to be deleted", deploymentName, resourceGroup) + stateConf := &resource.StateChangeConf{ + Pending: []string{"200"}, + Target: []string{"404"}, + Timeout: 40 * time.Minute, + Refresh: templateDeploymentStateStatusCodeRefreshFunc(ctx, client, resourceGroup, deploymentName), + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for Template Deployment (%q in Resource Group %q) to be deleted: %+v", deploymentName, resourceGroup, err) + } + + return nil } }