diff --git a/mmv1/products/accesscontextmanager/ServicePerimeter.yaml b/mmv1/products/accesscontextmanager/ServicePerimeter.yaml index 9200f3588ef7..14e83c95cd62 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeter.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeter.yaml @@ -68,6 +68,7 @@ custom_code: encoder: templates/terraform/encoders/access_level_never_send_parent.go.tmpl constants: templates/terraform/constants/access_context_manager.go.tmpl custom_import: templates/terraform/custom_import/set_access_policy_parent_from_self_link.go.tmpl + pre_update: templates/terraform/pre_update/access_context_manager_service_perimeter.go.tmpl samples: - name: access_context_manager_service_perimeter_basic primary_resource_id: service-perimeter @@ -138,6 +139,11 @@ properties: description: | Time the AccessPolicy was updated in UTC. output: true + - name: etag + type: Fingerprint + description: | + The etag for the version of the ServicePerimeter that this request is based on. + output: true - name: perimeterType type: Enum description: | diff --git a/mmv1/products/accesscontextmanager/ServicePerimeterIngressPolicy.yaml b/mmv1/products/accesscontextmanager/ServicePerimeterIngressPolicy.yaml index dd1b3d2f2763..a99bdb3c5be8 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeterIngressPolicy.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeterIngressPolicy.yaml @@ -35,9 +35,9 @@ references: docs: base_url: "" self_link: '{{perimeter}}' -create_url: '{{perimeter}}?updateMask=status.ingressPolicies' -delete_url: '{{perimeter}}?updateMask=status.ingressPolicies' -update_url: '{{perimeter}}?updateMask=status.ingressPolicies' +create_url: '{{perimeter}}' +delete_url: '{{perimeter}}' +update_url: '{{perimeter}}' create_verb: PATCH update_verb: PATCH delete_verb: PATCH @@ -76,6 +76,7 @@ custom_code: constants: templates/terraform/constants/access_context_manager.go.tmpl pre_create: templates/terraform/pre_create/access_context_manager_service_perimeter_ingress_policy.go.tmpl post_read: templates/terraform/post_read/access_context_manager_service_perimeter_resource.go.tmpl + pre_update: templates/terraform/pre_create/access_context_manager_service_perimeter_ingress_policy.go.tmpl pre_delete: templates/terraform/pre_delete/access_context_manager_service_perimeter_ingress_policy.go.tmpl custom_import: templates/terraform/custom_import/access_context_manager_service_perimeter_ingress_policy.go.tmpl samples: diff --git a/mmv1/templates/terraform/pre_update/access_context_manager_service_perimeter.go.tmpl b/mmv1/templates/terraform/pre_update/access_context_manager_service_perimeter.go.tmpl new file mode 100644 index 000000000000..1821167f118a --- /dev/null +++ b/mmv1/templates/terraform/pre_update/access_context_manager_service_perimeter.go.tmpl @@ -0,0 +1,41 @@ +{{/* + The license inside this block applies to this file + Copyright 2026 Google Inc. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ -}} +newUpdateMask := []string{} +for _, mask := range updateMask { + switch mask { +{{- range $topProp := $.RootProperties }} +{{- if or (eq $topProp.Name "status") (eq $topProp.Name "spec") }} + case "{{ $topProp.ApiName }}": + {{ underscore $topProp.Name }}SubfieldsChanged := false +{{- range $subProp := $topProp.UserProperties }} +{{- if and (not $subProp.Output) (not $subProp.ClientSide) }} + if d.HasChange("{{ underscore $topProp.Name }}.0.{{ underscore $subProp.Name }}") { + newUpdateMask = append(newUpdateMask, "{{ $topProp.ApiName }}.{{ $subProp.ApiName }}") + {{ underscore $topProp.Name }}SubfieldsChanged = true + } +{{- end }} +{{- end }} + if !{{ underscore $topProp.Name }}SubfieldsChanged { + newUpdateMask = append(newUpdateMask, "{{ $topProp.ApiName }}") + } +{{- end }} +{{- end }} + default: + newUpdateMask = append(newUpdateMask, mask) + } +} +updateMask = newUpdateMask +url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) +if err != nil { + return err +} diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_policy_test.go.tmpl b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_policy_test.go.tmpl index 698595227f53..fa6ed1860eff 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_policy_test.go.tmpl +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_policy_test.go.tmpl @@ -38,6 +38,7 @@ func TestAccAccessContextManager(t *testing.T) { "service_perimeter_psc_endpoint": testAccAccessContextManagerServicePerimeter_pscEndpointTest, "service_perimeter_egress_policy": testAccAccessContextManagerServicePerimeterEgressPolicy_basicTest, "service_perimeter_egress_policy_update": testAccAccessContextManagerServicePerimeterEgressPolicy_updateTest, + "service_perimeter_egress_policy_parent_update": testAccAccessContextManagerServicePerimeterEgressPolicy_parentPerimeterUpdateTest, "service_perimeter_dry_run_egress_policy": testAccAccessContextManagerServicePerimeterDryRunEgressPolicy_basicTest, "service_perimeter_dry_run_egress_policy_update": testAccAccessContextManagerServicePerimeterDryRunEgressPolicy_updateTest, "service_perimeter_egress_policy_psc_endpoint": testAccAccessContextManagerServicePerimeterEgressPolicy_pscEndpointTest, diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_dry_run_egress_policy_test.go b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_dry_run_egress_policy_test.go index c1eabe3278b1..1ab4b4343a79 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_dry_run_egress_policy_test.go +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_dry_run_egress_policy_test.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" "testing" + "time" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/plancheck" @@ -60,7 +61,7 @@ func BootstrapServicePerimeterProjects(t *testing.T, desiredProjects int) []*clo t.Fatalf("Error bootstrapping shared test project: %s", err) } - err = resourcemanager.ResourceManagerOperationWaitTime(config, opAsMap, "creating project", config.UserAgent, 4) + err = resourcemanager.ResourceManagerOperationWaitTime(config, opAsMap, "creating project", config.UserAgent, 4*time.Minute) if err != nil { t.Fatalf("Error bootstrapping shared test project: %s", err) } diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy_test.go b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy_test.go index cc5f62bf4a43..a4f0cc938dbe 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy_test.go +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy_test.go @@ -405,3 +405,135 @@ resource "google_access_context_manager_service_perimeter" "test-access" { } `, org, policyTitle, perimeterTitleName, perimeterTitleName) } + +// This is a regression test for https://github.com/hashicorp/terraform-provider-google/issues/29005 +func testAccAccessContextManagerServicePerimeterEgressPolicy_parentPerimeterUpdateTest(t *testing.T) { + org := envvar.GetTestOrgFromEnv(t) + projects := BootstrapServicePerimeterProjects(t, 7) + + policyTitle := acctest.RandString(t, 10) + perimeterTitle := "perimeter" + + perimeterProject := projects[0].ProjectNumber + var egressProjects []int64 + for i := 1; i <= 6; i++ { + egressProjects = append(egressProjects, projects[i].ProjectNumber) + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccAccessContextManagerServicePerimeterEgressPolicy_parentPerimeterUpdate_step(org, policyTitle, perimeterTitle, "", egressProjects), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_access_context_manager_service_perimeter_egress_policy.test-access1", "egress_to.0.resources.#", "6"), + ), + }, + { + Config: testAccAccessContextManagerServicePerimeterEgressPolicy_parentPerimeterUpdate_step(org, policyTitle, perimeterTitle, fmt.Sprintf(`["projects/%d"]`, perimeterProject), egressProjects), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction( + "google_access_context_manager_service_perimeter.test-access", + plancheck.ResourceActionUpdate, + ), + plancheck.ExpectResourceAction( + "google_access_context_manager_service_perimeter_egress_policy.test-access1", + plancheck.ResourceActionNoop, + ), + }, + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_access_context_manager_service_perimeter.test-access", "status.0.resources.#", "1"), + resource.TestCheckResourceAttr("google_access_context_manager_service_perimeter_egress_policy.test-access1", "egress_to.0.resources.#", "6"), + ), + }, + { + Config: testAccAccessContextManagerServicePerimeterEgressPolicy_parentPerimeterUpdate_step(org, policyTitle, perimeterTitle, fmt.Sprintf(`["projects/%d"]`, perimeterProject), egressProjects[:5]), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction( + "google_access_context_manager_service_perimeter.test-access", + plancheck.ResourceActionNoop, + ), + plancheck.ExpectResourceAction( + "google_access_context_manager_service_perimeter_egress_policy.test-access1", + plancheck.ResourceActionUpdate, + ), + }, + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_access_context_manager_service_perimeter_egress_policy.test-access1", "egress_to.0.resources.#", "5"), + ), + }, + { + Config: testAccAccessContextManagerServicePerimeterEgressPolicy_parentPerimeterUpdate_base(org, policyTitle, perimeterTitle, fmt.Sprintf(`["projects/%d"]`, perimeterProject)), + Check: testAccCheckAccessContextManagerServicePerimeterEgressPolicyDestroyProducer(t), + }, + }, + }) +} + +func formatProjectsHCL(projectNumbers []int64) string { + var formatted []string + for _, num := range projectNumbers { + formatted = append(formatted, fmt.Sprintf(`"projects/%d"`, num)) + } + return strings.Join(formatted, ",\n ") +} + +func testAccAccessContextManagerServicePerimeterEgressPolicy_parentPerimeterUpdate_base(org, policyTitle, perimeterTitleName, perimeterResources string) string { + resourcesBlock := "" + if perimeterResources != "" { + resourcesBlock = fmt.Sprintf("resources = %s", perimeterResources) + } + return fmt.Sprintf(` +resource "google_access_context_manager_access_policy" "test-access" { + parent = "organizations/%s" + title = "%s" +} + +resource "google_access_context_manager_service_perimeter" "test-access" { + parent = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}" + name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/servicePerimeters/%s" + title = "%s" + status { + restricted_services = ["storage.googleapis.com"] + %s + } + + lifecycle { + ignore_changes = [ + status[0].egress_policies, + status[0].ingress_policies, + ] + } +} +`, org, policyTitle, perimeterTitleName, perimeterTitleName, resourcesBlock) +} + +func testAccAccessContextManagerServicePerimeterEgressPolicy_parentPerimeterUpdate_step(org, policyTitle, perimeterTitleName string, perimeterResources string, egressProjects []int64) string { + return fmt.Sprintf(` +%s + +resource "google_access_context_manager_service_perimeter_egress_policy" "test-access1" { + perimeter = google_access_context_manager_service_perimeter.test-access.name + title = "egress policy update test" + egress_from { + identity_type = "ANY_USER_ACCOUNT" + } + egress_to { + resources = [ + %s, + ] + operations { + service_name = "storage.googleapis.com" + method_selectors { + method = "google.storage.objects.get" + } + } + } +} +`, testAccAccessContextManagerServicePerimeterEgressPolicy_parentPerimeterUpdate_base(org, policyTitle, perimeterTitleName, perimeterResources), formatProjectsHCL(egressProjects)) +} diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_ingress_policy_test.go b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_ingress_policy_test.go index b64339ef4de4..58777187687f 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_ingress_policy_test.go +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_ingress_policy_test.go @@ -180,7 +180,7 @@ func testAccAccessContextManagerServicePerimeterIngressPolicy_updateTest(t *test policyTitle := acctest.RandString(t, 10) perimeterTitle := "perimeter" - projectNumber := envvar.GetTestProjectNumberFromEnv() + projects := BootstrapServicePerimeterProjects(t, 2) acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, @@ -190,7 +190,7 @@ func testAccAccessContextManagerServicePerimeterIngressPolicy_updateTest(t *test Config: testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step1(org, policyTitle, perimeterTitle), }, { - Config: testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step2(org, policyTitle, perimeterTitle), + Config: testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step2(org, policyTitle, perimeterTitle, projects[0].ProjectNumber), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectResourceAction( @@ -201,7 +201,18 @@ func testAccAccessContextManagerServicePerimeterIngressPolicy_updateTest(t *test }, }, { - Config: testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step3(org, policyTitle, perimeterTitle, projectNumber), + Config: testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step3(org, policyTitle, perimeterTitle, projects[0].ProjectNumber), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction( + "google_access_context_manager_service_perimeter_ingress_policy.test-access1", + plancheck.ResourceActionUpdate, + ), + }, + }, + }, + { + Config: testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step4(org, policyTitle, perimeterTitle, projects[1].ProjectNumber), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectResourceAction( @@ -219,7 +230,7 @@ func testAccAccessContextManagerServicePerimeterIngressPolicy_updateTest(t *test }) } -// step 1: create +// step 1: create with identity_type, operations with method and permission selectors func testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step1(org, policyTitle, perimeterTitleName string) string { return fmt.Sprintf(` %s @@ -243,16 +254,16 @@ resource "google_access_context_manager_service_perimeter_ingress_policy" "test- `, testAccAccessContextManagerServicePerimeterIngressPolicy_destroy(org, policyTitle, perimeterTitleName)) } -// step 2: change identity_type, swap to access_level source and roles -func testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step2(org, policyTitle, perimeterTitleName string) string { +// step 2: change identity_type, add access_level source, update resources, add roles, omit operations +func testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step2(org, policyTitle, perimeterTitleName string, projectNumber int64) string { return fmt.Sprintf(` %s -resource "google_access_context_manager_access_level" "update-test" { +resource "google_access_context_manager_access_level" "update-test1" { parent = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}" - name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/updatetestlevel" - title = "updatetestlevel" - description = "Access level for ingress update test" + name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/updatetestlevel1" + title = "updatetestlevel1" + description = "Access level 1 for ingress update test" basic { conditions { ip_subnetworks = ["192.0.4.0/24"] @@ -266,27 +277,27 @@ resource "google_access_context_manager_service_perimeter_ingress_policy" "test- ingress_from { identity_type = "ANY_USER_ACCOUNT" sources { - access_level = google_access_context_manager_access_level.update-test.name + access_level = google_access_context_manager_access_level.update-test1.name } } ingress_to { - resources = ["*"] + resources = ["projects/%d"] roles = ["roles/bigquery.admin"] } } -`, testAccAccessContextManagerServicePerimeterIngressPolicy_destroy(org, policyTitle, perimeterTitleName)) +`, testAccAccessContextManagerServicePerimeterIngressPolicy_destroy(org, policyTitle, perimeterTitleName), projectNumber) } -// step 3: switch to project resource source and permission selector -func testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step3(org, policyTitle, perimeterTitleName, projectNumber string) string { +// step 3: omit identity_type, add identities, update access_level source and add resource source, update resources and roles +func testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step3(org, policyTitle, perimeterTitleName string, projectNumber int64) string { return fmt.Sprintf(` %s -resource "google_access_context_manager_access_level" "update-test" { +resource "google_access_context_manager_access_level" "update-test1" { parent = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}" - name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/updatetestlevel" - title = "updatetestlevel" - description = "Access level for ingress update test" + name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/updatetestlevel1" + title = "updatetestlevel1" + description = "Access level 1 for ingress update test" basic { conditions { ip_subnetworks = ["192.0.4.0/24"] @@ -294,17 +305,78 @@ resource "google_access_context_manager_access_level" "update-test" { } } +resource "google_access_context_manager_access_level" "update-test2" { + parent = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}" + name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/updatetestlevel2" + title = "updatetestlevel2" + description = "Access level 2 for ingress update test" + basic { + conditions { + ip_subnetworks = ["192.0.5.0/24"] + } + } +} + resource "google_access_context_manager_service_perimeter_ingress_policy" "test-access1" { perimeter = google_access_context_manager_service_perimeter.test-access.name title = "ingress policy update test" ingress_from { - identity_type = "ANY_IDENTITY" + identities = ["group:test@google.com"] sources { - resource = "projects/%s" + access_level = google_access_context_manager_access_level.update-test2.name + } + sources { + resource = "projects/%d" } } ingress_to { resources = ["*"] + roles = ["roles/bigquery.dataViewer"] + } +} +`, testAccAccessContextManagerServicePerimeterIngressPolicy_destroy(org, policyTitle, perimeterTitleName), projectNumber) +} + +// step 4: update identities, update resource source and omit access_level source, update resources, omit roles and add updated operations +func testAccAccessContextManagerServicePerimeterIngressPolicy_ingressPolicyUpdate_step4(org, policyTitle, perimeterTitleName string, projectNumber int64) string { + return fmt.Sprintf(` +%s + +resource "google_access_context_manager_access_level" "update-test1" { + parent = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}" + name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/updatetestlevel1" + title = "updatetestlevel1" + description = "Access level 1 for ingress update test" + basic { + conditions { + ip_subnetworks = ["192.0.4.0/24"] + } + } +} + +resource "google_access_context_manager_access_level" "update-test2" { + parent = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}" + name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/updatetestlevel2" + title = "updatetestlevel2" + description = "Access level 2 for ingress update test" + basic { + conditions { + ip_subnetworks = ["192.0.5.0/24"] + } + } +} + +resource "google_access_context_manager_service_perimeter_ingress_policy" "test-access1" { + perimeter = google_access_context_manager_service_perimeter.test-access.name + title = "ingress policy update test" + ingress_from { + identities = ["group:test2@google.com"] + sources { + resource = "projects/%d" + } + } + ingress_to { + resources = ["projects/%d"] operations { service_name = "bigquery.googleapis.com" method_selectors { @@ -316,7 +388,7 @@ resource "google_access_context_manager_service_perimeter_ingress_policy" "test- } } } -`, testAccAccessContextManagerServicePerimeterIngressPolicy_destroy(org, policyTitle, perimeterTitleName), projectNumber) +`, testAccAccessContextManagerServicePerimeterIngressPolicy_destroy(org, policyTitle, perimeterTitleName), projectNumber, projectNumber) } func testAccAccessContextManagerServicePerimeterIngressPolicy_destroy(org, policyTitle, perimeterTitleName string) string { diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_test.go.tmpl b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_test.go.tmpl index be8c50bb5ace..573810ebe0dc 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_test.go.tmpl +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_test.go.tmpl @@ -27,6 +27,9 @@ func testAccAccessContextManagerServicePerimeter_basicTest(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAccessContextManagerServicePerimeter_basic(org, "my policy", "level", "perimeter"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_access_context_manager_service_perimeter.test-access", "etag"), + ), }, { ResourceName: "google_access_context_manager_service_perimeter.test-access", @@ -48,6 +51,9 @@ func testAccAccessContextManagerServicePerimeter_updateTest(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAccessContextManagerServicePerimeter_basic(org, "my policy", "level", "perimeter"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_access_context_manager_service_perimeter.test-access", "etag"), + ), }, { ResourceName: "google_access_context_manager_service_perimeter.test-access", @@ -56,6 +62,9 @@ func testAccAccessContextManagerServicePerimeter_updateTest(t *testing.T) { }, { Config: testAccAccessContextManagerServicePerimeter_update(org, "my policy", "level", "perimeter"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_access_context_manager_service_perimeter.test-access", "etag"), + ), }, { ResourceName: "google_access_context_manager_service_perimeter.test-access",