diff --git a/mmv1/products/healthcare/terraform.yaml b/mmv1/products/healthcare/terraform.yaml index 61fb0e8e7bae..036b968c45ff 100644 --- a/mmv1/products/healthcare/terraform.yaml +++ b/mmv1/products/healthcare/terraform.yaml @@ -59,6 +59,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides fhir_store_name: "example-fhir-store" pubsub_topic: "fhir-notifications" bq_dataset_name: "bq_example_dataset" + test_vars_overrides: + policyChanged: 'BootstrapPSARoles(t, "gsp-sa-healthcare", []string{"roles/bigquery.dataEditor", "roles/bigquery.jobUser"})' - !ruby/object:Provider::Terraform::Examples name: "healthcare_fhir_store_notification_config" primary_resource_id: "default" @@ -107,6 +109,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides pubsub_topic: "dicom-notifications" bq_dataset_name: "dicom_bq_ds" bq_table_name: "dicom_bq_tb" + test_vars_overrides: + policyChanged: 'BootstrapPSARoles(t, "gsp-sa-healthcare", []string{"roles/bigquery.dataEditor", "roles/bigquery.jobUser"})' properties: creationTime: !ruby/object:Overrides::Terraform::PropertyOverride exclude: true diff --git a/mmv1/templates/terraform/examples/healthcare_fhir_store_streaming_config.tf.erb b/mmv1/templates/terraform/examples/healthcare_fhir_store_streaming_config.tf.erb index 7cf47c4943aa..d9a627d487fe 100644 --- a/mmv1/templates/terraform/examples/healthcare_fhir_store_streaming_config.tf.erb +++ b/mmv1/templates/terraform/examples/healthcare_fhir_store_streaming_config.tf.erb @@ -21,25 +21,6 @@ resource "google_healthcare_fhir_store" "default" { } } } - - depends_on = [ - google_project_iam_member.bigquery_editor, - google_project_iam_member.bigquery_job_user - ] -} - -data "google_project" "project" {} - -resource "google_project_iam_member" "bigquery_editor" { - project = data.google_project.project.project_id - role = "roles/bigquery.dataEditor" - member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-healthcare.iam.gserviceaccount.com" -} - -resource "google_project_iam_member" "bigquery_job_user" { - project = data.google_project.project.project_id - role = "roles/bigquery.jobUser" - member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-healthcare.iam.gserviceaccount.com" } resource "google_pubsub_topic" "topic" { @@ -57,4 +38,4 @@ resource "google_bigquery_dataset" "bq_dataset" { description = "This is a test description" location = "US" delete_contents_on_destroy = true -} \ No newline at end of file +} diff --git a/mmv1/third_party/terraform/utils/bootstrap_utils_test.go b/mmv1/third_party/terraform/utils/bootstrap_utils_test.go index 5ca257066068..5b64718d8695 100644 --- a/mmv1/third_party/terraform/utils/bootstrap_utils_test.go +++ b/mmv1/third_party/terraform/utils/bootstrap_utils_test.go @@ -516,6 +516,82 @@ func BootstrapProject(t *testing.T, projectID, billingAccount string, services [ return project } +// BootstrapAllPSARoles ensures that the given project's IAM +// policy grants the given service agents the given roles. +// This is important to bootstrap because using iam policy resources means that +// deleting them removes permissions for concurrent tests. +// Return whether the policy changed. +func BootstrapAllPSARoles(t *testing.T, agentNames, roles []string) bool { + config := BootstrapConfig(t) + if config == nil { + t.Fatal("Could not bootstrap a config for BootstrapAllPSARoles.") + return false + } + client := config.NewResourceManagerClient(config.userAgent) + + // Get the project since we need its number, id, and policy. + project, err := client.Projects.Get(getTestProjectFromEnv()).Do() + if err != nil { + t.Fatalf("Error getting project with id %q: %s", project.ProjectId, err) + return false + } + + getPolicyRequest := &cloudresourcemanager.GetIamPolicyRequest{} + policy, err := client.Projects.GetIamPolicy(project.ProjectId, getPolicyRequest).Do() + if err != nil { + t.Fatalf("Error getting project iam policy: %v", err) + return false + } + + var members []string + for _, agentName := range agentNames { + member := fmt.Sprintf("serviceAccount:service-%d@%s.iam.gserviceaccount.com", project.ProjectNumber, agentName) + members = append(members, member) + } + + // Create the bindings we need to add to the policy. + var newBindings []*cloudresourcemanager.Binding + for _, role := range roles { + newBindings = append(newBindings, &cloudresourcemanager.Binding{ + Role: role, + Members: members, + }) + } + + mergedBindings := mergeBindings(append(policy.Bindings, newBindings...)) + + if !compareBindings(policy.Bindings, mergedBindings) { + // The policy must change. + setPolicyRequest := &cloudresourcemanager.SetIamPolicyRequest{Policy: policy} + policy, err = client.Projects.SetIamPolicy(project.ProjectId, setPolicyRequest).Do() + if err != nil { + t.Fatalf("Error setting project iam policy: %v", err) + return false + } + return true + } + + return false +} + +// BootstrapAllPSARole is a version of BootstrapAllPSARoles for granting a +// single role to multiple service agents. +func BootstrapAllPSARole(t *testing.T, agentNames []string, role string) bool { + return BootstrapAllPSARoles(t, agentNames, []string{role}) +} + +// BootstrapPSARoles is a version of BootstrapAllPSARoles for granting roles to +// a single service agent. +func BootstrapPSARoles(t *testing.T, agentName string, roles []string) bool { + return BootstrapAllPSARoles(t, []string{agentName}, roles) +} + +// BootstrapPSARole is a simplified version of BootstrapPSARoles for granting a +// single role to a single service agent. +func BootstrapPSARole(t *testing.T, agentName, role string) bool { + return BootstrapPSARoles(t, agentName, []string{role}) +} + func BootstrapConfig(t *testing.T) *Config { if v := os.Getenv("TF_ACC"); v == "" { t.Skip("Acceptance tests and bootstrapping skipped unless env 'TF_ACC' set")