diff --git a/mmv1/third_party/terraform/services/bigquery/iam_bigquery_dataset.go b/mmv1/third_party/terraform/services/bigquery/iam_bigquery_dataset.go index f6966ac4d2d2..740da92602ad 100644 --- a/mmv1/third_party/terraform/services/bigquery/iam_bigquery_dataset.go +++ b/mmv1/third_party/terraform/services/bigquery/iam_bigquery_dataset.go @@ -214,7 +214,6 @@ func iamMemberToAccess(member string) (string, string, error) { if strings.HasPrefix(member, "deleted:") { return "", "", fmt.Errorf("BigQuery Dataset IAM member is deleted: %s", member) } - pieces := strings.SplitN(member, ":", 2) if len(pieces) > 1 { switch pieces[0] { @@ -222,19 +221,19 @@ func iamMemberToAccess(member string) (string, string, error) { return "groupByEmail", pieces[1], nil case "domain": return "domain", pieces[1], nil + case "iamMember": + return "iamMember", pieces[1], nil case "user": return "userByEmail", pieces[1], nil case "serviceAccount": return "userByEmail", pieces[1], nil - default: - return "", "", fmt.Errorf("Failed to parse BigQuery Dataset IAM member type: %s", member) } } if member == "projectOwners" || member == "projectReaders" || member == "projectWriters" || member == "allAuthenticatedUsers" { // These are special BigQuery Dataset permissions return "specialGroup", member, nil } - return "iamMember", member, nil + return "", "", fmt.Errorf("Failed to parse BigQuery Dataset IAM member type: %s", member) } func accessToIamMember(access map[string]interface{}) (string, error) { @@ -249,7 +248,7 @@ func accessToIamMember(access map[string]interface{}) (string, error) { return member.(string), nil } if member, ok := access["iamMember"]; ok { - return member.(string), nil + return fmt.Sprintf("iamMember:%s", member.(string)), nil } if _, ok := access["view"]; ok { // view does not map to an IAM member, use access instead diff --git a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_iam_member_test.go b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_iam_member_test.go index d138457f702d..0ab43e371d9d 100644 --- a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_iam_member_test.go +++ b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_iam_member_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-provider-google/google/envvar" ) -func TestAccBigqueryDatasetIamMember_basic(t *testing.T) { +func TestAccBigqueryDatasetIamMember_serviceAccount(t *testing.T) { t.Parallel() datasetID := fmt.Sprintf("tf_test_%s", acctest.RandString(t, 10)) @@ -25,27 +25,55 @@ func TestAccBigqueryDatasetIamMember_basic(t *testing.T) { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), Steps: []resource.TestStep{ { - Config: testAccBigqueryDatasetIamMember_basic(datasetID, saID), + Config: testAccBigqueryDatasetIamMember_serviceAccount(datasetID, saID), Check: testAccCheckBigQueryDatasetAccessPresent(t, "google_bigquery_dataset.dataset", expected), }, { // Destroy step instead of CheckDestroy so we can check the access is removed without deleting the dataset - Config: testAccBigqueryDatasetIamMember_destroy(datasetID, "dataset"), + Config: testAccBigqueryDatasetIamMember_destroy(datasetID), Check: testAccCheckBigQueryDatasetAccessAbsent(t, "google_bigquery_dataset.dataset", expected), }, }, }) } -func testAccBigqueryDatasetIamMember_destroy(datasetID, rs string) string { +func TestAccBigqueryDatasetIamMember_iamMember(t *testing.T) { + t.Parallel() + + datasetID := fmt.Sprintf("tf_test_%s", acctest.RandString(t, 10)) + wifIDs := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)) + + expected := map[string]interface{}{ + "role": "roles/viewer", + "iamMember": fmt.Sprintf("principal://iam.googleapis.com/projects/%s/locations/global/workloadIdentityPools/%s/subject/test", envvar.GetTestProjectNumberFromEnv(), wifIDs), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccBigqueryDatasetIamMember_iamMember(datasetID, wifIDs), + Check: testAccCheckBigQueryDatasetAccessPresent(t, "google_bigquery_dataset.dataset", expected), + }, + { + // Destroy step instead of CheckDestroy so we can check the access is removed without deleting the dataset + Config: testAccBigqueryDatasetIamMember_destroy(datasetID), + Check: testAccCheckBigQueryDatasetAccessAbsent(t, "google_bigquery_dataset.dataset", expected), + }, + }, + }) +} + +func testAccBigqueryDatasetIamMember_destroy(datasetID string) string { return fmt.Sprintf(` -resource "google_bigquery_dataset" "%s" { +resource "google_bigquery_dataset" "dataset" { dataset_id = "%s" } -`, rs, datasetID) +`, datasetID) } -func testAccBigqueryDatasetIamMember_basic(datasetID, saID string) string { +func testAccBigqueryDatasetIamMember_serviceAccount(datasetID, saID string) string { return fmt.Sprintf(` resource "google_bigquery_dataset_iam_member" "access" { dataset_id = google_bigquery_dataset.dataset.dataset_id @@ -62,3 +90,32 @@ resource "google_service_account" "bqviewer" { } `, datasetID, saID) } + +func testAccBigqueryDatasetIamMember_iamMember(datasetID, wifIDs string) string { + return fmt.Sprintf(` +resource "google_bigquery_dataset_iam_member" "access" { + dataset_id = google_bigquery_dataset.dataset.dataset_id + role = "roles/viewer" + member = "iamMember:principal://iam.googleapis.com/${google_iam_workload_identity_pool.wif_pool.name}/subject/test" +} + +resource "google_bigquery_dataset" "dataset" { + dataset_id = "%s" +} + +resource "google_iam_workload_identity_pool" "wif_pool" { + workload_identity_pool_id = "%s" +} + +resource "google_iam_workload_identity_pool_provider" "wif_provider" { + workload_identity_pool_id = google_iam_workload_identity_pool.wif_pool.workload_identity_pool_id + workload_identity_pool_provider_id = "%s" + attribute_mapping = { + "google.subject" = "assertion.sub" + } + oidc { + issuer_uri = "https://issuer-uri.com" + } +} +`, datasetID, wifIDs, wifIDs) +} diff --git a/mmv1/third_party/terraform/tpgiamresource/iam.go.erb b/mmv1/third_party/terraform/tpgiamresource/iam.go.erb index 6dcb6c1ba8f7..ce9fce404baf 100644 --- a/mmv1/third_party/terraform/tpgiamresource/iam.go.erb +++ b/mmv1/third_party/terraform/tpgiamresource/iam.go.erb @@ -274,6 +274,11 @@ func normalizeIamMemberCasing(member string) string { if len(pieces) > 2 && !iamMemberIsCaseSensitive(strings.TrimPrefix(member, "deleted:")) { pieces[2] = strings.ToLower(pieces[2]) } + } else if strings.HasPrefix(member, "iamMember:") { + pieces = strings.SplitN(member, ":", 3) + if len(pieces) > 2 && !iamMemberIsCaseSensitive(strings.TrimPrefix(member, "iamMember:")) { + pieces[2] = strings.ToLower(pieces[2]) + } } else if !iamMemberIsCaseSensitive(member) { pieces = strings.SplitN(member, ":", 2) if len(pieces) > 1 { diff --git a/mmv1/third_party/terraform/website/docs/r/bigquery_dataset_iam.html.markdown b/mmv1/third_party/terraform/website/docs/r/bigquery_dataset_iam.html.markdown index e911137bdfae..4702defcded5 100644 --- a/mmv1/third_party/terraform/website/docs/r/bigquery_dataset_iam.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/bigquery_dataset_iam.html.markdown @@ -86,12 +86,13 @@ The following arguments are supported: * `member/members` - (Required) Identities that will be granted the privilege in `role`. Each entry can have one of the following values: - * **allUsers**: A special identifier that represents anyone who is on the internet; with or without a Google account. * **allAuthenticatedUsers**: A special identifier that represents anyone who is authenticated with a Google account or a service account. - * **user:{emailid}**: An email address that represents a specific Google account. For example, alice@gmail.com or joe@example.com. - * **serviceAccount:{emailid}**: An email address that represents a service account. For example, my-other-app@appspot.gserviceaccount.com. - * **group:{emailid}**: An email address that represents a Google group. For example, admins@example.com. + * **allUsers**: A special identifier that represents anyone who is on the internet; with or without a Google account. * **domain:{domain}**: A G Suite domain (primary, instead of alias) name that represents all the users of that domain. For example, google.com or example.com. + * **group:{emailid}**: An email address that represents a Google group. For example, admins@example.com. + * **iamMember:{principal}**: Some other type of member that appears in the IAM Policy but isn't a user, group, domain, or special group. This is used for example for workload/workforce federated identities (principal, principalSet). + * **serviceAccount:{emailid}**: An email address that represents a service account. For example, my-other-app@appspot.gserviceaccount.com. + * **user:{emailid}**: An email address that represents a specific Google account. For example, alice@gmail.com or joe@example.com. * `role` - (Required) The role that should be applied. Only one `google_bigquery_dataset_iam_binding` can be used per role. Note that custom roles must be of the format