diff --git a/internal/services/cdn/cdn_endpoint_resource.go b/internal/services/cdn/cdn_endpoint_resource.go index 43a1083ca0bc..f6dbe8cb82f6 100644 --- a/internal/services/cdn/cdn_endpoint_resource.go +++ b/internal/services/cdn/cdn_endpoint_resource.go @@ -5,6 +5,8 @@ import ( "log" "time" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2020-09-01/cdn" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" @@ -210,24 +212,22 @@ func resourceCdnEndpoint() *pluginsdk.Resource { func resourceCdnEndpointCreate(d *pluginsdk.ResourceData, meta interface{}) error { endpointsClient := meta.(*clients.Client).Cdn.EndpointsClient profilesClient := meta.(*clients.Client).Cdn.ProfilesClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() log.Printf("[INFO] preparing arguments for Azure ARM CDN EndPoint creation.") - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) - profileName := d.Get("profile_name").(string) - - existing, err := endpointsClient.Get(ctx, resourceGroup, profileName, name) + id := parse.NewEndpointID(subscriptionId, d.Get("resource_group_name").(string), d.Get("profile_name").(string), d.Get("name").(string)) + existing, err := endpointsClient.Get(ctx, id.ResourceGroup, id.ProfileName, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing CDN Endpoint %q (Profile %q / Resource Group %q): %s", name, profileName, resourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_cdn_endpoint", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_cdn_endpoint", id.ID()) } location := azure.NormalizeLocation(d.Get("location").(string)) @@ -282,9 +282,9 @@ func resourceCdnEndpointCreate(d *pluginsdk.ResourceData, meta interface{}) erro endpoint.EndpointProperties.Origins = &origins } - profile, err := profilesClient.Get(ctx, resourceGroup, profileName) + profile, err := profilesClient.Get(ctx, id.ResourceGroup, id.ProfileName) if err != nil { - return fmt.Errorf("creating CDN Endpoint %q while getting CDN Profile (Profile %q / Resource Group %q): %+v", name, profileName, resourceGroup, err) + return fmt.Errorf("retrieving parent CDN Profile for %s: %+v", id, err) } if profile.Sku != nil { @@ -304,32 +304,22 @@ func resourceCdnEndpointCreate(d *pluginsdk.ResourceData, meta interface{}) erro } } - future, err := endpointsClient.Create(ctx, resourceGroup, profileName, name, endpoint) + future, err := endpointsClient.Create(ctx, id.ResourceGroup, id.ProfileName, id.Name, endpoint) if err != nil { - return fmt.Errorf("creating CDN Endpoint %q (Profile %q / Resource Group %q): %+v", name, profileName, resourceGroup, err) + return fmt.Errorf("creating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, endpointsClient.Client); err != nil { - return fmt.Errorf("waiting for CDN Endpoint %q (Profile %q / Resource Group %q) to finish creating: %+v", name, profileName, resourceGroup, err) - } - - read, err := endpointsClient.Get(ctx, resourceGroup, profileName, name) - if err != nil { - return fmt.Errorf("retrieving CDN Endpoint %q (Profile %q / Resource Group %q): %+v", name, profileName, resourceGroup, err) - } - - id, err := parse.EndpointID(*read.ID) - if err != nil { - return err + return fmt.Errorf("waiting for the creation of %s: %+v", id, err) } d.SetId(id.ID()) - return resourceCdnEndpointRead(d, meta) } func resourceCdnEndpointUpdate(d *pluginsdk.ResourceData, meta interface{}) error { endpointsClient := meta.(*clients.Client).Cdn.EndpointsClient + profilesClient := meta.(*clients.Client).Cdn.ProfilesClient ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) defer cancel() @@ -382,13 +372,9 @@ func resourceCdnEndpointUpdate(d *pluginsdk.ResourceData, meta interface{}) erro endpoint.EndpointPropertiesUpdateParameters.ProbePath = utils.String(probePath) } - profilesClient := meta.(*clients.Client).Cdn.ProfilesClient - profileGetCtx, profileGetCancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) - defer profileGetCancel() - - profile, err := profilesClient.Get(profileGetCtx, id.ResourceGroup, id.ProfileName) + profile, err := profilesClient.Get(ctx, id.ResourceGroup, id.ProfileName) if err != nil { - return fmt.Errorf("creating CDN Endpoint %q while getting CDN Profile (Profile %q / Resource Group %q): %+v", id.Name, id.ProfileName, id.ResourceGroup, err) + return fmt.Errorf("retrieving parent CDN Profile for %s: %+v", *id, err) } if profile.Sku != nil { @@ -410,11 +396,11 @@ func resourceCdnEndpointUpdate(d *pluginsdk.ResourceData, meta interface{}) erro future, err := endpointsClient.Update(ctx, id.ResourceGroup, id.ProfileName, id.Name, endpoint) if err != nil { - return fmt.Errorf("updating CDN Endpoint %q (Profile %q / Resource Group %q): %s", id.Name, id.ProfileName, id.ResourceGroup, err) + return fmt.Errorf("updating %s: %+v", *id, err) } if err = future.WaitForCompletionRef(ctx, endpointsClient.Client); err != nil { - return fmt.Errorf("waiting for the CDN Endpoint %q (Profile %q / Resource Group %q) to finish updating: %+v", id.Name, id.ProfileName, id.ResourceGroup, err) + return fmt.Errorf("waiting for update of %s: %+v", *id, err) } return resourceCdnEndpointRead(d, meta) @@ -430,8 +416,6 @@ func resourceCdnEndpointRead(d *pluginsdk.ResourceData, meta interface{}) error return err } - log.Printf("[INFO] Retrieving CDN Endpoint %q (Profile %q / Resource Group %q)", id.Name, id.ProfileName, id.ResourceGroup) - resp, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { @@ -439,16 +423,13 @@ func resourceCdnEndpointRead(d *pluginsdk.ResourceData, meta interface{}) error return nil } - return fmt.Errorf("making Read request on Azure CDN Endpoint %q (Profile %q / Resource Group %q): %+v", id.Name, id.ProfileName, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } d.Set("name", id.Name) d.Set("resource_group_name", id.ResourceGroup) d.Set("profile_name", id.ProfileName) - - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } + d.Set("location", location.NormalizeNilable(resp.Location)) if props := resp.EndpointProperties; props != nil { d.Set("host_name", props.HostName) @@ -460,11 +441,13 @@ func resourceCdnEndpointRead(d *pluginsdk.ResourceData, meta interface{}) error d.Set("probe_path", props.ProbePath) d.Set("optimization_type", string(props.OptimizationType)) + compressionEnabled := false if _, ok := d.GetOk("is_compression_enabled"); ok { - if compressionEnabled := props.IsCompressionEnabled; compressionEnabled != nil { - d.Set("is_compression_enabled", compressionEnabled) + if v := props.IsCompressionEnabled; v != nil { + compressionEnabled = *v } } + d.Set("is_compression_enabled", compressionEnabled) contentTypes := flattenAzureRMCdnEndpointContentTypes(props.ContentTypesToCompress) if err := d.Set("content_types_to_compress", contentTypes); err != nil { @@ -481,7 +464,7 @@ func resourceCdnEndpointRead(d *pluginsdk.ResourceData, meta interface{}) error return fmt.Errorf("setting `origin`: %+v", err) } - flattenedDeliveryPolicies, err := flattenArmCdnEndpointDeliveryPolicy(props.DeliveryPolicy) + flattenedDeliveryPolicies, err := flattenEndpointDeliveryPolicy(props.DeliveryPolicy) if err != nil { return err } @@ -508,11 +491,11 @@ func resourceCdnEndpointDelete(d *pluginsdk.ResourceData, meta interface{}) erro future, err := client.Delete(ctx, id.ResourceGroup, id.ProfileName, id.Name) if err != nil { - return fmt.Errorf("deleting CDN Endpoint %q (Profile %q / Resource Group %q): %+v", id.Name, id.ProfileName, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for CDN Endpoint %q (Profile %q / Resource Group %q) to be deleted: %+v", id.Name, id.ProfileName, id.ResourceGroup, err) + return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) } return nil @@ -701,7 +684,7 @@ type flattenedEndpointDeliveryPolicies struct { deliveryRules []interface{} } -func flattenArmCdnEndpointDeliveryPolicy(input *cdn.EndpointPropertiesUpdateParametersDeliveryPolicy) (*flattenedEndpointDeliveryPolicies, error) { +func flattenEndpointDeliveryPolicy(input *cdn.EndpointPropertiesUpdateParametersDeliveryPolicy) (*flattenedEndpointDeliveryPolicies, error) { output := flattenedEndpointDeliveryPolicies{ globalDeliveryRules: make([]interface{}, 0), deliveryRules: make([]interface{}, 0), diff --git a/internal/services/cdn/cdn_profile_resource.go b/internal/services/cdn/cdn_profile_resource.go index 0c41fc10634d..5541941084f8 100644 --- a/internal/services/cdn/cdn_profile_resource.go +++ b/internal/services/cdn/cdn_profile_resource.go @@ -2,9 +2,10 @@ package cdn import ( "fmt" - "log" "time" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2020-09-01/cdn" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" @@ -75,24 +76,21 @@ func resourceCdnProfile() *pluginsdk.Resource { func resourceCdnProfileCreate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).Cdn.ProfilesClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - log.Printf("[INFO] preparing arguments for Azure ARM CDN Profile creation.") - - name := d.Get("name").(string) - resGroup := d.Get("resource_group_name").(string) - + id := parse.NewProfileID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing CDN Profile %q (Resource Group %q): %s", name, resGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_cdn_profile", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_cdn_profile", id.ID()) } } @@ -108,26 +106,13 @@ func resourceCdnProfileCreate(d *pluginsdk.ResourceData, meta interface{}) error }, } - future, err := client.Create(ctx, resGroup, name, cdnProfile) + future, err := client.Create(ctx, id.ResourceGroup, id.Name, cdnProfile) if err != nil { - return err + return fmt.Errorf("creating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return err - } - - read, err := client.Get(ctx, resGroup, name) - if err != nil { - return err - } - if read.ID == nil { - return fmt.Errorf("Cannot read CDN Profile %s (resource group %s) ID", name, resGroup) - } - - id, err := parse.ProfileID(*read.ID) - if err != nil { - return err + return fmt.Errorf("waiting for the creation of %s: %+v", id, err) } d.SetId(id.ID()) @@ -149,8 +134,6 @@ func resourceCdnProfileUpdate(d *pluginsdk.ResourceData, meta interface{}) error return err } - // name := d.Get("name").(string) - // resourceGroup := d.Get("resource_group_name").(string) newTags := d.Get("tags").(map[string]interface{}) props := cdn.ProfileUpdateParameters{ @@ -159,11 +142,10 @@ func resourceCdnProfileUpdate(d *pluginsdk.ResourceData, meta interface{}) error future, err := client.Update(ctx, id.ResourceGroup, id.Name, props) if err != nil { - return fmt.Errorf("issuing update request for CDN Profile %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("updating %s: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for the update of CDN Profile %q (Resource Group %q) to commplete: %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for the update of %s: %+v", *id, err) } return resourceCdnProfileRead(d, meta) @@ -190,9 +172,7 @@ func resourceCdnProfileRead(d *pluginsdk.ResourceData, meta interface{}) error { d.Set("name", id.Name) d.Set("resource_group_name", id.ResourceGroup) - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } + d.Set("location", location.NormalizeNilable(resp.Location)) if sku := resp.Sku; sku != nil { d.Set("sku", string(sku.Name)) @@ -213,11 +193,11 @@ func resourceCdnProfileDelete(d *pluginsdk.ResourceData, meta interface{}) error future, err := client.Delete(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("deleting CDN Profile %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of CDN Profile %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for the deletion of %s: %+v", *id, err) } return err diff --git a/internal/services/dns/dns_a_record_resource.go b/internal/services/dns/dns_a_record_resource.go index dccd6ac08d1d..12b55baeaff8 100644 --- a/internal/services/dns/dns_a_record_resource.go +++ b/internal/services/dns/dns_a_record_resource.go @@ -100,8 +100,8 @@ func resourceDnsARecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dns_a_record", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dns_a_record", resourceId.ID()) } } diff --git a/internal/services/dns/dns_aaaa_record_resource.go b/internal/services/dns/dns_aaaa_record_resource.go index d81ef65ae1c2..1e73a6f6c727 100644 --- a/internal/services/dns/dns_aaaa_record_resource.go +++ b/internal/services/dns/dns_aaaa_record_resource.go @@ -104,8 +104,8 @@ func resourceDnsAaaaRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dns_aaaa_record", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dns_aaaa_record", resourceId.ID()) } } diff --git a/internal/services/dns/dns_caa_record_resource.go b/internal/services/dns/dns_caa_record_resource.go index 3d7682c82b0f..0eaac727f1b2 100644 --- a/internal/services/dns/dns_caa_record_resource.go +++ b/internal/services/dns/dns_caa_record_resource.go @@ -117,8 +117,8 @@ func resourceDnsCaaRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{ } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dns_caa_record", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dns_caa_record", resourceId.ID()) } } diff --git a/internal/services/dns/dns_cname_record_resource.go b/internal/services/dns/dns_cname_record_resource.go index b179530a1a2b..7b9c40ac7d6d 100644 --- a/internal/services/dns/dns_cname_record_resource.go +++ b/internal/services/dns/dns_cname_record_resource.go @@ -97,8 +97,8 @@ func resourceDnsCNameRecordCreateUpdate(d *pluginsdk.ResourceData, meta interfac } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dns_cname_record", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dns_cname_record", resourceId.ID()) } } diff --git a/internal/services/dns/dns_mx_record_resource.go b/internal/services/dns/dns_mx_record_resource.go index 53886693b7a8..a74cd20754a5 100644 --- a/internal/services/dns/dns_mx_record_resource.go +++ b/internal/services/dns/dns_mx_record_resource.go @@ -107,8 +107,8 @@ func resourceDnsMxRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{} } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dns_mx_record", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dns_mx_record", resourceId.ID()) } } diff --git a/internal/services/dns/dns_ns_record_resource.go b/internal/services/dns/dns_ns_record_resource.go index 1f1966b95894..afe07c4ba3b3 100644 --- a/internal/services/dns/dns_ns_record_resource.go +++ b/internal/services/dns/dns_ns_record_resource.go @@ -91,8 +91,8 @@ func resourceDnsNsRecordCreate(d *pluginsdk.ResourceData, meta interface{}) erro } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dns_ns_record", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dns_ns_record", resourceId.ID()) } ttl := int64(d.Get("ttl").(int)) diff --git a/internal/services/dns/dns_ptr_record_resource.go b/internal/services/dns/dns_ptr_record_resource.go index 06c3740a65ba..de7e5c38e829 100644 --- a/internal/services/dns/dns_ptr_record_resource.go +++ b/internal/services/dns/dns_ptr_record_resource.go @@ -90,8 +90,8 @@ func resourceDnsPtrRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{ } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dns_ptr_record", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dns_ptr_record", resourceId.ID()) } } diff --git a/internal/services/dns/dns_srv_record_resource.go b/internal/services/dns/dns_srv_record_resource.go index ff76d96da903..5616eaeb8047 100644 --- a/internal/services/dns/dns_srv_record_resource.go +++ b/internal/services/dns/dns_srv_record_resource.go @@ -112,8 +112,8 @@ func resourceDnsSrvRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{ } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dns_srv_record", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dns_srv_record", resourceId.ID()) } } diff --git a/internal/services/dns/dns_txt_record_resource.go b/internal/services/dns/dns_txt_record_resource.go index a1084f28361f..3d53d2cf318c 100644 --- a/internal/services/dns/dns_txt_record_resource.go +++ b/internal/services/dns/dns_txt_record_resource.go @@ -98,8 +98,8 @@ func resourceDnsTxtRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{ } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dns_txt_record", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dns_txt_record", resourceId.ID()) } } diff --git a/internal/services/dns/dns_zone_resource.go b/internal/services/dns/dns_zone_resource.go index 1ce9faeb5d35..729fe806dc91 100644 --- a/internal/services/dns/dns_zone_resource.go +++ b/internal/services/dns/dns_zone_resource.go @@ -165,8 +165,8 @@ func resourceDnsZoneCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) er } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dns_zone", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dns_zone", resourceId.ID()) } } diff --git a/internal/services/firewall/firewall_resource.go b/internal/services/firewall/firewall_resource.go index 99bfd1d6c683..69edb9e06d38 100644 --- a/internal/services/firewall/firewall_resource.go +++ b/internal/services/firewall/firewall_resource.go @@ -220,29 +220,27 @@ func resourceFirewall() *pluginsdk.Resource { func resourceFirewallCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).Firewall.AzureFirewallsClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() log.Printf("[INFO] preparing arguments for AzureRM Azure Firewall creation") - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) + id := parse.NewFirewallID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - existing, err := client.Get(ctx, resourceGroup, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.AzureFirewallName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Firewall %q (Resource Group %q): %s", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %s", id, err) } } - if d.IsNewResource() { - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_firewall", *existing.ID) - } + if d.IsNewResource() && !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_firewall", id.ID()) } if err := validateFirewallIPConfigurationSettings(d.Get("ip_configuration").([]interface{})); err != nil { - return fmt.Errorf("validating Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("validating %s: %+v", id, err) } location := azure.NormalizeLocation(d.Get("location").(string)) @@ -326,8 +324,8 @@ func resourceFirewallCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) e } } - locks.ByName(name, azureFirewallResourceName) - defer locks.UnlockByName(name, azureFirewallResourceName) + locks.ByName(id.AzureFirewallName, azureFirewallResourceName) + defer locks.UnlockByName(id.AzureFirewallName, azureFirewallResourceName) locks.MultipleByName(vnetToLock, VirtualNetworkResourceName) defer locks.UnlockMultipleByName(vnetToLock, VirtualNetworkResourceName) @@ -336,15 +334,15 @@ func resourceFirewallCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) e defer locks.UnlockMultipleByName(subnetToLock, SubnetResourceName) if !d.IsNewResource() { - exists, err2 := client.Get(ctx, resourceGroup, name) + exists, err2 := client.Get(ctx, id.ResourceGroup, id.AzureFirewallName) if err2 != nil { if utils.ResponseWasNotFound(exists.Response) { - return fmt.Errorf("retrieving existing Firewall %q (Resource Group %q): firewall not found in resource group", name, resourceGroup) + return fmt.Errorf("retrieving existing %s: firewall not found in resource group", id) } - return fmt.Errorf("retrieving existing Firewall %q (Resource Group %q): %s", name, resourceGroup, err2) + return fmt.Errorf("retrieving existing %s: %+v", id, err2) } if exists.AzureFirewallPropertiesFormat == nil { - return fmt.Errorf("retrieving existing rules (Firewall %q / Resource Group %q): `props` was nil", name, resourceGroup) + return fmt.Errorf("retrieving existing rules for %s: `props` was nil", id) } props := *exists.AzureFirewallPropertiesFormat parameters.AzureFirewallPropertiesFormat.ApplicationRuleCollections = props.ApplicationRuleCollections @@ -352,26 +350,16 @@ func resourceFirewallCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) e parameters.AzureFirewallPropertiesFormat.NatRuleCollections = props.NatRuleCollections } - future, err := client.CreateOrUpdate(ctx, resourceGroup, name, parameters) + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.AzureFirewallName, parameters) if err != nil { - return fmt.Errorf("creating/updating Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("creating/updating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation/update of Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for creation/update of %s: %+v", id, err) } - read, err := client.Get(ctx, resourceGroup, name) - if err != nil { - return fmt.Errorf("retrieving Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - if read.ID == nil { - return fmt.Errorf("Cannot read Azure Firewall %q (Resource Group %q) ID", name, resourceGroup) - } - - d.SetId(*read.ID) - + d.SetId(id.ID()) return resourceFirewallRead(d, meta) } diff --git a/internal/services/healthcare/healthcare_service_data_source.go b/internal/services/healthcare/healthcare_service_data_source.go index ac4dadd9f1a2..299d148191f9 100644 --- a/internal/services/healthcare/healthcare_service_data_source.go +++ b/internal/services/healthcare/healthcare_service_data_source.go @@ -4,6 +4,8 @@ import ( "fmt" "time" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/parse" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" @@ -121,27 +123,28 @@ func dataSourceHealthcareService() *pluginsdk.Resource { func dataSourceHealthcareServiceRead(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).HealthCare.HealthcareServiceClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) - - resp, err := client.Get(ctx, resourceGroup, name) + id := parse.NewServiceID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + resp, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("HealthCare Service %q was not found in Resource Group %q", name, resourceGroup) + return fmt.Errorf("%s was not found", id) } - return fmt.Errorf("making Read request on Azure Healthcare Service %q (Resource Group %q): %+v", name, resourceGroup, err) + + return fmt.Errorf("retrieving %s: %+v", id, err) } - d.SetId(*resp.ID) + d.SetId(id.ID()) + if kind := resp.Kind; string(kind) != "" { d.Set("kind", kind) } if props := resp.Properties; props != nil { - if err := d.Set("access_policy_object_ids", flattenHealthcareAccessPolicies(props.AccessPolicies)); err != nil { + if err := d.Set("access_policy_object_ids", flattenAccessPolicies(props.AccessPolicies)); err != nil { return fmt.Errorf("setting `access_policy_object_ids`: %+v", err) } @@ -158,11 +161,11 @@ func dataSourceHealthcareServiceRead(d *pluginsdk.ResourceData, meta interface{} d.Set("cosmosdb_key_vault_key_versionless_id", cosmodDbKeyVaultKeyVersionlessId) d.Set("cosmosdb_throughput", cosmosDbThroughput) - if err := d.Set("authentication_configuration", flattenHealthcareAuthConfig(props.AuthenticationConfiguration)); err != nil { + if err := d.Set("authentication_configuration", flattenAuthentication(props.AuthenticationConfiguration)); err != nil { return fmt.Errorf("setting `authentication_configuration`: %+v", err) } - if err := d.Set("cors_configuration", flattenHealthcareCorsConfig(props.CorsConfiguration)); err != nil { + if err := d.Set("cors_configuration", flattenCorsConfig(props.CorsConfiguration)); err != nil { return fmt.Errorf("setting `cors_configuration`: %+v", err) } } diff --git a/internal/services/healthcare/healthcare_service_resource.go b/internal/services/healthcare/healthcare_service_resource.go index 99b057b8407b..3816f9efd70f 100644 --- a/internal/services/healthcare/healthcare_service_resource.go +++ b/internal/services/healthcare/healthcare_service_resource.go @@ -200,46 +200,42 @@ func resourceHealthcareService() *pluginsdk.Resource { func resourceHealthcareServiceCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).HealthCare.HealthcareServiceClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - log.Printf("[INFO] preparing arguments for Azure ARM Healthcare Service creation.") - - name := d.Get("name").(string) - resGroup := d.Get("resource_group_name").(string) - - location := azure.NormalizeLocation(d.Get("location").(string)) - t := d.Get("tags").(map[string]interface{}) - - kind := d.Get("kind").(string) - + id := parse.NewServiceID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Healthcare Service %q (Resource Group %q): %s", name, resGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_healthcare_service", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_healthcare_service", id.ID()) } } - cosmosDbConfiguration, err := expandAzureRMhealthcareapisCosmosDbConfiguration(d) + cosmosDbConfiguration, err := expandsCosmosDBConfiguration(d) if err != nil { return fmt.Errorf("expanding cosmosdb_configuration: %+v", err) } + location := azure.NormalizeLocation(d.Get("location").(string)) + t := d.Get("tags").(map[string]interface{}) + kind := d.Get("kind").(string) + healthcareServiceDescription := healthcareapis.ServicesDescription{ Location: utils.String(location), Tags: tags.Expand(t), Kind: healthcareapis.Kind(kind), Properties: &healthcareapis.ServicesProperties{ - AccessPolicies: expandAzureRMhealthcareapisAccessPolicyEntries(d), + AccessPolicies: expandAccessPolicyEntries(d), CosmosDbConfiguration: cosmosDbConfiguration, - CorsConfiguration: expandAzureRMhealthcareapisCorsConfiguration(d), - AuthenticationConfiguration: expandAzureRMhealthcareapisAuthentication(d), + CorsConfiguration: expandCorsConfiguration(d), + AuthenticationConfiguration: expandAuthentication(d), }, } @@ -250,25 +246,16 @@ func resourceHealthcareServiceCreateUpdate(d *pluginsdk.ResourceData, meta inter healthcareServiceDescription.Properties.PublicNetworkAccess = healthcareapis.Enabled } - future, err := client.CreateOrUpdate(ctx, resGroup, name, healthcareServiceDescription) + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, healthcareServiceDescription) if err != nil { - return fmt.Errorf("Creating/Updating Healthcare Service %q (Resource Group %q): %+v", name, resGroup, err) + return fmt.Errorf("creating/updating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Creating/Updating Healthcare Service %q (Resource Group %q): %+v", name, resGroup, err) - } - - read, err := client.Get(ctx, resGroup, name) - if err != nil { - return fmt.Errorf("Retrieving Healthcare Service %q (Resource Group %q): %+v", name, resGroup, err) + return fmt.Errorf("waiting for creation/update of %s: %+v", id, err) } - if read.ID == nil { - return fmt.Errorf("Cannot read Healthcare Service %q (resource group %q) ID", name, resGroup) - } - - d.SetId(*read.ID) + d.SetId(id.ID()) return resourceHealthcareServiceRead(d, meta) } @@ -285,12 +272,12 @@ func resourceHealthcareServiceRead(d *pluginsdk.ResourceData, meta interface{}) resp, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[WARN] Healthcare Service %q was not found (Resource Group %q)", id.Name, id.ResourceGroup) + log.Printf("[WARN] %s was not found - removing from state!", *id) d.SetId("") return nil } - return fmt.Errorf("making Read request on Azure Healthcare Service %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } d.Set("name", id.Name) @@ -303,7 +290,7 @@ func resourceHealthcareServiceRead(d *pluginsdk.ResourceData, meta interface{}) d.Set("kind", kind) } if props := resp.Properties; props != nil { - if err := d.Set("access_policy_object_ids", flattenHealthcareAccessPolicies(props.AccessPolicies)); err != nil { + if err := d.Set("access_policy_object_ids", flattenAccessPolicies(props.AccessPolicies)); err != nil { return fmt.Errorf("setting `access_policy_object_ids`: %+v", err) } @@ -325,11 +312,11 @@ func resourceHealthcareServiceRead(d *pluginsdk.ResourceData, meta interface{}) d.Set("public_network_access_enabled", false) } - if err := d.Set("authentication_configuration", flattenHealthcareAuthConfig(props.AuthenticationConfiguration)); err != nil { + if err := d.Set("authentication_configuration", flattenAuthentication(props.AuthenticationConfiguration)); err != nil { return fmt.Errorf("setting `authentication_configuration`: %+v", err) } - if err := d.Set("cors_configuration", flattenHealthcareCorsConfig(props.CorsConfiguration)); err != nil { + if err := d.Set("cors_configuration", flattenCorsConfig(props.CorsConfiguration)); err != nil { return fmt.Errorf("setting `cors_configuration`: %+v", err) } } @@ -359,7 +346,7 @@ func resourceHealthcareServiceDelete(d *pluginsdk.ResourceData, meta interface{} return nil } -func expandAzureRMhealthcareapisAccessPolicyEntries(d *pluginsdk.ResourceData) *[]healthcareapis.ServiceAccessPolicyEntry { +func expandAccessPolicyEntries(d *pluginsdk.ResourceData) *[]healthcareapis.ServiceAccessPolicyEntry { accessPolicyObjectIds := d.Get("access_policy_object_ids").(*pluginsdk.Set).List() svcAccessPolicyArray := make([]healthcareapis.ServiceAccessPolicyEntry, 0) @@ -371,7 +358,7 @@ func expandAzureRMhealthcareapisAccessPolicyEntries(d *pluginsdk.ResourceData) * return &svcAccessPolicyArray } -func expandAzureRMhealthcareapisCorsConfiguration(d *pluginsdk.ResourceData) *healthcareapis.ServiceCorsConfigurationInfo { +func expandCorsConfiguration(d *pluginsdk.ResourceData) *healthcareapis.ServiceCorsConfigurationInfo { corsConfigRaw := d.Get("cors_configuration").([]interface{}) if len(corsConfigRaw) == 0 { @@ -396,7 +383,7 @@ func expandAzureRMhealthcareapisCorsConfiguration(d *pluginsdk.ResourceData) *he return cors } -func expandAzureRMhealthcareapisAuthentication(d *pluginsdk.ResourceData) *healthcareapis.ServiceAuthenticationConfigurationInfo { +func expandAuthentication(d *pluginsdk.ResourceData) *healthcareapis.ServiceAuthenticationConfigurationInfo { authConfigRaw := d.Get("authentication_configuration").([]interface{}) if len(authConfigRaw) == 0 { @@ -416,7 +403,7 @@ func expandAzureRMhealthcareapisAuthentication(d *pluginsdk.ResourceData) *healt return auth } -func expandAzureRMhealthcareapisCosmosDbConfiguration(d *pluginsdk.ResourceData) (*healthcareapis.ServiceCosmosDbConfigurationInfo, error) { +func expandsCosmosDBConfiguration(d *pluginsdk.ResourceData) (*healthcareapis.ServiceCosmosDbConfigurationInfo, error) { throughput := int32(d.Get("cosmosdb_throughput").(int)) cosmosdb := &healthcareapis.ServiceCosmosDbConfigurationInfo{ @@ -434,7 +421,7 @@ func expandAzureRMhealthcareapisCosmosDbConfiguration(d *pluginsdk.ResourceData) return cosmosdb, nil } -func flattenHealthcareAccessPolicies(policies *[]healthcareapis.ServiceAccessPolicyEntry) []string { +func flattenAccessPolicies(policies *[]healthcareapis.ServiceAccessPolicyEntry) []string { result := make([]string, 0) if policies == nil { @@ -450,7 +437,7 @@ func flattenHealthcareAccessPolicies(policies *[]healthcareapis.ServiceAccessPol return result } -func flattenHealthcareAuthConfig(input *healthcareapis.ServiceAuthenticationConfigurationInfo) []interface{} { +func flattenAuthentication(input *healthcareapis.ServiceAuthenticationConfigurationInfo) []interface{} { if input == nil { return []interface{}{} } @@ -476,7 +463,7 @@ func flattenHealthcareAuthConfig(input *healthcareapis.ServiceAuthenticationConf } } -func flattenHealthcareCorsConfig(input *healthcareapis.ServiceCorsConfigurationInfo) []interface{} { +func flattenCorsConfig(input *healthcareapis.ServiceCorsConfigurationInfo) []interface{} { if input == nil { return []interface{}{} } diff --git a/internal/services/hsm/dedicated_hardware_security_module_resource.go b/internal/services/hsm/dedicated_hardware_security_module_resource.go index c6168a7cf7fd..dd6899837958 100644 --- a/internal/services/hsm/dedicated_hardware_security_module_resource.go +++ b/internal/services/hsm/dedicated_hardware_security_module_resource.go @@ -106,20 +106,19 @@ func resourceDedicatedHardwareSecurityModule() *pluginsdk.Resource { func resourceDedicatedHardwareSecurityModuleCreate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).HSM.DedicatedHsmClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) - - existing, err := client.Get(ctx, resourceGroup, name) + id := parse.NewDedicatedHardwareSecurityModuleID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + existing, err := client.Get(ctx, id.ResourceGroup, id.DedicatedHSMName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for present of existing Dedicated Hardware Security Module %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_dedicated_hardware_security_module", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_dedicated_hardware_security_module", id.ID()) } parameters := hardwaresecuritymodules.DedicatedHsm{ @@ -141,25 +140,16 @@ func resourceDedicatedHardwareSecurityModuleCreate(d *pluginsdk.ResourceData, me parameters.Zones = azure.ExpandZones(v.([]interface{})) } - future, err := client.CreateOrUpdate(ctx, resourceGroup, name, parameters) + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.DedicatedHSMName, parameters) if err != nil { - return fmt.Errorf("creating Dedicated Hardware Security Module %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("creating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting on creating future for Dedicated Hardware Security Module %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - resp, err := client.Get(ctx, resourceGroup, name) - if err != nil { - return fmt.Errorf("retrieving Dedicated Hardware Security Module %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - if resp.ID == nil || *resp.ID == "" { - return fmt.Errorf("empty or nil ID returned for Dedicated Hardware Security Module %q (Resource Group %q) ID", name, resourceGroup) + return fmt.Errorf("waiting for creation of %s: %+v", id, err) } - d.SetId(*resp.ID) + d.SetId(id.ID()) return resourceDedicatedHardwareSecurityModuleRead(d, meta) } diff --git a/internal/services/loadbalancer/loadbalancer_resource.go b/internal/services/loadbalancer/loadbalancer_resource.go index c9253b593a26..e1b15c6caf83 100644 --- a/internal/services/loadbalancer/loadbalancer_resource.go +++ b/internal/services/loadbalancer/loadbalancer_resource.go @@ -264,12 +264,12 @@ func resourceArmLoadBalancerCreateUpdate(d *pluginsdk.ResourceData, meta interfa existing, err := client.Get(ctx, id.ResourceGroup, id.Name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Load Balancer %q (Resource Group %q): %s", id.Name, id.ResourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_lb", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_lb", id.ID()) } } @@ -307,11 +307,10 @@ func resourceArmLoadBalancerCreateUpdate(d *pluginsdk.ResourceData, meta interfa future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, loadBalancer) if err != nil { - return fmt.Errorf("creating/updating Load Balancer %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("creating/updating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("creating/Updating Load Balancer %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for creation/update of %s: %+v", id, err) } d.SetId(id.ID()) @@ -332,11 +331,11 @@ func resourceArmLoadBalancerRead(d *pluginsdk.ResourceData, meta interface{}) er resp, err := client.Get(ctx, id.ResourceGroup, id.Name, "") if err != nil { if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] %s was not found - removing from state", *id) d.SetId("") - log.Printf("[INFO] Load Balancer %q not found. Removing from state", id.Name) return nil } - return fmt.Errorf("failed to retrieve Load Balancer By ID: %+v", err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } d.Set("name", id.Name) @@ -390,11 +389,11 @@ func resourceArmLoadBalancerDelete(d *pluginsdk.ResourceData, meta interface{}) future, err := client.Delete(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("deleting Load Balancer %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of Load Balancer %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for the deletion of %s: %+v", *id, err) } return nil diff --git a/internal/services/media/media_job_resource.go b/internal/services/media/media_job_resource.go index 0cda03cc8d7c..e7ff5acfab86 100644 --- a/internal/services/media/media_job_resource.go +++ b/internal/services/media/media_job_resource.go @@ -158,8 +158,8 @@ func resourceMediaJobCreate(d *pluginsdk.ResourceData, meta interface{}) error { } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_media_job", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_media_job", resourceId.ID()) } } diff --git a/internal/services/media/media_streaming_locator_resource.go b/internal/services/media/media_streaming_locator_resource.go index 13008398c378..81a81a558691 100644 --- a/internal/services/media/media_streaming_locator_resource.go +++ b/internal/services/media/media_streaming_locator_resource.go @@ -178,8 +178,8 @@ func resourceMediaStreamingLocatorCreate(d *pluginsdk.ResourceData, meta interfa } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_media_streaming_locator", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_media_streaming_locator", resourceID.ID()) } } diff --git a/internal/services/mixedreality/spatial_anchors_account_data_source.go b/internal/services/mixedreality/spatial_anchors_account_data_source.go index abda82bd5789..47b2bafb52e4 100644 --- a/internal/services/mixedreality/spatial_anchors_account_data_source.go +++ b/internal/services/mixedreality/spatial_anchors_account_data_source.go @@ -5,6 +5,8 @@ import ( "regexp" "time" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/mixedreality/parse" @@ -59,21 +61,19 @@ func dataSourceSpatialAnchorsAccountRead(d *pluginsdk.ResourceData, meta interfa resourceGroup := d.Get("resource_group_name").(string) id := parse.NewSpatialAnchorsAccountID(subscriptionId, resourceGroup, name) - resp, err := client.Get(ctx, resourceGroup, name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - return nil + return fmt.Errorf("%s was not found", id) } - return fmt.Errorf("retrieving Spatial Anchors Account %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", id, err) } + d.SetId(id.ID()) - d.Set("name", resp.Name) + d.Set("name", id.Name) d.Set("resource_group_name", id.ResourceGroup) - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } + d.Set("location", location.NormalizeNilable(resp.Location)) if props := resp.AccountProperties; props != nil { d.Set("account_domain", props.AccountDomain) diff --git a/internal/services/mixedreality/spatial_anchors_account_resource.go b/internal/services/mixedreality/spatial_anchors_account_resource.go index 2c1b16dbf1ea..5a79a6dd9c25 100644 --- a/internal/services/mixedreality/spatial_anchors_account_resource.go +++ b/internal/services/mixedreality/spatial_anchors_account_resource.go @@ -5,6 +5,8 @@ import ( "regexp" "time" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/Azure/azure-sdk-for-go/services/mixedreality/mgmt/2021-01-01/mixedreality" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" @@ -67,24 +69,24 @@ func resourceSpatialAnchorsAccount() *pluginsdk.Resource { func resourceSpatialAnchorsAccountCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).MixedReality.SpatialAnchorsAccountClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) - resourceGroup := d.Get("resource_group_name").(string) t := d.Get("tags").(map[string]interface{}) + id := parse.NewSpatialAnchorsAccountID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, resourceGroup, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Spatial Anchors Account %q (Resource Group %q): %s", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_spatial_anchors_account", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_spatial_anchors_account", id.ID()) } } @@ -93,21 +95,11 @@ func resourceSpatialAnchorsAccountCreateUpdate(d *pluginsdk.ResourceData, meta i Tags: tags.Expand(t), } - if _, err := client.Create(ctx, resourceGroup, name, account); err != nil { - return fmt.Errorf("creating/updating Spatial Anchors Account %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - resp, err := client.Get(ctx, resourceGroup, name) - if err != nil { - return fmt.Errorf("retrieving Spatial Anchors Account %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - if resp.ID == nil { - return fmt.Errorf("cannot read Spatial Anchors Account %q (Resource Group %q) ID", name, resourceGroup) + if _, err := client.Create(ctx, id.ResourceGroup, id.Name, account); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } - d.SetId(*resp.ID) - + d.SetId(id.ID()) return resourceSpatialAnchorsAccountRead(d, meta) } @@ -128,14 +120,12 @@ func resourceSpatialAnchorsAccountRead(d *pluginsdk.ResourceData, meta interface return nil } - return fmt.Errorf("retrieving Spatial Anchors Account %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", resp.Name) + d.Set("name", id.Name) d.Set("resource_group_name", id.ResourceGroup) - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } + d.Set("location", location.NormalizeNilable(resp.Location)) if props := resp.AccountProperties; props != nil { d.Set("account_domain", props.AccountDomain) @@ -158,7 +148,7 @@ func resourceSpatialAnchorsAccountDelete(d *pluginsdk.ResourceData, meta interfa response, err := client.Delete(ctx, id.ResourceGroup, id.Name) if err != nil { if !utils.ResponseWasNotFound(response) { - return fmt.Errorf("deleting Spatial Anchors Account %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } } diff --git a/internal/services/netapp/netapp_account_resource.go b/internal/services/netapp/netapp_account_resource.go index 2c1a5791f3e5..31c83a7fc9e9 100644 --- a/internal/services/netapp/netapp_account_resource.go +++ b/internal/services/netapp/netapp_account_resource.go @@ -8,7 +8,6 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/services/netapp/mgmt/2021-06-01/netapp" - "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" @@ -108,21 +107,20 @@ func resourceNetAppAccount() *pluginsdk.Resource { func resourceNetAppAccountCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).NetApp.AccountClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) - + id := parse.NewAccountID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, resourceGroup, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.NetAppAccountName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for present of existing NetApp Account %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_netapp_account", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_netapp_account", id.ID()) } } @@ -137,23 +135,15 @@ func resourceNetAppAccountCreateUpdate(d *pluginsdk.ResourceData, meta interface Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - future, err := client.CreateOrUpdate(ctx, accountParameters, resourceGroup, name) + future, err := client.CreateOrUpdate(ctx, accountParameters, id.ResourceGroup, id.NetAppAccountName) if err != nil { - return fmt.Errorf("creating NetApp Account %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("creating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of NetApp Account %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - resp, err := client.Get(ctx, resourceGroup, name) - if err != nil { - return fmt.Errorf("retrieving NetApp Account %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for the creation of %s: %+v", id, err) } - if resp.ID == nil { - return fmt.Errorf("Cannot read NetApp Account %q (Resource Group %q) ID", name, resourceGroup) - } - d.SetId(*resp.ID) + d.SetId(id.ID()) return resourceNetAppAccountRead(d, meta) } @@ -170,15 +160,16 @@ func resourceNetAppAccountRead(d *pluginsdk.ResourceData, meta interface{}) erro resp, err := client.Get(ctx, id.ResourceGroup, id.NetAppAccountName) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] NetApp Accounts %q does not exist - removing from state", d.Id()) + log.Printf("[INFO] %s does not exist - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("reading NetApp Accounts %q (Resource Group %q): %+v", id.NetAppAccountName, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", resp.Name) + d.Set("name", id.NetAppAccountName) d.Set("resource_group_name", id.ResourceGroup) + if location := resp.Location; location != nil { d.Set("location", azure.NormalizeLocation(*location)) } @@ -198,13 +189,10 @@ func resourceNetAppAccountDelete(d *pluginsdk.ResourceData, meta interface{}) er future, err := client.Delete(ctx, id.ResourceGroup, id.NetAppAccountName) if err != nil { - return fmt.Errorf("deleting NetApp Account %q (Resource Group %q): %+v", id.NetAppAccountName, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - if !response.WasNotFound(future.Response()) { - return fmt.Errorf("waiting for deleting NetApp Account %q (Resource Group %q): %+v", id.NetAppAccountName, id.ResourceGroup, err) - } + return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) } return nil diff --git a/internal/services/netapp/netapp_pool_resource.go b/internal/services/netapp/netapp_pool_resource.go index 0ba19876931e..5935df0ef401 100644 --- a/internal/services/netapp/netapp_pool_resource.go +++ b/internal/services/netapp/netapp_pool_resource.go @@ -81,22 +81,20 @@ func resourceNetAppPool() *pluginsdk.Resource { func resourceNetAppPoolCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).NetApp.PoolClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) - accountName := d.Get("account_name").(string) - + id := parse.NewCapacityPoolID(subscriptionId, d.Get("resource_group_name").(string), d.Get("account_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, resourceGroup, accountName, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.NetAppAccountName, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for present of existing NetApp Pool %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_netapp_pool", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_netapp_pool", id.ID()) } } @@ -115,23 +113,15 @@ func resourceNetAppPoolCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - future, err := client.CreateOrUpdate(ctx, capacityPoolParameters, resourceGroup, accountName, name) + future, err := client.CreateOrUpdate(ctx, capacityPoolParameters, id.ResourceGroup, id.NetAppAccountName, id.Name) if err != nil { - return fmt.Errorf("creating NetApp Pool %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("creating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of NetApp Pool %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - resp, err := client.Get(ctx, resourceGroup, accountName, name) - if err != nil { - return fmt.Errorf("retrieving NetApp Pool %q (Resource Group %q): %+v", name, resourceGroup, err) - } - if resp.ID == nil { - return fmt.Errorf("Cannot read NetApp Pool %q (Resource Group %q) ID", name, resourceGroup) + return fmt.Errorf("waiting for creation of %s: %+v", id, err) } - d.SetId(*resp.ID) + d.SetId(id.ID()) return resourceNetAppPoolRead(d, meta) } @@ -148,11 +138,11 @@ func resourceNetAppPoolRead(d *pluginsdk.ResourceData, meta interface{}) error { resp, err := client.Get(ctx, id.ResourceGroup, id.NetAppAccountName, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] NetApp Pools %q does not exist - removing from state", d.Id()) + log.Printf("[INFO] %s does not exist - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("reading NetApp Pools %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } d.Set("name", id.Name) @@ -187,7 +177,7 @@ func resourceNetAppPoolDelete(d *pluginsdk.ResourceData, meta interface{}) error } if _, err = client.Delete(ctx, id.ResourceGroup, id.NetAppAccountName, id.Name); err != nil { - return fmt.Errorf("deleting NetApp Pool %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } // The resource NetApp Pool depends on the resource NetApp Account. @@ -195,30 +185,34 @@ func resourceNetAppPoolDelete(d *pluginsdk.ResourceData, meta interface{}) error // Then it tries to immediately delete NetApp Account but it still throws error `Can not delete resource before nested resources are deleted.` // In this case we're going to re-check status code again. // For more details, see related Bug: https://github.com/Azure/azure-sdk-for-go/issues/6374 - log.Printf("[DEBUG] Waiting for NetApp Pool %q (Resource Group %q) to be deleted", id.Name, id.ResourceGroup) + log.Printf("[DEBUG] Waiting for %s to be deleted", *id) + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context has no deadline") + } stateConf := &pluginsdk.StateChangeConf{ ContinuousTargetOccurence: 5, Delay: 10 * time.Second, MinTimeout: 10 * time.Second, Pending: []string{"200", "202"}, Target: []string{"204", "404"}, - Refresh: netappPoolDeleteStateRefreshFunc(ctx, client, id.ResourceGroup, id.NetAppAccountName, id.Name), - Timeout: d.Timeout(pluginsdk.TimeoutDelete), + Refresh: netappPoolDeleteStateRefreshFunc(ctx, client, *id), + Timeout: time.Until(deadline), } if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for NetApp Pool %q (Resource Group %q) to be deleted: %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for %s to be deleted: %+v", *id, err) } return nil } -func netappPoolDeleteStateRefreshFunc(ctx context.Context, client *netapp.PoolsClient, resourceGroupName string, accountName string, name string) pluginsdk.StateRefreshFunc { +func netappPoolDeleteStateRefreshFunc(ctx context.Context, client *netapp.PoolsClient, id parse.CapacityPoolId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { - res, err := client.Get(ctx, resourceGroupName, accountName, name) + res, err := client.Get(ctx, id.ResourceGroup, id.NetAppAccountName, id.Name) if err != nil { if !utils.ResponseWasNotFound(res.Response) { - return nil, "", fmt.Errorf("retrieving NetApp Pool %q (Resource Group %q): %s", name, resourceGroupName, err) + return nil, "", fmt.Errorf("retrieving %s: %+v", id, err) } } diff --git a/internal/services/netapp/netapp_volume_resource.go b/internal/services/netapp/netapp_volume_resource.go index 2b5137fbb14f..e958a9307879 100644 --- a/internal/services/netapp/netapp_volume_resource.go +++ b/internal/services/netapp/netapp_volume_resource.go @@ -269,23 +269,20 @@ func resourceNetAppVolume() *pluginsdk.Resource { func resourceNetAppVolumeCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).NetApp.VolumeClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) - accountName := d.Get("account_name").(string) - poolName := d.Get("pool_name").(string) - + id := parse.NewVolumeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("account_name").(string), d.Get("pool_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, resourceGroup, accountName, poolName, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.NetAppAccountName, id.CapacityPoolName, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for present of existing NetApp Volume %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_netapp_volume", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_netapp_volume", id.ID()) } } @@ -301,10 +298,10 @@ func resourceNetAppVolumeCreateUpdate(d *pluginsdk.ResourceData, meta interface{ // Handling security style property securityStyle := d.Get("security_style").(string) if strings.EqualFold(securityStyle, "unix") && len(protocols) == 1 && strings.EqualFold(protocols[0].(string), "cifs") { - return fmt.Errorf("Unix security style cannot be used in a CIFS enabled volume for volume %q (Resource Group %q)", name, resourceGroup) + return fmt.Errorf("Unix security style cannot be used in a CIFS enabled volume for %s", id) } if strings.EqualFold(securityStyle, "ntfs") && len(protocols) == 1 && (strings.EqualFold(protocols[0].(string), "nfsv3") || strings.EqualFold(protocols[0].(string), "nfsv4.1")) { - return fmt.Errorf("Ntfs security style cannot be used in a NFSv3/NFSv4.1 enabled volume for volume %q (Resource Group %q)", name, resourceGroup) + return fmt.Errorf("Ntfs security style cannot be used in a NFSv3/NFSv4.1 enabled volume for %s", id) } storageQuotaInGB := int64(d.Get("storage_quota_in_gb").(int) * 1073741824) @@ -377,17 +374,17 @@ func resourceNetAppVolumeCreateUpdate(d *pluginsdk.ResourceData, meta interface{ if !strings.EqualFold(string(sourceVolume.ServiceLevel), serviceLevel) { propertyMismatch = append(propertyMismatch, "service_level") } - if !strings.EqualFold(parsedVolumeID.ResourceGroup, resourceGroup) { + if !strings.EqualFold(parsedVolumeID.ResourceGroup, id.ResourceGroup) { propertyMismatch = append(propertyMismatch, "resource_group_name") } - if !strings.EqualFold(parsedVolumeID.NetAppAccountName, accountName) { + if !strings.EqualFold(parsedVolumeID.NetAppAccountName, id.NetAppAccountName) { propertyMismatch = append(propertyMismatch, "account_name") } - if !strings.EqualFold(parsedVolumeID.CapacityPoolName, poolName) { + if !strings.EqualFold(parsedVolumeID.CapacityPoolName, id.CapacityPoolName) { propertyMismatch = append(propertyMismatch, "pool_name") } if len(propertyMismatch) > 0 { - return fmt.Errorf("Following NetApp Volume properties on new Volume from Snapshot does not match Snapshot's source Volume %q (Resource Group %q): %+v", name, resourceGroup, propertyMismatch) + return fmt.Errorf("Following NetApp Volume properties on new Volume from Snapshot does not match Snapshot's source %s: %s", id, strings.Join(propertyMismatch, ", ")) } } @@ -409,18 +406,16 @@ func resourceNetAppVolumeCreateUpdate(d *pluginsdk.ResourceData, meta interface{ Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - future, err := client.CreateOrUpdate(ctx, parameters, resourceGroup, accountName, poolName, name) + future, err := client.CreateOrUpdate(ctx, parameters, id.ResourceGroup, id.NetAppAccountName, id.CapacityPoolName, id.Name) if err != nil { - return fmt.Errorf("creating NetApp Volume %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("creating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of NetApp Volume %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for the creation of %s: %+v", id, err) } // Waiting for volume be completely provisioned - id := parse.NewVolumeID(client.SubscriptionID, resourceGroup, accountName, poolName, name) - log.Printf("[DEBUG] Waiting for NetApp Volume Provisioning Service %q (Resource Group %q) to complete", id.Name, id.ResourceGroup) - if err := waitForVolumeCreation(ctx, client, id, d.Timeout(pluginsdk.TimeoutDelete)); err != nil { + if err := waitForVolumeCreation(ctx, client, id); err != nil { return err } @@ -452,7 +447,7 @@ func resourceNetAppVolumeCreateUpdate(d *pluginsdk.ResourceData, meta interface{ // Wait for volume replication authorization to complete log.Printf("[DEBUG] Waiting for replication authorization on NetApp Volume Provisioning Service %q (Resource Group %q) to complete", id.Name, id.ResourceGroup) - if err := waitForReplAuthorization(ctx, client, id, d.Timeout(pluginsdk.TimeoutDelete)); err != nil { + if err := waitForReplAuthorization(ctx, client, id); err != nil { return err } } @@ -475,11 +470,11 @@ func resourceNetAppVolumeRead(d *pluginsdk.ResourceData, meta interface{}) error resp, err := client.Get(ctx, id.ResourceGroup, id.NetAppAccountName, id.CapacityPoolName, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] NetApp Volumes %q does not exist - removing from state", d.Id()) + log.Printf("[INFO] %s was not found - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("reading NetApp Volumes %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("reading %s: %+v", *id, err) } d.Set("name", id.Name) @@ -527,70 +522,78 @@ func resourceNetAppVolumeDelete(d *pluginsdk.ResourceData, meta interface{}) err dataProtectionReplicationRaw := d.Get("data_protection_replication").([]interface{}) dataProtectionReplication := expandNetAppVolumeDataProtectionReplication(dataProtectionReplicationRaw) - if replVolumeID := id; dataProtectionReplication != nil && dataProtectionReplication.Replication != nil { + if replicaVolumeId := id; dataProtectionReplication != nil && dataProtectionReplication.Replication != nil { + if dataProtectionReplication.Replication.RemoteVolumeResourceID == nil { + return fmt.Errorf("remote volume id was nil") + } + if strings.ToLower(string(dataProtectionReplication.Replication.EndpointType)) != "dst" { // This is the case where primary volume started the deletion, in this case, to be consistent we will remove replication from secondary - replVolumeID, err = parse.VolumeID(*dataProtectionReplication.Replication.RemoteVolumeResourceID) + replicaVolumeId, err = parse.VolumeID(*dataProtectionReplication.Replication.RemoteVolumeResourceID) if err != nil { return err } } // Checking replication status before deletion, it need to be broken before proceeding with deletion - if res, err := client.ReplicationStatusMethod(ctx, replVolumeID.ResourceGroup, replVolumeID.NetAppAccountName, replVolumeID.CapacityPoolName, replVolumeID.Name); err == nil { + if res, err := client.ReplicationStatusMethod(ctx, replicaVolumeId.ResourceGroup, replicaVolumeId.NetAppAccountName, replicaVolumeId.CapacityPoolName, replicaVolumeId.Name); err == nil { // Wait for replication state = "mirrored" if strings.ToLower(string(res.MirrorState)) == "uninitialized" { - if err := waitForReplMirrorState(ctx, client, *replVolumeID, d.Timeout(pluginsdk.TimeoutDelete), "mirrored"); err != nil { - return err + if err := waitForReplMirrorState(ctx, client, *replicaVolumeId, "mirrored"); err != nil { + return fmt.Errorf("waiting for replica %s to become 'mirrored': %+v", *replicaVolumeId, err) } } // Breaking replication _, err = client.BreakReplication(ctx, - replVolumeID.ResourceGroup, - replVolumeID.NetAppAccountName, - replVolumeID.CapacityPoolName, - replVolumeID.Name, + replicaVolumeId.ResourceGroup, + replicaVolumeId.NetAppAccountName, + replicaVolumeId.CapacityPoolName, + replicaVolumeId.Name, &netapp.BreakReplicationRequest{ ForceBreakReplication: utils.Bool(true), }) if err != nil { - return fmt.Errorf("deleting replication from NetApp Volume %q (Resource Group %q): %+v", replVolumeID.Name, replVolumeID.ResourceGroup, err) + return fmt.Errorf("breaking replication for %s: %+v", *replicaVolumeId, err) } // Waiting for replication be in broken state - log.Printf("[DEBUG] Waiting for replication on NetApp Volume Provisioning Service %q (Resource Group %q) to be in broken state", replVolumeID.Name, replVolumeID.ResourceGroup) - if err := waitForReplMirrorState(ctx, client, *replVolumeID, d.Timeout(pluginsdk.TimeoutDelete), "broken"); err != nil { - return err + log.Printf("[DEBUG] Waiting for the replication of %s to be in broken state", *replicaVolumeId) + if err := waitForReplMirrorState(ctx, client, *replicaVolumeId, "broken"); err != nil { + return fmt.Errorf("waiting for the breaking of replication for %s: %+v", *replicaVolumeId, err) } } // Deleting replication and waiting for it to fully complete the operation - if _, err = client.DeleteReplication(ctx, replVolumeID.ResourceGroup, replVolumeID.NetAppAccountName, replVolumeID.CapacityPoolName, replVolumeID.Name); err != nil { - return fmt.Errorf("deleting replication from NetApp Volume %q (Resource Group %q): %+v", replVolumeID.Name, replVolumeID.ResourceGroup, err) + if _, err = client.DeleteReplication(ctx, replicaVolumeId.ResourceGroup, replicaVolumeId.NetAppAccountName, replicaVolumeId.CapacityPoolName, replicaVolumeId.Name); err != nil { + return fmt.Errorf("deleting replicate %s: %+v", *replicaVolumeId, err) } - log.Printf("[DEBUG] Waiting for replication on NetApp Volume Provisioning Service %q (Resource Group %q) to be deleted", replVolumeID.Name, replVolumeID.ResourceGroup) - if err := waitForReplicationDeletion(ctx, client, *replVolumeID, d.Timeout(pluginsdk.TimeoutDelete)); err != nil { - return err + log.Printf("[DEBUG] Waiting for the replica of %s to be deleted", replicaVolumeId) + if err := waitForReplicationDeletion(ctx, client, *replicaVolumeId); err != nil { + return fmt.Errorf("waiting for the replica %s to be deleted: %+v", *replicaVolumeId, err) } } // Deleting volume and waiting for it fo fully complete the operation if _, err = client.Delete(ctx, id.ResourceGroup, id.NetAppAccountName, id.CapacityPoolName, id.Name); err != nil { - return fmt.Errorf("deleting NetApp Volume %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } - log.Printf("[DEBUG] Waiting for NetApp Volume Provisioning Service %q (Resource Group %q) to be deleted", id.Name, id.ResourceGroup) - if err := waitForVolumeDeletion(ctx, client, *id, d.Timeout(pluginsdk.TimeoutDelete)); err != nil { - return err + log.Printf("[DEBUG] Waiting for %s to be deleted", *id) + if err := waitForVolumeDeletion(ctx, client, *id); err != nil { + return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) } return nil } -func waitForVolumeCreation(ctx context.Context, client *netapp.VolumesClient, id parse.VolumeId, timeout time.Duration) error { +func waitForVolumeCreation(ctx context.Context, client *netapp.VolumesClient, id parse.VolumeId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } stateConf := &pluginsdk.StateChangeConf{ ContinuousTargetOccurence: 5, Delay: 10 * time.Second, @@ -598,17 +601,21 @@ func waitForVolumeCreation(ctx context.Context, client *netapp.VolumesClient, id Pending: []string{"204", "404"}, Target: []string{"200", "202"}, Refresh: netappVolumeStateRefreshFunc(ctx, client, id), - Timeout: timeout, + Timeout: time.Until(deadline), } if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting NetApp Volume Provisioning Service %q (Resource Group %q) to complete: %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for %s to finish creating: %+v", id, err) } return nil } -func waitForReplAuthorization(ctx context.Context, client *netapp.VolumesClient, id parse.VolumeId, timeout time.Duration) error { +func waitForReplAuthorization(ctx context.Context, client *netapp.VolumesClient, id parse.VolumeId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } stateConf := &pluginsdk.StateChangeConf{ ContinuousTargetOccurence: 5, Delay: 10 * time.Second, @@ -616,7 +623,7 @@ func waitForReplAuthorization(ctx context.Context, client *netapp.VolumesClient, Pending: []string{"204", "404", "400"}, // TODO: Remove 400 when bug is fixed on RP side, where replicationStatus returns 400 at some point during authorization process Target: []string{"200", "202"}, Refresh: netappVolumeReplicationStateRefreshFunc(ctx, client, id), - Timeout: timeout, + Timeout: time.Until(deadline), } if _, err := stateConf.WaitForStateContext(ctx); err != nil { @@ -626,7 +633,11 @@ func waitForReplAuthorization(ctx context.Context, client *netapp.VolumesClient, return nil } -func waitForReplMirrorState(ctx context.Context, client *netapp.VolumesClient, id parse.VolumeId, timeout time.Duration, desiredState string) error { +func waitForReplMirrorState(ctx context.Context, client *netapp.VolumesClient, id parse.VolumeId, desiredState string) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } stateConf := &pluginsdk.StateChangeConf{ ContinuousTargetOccurence: 5, Delay: 10 * time.Second, @@ -634,17 +645,22 @@ func waitForReplMirrorState(ctx context.Context, client *netapp.VolumesClient, i Pending: []string{"200"}, // 200 means mirror state is still Mirrored Target: []string{"204"}, // 204 means mirror state is <> than Mirrored Refresh: netappVolumeReplicationMirrorStateRefreshFunc(ctx, client, id, desiredState), - Timeout: timeout, + Timeout: time.Until(deadline), } if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for NetApp Volume %q (Resource Group %q) to be in %s mirroring state: %+v", id.Name, id.ResourceGroup, desiredState, err) + return fmt.Errorf("waiting for %s to be in the state %q: %+v", id, desiredState, err) } return nil } -func waitForReplicationDeletion(ctx context.Context, client *netapp.VolumesClient, id parse.VolumeId, timeout time.Duration) error { +func waitForReplicationDeletion(ctx context.Context, client *netapp.VolumesClient, id parse.VolumeId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } + stateConf := &pluginsdk.StateChangeConf{ ContinuousTargetOccurence: 5, Delay: 10 * time.Second, @@ -652,17 +668,21 @@ func waitForReplicationDeletion(ctx context.Context, client *netapp.VolumesClien Pending: []string{"200", "202", "400"}, // TODO: Remove 400 when bug is fixed on RP side, where replicationStatus returns 400 while it is in "Deleting" state Target: []string{"404"}, Refresh: netappVolumeReplicationStateRefreshFunc(ctx, client, id), - Timeout: timeout, + Timeout: time.Until(deadline), } if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for NetApp Volume replication %q (Resource Group %q) to be deleted: %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for Replication of %s to be deleted: %+v", id, err) } return nil } -func waitForVolumeDeletion(ctx context.Context, client *netapp.VolumesClient, id parse.VolumeId, timeout time.Duration) error { +func waitForVolumeDeletion(ctx context.Context, client *netapp.VolumesClient, id parse.VolumeId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } stateConf := &pluginsdk.StateChangeConf{ ContinuousTargetOccurence: 5, Delay: 10 * time.Second, @@ -670,11 +690,11 @@ func waitForVolumeDeletion(ctx context.Context, client *netapp.VolumesClient, id Pending: []string{"200", "202"}, Target: []string{"204", "404"}, Refresh: netappVolumeStateRefreshFunc(ctx, client, id), - Timeout: timeout, + Timeout: time.Until(deadline), } if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for NetApp Volume Provisioning Service %q (Resource Group %q) to be deleted: %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for %s to be deleted: %+v", id, err) } return nil @@ -703,9 +723,6 @@ func netappVolumeReplicationMirrorStateRefreshFunc(ctx context.Context, client * return nil, "", fmt.Errorf("Invalid desired mirror state was passed to check mirror replication state (%s), possible values: (%+v)", desiredState, netapp.PossibleMirrorStateValues()) } - // Setting 200 as default response - response := 200 - res, err := client.ReplicationStatusMethod(ctx, id.ResourceGroup, id.NetAppAccountName, id.CapacityPoolName, id.Name) if err != nil { if !utils.ResponseWasNotFound(res.Response) { @@ -713,6 +730,9 @@ func netappVolumeReplicationMirrorStateRefreshFunc(ctx context.Context, client * } } + // TODO: fix this refresh function to use strings instead of fake status codes + // Setting 200 as default response + response := 200 if strings.EqualFold(string(res.MirrorState), desiredState) { // return 204 if state matches desired state response = 204 diff --git a/internal/services/privatedns/private_dns_a_record_resource.go b/internal/services/privatedns/private_dns_a_record_resource.go index b7702386be87..af7da3e60f28 100644 --- a/internal/services/privatedns/private_dns_a_record_resource.go +++ b/internal/services/privatedns/private_dns_a_record_resource.go @@ -90,7 +90,7 @@ func resourcePrivateDnsARecordCreateUpdate(d *pluginsdk.ResourceData, meta inter } if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_private_dns_a_record", *existing.ID) + return tf.ImportAsExistsError("azurerm_private_dns_a_record", resourceId.ID()) } } diff --git a/internal/services/privatedns/private_dns_aaaa_record_resource.go b/internal/services/privatedns/private_dns_aaaa_record_resource.go index 647d2425bc59..d798e16d3ede 100644 --- a/internal/services/privatedns/private_dns_aaaa_record_resource.go +++ b/internal/services/privatedns/private_dns_aaaa_record_resource.go @@ -86,7 +86,7 @@ func resourcePrivateDnsAaaaRecordCreateUpdate(d *pluginsdk.ResourceData, meta in } if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_private_dns_aaaa_record", *existing.ID) + return tf.ImportAsExistsError("azurerm_private_dns_aaaa_record", resourceId.ID()) } } diff --git a/internal/services/privatedns/private_dns_cname_record_resource.go b/internal/services/privatedns/private_dns_cname_record_resource.go index 0dd2b4bfc9d2..98c273e0fd19 100644 --- a/internal/services/privatedns/private_dns_cname_record_resource.go +++ b/internal/services/privatedns/private_dns_cname_record_resource.go @@ -92,7 +92,7 @@ func resourcePrivateDnsCNameRecordCreateUpdate(d *pluginsdk.ResourceData, meta i } if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_private_dns_cname_record", *existing.ID) + return tf.ImportAsExistsError("azurerm_private_dns_cname_record", resourceId.ID()) } } diff --git a/internal/services/privatedns/private_dns_mx_record_resource.go b/internal/services/privatedns/private_dns_mx_record_resource.go index 12011f5a2a7c..13450dd28d15 100644 --- a/internal/services/privatedns/private_dns_mx_record_resource.go +++ b/internal/services/privatedns/private_dns_mx_record_resource.go @@ -108,7 +108,7 @@ func resourcePrivateDnsMxRecordCreateUpdate(d *pluginsdk.ResourceData, meta inte } if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_private_dns_mx_record", *existing.ID) + return tf.ImportAsExistsError("azurerm_private_dns_mx_record", resourceId.ID()) } } diff --git a/internal/services/privatedns/private_dns_ptr_record_resource.go b/internal/services/privatedns/private_dns_ptr_record_resource.go index d5f07a3f2b34..19b367ca5068 100644 --- a/internal/services/privatedns/private_dns_ptr_record_resource.go +++ b/internal/services/privatedns/private_dns_ptr_record_resource.go @@ -93,7 +93,7 @@ func resourcePrivateDnsPtrRecordCreateUpdate(d *pluginsdk.ResourceData, meta int } if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_private_dns_ptr_record", *existing.ID) + return tf.ImportAsExistsError("azurerm_private_dns_ptr_record", resourceId.ID()) } } diff --git a/internal/services/search/search_service_resource.go b/internal/services/search/search_service_resource.go index 08ee87b0378f..5f9695854eeb 100644 --- a/internal/services/search/search_service_resource.go +++ b/internal/services/search/search_service_resource.go @@ -5,6 +5,8 @@ import ( "log" "time" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/Azure/azure-sdk-for-go/services/search/mgmt/2020-03-13/search" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" @@ -156,12 +158,25 @@ func resourceSearchService() *pluginsdk.Resource { func resourceSearchServiceCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).Search.ServicesClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) + id := parse.NewSearchServiceID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + if d.IsNewResource() { + existing, err := client.Get(ctx, id.ResourceGroup, id.Name, nil) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_search_service", id.ID()) + } + } + location := azure.NormalizeLocation(d.Get("location").(string)) - resourceGroup := d.Get("resource_group_name").(string) skuName := d.Get("sku").(string) publicNetworkAccess := search.Enabled @@ -171,19 +186,6 @@ func resourceSearchServiceCreateUpdate(d *pluginsdk.ResourceData, meta interface t := d.Get("tags").(map[string]interface{}) - if d.IsNewResource() { - existing, err := client.Get(ctx, resourceGroup, name, nil) - if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Search Service %q (ResourceGroup %q): %s", name, resourceGroup, err) - } - } - - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_search_service", *existing.ID) - } - } - properties := search.Service{ Location: utils.String(location), Sku: &search.Sku{ @@ -209,22 +211,16 @@ func resourceSearchServiceCreateUpdate(d *pluginsdk.ResourceData, meta interface properties.ServiceProperties.PartitionCount = utils.Int32(partitionCount) } - future, err := client.CreateOrUpdate(ctx, resourceGroup, name, properties, nil) + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, properties, nil) if err != nil { - return fmt.Errorf("issuing create/update request for Search Service %q (ResourceGroup %q): %s", name, resourceGroup, err) + return fmt.Errorf("creating/updating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for the completion of the creating/updating of Search Service %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - resp, err := client.Get(ctx, resourceGroup, name, nil) - if err != nil { - return fmt.Errorf("issuing get request for Search Service %q (ResourceGroup %q): %s", name, resourceGroup, err) + return fmt.Errorf("waiting for the creation/update of %s: %+v", id, err) } - d.SetId(*resp.ID) - + d.SetId(id.ID()) return resourceSearchServiceRead(d, meta) } @@ -241,19 +237,17 @@ func resourceSearchServiceRead(d *pluginsdk.ResourceData, meta interface{}) erro resp, err := client.Get(ctx, id.ResourceGroup, id.Name, nil) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] Error reading Search Service %q - removing from state", d.Id()) + log.Printf("[DEBUG] %s was not found - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("reading Search Service: %+v", err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } d.Set("name", id.Name) d.Set("resource_group_name", id.ResourceGroup) - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } + d.Set("location", location.NormalizeNilable(resp.Location)) if sku := resp.Sku; sku != nil { d.Set("sku", string(sku.Name)) @@ -309,7 +303,7 @@ func resourceSearchServiceDelete(d *pluginsdk.ResourceData, meta interface{}) er return nil } - return fmt.Errorf("deleting Search Service %q (resource group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil diff --git a/internal/services/securitycenter/azuresdkhacks/security_center_contact.go b/internal/services/securitycenter/azuresdkhacks/security_center_contact.go index a1fb668f11dd..4e28a2834ec1 100644 --- a/internal/services/securitycenter/azuresdkhacks/security_center_contact.go +++ b/internal/services/securitycenter/azuresdkhacks/security_center_contact.go @@ -10,7 +10,7 @@ import ( "github.com/Azure/go-autorest/autorest/validation" ) -func CreateSecurityCenterContact(client *security.ContactsClient, ctx context.Context, securityContactName string, securityContact security.Contact) (result security.Contact, err error) { +func CreateSecurityCenterContact(ctx context.Context, client *security.ContactsClient, securityContactName string, securityContact security.Contact) (result security.Contact, err error) { if err := validation.Validate([]validation.Validation{ { TargetValue: client.SubscriptionID, diff --git a/internal/services/securitycenter/azuresdkhacks/security_center_setting.go b/internal/services/securitycenter/azuresdkhacks/security_center_setting.go index 252794fc432e..ec5bf848cd64 100644 --- a/internal/services/securitycenter/azuresdkhacks/security_center_setting.go +++ b/internal/services/securitycenter/azuresdkhacks/security_center_setting.go @@ -10,7 +10,7 @@ import ( "github.com/Azure/go-autorest/autorest/azure" ) -func GetSecurityCenterSetting(client *security.SettingsClient, ctx context.Context, settingName string) (setting security.DataExportSettings, err error) { +func GetSecurityCenterSetting(ctx context.Context, client *security.SettingsClient, settingName string) (setting security.DataExportSettings, err error) { // NOTE: client.Get() returns security.Setting, which doesn't contain the "Enabled" property // https://github.com/Azure/azure-sdk-for-go/issues/12724 req, err := client.GetPreparer(ctx, settingName) diff --git a/internal/services/securitycenter/parse/automation.go b/internal/services/securitycenter/parse/automation.go new file mode 100644 index 000000000000..c862dea3e428 --- /dev/null +++ b/internal/services/securitycenter/parse/automation.go @@ -0,0 +1,69 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" +) + +type AutomationId struct { + SubscriptionId string + ResourceGroup string + Name string +} + +func NewAutomationID(subscriptionId, resourceGroup, name string) AutomationId { + return AutomationId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + Name: name, + } +} + +func (id AutomationId) String() string { + segments := []string{ + fmt.Sprintf("Name %q", id.Name), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Automation", segmentsStr) +} + +func (id AutomationId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Security/automations/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.Name) +} + +// AutomationID parses a Automation ID into an AutomationId struct +func AutomationID(input string) (*AutomationId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := AutomationId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.Name, err = id.PopSegment("automations"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/securitycenter/parse/automation_test.go b/internal/services/securitycenter/parse/automation_test.go new file mode 100644 index 000000000000..4613f1a5cfef --- /dev/null +++ b/internal/services/securitycenter/parse/automation_test.go @@ -0,0 +1,112 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = AutomationId{} + +func TestAutomationIDFormatter(t *testing.T) { + actual := NewAutomationID("12345678-1234-9876-4563-123456789012", "group1", "testAutomation1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Security/automations/testAutomation1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestAutomationID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *AutomationId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Security/", + Error: true, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Security/automations/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Security/automations/testAutomation1", + Expected: &AutomationId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "group1", + Name: "testAutomation1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.SECURITY/AUTOMATIONS/TESTAUTOMATION1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := AutomationID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + } +} diff --git a/internal/services/securitycenter/parse/contact.go b/internal/services/securitycenter/parse/contact.go new file mode 100644 index 000000000000..8082e653f488 --- /dev/null +++ b/internal/services/securitycenter/parse/contact.go @@ -0,0 +1,61 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" +) + +type ContactId struct { + SubscriptionId string + SecurityContactName string +} + +func NewContactID(subscriptionId, securityContactName string) ContactId { + return ContactId{ + SubscriptionId: subscriptionId, + SecurityContactName: securityContactName, + } +} + +func (id ContactId) String() string { + segments := []string{ + fmt.Sprintf("Security Contact Name %q", id.SecurityContactName), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Contact", segmentsStr) +} + +func (id ContactId) ID() string { + fmtString := "/subscriptions/%s/providers/Microsoft.Security/securityContacts/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.SecurityContactName) +} + +// ContactID parses a Contact ID into an ContactId struct +func ContactID(input string) (*ContactId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := ContactId{ + SubscriptionId: id.SubscriptionID, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.SecurityContactName, err = id.PopSegment("securityContacts"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/securitycenter/parse/contact_test.go b/internal/services/securitycenter/parse/contact_test.go new file mode 100644 index 000000000000..660f67de5199 --- /dev/null +++ b/internal/services/securitycenter/parse/contact_test.go @@ -0,0 +1,96 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = ContactId{} + +func TestContactIDFormatter(t *testing.T) { + actual := NewContactID("12345678-1234-9876-4563-123456789012", "contact1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/securityContacts/contact1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestContactID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *ContactId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing SecurityContactName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/", + Error: true, + }, + + { + // missing value for SecurityContactName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/securityContacts/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/securityContacts/contact1", + Expected: &ContactId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + SecurityContactName: "contact1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.SECURITY/SECURITYCONTACTS/CONTACT1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := ContactID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.SecurityContactName != v.Expected.SecurityContactName { + t.Fatalf("Expected %q but got %q for SecurityContactName", v.Expected.SecurityContactName, actual.SecurityContactName) + } + } +} diff --git a/internal/services/securitycenter/parse/pricing.go b/internal/services/securitycenter/parse/pricing.go new file mode 100644 index 000000000000..ad86e193c1dc --- /dev/null +++ b/internal/services/securitycenter/parse/pricing.go @@ -0,0 +1,61 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" +) + +type PricingId struct { + SubscriptionId string + Name string +} + +func NewPricingID(subscriptionId, name string) PricingId { + return PricingId{ + SubscriptionId: subscriptionId, + Name: name, + } +} + +func (id PricingId) String() string { + segments := []string{ + fmt.Sprintf("Name %q", id.Name), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Pricing", segmentsStr) +} + +func (id PricingId) ID() string { + fmtString := "/subscriptions/%s/providers/Microsoft.Security/pricings/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.Name) +} + +// PricingID parses a Pricing ID into an PricingId struct +func PricingID(input string) (*PricingId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := PricingId{ + SubscriptionId: id.SubscriptionID, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.Name, err = id.PopSegment("pricings"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/securitycenter/parse/pricing_test.go b/internal/services/securitycenter/parse/pricing_test.go new file mode 100644 index 000000000000..81bdf01783b4 --- /dev/null +++ b/internal/services/securitycenter/parse/pricing_test.go @@ -0,0 +1,96 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = PricingId{} + +func TestPricingIDFormatter(t *testing.T) { + actual := NewPricingID("12345678-1234-9876-4563-123456789012", "pricing1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/pricings/pricing1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestPricingID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *PricingId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/", + Error: true, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/pricings/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/pricings/pricing1", + Expected: &PricingId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + Name: "pricing1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.SECURITY/PRICINGS/PRICING1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := PricingID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + } +} diff --git a/internal/services/securitycenter/parse/security_center_automation.go b/internal/services/securitycenter/parse/security_center_automation.go deleted file mode 100644 index 07f4c6ac04ff..000000000000 --- a/internal/services/securitycenter/parse/security_center_automation.go +++ /dev/null @@ -1,33 +0,0 @@ -package parse - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" -) - -type SecurityCenterAutomationId struct { - AutomationName string - ResourceGroup string -} - -func SecurityCenterAutomationID(input string) (*SecurityCenterAutomationId, error) { - id, err := azure.ParseAzureResourceID(input) - if err != nil { - return nil, fmt.Errorf("Unable to parse Security Center Automation ID %q: %+v", input, err) - } - - automation := SecurityCenterAutomationId{ - ResourceGroup: id.ResourceGroup, - } - - if automation.AutomationName, err = id.PopSegment("automations"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &automation, nil -} diff --git a/internal/services/securitycenter/parse/security_center_automation_test.go b/internal/services/securitycenter/parse/security_center_automation_test.go deleted file mode 100644 index 69dd1ab404a5..000000000000 --- a/internal/services/securitycenter/parse/security_center_automation_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package parse - -import "testing" - -func TestSecurityCentreAutomationID(t *testing.T) { - testData := []struct { - Name string - Input string - Error bool - Expect *SecurityCenterAutomationId - }{ - { - Name: "Empty", - Input: "", - Error: true, - }, - { - Name: "No Resource Group", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/", - Error: true, - }, - { - Name: "No Automation Segment", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRG1/providers/Microsoft.Security/", - Error: true, - }, - { - Name: "No Automation Value", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRG1/providers/Microsoft.Security/automations/", - Error: true, - }, - { - Name: "Security Center Automation ID", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRG1/providers/Microsoft.Security/automations/testAutomation", - Expect: &SecurityCenterAutomationId{ - ResourceGroup: "testRG1", - AutomationName: "testAutomation", - }, - }, - { - Name: "Wrong Case", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRG1/providers/Microsoft.Security/Automations/testAutomation", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Name) - - actual, err := SecurityCenterAutomationID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expected a value but got an error: %s", err) - } - - if actual.AutomationName != v.Expect.AutomationName { - t.Fatalf("Expected %q but got %q for Automation Name", v.Expect.AutomationName, actual.AutomationName) - } - - if actual.ResourceGroup != v.Expect.ResourceGroup { - t.Fatalf("Expected %q but got %q for Resource Group Name", v.Expect.ResourceGroup, actual.ResourceGroup) - } - } -} diff --git a/internal/services/securitycenter/parse/security_center_setting.go b/internal/services/securitycenter/parse/security_center_setting.go deleted file mode 100644 index 1264823d91e6..000000000000 --- a/internal/services/securitycenter/parse/security_center_setting.go +++ /dev/null @@ -1,30 +0,0 @@ -package parse - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" -) - -type SecurityCenterSettingId struct { - SettingName string -} - -func SecurityCenterSettingID(input string) (*SecurityCenterSettingId, error) { - id, err := azure.ParseAzureResourceID(input) - if err != nil { - return nil, fmt.Errorf("Unable to parse Security Center Setting ID %q: %+v", input, err) - } - - setting := SecurityCenterSettingId{} - - if setting.SettingName, err = id.PopSegment("settings"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &setting, nil -} diff --git a/internal/services/securitycenter/parse/security_center_setting_test.go b/internal/services/securitycenter/parse/security_center_setting_test.go deleted file mode 100644 index f3348a7d52bf..000000000000 --- a/internal/services/securitycenter/parse/security_center_setting_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package parse - -import ( - "testing" -) - -func TestSecurityCenterSettingID(t *testing.T) { - testData := []struct { - Name string - Input string - Error bool - Expect *SecurityCenterSettingId - }{ - { - Name: "Empty", - Input: "", - Error: true, - }, - { - Name: "No Settings Segment", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000", - Error: true, - }, - { - Name: "No Settings Value", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/settings/", - Error: true, - }, - { - Name: "Security Center Setting ID", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/settings/MCAS", - Expect: &SecurityCenterSettingId{ - SettingName: "MCAS", - }, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Name) - - actual, err := SecurityCenterSettingID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expected a value but got an error: %s", err) - } - - if actual.SettingName != v.Expect.SettingName { - t.Fatalf("Expected %q but got %q for Name", v.Expect.SettingName, actual.SettingName) - } - } -} diff --git a/internal/services/securitycenter/parse/security_center_subscription_pricing.go b/internal/services/securitycenter/parse/security_center_subscription_pricing.go deleted file mode 100644 index 19a216100850..000000000000 --- a/internal/services/securitycenter/parse/security_center_subscription_pricing.go +++ /dev/null @@ -1,30 +0,0 @@ -package parse - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" -) - -type SecurityCenterSubscriptionPricingId struct { - ResourceType string -} - -func SecurityCenterSubscriptionPricingID(input string) (*SecurityCenterSubscriptionPricingId, error) { - id, err := azure.ParseAzureResourceID(input) - if err != nil { - return nil, fmt.Errorf("unable to parse Security Center Subscription Pricing ID %q: %+v", input, err) - } - - pricing := SecurityCenterSubscriptionPricingId{} - - if pricing.ResourceType, err = id.PopSegment("pricings"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &pricing, nil -} diff --git a/internal/services/securitycenter/parse/security_center_subscription_pricing_test.go b/internal/services/securitycenter/parse/security_center_subscription_pricing_test.go deleted file mode 100644 index 7a362237ba12..000000000000 --- a/internal/services/securitycenter/parse/security_center_subscription_pricing_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package parse - -import ( - "testing" -) - -func TestSecurityCenterSubscriptionPricingID(t *testing.T) { - testData := []struct { - ResourceType string - Input string - Error bool - Expect *SecurityCenterSubscriptionPricingId - }{ - { - ResourceType: "Empty", - Input: "", - Error: true, - }, - { - ResourceType: "No Pricings Segment", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000", - Error: true, - }, - { - ResourceType: "No Pricings Value", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/pricings/", - Error: true, - }, - { - ResourceType: "Security Center Subscription Pricing ID", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/pricings/VirtualMachines", - Expect: &SecurityCenterSubscriptionPricingId{ - ResourceType: "VirtualMachines", - }, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.ResourceType) - - actual, err := SecurityCenterSubscriptionPricingID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expected a value but got an error: %s", err) - } - - if actual.ResourceType != v.Expect.ResourceType { - t.Fatalf("Expected %q but got %q for ResourceType", v.Expect.ResourceType, actual.ResourceType) - } - } -} diff --git a/internal/services/securitycenter/parse/setting.go b/internal/services/securitycenter/parse/setting.go new file mode 100644 index 000000000000..49f06be9a16f --- /dev/null +++ b/internal/services/securitycenter/parse/setting.go @@ -0,0 +1,61 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" +) + +type SettingId struct { + SubscriptionId string + Name string +} + +func NewSettingID(subscriptionId, name string) SettingId { + return SettingId{ + SubscriptionId: subscriptionId, + Name: name, + } +} + +func (id SettingId) String() string { + segments := []string{ + fmt.Sprintf("Name %q", id.Name), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Setting", segmentsStr) +} + +func (id SettingId) ID() string { + fmtString := "/subscriptions/%s/providers/Microsoft.Security/settings/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.Name) +} + +// SettingID parses a Setting ID into an SettingId struct +func SettingID(input string) (*SettingId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := SettingId{ + SubscriptionId: id.SubscriptionID, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.Name, err = id.PopSegment("settings"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/securitycenter/parse/setting_test.go b/internal/services/securitycenter/parse/setting_test.go new file mode 100644 index 000000000000..22dcd4828355 --- /dev/null +++ b/internal/services/securitycenter/parse/setting_test.go @@ -0,0 +1,96 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = SettingId{} + +func TestSettingIDFormatter(t *testing.T) { + actual := NewSettingID("12345678-1234-9876-4563-123456789012", "setting1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/settings/setting1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestSettingID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *SettingId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/", + Error: true, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/settings/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/settings/setting1", + Expected: &SettingId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + Name: "setting1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.SECURITY/SETTINGS/SETTING1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := SettingID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + } +} diff --git a/internal/services/securitycenter/parse/workspace.go b/internal/services/securitycenter/parse/workspace.go new file mode 100644 index 000000000000..2058ae5d87c6 --- /dev/null +++ b/internal/services/securitycenter/parse/workspace.go @@ -0,0 +1,61 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" +) + +type WorkspaceId struct { + SubscriptionId string + WorkspaceSettingName string +} + +func NewWorkspaceID(subscriptionId, workspaceSettingName string) WorkspaceId { + return WorkspaceId{ + SubscriptionId: subscriptionId, + WorkspaceSettingName: workspaceSettingName, + } +} + +func (id WorkspaceId) String() string { + segments := []string{ + fmt.Sprintf("Workspace Setting Name %q", id.WorkspaceSettingName), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Workspace", segmentsStr) +} + +func (id WorkspaceId) ID() string { + fmtString := "/subscriptions/%s/providers/Microsoft.Security/workspaceSettings/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.WorkspaceSettingName) +} + +// WorkspaceID parses a Workspace ID into an WorkspaceId struct +func WorkspaceID(input string) (*WorkspaceId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := WorkspaceId{ + SubscriptionId: id.SubscriptionID, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.WorkspaceSettingName, err = id.PopSegment("workspaceSettings"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/securitycenter/parse/workspace_test.go b/internal/services/securitycenter/parse/workspace_test.go new file mode 100644 index 000000000000..8b5dfe3d7534 --- /dev/null +++ b/internal/services/securitycenter/parse/workspace_test.go @@ -0,0 +1,96 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = WorkspaceId{} + +func TestWorkspaceIDFormatter(t *testing.T) { + actual := NewWorkspaceID("12345678-1234-9876-4563-123456789012", "workspace1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/workspaceSettings/workspace1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestWorkspaceID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *WorkspaceId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing WorkspaceSettingName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/", + Error: true, + }, + + { + // missing value for WorkspaceSettingName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/workspaceSettings/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/workspaceSettings/workspace1", + Expected: &WorkspaceId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + WorkspaceSettingName: "workspace1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.SECURITY/WORKSPACESETTINGS/WORKSPACE1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := WorkspaceID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.WorkspaceSettingName != v.Expected.WorkspaceSettingName { + t.Fatalf("Expected %q but got %q for WorkspaceSettingName", v.Expected.WorkspaceSettingName, actual.WorkspaceSettingName) + } + } +} diff --git a/internal/services/securitycenter/resourceids.go b/internal/services/securitycenter/resourceids.go index 8af2af3de31c..b0b8000a760f 100644 --- a/internal/services/securitycenter/resourceids.go +++ b/internal/services/securitycenter/resourceids.go @@ -1,4 +1,10 @@ package securitycenter +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Automation -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Security/automations/testAutomation1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Contact -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/securityContacts/contact1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Pricing -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/pricings/pricing1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Setting -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/settings/setting1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Workspace -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/workspaceSettings/workspace1 + //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=AssessmentMetadata -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/assessmentMetadata/metadata1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=IotSecuritySolution -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Security/IoTSecuritySolutions/solution1 diff --git a/internal/services/securitycenter/security_center_automation_resource.go b/internal/services/securitycenter/security_center_automation_resource.go index d78b3e355bbf..ebf0c259343a 100644 --- a/internal/services/securitycenter/security_center_automation_resource.go +++ b/internal/services/securitycenter/security_center_automation_resource.go @@ -6,6 +6,8 @@ import ( "strings" "time" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/Azure/azure-sdk-for-go/services/preview/security/mgmt/v3.0/security" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" @@ -29,8 +31,10 @@ func resourceSecurityCenterAutomation() *pluginsdk.Resource { Update: resourceSecurityCenterAutomationCreateUpdate, Delete: resourceSecurityCenterAutomationDelete, - // TODO: replace this with an importer which validates the ID during import - Importer: pluginsdk.DefaultImporter(), + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.AutomationID(id) + return err + }), Timeouts: &pluginsdk.ResourceTimeout{ Create: pluginsdk.DefaultTimeout(30 * time.Minute), @@ -195,27 +199,25 @@ func resourceSecurityCenterAutomation() *pluginsdk.Resource { func resourceSecurityCenterAutomationCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).SecurityCenter.AutomationsClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - // Core resource props - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) - location := azure.NormalizeLocation(d.Get("location").(string)) - + id := parse.NewAutomationID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, resourceGroup, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Security Center automation %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_security_center_automation", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_security_center_automation", id.ID()) } } + location := azure.NormalizeLocation(d.Get("location").(string)) enabled := d.Get("enabled").(bool) // Build automation struct @@ -241,13 +243,11 @@ func resourceSecurityCenterAutomationCreateUpdate(d *pluginsdk.ResourceData, met return err } - resp, err := client.CreateOrUpdate(ctx, resourceGroup, name, automation) - if err != nil { - return fmt.Errorf("creating Security Center automation: %+v", err) + if _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, automation); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) } - // Important steps - d.SetId(*resp.ID) + d.SetId(id.ID()) return resourceSecurityCenterAutomationRead(d, meta) } @@ -256,30 +256,25 @@ func resourceSecurityCenterAutomationRead(d *pluginsdk.ResourceData, meta interf ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.SecurityCenterAutomationID(d.Id()) + id, err := parse.AutomationID(d.Id()) if err != nil { return err } - resourceGroup := id.ResourceGroup - name := id.AutomationName - - resp, err := client.Get(ctx, resourceGroup, name) + resp, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] Error reading Security Center automation %q - removing from state", d.Id()) + log.Printf("[INFO] %s was not found - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("reading Security Center automation %s: %v", name, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", name) - d.Set("resource_group_name", resourceGroup) - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } + d.Set("name", id.Name) + d.Set("resource_group_name", id.ResourceGroup) + d.Set("location", location.NormalizeNilable(resp.Location)) if properties := resp.AutomationProperties; properties != nil { d.Set("description", properties.Description) @@ -318,21 +313,13 @@ func resourceSecurityCenterAutomationDelete(d *pluginsdk.ResourceData, meta inte ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.SecurityCenterAutomationID(d.Id()) + id, err := parse.AutomationID(d.Id()) if err != nil { return err } - resourceGroup := id.ResourceGroup - name := id.AutomationName - - resp, err := client.Delete(ctx, resourceGroup, name) - if err != nil { - if utils.ResponseWasNotFound(resp) { - log.Printf("[DEBUG] Security Center automation was not found: %v", err) - return nil - } - return fmt.Errorf("deleting Security Center automation: %+v", err) + if _, err := client.Delete(ctx, id.ResourceGroup, id.Name); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil diff --git a/internal/services/securitycenter/security_center_automation_resource_test.go b/internal/services/securitycenter/security_center_automation_resource_test.go index 1526be4220c5..1ad6993d0c57 100644 --- a/internal/services/securitycenter/security_center_automation_resource_test.go +++ b/internal/services/securitycenter/security_center_automation_resource_test.go @@ -207,14 +207,14 @@ func TestAccSecurityCenterAutomation_sourceMulti(t *testing.T) { } func (t SecurityCenterAutomationResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.SecurityCenterAutomationID(state.ID) + id, err := parse.AutomationID(state.ID) if err != nil { return nil, err } - resp, err := clients.SecurityCenter.AutomationsClient.Get(ctx, id.ResourceGroup, id.AutomationName) + resp, err := clients.SecurityCenter.AutomationsClient.Get(ctx, id.ResourceGroup, id.Name) if err != nil { - return nil, fmt.Errorf("reading Security Center automation %q (resource group: %q): %v", id.AutomationName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } return utils.Bool(resp.AutomationProperties != nil), nil diff --git a/internal/services/securitycenter/security_center_contact_resource.go b/internal/services/securitycenter/security_center_contact_resource.go index 42e670482187..3ea8c076a286 100644 --- a/internal/services/securitycenter/security_center_contact_resource.go +++ b/internal/services/securitycenter/security_center_contact_resource.go @@ -5,6 +5,8 @@ import ( "log" "time" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/securitycenter/parse" + "github.com/Azure/azure-sdk-for-go/services/preview/security/mgmt/v3.0/security" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -28,8 +30,10 @@ func resourceSecurityCenterContact() *pluginsdk.Resource { Update: resourceSecurityCenterContactCreateUpdate, Delete: resourceSecurityCenterContactDelete, - // TODO: replace this with an importer which validates the ID during import - Importer: pluginsdk.DefaultImporter(), + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.ContactID(id) + return err + }), Timeouts: &pluginsdk.ResourceTimeout{ Create: pluginsdk.DefaultTimeout(60 * time.Minute), @@ -65,22 +69,23 @@ func resourceSecurityCenterContact() *pluginsdk.Resource { } func resourceSecurityCenterContactCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + // TODO: split this Create/Update client := meta.(*clients.Client).SecurityCenter.ContactsClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := securityCenterContactName - + id := parse.NewContactID(subscriptionId, securityCenterContactName) if d.IsNewResource() { - existing, err := client.Get(ctx, name) + existing, err := client.Get(ctx, id.SubscriptionId) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("Checking for presence of existing Security Center Contact: %+v", err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_security_center_contact", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_security_center_contact", id.ID()) } } @@ -106,20 +111,12 @@ func resourceSecurityCenterContactCreateUpdate(d *pluginsdk.ResourceData, meta i if d.IsNewResource() { // TODO: switch back when the Swagger/API bug has been fixed: // https://github.com/Azure/azure-rest-api-specs/issues/10717 (an undefined 201) - if _, err := azuresdkhacks.CreateSecurityCenterContact(client, ctx, name, contact); err != nil { + if _, err := azuresdkhacks.CreateSecurityCenterContact(ctx, client, id.SecurityContactName, contact); err != nil { return fmt.Errorf("Creating Security Center Contact: %+v", err) } - resp, err := client.Get(ctx, name) - if err != nil { - return fmt.Errorf("Reading Security Center Contact: %+v", err) - } - if resp.ID == nil { - return fmt.Errorf("Security Center Contact ID is nil") - } - - d.SetId(*resp.ID) - } else if _, err := client.Update(ctx, name, contact); err != nil { + d.SetId(id.ID()) + } else if _, err := client.Update(ctx, id.SecurityContactName, contact); err != nil { return fmt.Errorf("Updating Security Center Contact: %+v", err) } @@ -131,24 +128,27 @@ func resourceSecurityCenterContactRead(d *pluginsdk.ResourceData, meta interface ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - name := securityCenterContactName + id, err := parse.ContactID(d.Id()) + if err != nil { + return err + } - resp, err := client.Get(ctx, name) + resp, err := client.Get(ctx, id.SecurityContactName) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[DEBUG] Security Center Subscription Contact was not found: %v", err) + log.Printf("[DEBUG] %s was not found - removing from state!", id) d.SetId("") return nil } - return fmt.Errorf("Reading Security Center Contact: %+v", err) + return fmt.Errorf("retrieving %s: %+v", id, err) } - if properties := resp.ContactProperties; properties != nil { - d.Set("email", properties.Email) - d.Set("phone", properties.Phone) - d.Set("alert_notifications", properties.AlertNotifications == security.On) - d.Set("alerts_to_admins", properties.AlertsToAdmins == security.AlertsToAdminsOn) + if props := resp.ContactProperties; props != nil { + d.Set("email", props.Email) + d.Set("phone", props.Phone) + d.Set("alert_notifications", props.AlertNotifications == security.On) + d.Set("alerts_to_admins", props.AlertsToAdmins == security.AlertsToAdminsOn) } return nil @@ -159,16 +159,13 @@ func resourceSecurityCenterContactDelete(d *pluginsdk.ResourceData, meta interfa ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - name := securityCenterContactName - - resp, err := client.Delete(ctx, name) + id, err := parse.ContactID(d.Id()) if err != nil { - if utils.ResponseWasNotFound(resp) { - log.Printf("[DEBUG] Security Center Subscription Contact was not found: %v", err) - return nil - } + return err + } - return fmt.Errorf("Deleting Security Center Contact: %+v", err) + if _, err := client.Delete(ctx, id.SecurityContactName); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil diff --git a/internal/services/securitycenter/security_center_setting_resource.go b/internal/services/securitycenter/security_center_setting_resource.go index 169b3bfa47a2..b679316c86c9 100644 --- a/internal/services/securitycenter/security_center_setting_resource.go +++ b/internal/services/securitycenter/security_center_setting_resource.go @@ -14,6 +14,8 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" ) +// TODO: this resource should be split into data_export_setting and alert_sync_setting + func resourceSecurityCenterSetting() *pluginsdk.Resource { return &pluginsdk.Resource{ Create: resourceSecurityCenterSettingUpdate, @@ -21,8 +23,10 @@ func resourceSecurityCenterSetting() *pluginsdk.Resource { Update: resourceSecurityCenterSettingUpdate, Delete: resourceSecurityCenterSettingDelete, - // TODO: replace this with an importer which validates the ID during import - Importer: pluginsdk.DefaultImporter(), + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.SettingID(id) + return err + }), Timeouts: &pluginsdk.ResourceTimeout{ Create: pluginsdk.DefaultTimeout(10 * time.Minute), @@ -50,10 +54,13 @@ func resourceSecurityCenterSetting() *pluginsdk.Resource { func resourceSecurityCenterSettingUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).SecurityCenter.SettingClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - settingName := d.Get("setting_name").(string) + // TODO: requires import if it's enabled + + id := parse.NewSettingID(subscriptionId, d.Get("setting_name").(string)) enabled := d.Get("enabled").(bool) setting := security.DataExportSettings{ DataExportSettingProperties: &security.DataExportSettingProperties{ @@ -62,22 +69,11 @@ func resourceSecurityCenterSettingUpdate(d *pluginsdk.ResourceData, meta interfa Kind: security.KindDataExportSettings, } - if _, err := client.Update(ctx, settingName, setting); err != nil { - return fmt.Errorf("Creating/updating Security Center pricing: %+v", err) - } - // TODO: switch to back when Swagger/API bug has been fixed: - // https://github.com/Azure/azure-sdk-for-go/issues/12724 - resp, err := azuresdkhacks.GetSecurityCenterSetting(client, ctx, settingName) - if err != nil { - return fmt.Errorf("Reading Security Center setting: %+v", err) - } - - if resp.ID == nil || *resp.ID == "" { - return fmt.Errorf("Nil/empty ID returned for Security Center setting %q", settingName) + if _, err := client.Update(ctx, id.Name, setting); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) } - d.SetId(*resp.ID) - + d.SetId(id.ID()) return resourceSecurityCenterSettingRead(d, meta) } @@ -86,30 +82,29 @@ func resourceSecurityCenterSettingRead(d *pluginsdk.ResourceData, meta interface ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.SecurityCenterSettingID(d.Id()) + id, err := parse.SettingID(d.Id()) if err != nil { return err } + // TODO: switch to back when Swagger/API bug has been fixed: // https://github.com/Azure/azure-sdk-for-go/issues/12724 (`Enabled` field missing) - resp, err := azuresdkhacks.GetSecurityCenterSetting(client, ctx, id.SettingName) - if err != nil { - return fmt.Errorf("Reading Security Center setting: %+v", err) - } - + resp, err := azuresdkhacks.GetSecurityCenterSetting(ctx, client, id.Name) if err != nil { - return fmt.Errorf("Reading Security Center setting: %+v", err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } if properties := resp.DataExportSettingProperties; properties != nil { d.Set("enabled", properties.Enabled) } - d.Set("setting_name", id.SettingName) + d.Set("setting_name", id.Name) return nil } func resourceSecurityCenterSettingDelete(_ *pluginsdk.ResourceData, _ interface{}) error { + // TODO: disable this + log.Printf("[DEBUG] Security Center deletion invocation") return nil // cannot be deleted. } diff --git a/internal/services/securitycenter/security_center_setting_resource_test.go b/internal/services/securitycenter/security_center_setting_resource_test.go index aff3046cba18..db366f7dfda2 100644 --- a/internal/services/securitycenter/security_center_setting_resource_test.go +++ b/internal/services/securitycenter/security_center_setting_resource_test.go @@ -62,14 +62,14 @@ func TestAccSecurityCenterSetting_update(t *testing.T) { } func (SecurityCenterSettingResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.SecurityCenterSettingID(state.ID) + id, err := parse.SettingID(state.ID) if err != nil { return nil, err } - resp, err := clients.SecurityCenter.SettingClient.Get(ctx, id.SettingName) + resp, err := clients.SecurityCenter.SettingClient.Get(ctx, id.Name) if err != nil { - return nil, fmt.Errorf("reading Security Center Setting (%s): %+v", id.SettingName, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } return utils.Bool(resp.Value != nil), nil diff --git a/internal/services/securitycenter/security_center_subscription_pricing_resource.go b/internal/services/securitycenter/security_center_subscription_pricing_resource.go index 898eb3100d6a..598d23f43450 100644 --- a/internal/services/securitycenter/security_center_subscription_pricing_resource.go +++ b/internal/services/securitycenter/security_center_subscription_pricing_resource.go @@ -23,7 +23,7 @@ func resourceSecurityCenterSubscriptionPricing() *pluginsdk.Resource { Delete: resourceSecurityCenterSubscriptionPricingDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.SecurityCenterSubscriptionPricingID(id) + _, err := parse.PricingID(id) return err }), @@ -72,34 +72,24 @@ func resourceSecurityCenterSubscriptionPricing() *pluginsdk.Resource { func resourceSecurityCenterSubscriptionPricingUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).SecurityCenter.PricingClient - ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - // not doing import check as afaik it always exists (cannot be deleted) - // all this resource does is flip a boolean + // TODO: add a requires import check ensuring this is != Free (meaning we should likely remove Free as a SKU option?) + id := parse.NewPricingID(subscriptionId, d.Get("resource_type").(string)) pricing := security.Pricing{ PricingProperties: &security.PricingProperties{ PricingTier: security.PricingTier(d.Get("tier").(string)), }, } - resource_type := d.Get("resource_type").(string) - - if _, err := client.Update(ctx, resource_type, pricing); err != nil { - return fmt.Errorf("Creating/updating Security Center Subscription pricing: %+v", err) - } - - resp, err := client.Get(ctx, resource_type) - if err != nil { - return fmt.Errorf("Reading Security Center Subscription pricing: %+v", err) + if _, err := client.Update(ctx, id.Name, pricing); err != nil { + return fmt.Errorf("setting %s: %+v", id, err) } - if resp.ID == nil { - return fmt.Errorf("Security Center Subscription pricing ID is nil") - } - - d.SetId(*resp.ID) + d.SetId(id.ID()) return resourceSecurityCenterSubscriptionPricingRead(d, meta) } @@ -108,31 +98,33 @@ func resourceSecurityCenterSubscriptionPricingRead(d *pluginsdk.ResourceData, me ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.SecurityCenterSubscriptionPricingID(d.Id()) + id, err := parse.PricingID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceType) + resp, err := client.Get(ctx, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[DEBUG] %q Security Center Subscription was not found: %v", id.ResourceType, err) + log.Printf("[DEBUG] %s was not found - removing from state!", *id) d.SetId("") return nil } - return fmt.Errorf("Reading %q Security Center Subscription pricing: %+v", id.ResourceType, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } + d.Set("resource_type", id.Name) if properties := resp.PricingProperties; properties != nil { d.Set("tier", properties.PricingTier) } - d.Set("resource_type", id.ResourceType) return nil } func resourceSecurityCenterSubscriptionPricingDelete(_ *pluginsdk.ResourceData, _ interface{}) error { + // TODO: reset this back to Free + log.Printf("[DEBUG] Security Center Subscription deletion invocation") - return nil // cannot be deleted. + return nil } diff --git a/internal/services/securitycenter/security_center_subscription_pricing_resource_test.go b/internal/services/securitycenter/security_center_subscription_pricing_resource_test.go index c94b62733dc2..30b7f606211b 100644 --- a/internal/services/securitycenter/security_center_subscription_pricing_resource_test.go +++ b/internal/services/securitycenter/security_center_subscription_pricing_resource_test.go @@ -42,14 +42,14 @@ func TestAccSecurityCenterSubscriptionPricing_update(t *testing.T) { } func (SecurityCenterSubscriptionPricingResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.SecurityCenterSubscriptionPricingID(state.ID) + id, err := parse.PricingID(state.ID) if err != nil { return nil, err } - resp, err := clients.SecurityCenter.PricingClient.Get(ctx, id.ResourceType) + resp, err := clients.SecurityCenter.PricingClient.Get(ctx, id.Name) if err != nil { - return nil, fmt.Errorf("reading Security Center Subscription Pricing (%s): %+v", id.ResourceType, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } return utils.Bool(resp.PricingProperties != nil), nil diff --git a/internal/services/securitycenter/security_center_workspace_resource.go b/internal/services/securitycenter/security_center_workspace_resource.go index 89a86a835cc7..a79edc81fbf5 100644 --- a/internal/services/securitycenter/security_center_workspace_resource.go +++ b/internal/services/securitycenter/security_center_workspace_resource.go @@ -5,11 +5,13 @@ import ( "log" "time" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/securitycenter/parse" + "github.com/Azure/azure-sdk-for-go/services/preview/security/mgmt/v3.0/security" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/parse" + logAnalyticsParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -28,8 +30,10 @@ func resourceSecurityCenterWorkspace() *pluginsdk.Resource { Update: resourceSecurityCenterWorkspaceCreateUpdate, Delete: resourceSecurityCenterWorkspaceDelete, - // TODO: replace this with an importer which validates the ID during import - Importer: pluginsdk.DefaultImporter(), + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.WorkspaceID(id) + return err + }), Timeouts: &pluginsdk.ResourceTimeout{ Create: pluginsdk.DefaultTimeout(60 * time.Minute), @@ -58,26 +62,28 @@ func resourceSecurityCenterWorkspace() *pluginsdk.Resource { } func resourceSecurityCenterWorkspaceCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + // TODO: split this create/update + client := meta.(*clients.Client).SecurityCenter.WorkspaceClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := securityCenterWorkspaceName - + id := parse.NewWorkspaceID(subscriptionId, securityCenterWorkspaceName) if d.IsNewResource() { - existing, err := client.Get(ctx, name) + existing, err := client.Get(ctx, id.WorkspaceSettingName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("Checking for presence of existing Security Center Workspace: %+v", err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_security_center_workspace", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_security_center_workspace", id.ID()) } } - workspaceID, err := parse.LogAnalyticsWorkspaceID(d.Get("workspace_id").(string)) + logAnalyticsWorkspaceId, err := logAnalyticsParse.LogAnalyticsWorkspaceID(d.Get("workspace_id").(string)) if err != nil { return err } @@ -85,25 +91,30 @@ func resourceSecurityCenterWorkspaceCreateUpdate(d *pluginsdk.ResourceData, meta contact := security.WorkspaceSetting{ WorkspaceSettingProperties: &security.WorkspaceSettingProperties{ Scope: utils.String(d.Get("scope").(string)), - WorkspaceID: utils.String(workspaceID.ID()), + WorkspaceID: utils.String(logAnalyticsWorkspaceId.ID()), }, } if d.IsNewResource() { - if _, err = client.Create(ctx, name, contact); err != nil { + if _, err = client.Create(ctx, id.WorkspaceSettingName, contact); err != nil { return fmt.Errorf("Creating Security Center Workspace: %+v", err) } - } else if _, err = client.Update(ctx, name, contact); err != nil { + } else if _, err = client.Update(ctx, id.WorkspaceSettingName, contact); err != nil { return fmt.Errorf("Updating Security Center Workspace: %+v", err) } // api returns "" for workspace id after an create/update and eventually the new value + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } stateConf := &pluginsdk.StateChangeConf{ Pending: []string{"Waiting"}, Target: []string{"Populated"}, MinTimeout: 30 * time.Second, + Timeout: time.Until(deadline), Refresh: func() (interface{}, string, error) { - resp, err2 := client.Get(ctx, name) + resp, err2 := client.Get(ctx, id.WorkspaceSettingName) if err2 != nil { return resp, "Error", fmt.Errorf("Reading Security Center Workspace: %+v", err2) } @@ -118,19 +129,12 @@ func resourceSecurityCenterWorkspaceCreateUpdate(d *pluginsdk.ResourceData, meta }, } - if d.IsNewResource() { - stateConf.Timeout = d.Timeout(pluginsdk.TimeoutCreate) - } else { - stateConf.Timeout = d.Timeout(pluginsdk.TimeoutUpdate) - } - - resp, err := stateConf.WaitForStateContext(ctx) - if err != nil { - return fmt.Errorf("Waiting: %+v", err) + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for creation/update of %s: %+v", id, err) } if d.IsNewResource() { - d.SetId(*resp.(security.WorkspaceSetting).ID) + d.SetId(id.ID()) } return resourceSecurityCenterWorkspaceRead(d, meta) @@ -156,7 +160,7 @@ func resourceSecurityCenterWorkspaceRead(d *pluginsdk.ResourceData, meta interfa d.Set("scope", properties.Scope) workspaceId := "" if properties.WorkspaceID != nil { - id, err := parse.LogAnalyticsWorkspaceID(*properties.WorkspaceID) + id, err := logAnalyticsParse.LogAnalyticsWorkspaceID(*properties.WorkspaceID) if err != nil { return fmt.Errorf("Reading Security Center Log Analytics Workspace ID: %+v", err) } diff --git a/internal/services/securitycenter/security_center_workspace_resource_test.go b/internal/services/securitycenter/security_center_workspace_resource_test.go index e14224e7480c..6ff79b008a39 100644 --- a/internal/services/securitycenter/security_center_workspace_resource_test.go +++ b/internal/services/securitycenter/security_center_workspace_resource_test.go @@ -6,6 +6,8 @@ import ( "os" "testing" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/securitycenter/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -90,12 +92,15 @@ func TestAccSecurityCenterWorkspace_update(t *testing.T) { }) } -func (SecurityCenterWorkspaceResource) Exists(ctx context.Context, clients *clients.Client, _ *pluginsdk.InstanceState) (*bool, error) { - workspaceSettingName := "default" +func (SecurityCenterWorkspaceResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.WorkspaceID(state.ID) + if err != nil { + return nil, err + } - resp, err := clients.SecurityCenter.WorkspaceClient.Get(ctx, workspaceSettingName) + resp, err := clients.SecurityCenter.WorkspaceClient.Get(ctx, id.WorkspaceSettingName) if err != nil { - return nil, fmt.Errorf("reading Security Center Subscription Workspace Rule Set (%s): %+v", workspaceSettingName, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } return utils.Bool(resp.WorkspaceSettingProperties != nil), nil diff --git a/internal/services/securitycenter/validate/automation_id.go b/internal/services/securitycenter/validate/automation_id.go new file mode 100644 index 000000000000..654573b94b1c --- /dev/null +++ b/internal/services/securitycenter/validate/automation_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/securitycenter/parse" +) + +func AutomationID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.AutomationID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/securitycenter/validate/automation_id_test.go b/internal/services/securitycenter/validate/automation_id_test.go new file mode 100644 index 000000000000..59c4b1b56b5c --- /dev/null +++ b/internal/services/securitycenter/validate/automation_id_test.go @@ -0,0 +1,76 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestAutomationID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Security/", + Valid: false, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Security/automations/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Security/automations/testAutomation1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.SECURITY/AUTOMATIONS/TESTAUTOMATION1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := AutomationID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/securitycenter/validate/contact_id.go b/internal/services/securitycenter/validate/contact_id.go new file mode 100644 index 000000000000..05ac8eac919b --- /dev/null +++ b/internal/services/securitycenter/validate/contact_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/securitycenter/parse" +) + +func ContactID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.ContactID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/securitycenter/validate/contact_id_test.go b/internal/services/securitycenter/validate/contact_id_test.go new file mode 100644 index 000000000000..59c02d56863f --- /dev/null +++ b/internal/services/securitycenter/validate/contact_id_test.go @@ -0,0 +1,64 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestContactID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing SecurityContactName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/", + Valid: false, + }, + + { + // missing value for SecurityContactName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/securityContacts/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/securityContacts/contact1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.SECURITY/SECURITYCONTACTS/CONTACT1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := ContactID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/securitycenter/validate/pricing_id.go b/internal/services/securitycenter/validate/pricing_id.go new file mode 100644 index 000000000000..6962da857002 --- /dev/null +++ b/internal/services/securitycenter/validate/pricing_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/securitycenter/parse" +) + +func PricingID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.PricingID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/securitycenter/validate/pricing_id_test.go b/internal/services/securitycenter/validate/pricing_id_test.go new file mode 100644 index 000000000000..5be2b9a6e84d --- /dev/null +++ b/internal/services/securitycenter/validate/pricing_id_test.go @@ -0,0 +1,64 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestPricingID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/", + Valid: false, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/pricings/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/pricings/pricing1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.SECURITY/PRICINGS/PRICING1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := PricingID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/securitycenter/validate/setting_id.go b/internal/services/securitycenter/validate/setting_id.go new file mode 100644 index 000000000000..13f1d0a872c5 --- /dev/null +++ b/internal/services/securitycenter/validate/setting_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/securitycenter/parse" +) + +func SettingID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.SettingID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/securitycenter/validate/setting_id_test.go b/internal/services/securitycenter/validate/setting_id_test.go new file mode 100644 index 000000000000..48c13cc3167e --- /dev/null +++ b/internal/services/securitycenter/validate/setting_id_test.go @@ -0,0 +1,64 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestSettingID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/", + Valid: false, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/settings/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/settings/setting1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.SECURITY/SETTINGS/SETTING1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := SettingID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/securitycenter/validate/workspace_id.go b/internal/services/securitycenter/validate/workspace_id.go new file mode 100644 index 000000000000..b60e1f7e0b25 --- /dev/null +++ b/internal/services/securitycenter/validate/workspace_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/securitycenter/parse" +) + +func WorkspaceID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.WorkspaceID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/securitycenter/validate/workspace_id_test.go b/internal/services/securitycenter/validate/workspace_id_test.go new file mode 100644 index 000000000000..3644fe4e23cf --- /dev/null +++ b/internal/services/securitycenter/validate/workspace_id_test.go @@ -0,0 +1,64 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestWorkspaceID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing WorkspaceSettingName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/", + Valid: false, + }, + + { + // missing value for WorkspaceSettingName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/workspaceSettings/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Security/workspaceSettings/workspace1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.SECURITY/WORKSPACESETTINGS/WORKSPACE1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := WorkspaceID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/servicebus/servicebus_namespace_disaster_recovery_config_resource.go b/internal/services/servicebus/servicebus_namespace_disaster_recovery_config_resource.go index ed7eb84a6c21..8c58cf020502 100644 --- a/internal/services/servicebus/servicebus_namespace_disaster_recovery_config_resource.go +++ b/internal/services/servicebus/servicebus_namespace_disaster_recovery_config_resource.go @@ -93,26 +93,24 @@ func resourceServiceBusNamespaceDisasterRecoveryConfigCreate(d *pluginsdk.Resour ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - log.Printf("[INFO] preparing arguments for ServiceBus Namespace pairing create/update.") - - id, err := parse.NamespaceID(d.Get("primary_namespace_id").(string)) + namespaceId, err := parse.NamespaceID(d.Get("primary_namespace_id").(string)) if err != nil { return err } - aliasName := d.Get("name").(string) partnerNamespaceId := d.Get("partner_namespace_id").(string) + id := parse.NewNamespaceDisasterRecoveryConfigID(namespaceId.SubscriptionId, namespaceId.ResourceGroup, namespaceId.Name, d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.Name, aliasName) + existing, err := client.Get(ctx, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", aliasName, id.Name, id.ResourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_servicebus_namespace_disaster_recovery_config", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_servicebus_namespace_disaster_recovery_config", id.ID()) } } @@ -122,25 +120,15 @@ func resourceServiceBusNamespaceDisasterRecoveryConfigCreate(d *pluginsdk.Resour }, } - if _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, aliasName, parameters); err != nil { - return fmt.Errorf("creating/updating Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", aliasName, id.Name, id.ResourceGroup, err) - } - - if err := resourceServiceBusNamespaceDisasterRecoveryConfigWaitForState(ctx, client, id.ResourceGroup, id.Name, aliasName, d.Timeout(pluginsdk.TimeoutCreate)); err != nil { - return fmt.Errorf("waiting for replication to complete for Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", aliasName, id.Name, id.ResourceGroup, err) - } - - read, err := client.Get(ctx, id.ResourceGroup, id.Name, aliasName) - if err != nil { - return fmt.Errorf("reading Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %v", aliasName, id.Name, id.ResourceGroup, err) + if _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName, parameters); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } - if read.ID == nil { - return fmt.Errorf("got nil ID for Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q)", aliasName, id.Name, id.ResourceGroup) + if err := resourceServiceBusNamespaceDisasterRecoveryConfigWaitForState(ctx, client, id); err != nil { + return fmt.Errorf("waiting for replication to complete for %s: %+v", id, err) } - d.SetId(*read.ID) - + d.SetId(id.ID()) return resourceServiceBusNamespaceDisasterRecoveryConfigRead(d, meta) } @@ -158,13 +146,11 @@ func resourceServiceBusNamespaceDisasterRecoveryConfigUpdate(d *pluginsdk.Resour defer locks.UnlockByName(id.NamespaceName, serviceBusNamespaceResourceName) if d.HasChange("partner_namespace_id") { - breakPair, err := client.BreakPairing(ctx, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName) - if breakPair.StatusCode != http.StatusOK { - return fmt.Errorf("issuing break pairing request for Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + if _, err := client.BreakPairing(ctx, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName); err != nil { + return fmt.Errorf("breaking the pairing for %s: %+v", *id, err) } - - if err := resourceServiceBusNamespaceDisasterRecoveryConfigWaitForState(ctx, client, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName, d.Timeout(pluginsdk.TimeoutUpdate)); err != nil { - return fmt.Errorf("waiting for break pairing request to complete for Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + if err := resourceServiceBusNamespaceDisasterRecoveryConfigWaitForState(ctx, client, *id); err != nil { + return fmt.Errorf("waiting for the pairing to break for %s: %+v", *id, err) } } @@ -175,11 +161,10 @@ func resourceServiceBusNamespaceDisasterRecoveryConfigUpdate(d *pluginsdk.Resour } if _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName, parameters); err != nil { - return fmt.Errorf("creating/updating Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + return fmt.Errorf("creating/updating %s: %+v", *id, err) } - - if err := resourceServiceBusNamespaceDisasterRecoveryConfigWaitForState(ctx, client, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName, d.Timeout(pluginsdk.TimeoutUpdate)); err != nil { - return fmt.Errorf("waiting for replication to complete for Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + if err := resourceServiceBusNamespaceDisasterRecoveryConfigWaitForState(ctx, client, *id); err != nil { + return fmt.Errorf("waiting for %s to finish replicating: %+v", *id, err) } return resourceServiceBusNamespaceDisasterRecoveryConfigRead(d, meta) @@ -208,7 +193,10 @@ func resourceServiceBusNamespaceDisasterRecoveryConfigRead(d *pluginsdk.Resource d.Set("name", id.DisasterRecoveryConfigName) d.Set("primary_namespace_id", primaryId.ID()) - d.Set("partner_namespace_id", resp.ArmDisasterRecoveryProperties.PartnerNamespace) + + if props := resp.ArmDisasterRecoveryProperties; props != nil { + d.Set("partner_namespace_id", props.PartnerNamespace) + } keys, err := client.ListKeys(ctx, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName, serviceBusNamespaceDefaultAuthorizationRule) @@ -240,15 +228,15 @@ func resourceServiceBusNamespaceDisasterRecoveryConfigDelete(d *pluginsdk.Resour } if breakPair.StatusCode != http.StatusOK { - return fmt.Errorf("breaking pairing for Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + return fmt.Errorf("breaking pairing for %s: %+v", *id, err) } - if err := resourceServiceBusNamespaceDisasterRecoveryConfigWaitForState(ctx, client, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName, d.Timeout(pluginsdk.TimeoutDelete)); err != nil { - return fmt.Errorf("waiting for break pairing request to complete for Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + if err := resourceServiceBusNamespaceDisasterRecoveryConfigWaitForState(ctx, client, *id); err != nil { + return fmt.Errorf("waiting for the pairing to break for %s: %+v", *id, err) } if _, err := client.Delete(ctx, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName); err != nil { - return fmt.Errorf("issuing delete request for Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } // no future for deletion so wait for it to vanish @@ -263,7 +251,7 @@ func resourceServiceBusNamespaceDisasterRecoveryConfigDelete(d *pluginsdk.Resour if utils.ResponseWasNotFound(resp.Response) { return resp, strconv.Itoa(resp.StatusCode), nil } - return nil, "nil", fmt.Errorf("polling for the status of the Service Bus Namespace Disaster Recovery Configs %q deletion (Namespace %q / Resource Group %q): %v", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + return nil, "nil", fmt.Errorf("retrieving %s: %+v", *id, err) } return resp, strconv.Itoa(resp.StatusCode), nil @@ -271,7 +259,7 @@ func resourceServiceBusNamespaceDisasterRecoveryConfigDelete(d *pluginsdk.Resour } if _, err := deleteWait.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting the deletion of Service Bus Namespace Disaster Recovery Configs %q deletion (Namespace %q / Resource Group %q): %v", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + return fmt.Errorf("waiting the deletion of %s: %v", *id, err) } // it can take some time for the name to become available again @@ -284,7 +272,7 @@ func resourceServiceBusNamespaceDisasterRecoveryConfigDelete(d *pluginsdk.Resour Refresh: func() (interface{}, string, error) { resp, err := client.CheckNameAvailabilityMethod(ctx, id.ResourceGroup, id.NamespaceName, servicebus.CheckNameAvailability{Name: utils.String(id.DisasterRecoveryConfigName)}) if err != nil { - return resp, "Error", fmt.Errorf("checking if the Service Bus Namespace Disaster Recovery Configs %q name has been freed (Namespace %q / Resource Group %q): %v", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + return resp, "Error", fmt.Errorf("checking for the status of %s: %+v", *id, err) } return resp, string(resp.Reason), nil @@ -292,32 +280,36 @@ func resourceServiceBusNamespaceDisasterRecoveryConfigDelete(d *pluginsdk.Resour } if _, err := nameFreeWait.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting the the Service Bus Namespace Disaster Recovery Configs %q name to be available (Namespace %q / Resource Group %q): %v", id.DisasterRecoveryConfigName, id.NamespaceName, id.ResourceGroup, err) + return fmt.Errorf("checking if the name for %s has become free: %v", *id, err) } return nil } -func resourceServiceBusNamespaceDisasterRecoveryConfigWaitForState(ctx context.Context, client *servicebus.DisasterRecoveryConfigsClient, resourceGroup, namespaceName, name string, timeout time.Duration) error { +func resourceServiceBusNamespaceDisasterRecoveryConfigWaitForState(ctx context.Context, client *servicebus.DisasterRecoveryConfigsClient, id parse.NamespaceDisasterRecoveryConfigId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } stateConf := &pluginsdk.StateChangeConf{ Pending: []string{string(servicebus.ProvisioningStateDRAccepted)}, Target: []string{string(servicebus.ProvisioningStateDRSucceeded)}, MinTimeout: 30 * time.Second, - Timeout: timeout, + Timeout: time.Until(deadline), Refresh: func() (interface{}, string, error) { - read, err := client.Get(ctx, resourceGroup, namespaceName, name) + read, err := client.Get(ctx, id.ResourceGroup, id.NamespaceName, id.DisasterRecoveryConfigName) if err != nil { - return nil, "error", fmt.Errorf("wait read Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %v", name, namespaceName, resourceGroup, err) + return nil, "error", fmt.Errorf("retrieving %s: %+v", id, err) } if props := read.ArmDisasterRecoveryProperties; props != nil { if props.ProvisioningState == servicebus.ProvisioningStateDRFailed { - return read, "failed", fmt.Errorf("replication for Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q) failed", name, namespaceName, resourceGroup) + return read, "failed", fmt.Errorf("replication Failed for %s: %+v", id, err) } return read, string(props.ProvisioningState), nil } - return read, "nil", fmt.Errorf("waiting for replication error Service Bus Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): provisioning state is nil", name, namespaceName, resourceGroup) + return read, "nil", fmt.Errorf("waiting on replication of %s: %+v", id, err) }, } diff --git a/internal/services/servicefabric/service_fabric_cluster_resource.go b/internal/services/servicefabric/service_fabric_cluster_resource.go index f40fd30a71a4..e6115b8d0e2d 100644 --- a/internal/services/servicefabric/service_fabric_cluster_resource.go +++ b/internal/services/servicefabric/service_fabric_cluster_resource.go @@ -5,6 +5,8 @@ import ( "log" "time" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/Azure/azure-sdk-for-go/services/servicefabric/mgmt/2021-06-01/servicefabric" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" @@ -559,31 +561,21 @@ func resourceServiceFabricCluster() *pluginsdk.Resource { func resourceServiceFabricClusterCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).ServiceFabric.ClustersClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - log.Printf("[INFO] preparing arguments for Service Fabric Cluster creation.") - - resourceGroup := d.Get("resource_group_name").(string) - name := d.Get("name").(string) - location := d.Get("location").(string) - reliabilityLevel := d.Get("reliability_level").(string) - managementEndpoint := d.Get("management_endpoint").(string) - upgradeMode := d.Get("upgrade_mode").(string) - clusterCodeVersion := d.Get("cluster_code_version").(string) - vmImage := d.Get("vm_image").(string) - t := d.Get("tags").(map[string]interface{}) - + id := parse.NewClusterID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, resourceGroup, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Service Fabric Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_service_fabric_cluster", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_service_fabric_cluster", id.ID()) } } @@ -605,6 +597,14 @@ func resourceServiceFabricClusterCreateUpdate(d *pluginsdk.ResourceData, meta in nodeTypesRaw := d.Get("node_type").([]interface{}) nodeTypes := expandServiceFabricClusterNodeTypes(nodeTypesRaw) + location := d.Get("location").(string) + reliabilityLevel := d.Get("reliability_level").(string) + managementEndpoint := d.Get("management_endpoint").(string) + upgradeMode := d.Get("upgrade_mode").(string) + clusterCodeVersion := d.Get("cluster_code_version").(string) + vmImage := d.Get("vm_image").(string) + t := d.Get("tags").(map[string]interface{}) + cluster := servicefabric.Cluster{ Location: utils.String(location), Tags: tags.Expand(t), @@ -656,25 +656,16 @@ func resourceServiceFabricClusterCreateUpdate(d *pluginsdk.ResourceData, meta in cluster.ClusterProperties.ClusterCodeVersion = utils.String(clusterCodeVersion) } - future, err := client.CreateOrUpdate(ctx, resourceGroup, name, cluster) + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, cluster) if err != nil { - return fmt.Errorf("creating Service Fabric Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("creating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of Service Fabric Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for the creation of %s: %+v", id, err) } - read, err := client.Get(ctx, resourceGroup, name) - if err != nil { - return fmt.Errorf("retrieving Service Fabric Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) - } - if read.ID == nil { - return fmt.Errorf("Cannot read ID of Service Fabric Cluster %q (Resource Group %q)", name, resourceGroup) - } - - d.SetId(*read.ID) - + d.SetId(id.ID()) return resourceServiceFabricClusterRead(d, meta) } @@ -701,9 +692,7 @@ func resourceServiceFabricClusterRead(d *pluginsdk.ResourceData, meta interface{ d.Set("name", id.Name) d.Set("resource_group_name", id.ResourceGroup) - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } + d.Set("location", location.NormalizeNilable(resp.Location)) if props := resp.ClusterProperties; props != nil { d.Set("cluster_code_version", props.ClusterCodeVersion) diff --git a/internal/services/synapse/synapse_firewall_rule_resource.go b/internal/services/synapse/synapse_firewall_rule_resource.go index e0ddeb11fee3..eae2c9f4a7eb 100644 --- a/internal/services/synapse/synapse_firewall_rule_resource.go +++ b/internal/services/synapse/synapse_firewall_rule_resource.go @@ -70,22 +70,22 @@ func resourceSynapseFirewallRuleCreateUpdate(d *pluginsdk.ResourceData, meta int ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) workspaceId, err := parse.WorkspaceID(d.Get("synapse_workspace_id").(string)) if err != nil { return err } + id := parse.NewFirewallRuleID(workspaceId.SubscriptionId, workspaceId.ResourceGroup, workspaceId.Name, d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, workspaceId.ResourceGroup, workspaceId.Name, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Synapse Firewall Rule %q (Resource Group %q / Workspace %q): %+v", name, workspaceId.ResourceGroup, workspaceId.Name, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_synapse_firewall_rule", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_synapse_firewall_rule", id.ID()) } } @@ -96,21 +96,15 @@ func resourceSynapseFirewallRuleCreateUpdate(d *pluginsdk.ResourceData, meta int }, } - future, err := client.CreateOrUpdate(ctx, workspaceId.ResourceGroup, workspaceId.Name, name, parameters) + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.Name, parameters) if err != nil { - return fmt.Errorf("creating/updating Synapse Firewall Rule %q (Resource Group %q / Workspace %q): %+v", name, workspaceId.ResourceGroup, workspaceId.Name, err) + return fmt.Errorf("creating/updating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting on creation/updation for Synapse Firewall Rule %q (Resource Group %q / Workspace %q): %+v", name, workspaceId.ResourceGroup, workspaceId.Name, err) + return fmt.Errorf("waiting on creation/update of %s: %+v", id, err) } - resp, err := client.Get(ctx, workspaceId.ResourceGroup, workspaceId.Name, name) - if err != nil { - return fmt.Errorf("retrieving Synapse Firewall Rule %q (Resource Group %q / Workspace %q): %+v", name, workspaceId.ResourceGroup, workspaceId.Name, err) - } - - d.SetId(*resp.ID) - + d.SetId(id.ID()) return resourceSynapseFirewallRuleRead(d, meta) } @@ -159,11 +153,11 @@ func resourceSynapseFirewallRuleDelete(d *pluginsdk.ResourceData, meta interface future, err := client.Delete(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) if err != nil { - return fmt.Errorf("deleting Synapse Firewall Rule %q (Workspace %q / Resource Group %q): %+v", id.Name, id.WorkspaceName, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of Synapse Firewall Rule %q (Workspace %q / Resource Group %q): %+v", id.Name, id.WorkspaceName, id.ResourceGroup, err) + return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) } return nil diff --git a/internal/services/synapse/synapse_integration_runtime_azure_resource.go b/internal/services/synapse/synapse_integration_runtime_azure_resource.go index a819aceff662..a48e0e3a3525 100644 --- a/internal/services/synapse/synapse_integration_runtime_azure_resource.go +++ b/internal/services/synapse/synapse_integration_runtime_azure_resource.go @@ -110,7 +110,7 @@ func resourceSynapseIntegrationRuntimeAzureCreateUpdate(d *pluginsdk.ResourceDat } } if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_synapse_integration_runtime_azure", *existing.ID) + return tf.ImportAsExistsError("azurerm_synapse_integration_runtime_azure", id.ID()) } } diff --git a/internal/services/synapse/synapse_integration_runtime_self_hosted_resource.go b/internal/services/synapse/synapse_integration_runtime_self_hosted_resource.go index 84adfae259a6..1c923979122c 100644 --- a/internal/services/synapse/synapse_integration_runtime_self_hosted_resource.go +++ b/internal/services/synapse/synapse_integration_runtime_self_hosted_resource.go @@ -91,7 +91,7 @@ func resourceSynapseIntegrationRuntimeSelfHostedCreateUpdate(d *pluginsdk.Resour } } if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_synapse_integration_runtime_self_hosted", *existing.ID) + return tf.ImportAsExistsError("azurerm_synapse_integration_runtime_self_hosted", id.ID()) } } diff --git a/internal/services/synapse/synapse_managed_private_endpoint_resource.go b/internal/services/synapse/synapse_managed_private_endpoint_resource.go index bc6c2646429a..7f4d0ffda099 100644 --- a/internal/services/synapse/synapse_managed_private_endpoint_resource.go +++ b/internal/services/synapse/synapse_managed_private_endpoint_resource.go @@ -88,40 +88,34 @@ func resourceSynapseManagedPrivateEndpointCreate(d *pluginsdk.ResourceData, meta } virtualNetworkName := *workspace.WorkspaceProperties.ManagedVirtualNetwork - privateEndpointName := d.Get("name").(string) + id := parse.NewManagedPrivateEndpointID(workspaceId.SubscriptionId, workspaceId.ResourceGroup, workspaceId.Name, virtualNetworkName, d.Get("name").(string)) client, err := synapseClient.ManagedPrivateEndpointsClient(workspaceId.Name, environment.SynapseEndpointSuffix) if err != nil { - return err + return fmt.Errorf("building Client for %s: %+v", id, err) } // check exist - existing, err := client.Get(ctx, virtualNetworkName, privateEndpointName) + existing, err := client.Get(ctx, id.ManagedVirtualNetworkName, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for present of existing Synapse Managed Private Endpoint %q (Workspace %q / Resource Group %q): %+v", privateEndpointName, workspaceId.Name, workspaceId.ResourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_synapse_managed_private_endpoint", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_synapse_managed_private_endpoint", id.ID()) } - // create managedPrivateEndpoint := managedvirtualnetwork.ManagedPrivateEndpoint{ Properties: &managedvirtualnetwork.ManagedPrivateEndpointProperties{ PrivateLinkResourceID: utils.String(d.Get("target_resource_id").(string)), GroupID: utils.String(d.Get("subresource_name").(string)), }, } - resp, err := client.Create(ctx, virtualNetworkName, privateEndpointName, managedPrivateEndpoint) - if err != nil { - return fmt.Errorf("creating Synapse Managed Private Endpoint %q (Workspace %q / Resource Group %q): %+v", privateEndpointName, workspaceId.Name, workspaceId.ResourceGroup, err) - } - - if resp.ID == nil || *resp.ID == "" { - return fmt.Errorf("empty or nil ID returned for Synapse Managed Private Endpoint %q (Workspace %q / Resource Group %q)", privateEndpointName, workspaceId.Name, workspaceId.ResourceGroup) + if _, err := client.Create(ctx, id.ManagedVirtualNetworkName, id.Name, managedPrivateEndpoint); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) } - d.SetId(*resp.ID) + d.SetId(id.ID()) return resourceSynapseManagedPrivateEndpointRead(d, meta) } @@ -138,16 +132,17 @@ func resourceSynapseManagedPrivateEndpointRead(d *pluginsdk.ResourceData, meta i client, err := synapseClient.ManagedPrivateEndpointsClient(id.WorkspaceName, environment.SynapseEndpointSuffix) if err != nil { - return err + return fmt.Errorf("building Client for %s: %v", *id, err) } + resp, err := client.Get(ctx, id.ManagedVirtualNetworkName, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] Synapse Managed Private Endpoint %q does not exist - removing from state", d.Id()) + log.Printf("[INFO] %s does not exist - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("retrieving Synapse Managed Private Endpoint (Workspace %q / Resource Group %q): %+v", id.WorkspaceName, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } workspaceId := parse.NewWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName).ID() @@ -174,10 +169,11 @@ func resourceSynapseManagedPrivateEndpointDelete(d *pluginsdk.ResourceData, meta client, err := synapseClient.ManagedPrivateEndpointsClient(id.WorkspaceName, environment.SynapseEndpointSuffix) if err != nil { - return err + return fmt.Errorf("building Client for %s: %v", *id, err) } + if _, err := client.Delete(ctx, id.ManagedVirtualNetworkName, id.Name); err != nil { - return fmt.Errorf("deleting Synapse Managed Private Endpoint %q (Workspace %q / Resource Group %q): %+v", id, id.WorkspaceName, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil diff --git a/internal/services/synapse/synapse_role_assignment_resource.go b/internal/services/synapse/synapse_role_assignment_resource.go index 6f641ac6ddd2..78b8f4a9798f 100644 --- a/internal/services/synapse/synapse_role_assignment_resource.go +++ b/internal/services/synapse/synapse_role_assignment_resource.go @@ -137,6 +137,7 @@ func resourceSynapseRoleAssignmentCreate(d *pluginsdk.ResourceData, meta interfa return fmt.Errorf("checking for presence of existing Synapse Role Assignment (workspace %q): %+v", workspaceName, err) } } + // TODO: unpick this/refactor to use ID Formatters if listResp.Value != nil && len(*listResp.Value) != 0 { existing := (*listResp.Value)[0] if existing.ID != nil && *existing.ID != "" { diff --git a/internal/services/synapse/synapse_spark_pool_resource.go b/internal/services/synapse/synapse_spark_pool_resource.go index e22172018ea4..d3ca57a51ac5 100644 --- a/internal/services/synapse/synapse_spark_pool_resource.go +++ b/internal/services/synapse/synapse_spark_pool_resource.go @@ -215,22 +215,25 @@ func resourceSynapseSparkPoolCreate(d *pluginsdk.ResourceData, meta interface{}) ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - workspaceId, _ := parse.WorkspaceID(d.Get("synapse_workspace_id").(string)) + workspaceId, err := parse.WorkspaceID(d.Get("synapse_workspace_id").(string)) + if err != nil { + return fmt.Errorf("parsing `synapse_workspace_id`: %+v", err) + } + id := parse.NewSparkPoolID(workspaceId.SubscriptionId, workspaceId.ResourceGroup, workspaceId.Name, d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, workspaceId.ResourceGroup, workspaceId.Name, name) + existing, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.BigDataPoolName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for present of existing Synapse Spark Pool %q (Workspace %q / Resource Group %q): %+v", name, workspaceId.Name, workspaceId.ResourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_synapse_spark_pool", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_synapse_spark_pool", id.ID()) } } - workspace, err := workspaceClient.Get(ctx, workspaceId.ResourceGroup, workspaceId.Name) + workspace, err := workspaceClient.Get(ctx, id.ResourceGroup, id.WorkspaceName) if err != nil { return fmt.Errorf("reading Synapse workspace %q (Workspace %q / Resource Group %q): %+v", workspaceId.Name, workspaceId.Name, workspaceId.ResourceGroup, err) } @@ -261,32 +264,19 @@ func resourceSynapseSparkPoolCreate(d *pluginsdk.ResourceData, meta interface{}) } force := utils.Bool(false) - future, err := client.CreateOrUpdate(ctx, workspaceId.ResourceGroup, workspaceId.Name, name, bigDataPoolInfo, force) + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.BigDataPoolName, bigDataPoolInfo, force) if err != nil { - return fmt.Errorf("creating Synapse Spark Pool %q (Workspace %q / Resource Group %q): %+v", name, workspaceId.Name, workspaceId.ResourceGroup, err) + return fmt.Errorf("creating %s: %v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting on creating future for Synapse Spark Pool %q (Workspace %q / Resource Group %q): %+v", name, workspaceId.Name, workspaceId.ResourceGroup, err) + return fmt.Errorf("waiting for the creation of %s: %+v", id, err) } - resp, err := client.Get(ctx, workspaceId.ResourceGroup, workspaceId.Name, name) - if err != nil { - return fmt.Errorf("retrieving Synapse Spark Pool %q (Workspace %q / Resource Group %q): %+v", name, workspaceId.Name, workspaceId.ResourceGroup, err) - } - - if resp.ID == nil || *resp.ID == "" { - return fmt.Errorf("empty or nil ID returned for Synapse Spark Pool %q (Workspace %q / Resource Group %q) ID", name, workspaceId.Name, workspaceId.ResourceGroup) - } - - d.SetId(*resp.ID) + d.SetId(id.ID()) // Library Requirements can't be specified on Create so we'll call update after we've confirmed the Spark Pool has been created. - if libraryRequirements := expandArmSparkPoolLibraryRequirements(d.Get("library_requirement").([]interface{})); libraryRequirements != nil { - return resourceSynapseSparkPoolUpdate(d, meta) - } - - return resourceSynapseSparkPoolRead(d, meta) + return resourceSynapseSparkPoolUpdate(d, meta) } func resourceSynapseSparkPoolRead(d *pluginsdk.ResourceData, meta interface{}) error { @@ -324,9 +314,13 @@ func resourceSynapseSparkPoolRead(d *pluginsdk.ResourceData, meta interface{}) e } d.Set("cache_size", props.CacheSize) d.Set("compute_isolation_enabled", props.IsComputeIsolationEnabled) + + dynamicExecutorAllocationEnabled := false if props.DynamicExecutorAllocation != nil { - d.Set("dynamic_executor_allocation_enabled", props.DynamicExecutorAllocation.Enabled) + dynamicExecutorAllocationEnabled = *props.DynamicExecutorAllocation.Enabled } + d.Set("dynamic_executor_allocation_enabled", dynamicExecutorAllocationEnabled) + d.Set("node_count", props.NodeCount) d.Set("node_size", props.NodeSize) d.Set("node_size_family", string(props.NodeSizeFamily)) @@ -382,23 +376,13 @@ func resourceSynapseSparkPoolUpdate(d *pluginsdk.ResourceData, meta interface{}) force := utils.Bool(false) future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.BigDataPoolName, bigDataPoolInfo, force) if err != nil { - return fmt.Errorf("creating Synapse Spark Pool %q (Workspace %q / Resource Group %q): %+v", id.BigDataPoolName, id.WorkspaceName, id.ResourceGroup, err) + return fmt.Errorf("updating %s: %+v", *id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting on creating future for Synapse Spark Pool %q (Workspace %q / Resource Group %q): %+v", id.BigDataPoolName, id.WorkspaceName, id.ResourceGroup, err) - } - - resp, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.BigDataPoolName) - if err != nil { - return fmt.Errorf("retrieving Synapse Spark Pool %q (Workspace %q / Resource Group %q): %+v", id.BigDataPoolName, id.WorkspaceName, id.ResourceGroup, err) - } - - if resp.ID == nil || *resp.ID == "" { - return fmt.Errorf("empty or nil ID returned for Synapse Spark Pool %q (Workspace %q / Resource Group %q) ID", id.BigDataPoolName, id.WorkspaceName, id.ResourceGroup) + return fmt.Errorf("waiting for update of %s: %+v", *id, err) } - d.SetId(*resp.ID) return resourceSynapseSparkPoolRead(d, meta) } @@ -414,11 +398,11 @@ func resourceSynapseSparkPoolDelete(d *pluginsdk.ResourceData, meta interface{}) future, err := client.Delete(ctx, id.ResourceGroup, id.WorkspaceName, id.BigDataPoolName) if err != nil { - return fmt.Errorf("deleting Synapse Spark Pool %q (Workspace %q / Resource Group %q): %+v", id.BigDataPoolName, id.WorkspaceName, id.ResourceGroup, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deleting Synapse Spark Pool %q (Workspace %q / Resource Group %q): %+v", id.BigDataPoolName, id.WorkspaceName, id.ResourceGroup, err) + return fmt.Errorf("waiting for the deletion of %s: %+v", *id, err) } return nil diff --git a/internal/services/synapse/synapse_sql_pool_extended_auditing_policy_resource.go b/internal/services/synapse/synapse_sql_pool_extended_auditing_policy_resource.go index 0691c67b79e1..664fb3828157 100644 --- a/internal/services/synapse/synapse_sql_pool_extended_auditing_policy_resource.go +++ b/internal/services/synapse/synapse_sql_pool_extended_auditing_policy_resource.go @@ -98,9 +98,11 @@ func resourceSynapseSqlPoolExtendedAuditingPolicyCreateUpdate(d *pluginsdk.Resou } } - // if state is not disabled, we should import it. - if existing.ID != nil && *existing.ID != "" && existing.ExtendedSQLPoolBlobAuditingPolicyProperties != nil && existing.ExtendedSQLPoolBlobAuditingPolicyProperties.State != synapse.BlobAuditingPolicyStateDisabled { - return tf.ImportAsExistsError("azurerm_synapse_sql_pool_extended_auditing_policy", *existing.ID) + // if state is not disabled, we should flag to import it. + if !utils.ResponseWasNotFound(existing.Response) { + if props := existing.ExtendedSQLPoolBlobAuditingPolicyProperties; props != nil && props.State != synapse.BlobAuditingPolicyStateDisabled { + return tf.ImportAsExistsError("azurerm_synapse_sql_pool_extended_auditing_policy", id.ID()) + } } } @@ -124,7 +126,6 @@ func resourceSynapseSqlPoolExtendedAuditingPolicyCreateUpdate(d *pluginsdk.Resou } d.SetId(id.ID()) - return resourceSynapseSqlPoolExtendedAuditingPolicyRead(d, meta) } @@ -141,11 +142,11 @@ func resourceSynapseSqlPoolExtendedAuditingPolicyRead(d *pluginsdk.ResourceData, resp, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.SqlPoolName) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] synapse %s does not exist - removing from state", id) + log.Printf("[INFO] %s does not exist - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("retrieving %s: %+v", id, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } sqlPoolId := parse.NewSqlPoolID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName, id.SqlPoolName) diff --git a/internal/services/synapse/synapse_sql_pool_resource.go b/internal/services/synapse/synapse_sql_pool_resource.go index 151376fc7574..c422f344aaf6 100644 --- a/internal/services/synapse/synapse_sql_pool_resource.go +++ b/internal/services/synapse/synapse_sql_pool_resource.go @@ -167,20 +167,20 @@ func resourceSynapseSqlPoolCreate(d *pluginsdk.ResourceData, meta interface{}) e ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) workspaceId, err := parse.WorkspaceID(d.Get("synapse_workspace_id").(string)) if err != nil { return err } - existing, err := sqlClient.Get(ctx, workspaceId.ResourceGroup, workspaceId.Name, name) + id := parse.NewSqlPoolID(workspaceId.SubscriptionId, workspaceId.ResourceGroup, workspaceId.Name, d.Get("name").(string)) + existing, err := sqlClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Synapse Sql Pool %q (Workspace %q / Resource Group %q): %+v", name, workspaceId.Name, workspaceId.ResourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_synapse_sql_pool", *existing.ID) + return tf.ImportAsExistsError("azurerm_synapse_sql_pool", id.ID()) } workspace, err := workspaceClient.Get(ctx, workspaceId.ResourceGroup, workspaceId.Name) @@ -224,12 +224,12 @@ func resourceSynapseSqlPoolCreate(d *pluginsdk.ResourceData, meta interface{}) e sqlPoolInfo.SQLPoolResourceProperties.SourceDatabaseID = utils.String(sourceDatabaseId) } - future, err := sqlClient.Create(ctx, workspaceId.ResourceGroup, workspaceId.Name, name, sqlPoolInfo) + future, err := sqlClient.Create(ctx, id.ResourceGroup, id.WorkspaceName, id.Name, sqlPoolInfo) if err != nil { - return fmt.Errorf("creating Synapse SqlPool %q (Workspace %q / Resource Group %q): %+v", name, workspaceId.Name, workspaceId.ResourceGroup, err) + return fmt.Errorf("creating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, sqlClient.Client); err != nil { - return fmt.Errorf("waiting on creating future for Synapse SqlPool %q (Workspace %q / Resource Group %q): %+v", name, workspaceId.Name, workspaceId.ResourceGroup, err) + return fmt.Errorf("waiting for creation of %s: %+v", id, err) } if d.Get("data_encrypted").(bool) { @@ -238,21 +238,12 @@ func resourceSynapseSqlPoolCreate(d *pluginsdk.ResourceData, meta interface{}) e Status: synapse.TransparentDataEncryptionStatusEnabled, }, } - if _, err := sqlPTDEClient.CreateOrUpdate(ctx, workspaceId.ResourceGroup, workspaceId.Name, name, parameter); err != nil { + if _, err := sqlPTDEClient.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.Name, parameter); err != nil { return fmt.Errorf("setting `data_encrypted`: %+v", err) } } - resp, err := sqlClient.Get(ctx, workspaceId.ResourceGroup, workspaceId.Name, name) - if err != nil { - return fmt.Errorf("retrieving Synapse SqlPool %q (Workspace %q / Resource Group %q): %+v", name, workspaceId.Name, workspaceId.ResourceGroup, err) - } - - if resp.ID == nil || *resp.ID == "" { - return fmt.Errorf("empty or nil ID returned for Synapse Sql Pool %q (Workspace %q / Resource Group %q) ID", name, workspaceId.Name, workspaceId.ResourceGroup) - } - - d.SetId(*resp.ID) + d.SetId(id.ID()) return resourceSynapseSqlPoolRead(d, meta) } @@ -292,11 +283,15 @@ func resourceSynapseSqlPoolUpdate(d *pluginsdk.ResourceData, meta interface{}) e } if _, err := sqlClient.Update(ctx, id.ResourceGroup, id.WorkspaceName, id.Name, sqlPoolInfo); err != nil { - return fmt.Errorf("updating Synapse SqlPool %q (Workspace %q / Resource Group %q): %+v", id.Name, id.ResourceGroup, id.WorkspaceName, err) + return fmt.Errorf("updating %s: %+v", *id, err) } // wait for sku scale completion if d.HasChange("sku_name") { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } stateConf := &pluginsdk.StateChangeConf{ Pending: []string{ "Scaling", @@ -307,15 +302,14 @@ func resourceSynapseSqlPoolUpdate(d *pluginsdk.ResourceData, meta interface{}) e Refresh: synapseSqlPoolScaleStateRefreshFunc(ctx, sqlClient, id.ResourceGroup, id.WorkspaceName, id.Name), MinTimeout: 5 * time.Second, ContinuousTargetOccurence: 3, - Timeout: d.Timeout(pluginsdk.TimeoutUpdate), + Timeout: time.Until(deadline), } if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for scaling of Synapse SqlPool %q (Workspace %q / Resource Group %q): %+v", id.Name, id.WorkspaceName, id.ResourceGroup, err) + return fmt.Errorf("waiting for scaling of %s: %+v", *id, err) } } } - return resourceSynapseSqlPoolRead(d, meta) } @@ -333,11 +327,12 @@ func resourceSynapseSqlPoolRead(d *pluginsdk.ResourceData, meta interface{}) err resp, err := sqlClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] Synapse SQL Pool %q (Workspace %q / Resource Group %q) does not exist - removing from state", id.Name, id.WorkspaceName, id.ResourceGroup) + log.Printf("[INFO] %s was not found - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("retrieving Synapse SqlPool %q (Workspace %q / Resource Group %q): %+v", id.Name, id.WorkspaceName, id.ResourceGroup, err) + + return fmt.Errorf("retrieving %s: %+v", *id, err) } transparentDataEncryption, err := sqlPTDEClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) @@ -346,7 +341,6 @@ func resourceSynapseSqlPoolRead(d *pluginsdk.ResourceData, meta interface{}) err } workspaceId := parse.NewWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName).ID() - d.Set("name", id.Name) d.Set("synapse_workspace_id", workspaceId) if resp.Sku != nil { diff --git a/internal/services/synapse/synapse_workspace_extended_auditing_policy_resource.go b/internal/services/synapse/synapse_workspace_extended_auditing_policy_resource.go index f22d58cac209..d8ab8490e961 100644 --- a/internal/services/synapse/synapse_workspace_extended_auditing_policy_resource.go +++ b/internal/services/synapse/synapse_workspace_extended_auditing_policy_resource.go @@ -99,9 +99,11 @@ func resourceSynapseWorkspaceExtendedAuditingPolicyCreateUpdate(d *pluginsdk.Res } } - // if state is not disabled, we should import it. - if existing.ID != nil && *existing.ID != "" && existing.ExtendedServerBlobAuditingPolicyProperties != nil && existing.ExtendedServerBlobAuditingPolicyProperties.State != synapse.BlobAuditingPolicyStateDisabled { - return tf.ImportAsExistsError("azurerm_synapse_workspace_extended_auditing_policy", *existing.ID) + // if state is not disabled, we should flag it for import + if !utils.ResponseWasNotFound(existing.Response) { + if props := existing.ExtendedServerBlobAuditingPolicyProperties; props != nil && props.State != synapse.BlobAuditingPolicyStateDisabled { + return tf.ImportAsExistsError("azurerm_synapse_workspace_extended_auditing_policy", id.ID()) + } } } @@ -131,7 +133,6 @@ func resourceSynapseWorkspaceExtendedAuditingPolicyCreateUpdate(d *pluginsdk.Res } d.SetId(id.ID()) - return resourceSynapseWorkspaceExtendedAuditingPolicyRead(d, meta) } @@ -152,11 +153,10 @@ func resourceSynapseWorkspaceExtendedAuditingPolicyRead(d *pluginsdk.ResourceDat d.SetId("") return nil } - return fmt.Errorf("retrieving %s: %+v", id, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } workspaceId := parse.NewWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName) - d.Set("synapse_workspace_id", workspaceId.ID()) if props := resp.ExtendedServerBlobAuditingPolicyProperties; props != nil { diff --git a/internal/services/synapse/synapse_workspace_resource.go b/internal/services/synapse/synapse_workspace_resource.go index b97f41d0a6a9..8e469bf2a783 100644 --- a/internal/services/synapse/synapse_workspace_resource.go +++ b/internal/services/synapse/synapse_workspace_resource.go @@ -335,21 +335,20 @@ func resourceSynapseWorkspaceCreate(d *pluginsdk.ResourceData, meta interface{}) client := meta.(*clients.Client).Synapse.WorkspaceClient aadAdminClient := meta.(*clients.Client).Synapse.WorkspaceAadAdminsClient sqlAdminClient := meta.(*clients.Client).Synapse.WorkspaceSQLAadAdminsClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId identitySQLControlClient := meta.(*clients.Client).Synapse.WorkspaceManagedIdentitySQLControlSettingsClient ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) - - existing, err := client.Get(ctx, resourceGroup, name) + id := parse.NewWorkspaceID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + existing, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for present of existing Synapse Workspace %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_synapse_workspace", *existing.ID) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_synapse_workspace", id.ID()) } managedVirtualNetwork := "" @@ -407,48 +406,49 @@ func resourceSynapseWorkspaceCreate(d *pluginsdk.ResourceData, meta interface{}) workspaceInfo.ManagedVirtualNetworkSettings.AllowedAadTenantIdsForLinking = utils.ExpandStringSlice(allowedLinkingTenantIds.([]interface{})) } - future, err := client.CreateOrUpdate(ctx, resourceGroup, name, workspaceInfo) + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, workspaceInfo) if err != nil { - return fmt.Errorf("creating Synapse Workspace %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("creating %s: %+v", id, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting on creation for Synapse Workspace %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for creation of %s: %+v", id, err) } aadAdmin := expandArmWorkspaceAadAdminInfo(d.Get("aad_admin").([]interface{})) if aadAdmin != nil { - workspaceAadAdminsCreateOrUpdateFuture, err := aadAdminClient.CreateOrUpdate(ctx, resourceGroup, name, *aadAdmin) + future, err := aadAdminClient.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, *aadAdmin) if err != nil { - return fmt.Errorf("updating Synapse Workspace %q Azure AD Admin (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("configuring AzureAD Admin for %s: %+v", id, err) } - if err = workspaceAadAdminsCreateOrUpdateFuture.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting on updating for Synapse Workspace %q Azure AD Admin (Resource Group %q): %+v", name, resourceGroup, err) + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for configuration of AzureAD Admin for %s: %+v", id, err) } } sqlAdmin := expandArmWorkspaceAadAdminInfo(d.Get("sql_aad_admin").([]interface{})) if sqlAdmin != nil { - workspaceSqlAdminsCreateOrUpdateFuture, err := sqlAdminClient.CreateOrUpdate(ctx, resourceGroup, name, *sqlAdmin) + future, err := sqlAdminClient.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, *sqlAdmin) if err != nil { - return fmt.Errorf("updating Synapse Workspace %q Sql Admin (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("configuring Sql Admin for %s: %+v", id, err) } - if err = workspaceSqlAdminsCreateOrUpdateFuture.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting on updating for Synapse Workspace %q Sql Admin (Resource Group %q): %+v", name, resourceGroup, err) + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for configuration of Sql Admin for %s: %+v", id, err) } } sqlControlSettings := expandIdentityControlSQLSettings(d.Get("sql_identity_control_enabled").(bool)) - if _, err = identitySQLControlClient.CreateOrUpdate(ctx, resourceGroup, name, *sqlControlSettings); err != nil { - return fmt.Errorf("Granting workspace identity control for SQL pool: %+v", err) + future2, err := identitySQLControlClient.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, *sqlControlSettings) + if err != nil { + return fmt.Errorf("configuring Sql Identity Control for %s: %+v", id, err) + } + if err = future2.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for configuration of Sql Identity Control for %s: %+v", id, err) } - subscriptionId := meta.(*clients.Client).Account.SubscriptionId - id := parse.NewWorkspaceID(subscriptionId, resourceGroup, name) d.SetId(id.ID()) - return resourceSynapseWorkspaceRead(d, meta) } @@ -468,29 +468,29 @@ func resourceSynapseWorkspaceRead(d *pluginsdk.ResourceData, meta interface{}) e resp, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] synapse %q does not exist - removing from state", d.Id()) + log.Printf("[INFO] %s does not exist - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("retrieving Synapse Workspace %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } aadAdmin, err := aadAdminClient.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if !utils.ResponseWasNotFound(aadAdmin.Response) { - return fmt.Errorf("retrieving Synapse Workspace %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving AzureAD Admin for %s: %+v", *id, err) } } sqlAdmin, err := sqlAdminClient.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if !utils.ResponseWasNotFound(sqlAdmin.Response) { - return fmt.Errorf("retrieving Synapse Workspace %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving Sql Admin for %s: %+v", *id, err) } } sqlControlSettings, err := identitySQLControlClient.Get(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("retrieving workspace identity control for SQL pool: %+v", err) + return fmt.Errorf("retrieving Sql Identity Control for %s: %+v", *id, err) } d.Set("name", id.Name) diff --git a/website/docs/r/synapse_managed_private_endpoint.html.markdown b/website/docs/r/synapse_managed_private_endpoint.html.markdown index 216cad4cf005..ba84ed1e1c05 100644 --- a/website/docs/r/synapse_managed_private_endpoint.html.markdown +++ b/website/docs/r/synapse_managed_private_endpoint.html.markdown @@ -8,7 +8,7 @@ description: |- # azurerm_synapse_managed_private_endpoint -Allows you to Manages a Synapse Managed Private Endpoint. +Manages a Synapse Managed Private Endpoint. ## Example Usage