From 0bfb32c9cef3a8bb4ca0c3df5d1977309b5b0418 Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Wed, 13 Mar 2024 01:55:30 +0000 Subject: [PATCH] Add new resource Service for Apphub (#10132) * Add new resource for Service Project for Apphub FIXES https://github.com/hashicorp/terraform-provider-google/issues/17405 * Add new resource for Service Project for Apphub FIXES https://github.com/hashicorp/terraform-provider-google/issues/17405 * Add new resource for Service Project Attachment for Apphub FIXES https://github.com/hashicorp/terraform-provider-google/issues/17405 * Add new resource for Service Project Attachment for Apphub FIXES https://github.com/hashicorp/terraform-provider-google/issues/17405 * Add new resource for Service Project Attachment for Apphub FIXES https://github.com/hashicorp/terraform-provider-google/issues/17405 * Add new resource for Application for Apphub * Add new resource for Application for Apphub * Enable Apphub API in test cases * Add new resource for Service Project Attachment for Apphub FIXES https://github.com/hashicorp/terraform-provider-google/issues/17405 * Enable apphub API in test cases * Fix precheck error * Fix precheck error * Fix precheck error * Fix precheck error * Resolve comments * Fix lint error * Fix errors * Add new resource for Service for Apphub * Fix examples * Add test * Fix tests * Add IAM permissions * Add IAM permission * Fix IAM issues * Remove merged changes * Add billing account * Remove extra delay from tests * Add handwritten update test * Remove merged changes * Make changes to yaml file incorporating comments from https://github.com/GoogleCloudPlatform/magic-modules/pull/10155 * Fix lint errors in Service YAML definition * Add a delay between discovered resource fetch and forwarding rule creation * Add a delay between discovered resource fetch and forwarding rule creation * Add a delay between discovered resource fetch and forwarding rule creation * Batch update tests to improve speed * Use standard diff suppress function * Update region from us-east1 to us-central1 --------- Co-authored-by: Krishnan Gopal Co-authored-by: praseedhaPK <161299686+praseedhaPK@users.noreply.github.com> [upstream:4fa7f7c5c43d93b9813acd33ecf232f6cd6151ed] Signed-off-by: Modular Magician --- .changelog/10132.txt | 3 + google/provider/provider_mmv1_resources.go | 5 +- .../apphub/resource_apphub_service.go | 1043 +++++++++++++++++ .../resource_apphub_service_generated_test.go | 381 ++++++ .../apphub/resource_apphub_service_sweeper.go | 143 +++ .../apphub/resource_apphub_service_test.go | 165 +++ website/docs/r/apphub_service.html.markdown | 480 ++++++++ 7 files changed, 2218 insertions(+), 2 deletions(-) create mode 100644 .changelog/10132.txt create mode 100644 google/services/apphub/resource_apphub_service.go create mode 100644 google/services/apphub/resource_apphub_service_generated_test.go create mode 100644 google/services/apphub/resource_apphub_service_sweeper.go create mode 100644 google/services/apphub/resource_apphub_service_test.go create mode 100644 website/docs/r/apphub_service.html.markdown diff --git a/.changelog/10132.txt b/.changelog/10132.txt new file mode 100644 index 00000000000..ec5e55a891f --- /dev/null +++ b/.changelog/10132.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +google_apphub_service +``` \ No newline at end of file diff --git a/google/provider/provider_mmv1_resources.go b/google/provider/provider_mmv1_resources.go index 8d849b44bac..dff564a0d34 100644 --- a/google/provider/provider_mmv1_resources.go +++ b/google/provider/provider_mmv1_resources.go @@ -392,9 +392,9 @@ var handwrittenIAMDatasources = map[string]*schema.Resource{ } // Resources -// Generated resources: 393 +// Generated resources: 394 // Generated IAM resources: 234 -// Total generated resources: 627 +// Total generated resources: 628 var generatedResources = map[string]*schema.Resource{ "google_folder_access_approval_settings": accessapproval.ResourceAccessApprovalFolderSettings(), "google_organization_access_approval_settings": accessapproval.ResourceAccessApprovalOrganizationSettings(), @@ -446,6 +446,7 @@ var generatedResources = map[string]*schema.Resource{ "google_app_engine_service_split_traffic": appengine.ResourceAppEngineServiceSplitTraffic(), "google_app_engine_standard_app_version": appengine.ResourceAppEngineStandardAppVersion(), "google_apphub_application": apphub.ResourceApphubApplication(), + "google_apphub_service": apphub.ResourceApphubService(), "google_apphub_service_project_attachment": apphub.ResourceApphubServiceProjectAttachment(), "google_apphub_workload": apphub.ResourceApphubWorkload(), "google_artifact_registry_repository": artifactregistry.ResourceArtifactRegistryRepository(), diff --git a/google/services/apphub/resource_apphub_service.go b/google/services/apphub/resource_apphub_service.go new file mode 100644 index 00000000000..506392daeac --- /dev/null +++ b/google/services/apphub/resource_apphub_service.go @@ -0,0 +1,1043 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package apphub + +import ( + "fmt" + "log" + "reflect" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" + "github.com/hashicorp/terraform-provider-google/google/verify" +) + +func ResourceApphubService() *schema.Resource { + return &schema.Resource{ + Create: resourceApphubServiceCreate, + Read: resourceApphubServiceRead, + Update: resourceApphubServiceUpdate, + Delete: resourceApphubServiceDelete, + + Importer: &schema.ResourceImporter{ + State: resourceApphubServiceImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + CustomizeDiff: customdiff.All( + tpgresource.DefaultProviderProject, + ), + + Schema: map[string]*schema.Schema{ + "application_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Part of 'parent'. Full resource name of a parent Application. Example: projects/{HOST_PROJECT_ID}/locations/{LOCATION}/applications/{APPLICATION_ID}`, + }, + "discovered_service": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: tpgresource.ProjectNumberDiffSuppress, + Description: `Immutable. The resource name of the original discovered service.`, + }, + "location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Part of 'parent'. Full resource name of a parent Application. Example: projects/{HOST_PROJECT_ID}/locations/{LOCATION}/applications/{APPLICATION_ID}`, + }, + "service_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The Service identifier.`, + }, + "attributes": { + Type: schema.TypeList, + Optional: true, + Description: `Consumer provided attributes.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "business_owners": { + Type: schema.TypeList, + Optional: true, + Description: `Business team that ensures user needs are met and value is delivered`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email": { + Type: schema.TypeString, + Required: true, + Description: `Required. Email address of the contacts.`, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + Description: `Contact's name.`, + }, + }, + }, + }, + "criticality": { + Type: schema.TypeList, + Optional: true, + Description: `Criticality of the Application, Service, or Workload`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidateEnum([]string{"MISSION_CRITICAL", "HIGH", "MEDIUM", "LOW"}), + Description: `Criticality type. Possible values: ["MISSION_CRITICAL", "HIGH", "MEDIUM", "LOW"]`, + }, + }, + }, + }, + "developer_owners": { + Type: schema.TypeList, + Optional: true, + Description: `Developer team that owns development and coding.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email": { + Type: schema.TypeString, + Required: true, + Description: `Required. Email address of the contacts.`, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + Description: `Contact's name.`, + }, + }, + }, + }, + "environment": { + Type: schema.TypeList, + Optional: true, + Description: `Environment of the Application, Service, or Workload`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidateEnum([]string{"PRODUCTION", "STAGING", "TEST", "DEVELOPMENT"}), + Description: `Environment type. Possible values: ["PRODUCTION", "STAGING", "TEST", "DEVELOPMENT"]`, + }, + }, + }, + }, + "operator_owners": { + Type: schema.TypeList, + Optional: true, + Description: `Operator team that ensures runtime and operations.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email": { + Type: schema.TypeString, + Required: true, + Description: `Required. Email address of the contacts.`, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + Description: `Contact's name.`, + }, + }, + }, + }, + }, + }, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `User-defined description of a Service.`, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + Description: `User-defined name for the Service.`, + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. Create time.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `Identifier. The resource name of a Service. Format: +"projects/{host-project-id}/locations/{location}/applications/{application-id}/services/{service-id}"`, + }, + "service_properties": { + Type: schema.TypeList, + Computed: true, + Description: `Properties of an underlying cloud resource that can comprise a Service.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "gcp_project": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The service project identifier that the underlying cloud resource resides in.`, + }, + "location": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The location that the underlying resource resides in, for example, us-west1.`, + }, + "zone": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The location that the underlying resource resides in if it is zonal, for example, us-west1-a).`, + }, + }, + }, + }, + "service_reference": { + Type: schema.TypeList, + Computed: true, + Description: `Reference to an underlying networking resource that can comprise a Service.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uri": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The underlying resource URI (For example, URI of Forwarding Rule, URL Map, +and Backend Service).`, + }, + }, + }, + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. Service state. Possible values: STATE_UNSPECIFIED CREATING ACTIVE DELETING DETACHED`, + }, + "uid": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. A universally unique identifier (UUID) for the 'Service' in the UUID4 +format.`, + }, + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. Update time.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceApphubServiceCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + displayNameProp, err := expandApphubServiceDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(displayNameProp)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } + descriptionProp, err := expandApphubServiceDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !tpgresource.IsEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + attributesProp, err := expandApphubServiceAttributes(d.Get("attributes"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("attributes"); !tpgresource.IsEmptyValue(reflect.ValueOf(attributesProp)) && (ok || !reflect.DeepEqual(v, attributesProp)) { + obj["attributes"] = attributesProp + } + discoveredServiceProp, err := expandApphubServiceDiscoveredService(d.Get("discovered_service"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("discovered_service"); !tpgresource.IsEmptyValue(reflect.ValueOf(discoveredServiceProp)) && (ok || !reflect.DeepEqual(v, discoveredServiceProp)) { + obj["discoveredService"] = discoveredServiceProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{ApphubBasePath}}projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services?serviceId={{service_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new Service: %#v", obj) + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Service: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutCreate), + }) + if err != nil { + return fmt.Errorf("Error creating Service: %s", err) + } + + // Store the ID now + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + // Use the resource in the operation response to populate + // identity fields and d.Id() before read + var opRes map[string]interface{} + err = ApphubOperationWaitTimeWithResponse( + config, res, &opRes, project, "Creating Service", userAgent, + d.Timeout(schema.TimeoutCreate)) + if err != nil { + // The resource didn't actually create + d.SetId("") + + return fmt.Errorf("Error waiting to create Service: %s", err) + } + + if err := d.Set("name", flattenApphubServiceName(opRes["name"], d, config)); err != nil { + return err + } + + // This may have caused the ID to update - update it if so. + id, err = tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating Service %q: %#v", d.Id(), res) + + return resourceApphubServiceRead(d, meta) +} + +func resourceApphubServiceRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{ApphubBasePath}}projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Service: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("ApphubService %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + + if err := d.Set("name", flattenApphubServiceName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("display_name", flattenApphubServiceDisplayName(res["displayName"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("description", flattenApphubServiceDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("service_reference", flattenApphubServiceServiceReference(res["serviceReference"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("service_properties", flattenApphubServiceServiceProperties(res["serviceProperties"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("attributes", flattenApphubServiceAttributes(res["attributes"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("discovered_service", flattenApphubServiceDiscoveredService(res["discoveredService"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("create_time", flattenApphubServiceCreateTime(res["createTime"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("update_time", flattenApphubServiceUpdateTime(res["updateTime"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("uid", flattenApphubServiceUid(res["uid"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("state", flattenApphubServiceState(res["state"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + + return nil +} + +func resourceApphubServiceUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Service: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + displayNameProp, err := expandApphubServiceDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } + descriptionProp, err := expandApphubServiceDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + attributesProp, err := expandApphubServiceAttributes(d.Get("attributes"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("attributes"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, attributesProp)) { + obj["attributes"] = attributesProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{ApphubBasePath}}projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating Service %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("display_name") { + updateMask = append(updateMask, "displayName") + } + + if d.HasChange("description") { + updateMask = append(updateMask, "description") + } + + if d.HasChange("attributes") { + updateMask = append(updateMask, "attributes") + } + // updateMask is a URL parameter but not present in the schema, so ReplaceVars + // won't set it + url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + // if updateMask is empty we are not updating anything so skip the post + if len(updateMask) > 0 { + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutUpdate), + }) + + if err != nil { + return fmt.Errorf("Error updating Service %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating Service %q: %#v", d.Id(), res) + } + + err = ApphubOperationWaitTime( + config, res, project, "Updating Service", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + } + + return resourceApphubServiceRead(d, meta) +} + +func resourceApphubServiceDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Service: %s", err) + } + billingProject = project + + url, err := tpgresource.ReplaceVars(d, config, "{{ApphubBasePath}}projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}") + if err != nil { + return err + } + + var obj map[string]interface{} + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + log.Printf("[DEBUG] Deleting Service %q", d.Id()) + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutDelete), + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, "Service") + } + + err = ApphubOperationWaitTime( + config, res, project, "Deleting Service", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting Service %q: %#v", d.Id(), res) + return nil +} + +func resourceApphubServiceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*transport_tpg.Config) + if err := tpgresource.ParseImportId([]string{ + "^projects/(?P[^/]+)/locations/(?P[^/]+)/applications/(?P[^/]+)/services/(?P[^/]+)$", + "^(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)$", + "^(?P[^/]+)/(?P[^/]+)/(?P[^/]+)$", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenApphubServiceName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceDisplayName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceDescription(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceServiceReference(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["uri"] = + flattenApphubServiceServiceReferenceUri(original["uri"], d, config) + return []interface{}{transformed} +} +func flattenApphubServiceServiceReferenceUri(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceServiceProperties(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["gcp_project"] = + flattenApphubServiceServicePropertiesGcpProject(original["gcpProject"], d, config) + transformed["location"] = + flattenApphubServiceServicePropertiesLocation(original["location"], d, config) + transformed["zone"] = + flattenApphubServiceServicePropertiesZone(original["zone"], d, config) + return []interface{}{transformed} +} +func flattenApphubServiceServicePropertiesGcpProject(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceServicePropertiesLocation(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceServicePropertiesZone(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceAttributes(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["criticality"] = + flattenApphubServiceAttributesCriticality(original["criticality"], d, config) + transformed["environment"] = + flattenApphubServiceAttributesEnvironment(original["environment"], d, config) + transformed["developer_owners"] = + flattenApphubServiceAttributesDeveloperOwners(original["developerOwners"], d, config) + transformed["operator_owners"] = + flattenApphubServiceAttributesOperatorOwners(original["operatorOwners"], d, config) + transformed["business_owners"] = + flattenApphubServiceAttributesBusinessOwners(original["businessOwners"], d, config) + return []interface{}{transformed} +} +func flattenApphubServiceAttributesCriticality(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["type"] = + flattenApphubServiceAttributesCriticalityType(original["type"], d, config) + return []interface{}{transformed} +} +func flattenApphubServiceAttributesCriticalityType(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceAttributesEnvironment(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["type"] = + flattenApphubServiceAttributesEnvironmentType(original["type"], d, config) + return []interface{}{transformed} +} +func flattenApphubServiceAttributesEnvironmentType(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceAttributesDeveloperOwners(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "display_name": flattenApphubServiceAttributesDeveloperOwnersDisplayName(original["displayName"], d, config), + "email": flattenApphubServiceAttributesDeveloperOwnersEmail(original["email"], d, config), + }) + } + return transformed +} +func flattenApphubServiceAttributesDeveloperOwnersDisplayName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceAttributesDeveloperOwnersEmail(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceAttributesOperatorOwners(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "display_name": flattenApphubServiceAttributesOperatorOwnersDisplayName(original["displayName"], d, config), + "email": flattenApphubServiceAttributesOperatorOwnersEmail(original["email"], d, config), + }) + } + return transformed +} +func flattenApphubServiceAttributesOperatorOwnersDisplayName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceAttributesOperatorOwnersEmail(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceAttributesBusinessOwners(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "display_name": flattenApphubServiceAttributesBusinessOwnersDisplayName(original["displayName"], d, config), + "email": flattenApphubServiceAttributesBusinessOwnersEmail(original["email"], d, config), + }) + } + return transformed +} +func flattenApphubServiceAttributesBusinessOwnersDisplayName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceAttributesBusinessOwnersEmail(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceDiscoveredService(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceCreateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceUpdateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceUid(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenApphubServiceState(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func expandApphubServiceDisplayName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandApphubServiceDescription(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandApphubServiceAttributes(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedCriticality, err := expandApphubServiceAttributesCriticality(original["criticality"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCriticality); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["criticality"] = transformedCriticality + } + + transformedEnvironment, err := expandApphubServiceAttributesEnvironment(original["environment"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnvironment); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["environment"] = transformedEnvironment + } + + transformedDeveloperOwners, err := expandApphubServiceAttributesDeveloperOwners(original["developer_owners"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDeveloperOwners); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["developerOwners"] = transformedDeveloperOwners + } + + transformedOperatorOwners, err := expandApphubServiceAttributesOperatorOwners(original["operator_owners"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedOperatorOwners); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["operatorOwners"] = transformedOperatorOwners + } + + transformedBusinessOwners, err := expandApphubServiceAttributesBusinessOwners(original["business_owners"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBusinessOwners); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["businessOwners"] = transformedBusinessOwners + } + + return transformed, nil +} + +func expandApphubServiceAttributesCriticality(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedType, err := expandApphubServiceAttributesCriticalityType(original["type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedType); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["type"] = transformedType + } + + return transformed, nil +} + +func expandApphubServiceAttributesCriticalityType(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandApphubServiceAttributesEnvironment(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedType, err := expandApphubServiceAttributesEnvironmentType(original["type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedType); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["type"] = transformedType + } + + return transformed, nil +} + +func expandApphubServiceAttributesEnvironmentType(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandApphubServiceAttributesDeveloperOwners(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDisplayName, err := expandApphubServiceAttributesDeveloperOwnersDisplayName(original["display_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisplayName); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["displayName"] = transformedDisplayName + } + + transformedEmail, err := expandApphubServiceAttributesDeveloperOwnersEmail(original["email"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEmail); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["email"] = transformedEmail + } + + req = append(req, transformed) + } + return req, nil +} + +func expandApphubServiceAttributesDeveloperOwnersDisplayName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandApphubServiceAttributesDeveloperOwnersEmail(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandApphubServiceAttributesOperatorOwners(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDisplayName, err := expandApphubServiceAttributesOperatorOwnersDisplayName(original["display_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisplayName); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["displayName"] = transformedDisplayName + } + + transformedEmail, err := expandApphubServiceAttributesOperatorOwnersEmail(original["email"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEmail); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["email"] = transformedEmail + } + + req = append(req, transformed) + } + return req, nil +} + +func expandApphubServiceAttributesOperatorOwnersDisplayName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandApphubServiceAttributesOperatorOwnersEmail(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandApphubServiceAttributesBusinessOwners(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDisplayName, err := expandApphubServiceAttributesBusinessOwnersDisplayName(original["display_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisplayName); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["displayName"] = transformedDisplayName + } + + transformedEmail, err := expandApphubServiceAttributesBusinessOwnersEmail(original["email"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEmail); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["email"] = transformedEmail + } + + req = append(req, transformed) + } + return req, nil +} + +func expandApphubServiceAttributesBusinessOwnersDisplayName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandApphubServiceAttributesBusinessOwnersEmail(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandApphubServiceDiscoveredService(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} diff --git a/google/services/apphub/resource_apphub_service_generated_test.go b/google/services/apphub/resource_apphub_service_generated_test.go new file mode 100644 index 00000000000..63d595e7d71 --- /dev/null +++ b/google/services/apphub/resource_apphub_service_generated_test.go @@ -0,0 +1,381 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package apphub_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func TestAccApphubService_apphubServiceBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "billing_account": envvar.GetTestBillingAccountFromEnv(t), + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + "time": {}, + }, + CheckDestroy: testAccCheckApphubServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccApphubService_apphubServiceBasicExample(context), + }, + { + ResourceName: "google_apphub_service.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "application_id", "service_id"}, + }, + }, + }) +} + +func testAccApphubService_apphubServiceBasicExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_apphub_application" "application" { + location = "us-central1" + application_id = "tf-test-example-application-1%{random_suffix}" + scope { + type = "REGIONAL" + } +} + +resource "google_project" "service_project" { + project_id ="tf-test-project-1%{random_suffix}" + name = "Service Project" + org_id = "%{org_id}" + billing_account = "%{billing_account}" +} + +# Enable Compute API +resource "google_project_service" "compute_service_project" { + project = google_project.service_project.project_id + service = "compute.googleapis.com" +} + +resource "time_sleep" "wait_120s" { + depends_on = [google_project_service.compute_service_project] + + create_duration = "120s" +} + +resource "google_apphub_service_project_attachment" "service_project_attachment" { + service_project_attachment_id = google_project.service_project.project_id + depends_on = [time_sleep.wait_120s] +} + +# discovered service block +data "google_apphub_discovered_service" "catalog-service" { + provider = google + location = "us-central1" + service_uri = "//compute.googleapis.com/${google_compute_forwarding_rule.forwarding_rule.id}" + depends_on = [google_apphub_service_project_attachment.service_project_attachment, time_sleep.wait_120s_for_resource_ingestion] +} + +resource "time_sleep" "wait_120s_for_resource_ingestion" { + depends_on = [google_compute_forwarding_rule.forwarding_rule] + create_duration = "120s" +} + +resource "google_apphub_service" "example" { + location = "us-central1" + application_id = google_apphub_application.application.application_id + service_id = google_compute_forwarding_rule.forwarding_rule.name + discovered_service = data.google_apphub_discovered_service.catalog-service.name +} + + +#creates service + + +# VPC network +resource "google_compute_network" "ilb_network" { + name = "tf-test-l7-ilb-network%{random_suffix}" + project = google_project.service_project.project_id + auto_create_subnetworks = false + depends_on = [time_sleep.wait_120s] +} + + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "tf-test-l7-ilb-subnet%{random_suffix}" + project = google_project.service_project.project_id + ip_cidr_range = "10.0.1.0/24" + region = "us-central1" + network = google_compute_network.ilb_network.id +} + +# forwarding rule +resource "google_compute_forwarding_rule" "forwarding_rule" { + name ="tf-test-l7-ilb-forwarding-rule%{random_suffix}" + project = google_project.service_project.project_id + region = "us-central1" + ip_version = "IPV4" + load_balancing_scheme = "INTERNAL" + all_ports = true + backend_service = google_compute_region_backend_service.backend.id + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id +} + + + +# backend service +resource "google_compute_region_backend_service" "backend" { + name = "tf-test-l7-ilb-backend-subnet%{random_suffix}" + project = google_project.service_project.project_id + region = "us-central1" + health_checks = [google_compute_health_check.default.id] +} + +# health check +resource "google_compute_health_check" "default" { + name = "tf-test-l7-ilb-hc%{random_suffix}" + project = google_project.service_project.project_id + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "80" + } + depends_on = [time_sleep.wait_120s] +} +`, context) +} + +func TestAccApphubService_apphubServiceFullExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "billing_account": envvar.GetTestBillingAccountFromEnv(t), + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + "time": {}, + }, + CheckDestroy: testAccCheckApphubServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccApphubService_apphubServiceFullExample(context), + }, + { + ResourceName: "google_apphub_service.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "application_id", "service_id"}, + }, + }, + }) +} + +func testAccApphubService_apphubServiceFullExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_apphub_application" "application" { + location = "us-central1" + application_id = "tf-test-example-application-1%{random_suffix}" + scope { + type = "REGIONAL" + } +} + +resource "google_project" "service_project" { + project_id ="tf-test-project-1%{random_suffix}" + name = "Service Project" + org_id = "%{org_id}" + billing_account = "%{billing_account}" +} + +# Enable Compute API +resource "google_project_service" "compute_service_project" { + project = google_project.service_project.project_id + service = "compute.googleapis.com" +} + +resource "time_sleep" "wait_120s" { + depends_on = [google_project_service.compute_service_project] + + create_duration = "120s" +} + +resource "google_apphub_service_project_attachment" "service_project_attachment" { + service_project_attachment_id = google_project.service_project.project_id + depends_on = [time_sleep.wait_120s] +} + +# discovered service block +data "google_apphub_discovered_service" "catalog-service" { + provider = google + location = "us-central1" + service_uri = "//compute.googleapis.com/${google_compute_forwarding_rule.forwarding_rule.id}" + depends_on = [google_apphub_service_project_attachment.service_project_attachment, time_sleep.wait_120s_for_resource_ingestion] +} + +resource "time_sleep" "wait_120s_for_resource_ingestion" { + depends_on = [google_compute_forwarding_rule.forwarding_rule] + create_duration = "120s" +} + +resource "google_apphub_service" "example" { + location = "us-central1" + application_id = google_apphub_application.application.application_id + service_id = google_compute_forwarding_rule.forwarding_rule.name + discovered_service = data.google_apphub_discovered_service.catalog-service.name + display_name = "Example Service Full%{random_suffix}" + description = "Register service for testing%{random_suffix}" + attributes { + environment { + type = "STAGING" + } + criticality { + type = "MISSION_CRITICAL" + } + business_owners { + display_name = "Alice%{random_suffix}" + email = "alice@google.com%{random_suffix}" + } + developer_owners { + display_name = "Bob%{random_suffix}" + email = "bob@google.com%{random_suffix}" + } + operator_owners { + display_name = "Charlie%{random_suffix}" + email = "charlie@google.com%{random_suffix}" + } + } +} + + +#creates service + + +# VPC network +resource "google_compute_network" "ilb_network" { + name = "tf-test-l7-ilb-network%{random_suffix}" + project = google_project.service_project.project_id + auto_create_subnetworks = false + depends_on = [time_sleep.wait_120s] +} + + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "tf-test-l7-ilb-subnet%{random_suffix}" + project = google_project.service_project.project_id + ip_cidr_range = "10.0.1.0/24" + region = "us-central1" + network = google_compute_network.ilb_network.id +} + +# forwarding rule +resource "google_compute_forwarding_rule" "forwarding_rule" { + name ="tf-test-l7-ilb-forwarding-rule%{random_suffix}" + project = google_project.service_project.project_id + region = "us-central1" + ip_version = "IPV4" + load_balancing_scheme = "INTERNAL" + all_ports = true + backend_service = google_compute_region_backend_service.backend.id + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id +} + + + +# backend service +resource "google_compute_region_backend_service" "backend" { + name = "tf-test-l7-ilb-backend-subnet%{random_suffix}" + project = google_project.service_project.project_id + region = "us-central1" + health_checks = [google_compute_health_check.default.id] +} + +# health check +resource "google_compute_health_check" "default" { + name = "tf-test-l7-ilb-hc%{random_suffix}" + project = google_project.service_project.project_id + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "80" + } + depends_on = [time_sleep.wait_120s] +} +`, context) +} + +func testAccCheckApphubServiceDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_apphub_service" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := acctest.GoogleProviderConfig(t) + + url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{ApphubBasePath}}projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: config.UserAgent, + }) + if err == nil { + return fmt.Errorf("ApphubService still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/services/apphub/resource_apphub_service_sweeper.go b/google/services/apphub/resource_apphub_service_sweeper.go new file mode 100644 index 00000000000..51b6eec450a --- /dev/null +++ b/google/services/apphub/resource_apphub_service_sweeper.go @@ -0,0 +1,143 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package apphub + +import ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-provider-google/google/envvar" + "github.com/hashicorp/terraform-provider-google/google/sweeper" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func init() { + sweeper.AddTestSweepers("ApphubService", testSweepApphubService) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepApphubService(region string) error { + resourceName := "ApphubService" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sweeper.SharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := envvar.GetTestBillingAccountFromEnv(t) + + // Setup variables to replace in list template + d := &tpgresource.ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://apphub.googleapis.com/v1/projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services", "?")[0] + listUrl, err := tpgresource.ReplaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: config.Project, + RawURL: listUrl, + UserAgent: config.UserAgent, + }) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["services"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + var name string + // Id detected in the delete URL, attempt to use id. + if obj["id"] != nil { + name = tpgresource.GetResourceNameFromSelfLink(obj["id"].(string)) + } else if obj["name"] != nil { + name = tpgresource.GetResourceNameFromSelfLink(obj["name"].(string)) + } else { + log.Printf("[INFO][SWEEPER_LOG] %s resource name and id were nil", resourceName) + return nil + } + // Skip resources that shouldn't be sweeped + if !sweeper.IsSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://apphub.googleapis.com/v1/projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}" + deleteUrl, err := tpgresource.ReplaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: config.Project, + RawURL: deleteUrl, + UserAgent: config.UserAgent, + }) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + + return nil +} diff --git a/google/services/apphub/resource_apphub_service_test.go b/google/services/apphub/resource_apphub_service_test.go new file mode 100644 index 00000000000..8e65a8c9d13 --- /dev/null +++ b/google/services/apphub/resource_apphub_service_test.go @@ -0,0 +1,165 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package apphub_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func TestAccApphubService_serviceUpdate(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "billing_account": envvar.GetTestBillingAccountFromEnv(t), + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + "time": {}, + }, + CheckDestroy: testAccCheckApphubServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccApphubService_apphubServiceFullExample(context), + }, + { + ResourceName: "google_apphub_service.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "application_id", "service_id"}, + }, + { + Config: testAccApphubService_apphubServiceUpdate(context), + }, + { + ResourceName: "google_apphub_service.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "application_id", "service_id"}, + }, + }, + }) +} + +func testAccApphubService_apphubServiceUpdate(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_apphub_application" "application" { + location = "us-central1" + application_id = "tf-test-example-application-1%{random_suffix}" + scope { + type = "REGIONAL" + } +} + +resource "google_project" "service_project" { + project_id ="tf-test-project-1%{random_suffix}" + name = "Service Project" + org_id = "%{org_id}" + billing_account = "%{billing_account}" +} + +# Enable Compute API +resource "google_project_service" "compute_service_project" { + project = google_project.service_project.project_id + service = "compute.googleapis.com" +} + +resource "time_sleep" "wait_120s" { + depends_on = [google_project_service.compute_service_project] + + create_duration = "120s" +} + +resource "google_apphub_service_project_attachment" "service_project_attachment" { + service_project_attachment_id = google_project.service_project.project_id + depends_on = [time_sleep.wait_120s] +} + +# discovered service block +data "google_apphub_discovered_service" "catalog-service" { + provider = google + location = "us-central1" + service_uri = "//compute.googleapis.com/${google_compute_forwarding_rule.forwarding_rule.id}" + depends_on = [google_apphub_service_project_attachment.service_project_attachment, time_sleep.wait_120s_for_resource_ingestion] +} + +resource "time_sleep" "wait_120s_for_resource_ingestion" { + depends_on = [google_compute_forwarding_rule.forwarding_rule] + create_duration = "120s" +} + +resource "google_apphub_service" "example" { + location = "us-central1" + application_id = google_apphub_application.application.application_id + service_id = google_compute_forwarding_rule.forwarding_rule.name + discovered_service = data.google_apphub_discovered_service.catalog-service.name +} + + +#creates service + + +# VPC network +resource "google_compute_network" "ilb_network" { + name = "tf-test-l7-ilb-network%{random_suffix}" + project = google_project.service_project.project_id + auto_create_subnetworks = false + depends_on = [time_sleep.wait_120s] +} + + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "tf-test-l7-ilb-subnet%{random_suffix}" + project = google_project.service_project.project_id + ip_cidr_range = "10.0.1.0/24" + region = "us-central1" + network = google_compute_network.ilb_network.id +} + +# forwarding rule +resource "google_compute_forwarding_rule" "forwarding_rule" { + name ="tf-test-l7-ilb-forwarding-rule%{random_suffix}" + project = google_project.service_project.project_id + region = "us-central1" + ip_version = "IPV4" + load_balancing_scheme = "INTERNAL" + all_ports = true + backend_service = google_compute_region_backend_service.backend.id + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id +} + + + +# backend service +resource "google_compute_region_backend_service" "backend" { + name = "tf-test-l7-ilb-backend-subnet%{random_suffix}" + project = google_project.service_project.project_id + region = "us-central1" + health_checks = [google_compute_health_check.default.id] +} + +# health check +resource "google_compute_health_check" "default" { + name = "tf-test-l7-ilb-hc%{random_suffix}" + project = google_project.service_project.project_id + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "80" + } + depends_on = [time_sleep.wait_120s] +} +`, context) +} diff --git a/website/docs/r/apphub_service.html.markdown b/website/docs/r/apphub_service.html.markdown new file mode 100644 index 00000000000..abc1b879c4f --- /dev/null +++ b/website/docs/r/apphub_service.html.markdown @@ -0,0 +1,480 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: MMv1 *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "App Hub" +description: |- + Service is a network/api interface that exposes some functionality to clients for consumption over the network. +--- + +# google\_apphub\_service + +Service is a network/api interface that exposes some functionality to clients for consumption over the network. Service typically has one or more Workloads behind it. It registers identified service to the Application. + + + +## Example Usage - Apphub Service Basic + + +```hcl +resource "google_apphub_application" "application" { + location = "us-central1" + application_id = "example-application-1" + scope { + type = "REGIONAL" + } +} + +resource "google_project" "service_project" { + project_id ="project-1" + name = "Service Project" + org_id = "123456789" + billing_account = "000000-0000000-0000000-000000" +} + +# Enable Compute API +resource "google_project_service" "compute_service_project" { + project = google_project.service_project.project_id + service = "compute.googleapis.com" +} + +resource "time_sleep" "wait_120s" { + depends_on = [google_project_service.compute_service_project] + + create_duration = "120s" +} + +resource "google_apphub_service_project_attachment" "service_project_attachment" { + service_project_attachment_id = google_project.service_project.project_id + depends_on = [time_sleep.wait_120s] +} + +# discovered service block +data "google_apphub_discovered_service" "catalog-service" { + provider = google + location = "us-central1" + service_uri = "//compute.googleapis.com/${google_compute_forwarding_rule.forwarding_rule.id}" + depends_on = [google_apphub_service_project_attachment.service_project_attachment, time_sleep.wait_120s_for_resource_ingestion] +} + +resource "time_sleep" "wait_120s_for_resource_ingestion" { + depends_on = [google_compute_forwarding_rule.forwarding_rule] + create_duration = "120s" +} + +resource "google_apphub_service" "example" { + location = "us-central1" + application_id = google_apphub_application.application.application_id + service_id = google_compute_forwarding_rule.forwarding_rule.name + discovered_service = data.google_apphub_discovered_service.catalog-service.name +} + + +#creates service + + +# VPC network +resource "google_compute_network" "ilb_network" { + name = "l7-ilb-network" + project = google_project.service_project.project_id + auto_create_subnetworks = false + depends_on = [time_sleep.wait_120s] +} + + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "l7-ilb-subnet" + project = google_project.service_project.project_id + ip_cidr_range = "10.0.1.0/24" + region = "us-central1" + network = google_compute_network.ilb_network.id +} + +# forwarding rule +resource "google_compute_forwarding_rule" "forwarding_rule" { + name ="l7-ilb-forwarding-rule" + project = google_project.service_project.project_id + region = "us-central1" + ip_version = "IPV4" + load_balancing_scheme = "INTERNAL" + all_ports = true + backend_service = google_compute_region_backend_service.backend.id + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id +} + + + +# backend service +resource "google_compute_region_backend_service" "backend" { + name = "l7-ilb-backend-subnet" + project = google_project.service_project.project_id + region = "us-central1" + health_checks = [google_compute_health_check.default.id] +} + +# health check +resource "google_compute_health_check" "default" { + name = "l7-ilb-hc" + project = google_project.service_project.project_id + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "80" + } + depends_on = [time_sleep.wait_120s] +} +``` +## Example Usage - Apphub Service Full + + +```hcl +resource "google_apphub_application" "application" { + location = "us-central1" + application_id = "example-application-1" + scope { + type = "REGIONAL" + } +} + +resource "google_project" "service_project" { + project_id ="project-1" + name = "Service Project" + org_id = "123456789" + billing_account = "000000-0000000-0000000-000000" +} + +# Enable Compute API +resource "google_project_service" "compute_service_project" { + project = google_project.service_project.project_id + service = "compute.googleapis.com" +} + +resource "time_sleep" "wait_120s" { + depends_on = [google_project_service.compute_service_project] + + create_duration = "120s" +} + +resource "google_apphub_service_project_attachment" "service_project_attachment" { + service_project_attachment_id = google_project.service_project.project_id + depends_on = [time_sleep.wait_120s] +} + +# discovered service block +data "google_apphub_discovered_service" "catalog-service" { + provider = google + location = "us-central1" + service_uri = "//compute.googleapis.com/${google_compute_forwarding_rule.forwarding_rule.id}" + depends_on = [google_apphub_service_project_attachment.service_project_attachment, time_sleep.wait_120s_for_resource_ingestion] +} + +resource "time_sleep" "wait_120s_for_resource_ingestion" { + depends_on = [google_compute_forwarding_rule.forwarding_rule] + create_duration = "120s" +} + +resource "google_apphub_service" "example" { + location = "us-central1" + application_id = google_apphub_application.application.application_id + service_id = google_compute_forwarding_rule.forwarding_rule.name + discovered_service = data.google_apphub_discovered_service.catalog-service.name + display_name = "Example Service Full" + description = "Register service for testing" + attributes { + environment { + type = "STAGING" + } + criticality { + type = "MISSION_CRITICAL" + } + business_owners { + display_name = "Alice" + email = "alice@google.com" + } + developer_owners { + display_name = "Bob" + email = "bob@google.com" + } + operator_owners { + display_name = "Charlie" + email = "charlie@google.com" + } + } +} + + +#creates service + + +# VPC network +resource "google_compute_network" "ilb_network" { + name = "l7-ilb-network" + project = google_project.service_project.project_id + auto_create_subnetworks = false + depends_on = [time_sleep.wait_120s] +} + + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "l7-ilb-subnet" + project = google_project.service_project.project_id + ip_cidr_range = "10.0.1.0/24" + region = "us-central1" + network = google_compute_network.ilb_network.id +} + +# forwarding rule +resource "google_compute_forwarding_rule" "forwarding_rule" { + name ="l7-ilb-forwarding-rule" + project = google_project.service_project.project_id + region = "us-central1" + ip_version = "IPV4" + load_balancing_scheme = "INTERNAL" + all_ports = true + backend_service = google_compute_region_backend_service.backend.id + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id +} + + + +# backend service +resource "google_compute_region_backend_service" "backend" { + name = "l7-ilb-backend-subnet" + project = google_project.service_project.project_id + region = "us-central1" + health_checks = [google_compute_health_check.default.id] +} + +# health check +resource "google_compute_health_check" "default" { + name = "l7-ilb-hc" + project = google_project.service_project.project_id + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "80" + } + depends_on = [time_sleep.wait_120s] +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `discovered_service` - + (Required) + Immutable. The resource name of the original discovered service. + +* `location` - + (Required) + Part of `parent`. Full resource name of a parent Application. Example: projects/{HOST_PROJECT_ID}/locations/{LOCATION}/applications/{APPLICATION_ID} + +* `application_id` - + (Required) + Part of `parent`. Full resource name of a parent Application. Example: projects/{HOST_PROJECT_ID}/locations/{LOCATION}/applications/{APPLICATION_ID} + +* `service_id` - + (Required) + The Service identifier. + + +- - - + + +* `display_name` - + (Optional) + User-defined name for the Service. + +* `description` - + (Optional) + User-defined description of a Service. + +* `attributes` - + (Optional) + Consumer provided attributes. + Structure is [documented below](#nested_attributes). + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +The `attributes` block supports: + +* `criticality` - + (Optional) + Criticality of the Application, Service, or Workload + Structure is [documented below](#nested_criticality). + +* `environment` - + (Optional) + Environment of the Application, Service, or Workload + Structure is [documented below](#nested_environment). + +* `developer_owners` - + (Optional) + Developer team that owns development and coding. + Structure is [documented below](#nested_developer_owners). + +* `operator_owners` - + (Optional) + Operator team that ensures runtime and operations. + Structure is [documented below](#nested_operator_owners). + +* `business_owners` - + (Optional) + Business team that ensures user needs are met and value is delivered + Structure is [documented below](#nested_business_owners). + + +The `criticality` block supports: + +* `type` - + (Required) + Criticality type. + Possible values are: `MISSION_CRITICAL`, `HIGH`, `MEDIUM`, `LOW`. + +The `environment` block supports: + +* `type` - + (Required) + Environment type. + Possible values are: `PRODUCTION`, `STAGING`, `TEST`, `DEVELOPMENT`. + +The `developer_owners` block supports: + +* `display_name` - + (Optional) + Contact's name. + +* `email` - + (Required) + Required. Email address of the contacts. + +The `operator_owners` block supports: + +* `display_name` - + (Optional) + Contact's name. + +* `email` - + (Required) + Required. Email address of the contacts. + +The `business_owners` block supports: + +* `display_name` - + (Optional) + Contact's name. + +* `email` - + (Required) + Required. Email address of the contacts. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}` + +* `name` - + Identifier. The resource name of a Service. Format: + "projects/{host-project-id}/locations/{location}/applications/{application-id}/services/{service-id}" + +* `service_reference` - + Reference to an underlying networking resource that can comprise a Service. + Structure is [documented below](#nested_service_reference). + +* `service_properties` - + Properties of an underlying cloud resource that can comprise a Service. + Structure is [documented below](#nested_service_properties). + +* `create_time` - + Output only. Create time. + +* `update_time` - + Output only. Update time. + +* `uid` - + Output only. A universally unique identifier (UUID) for the `Service` in the UUID4 + format. + +* `state` - + Output only. Service state. Possible values: STATE_UNSPECIFIED CREATING ACTIVE DELETING DETACHED + + +The `service_reference` block contains: + +* `uri` - + (Output) + Output only. The underlying resource URI (For example, URI of Forwarding Rule, URL Map, + and Backend Service). + +The `service_properties` block contains: + +* `gcp_project` - + (Output) + Output only. The service project identifier that the underlying cloud resource resides in. + +* `location` - + (Output) + Output only. The location that the underlying resource resides in, for example, us-west1. + +* `zone` - + (Output) + Output only. The location that the underlying resource resides in if it is zonal, for example, us-west1-a). + +## Timeouts + +This resource provides the following +[Timeouts](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/retries-and-customizable-timeouts) configuration options: + +- `create` - Default is 20 minutes. +- `update` - Default is 20 minutes. +- `delete` - Default is 20 minutes. + +## Import + + +Service can be imported using any of these accepted formats: + +* `projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}` +* `{{project}}/{{location}}/{{application_id}}/{{service_id}}` +* `{{location}}/{{application_id}}/{{service_id}}` + + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Service using one of the formats above. For example: + +```tf +import { + id = "projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}}" + to = google_apphub_service.default +} +``` + +When using the [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import), Service can be imported using one of the formats above. For example: + +``` +$ terraform import google_apphub_service.default projects/{{project}}/locations/{{location}}/applications/{{application_id}}/services/{{service_id}} +$ terraform import google_apphub_service.default {{project}}/{{location}}/{{application_id}}/{{service_id}} +$ terraform import google_apphub_service.default {{location}}/{{application_id}}/{{service_id}} +``` + +## User Project Overrides + +This resource supports [User Project Overrides](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#user_project_override).