Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bootstrapping PSA Roles for Healthcare #7376

Merged
merged 13 commits into from
Mar 2, 2023
4 changes: 4 additions & 0 deletions mmv1/products/healthcare/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand All @@ -57,4 +38,4 @@ resource "google_bigquery_dataset" "bq_dataset" {
description = "This is a test description"
location = "US"
delete_contents_on_destroy = true
}
}
76 changes: 76 additions & 0 deletions mmv1/third_party/terraform/utils/bootstrap_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down