From 173f1297216532438f6ebb16b360c8f85692b342 Mon Sep 17 00:00:00 2001 From: The Magician Date: Fri, 11 Feb 2022 08:29:49 -0800 Subject: [PATCH] BigQuery Authorized datasets (#5700) (#4047) * Add authorized views to bigquery_dataset * Add authorized view to dataset_access * Fix test file * Add required to nested dataset field Signed-off-by: Modular Magician --- .changelog/5700.txt | 3 + google-beta/iam_bigquery_dataset.go | 4 + google-beta/resource_bigquery_dataset.go | 153 +++++++++++++++ .../resource_bigquery_dataset_access.go | 183 +++++++++++++++++- .../resource_bigquery_dataset_access_test.go | 55 ++++++ ...esource_bigquery_dataset_generated_test.go | 86 ++++++++ website/docs/r/bigquery_dataset.html.markdown | 95 +++++++++ .../r/bigquery_dataset_access.html.markdown | 51 +++++ 8 files changed, 624 insertions(+), 6 deletions(-) create mode 100644 .changelog/5700.txt diff --git a/.changelog/5700.txt b/.changelog/5700.txt new file mode 100644 index 0000000000..ea2bf8d125 --- /dev/null +++ b/.changelog/5700.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +bigquery: added support for authorized datasets to `google_bigquery_dataset.access` and `google_bigquery_dataset_access` +``` diff --git a/google-beta/iam_bigquery_dataset.go b/google-beta/iam_bigquery_dataset.go index 4443e1a439..2ad13d7ddb 100644 --- a/google-beta/iam_bigquery_dataset.go +++ b/google-beta/iam_bigquery_dataset.go @@ -238,6 +238,10 @@ func accessToIamMember(access map[string]interface{}) (string, error) { // view does not map to an IAM member, use access instead return "", fmt.Errorf("Failed to convert BigQuery Dataset access to IAM member. To use views with a dataset, please use dataset_access") } + if _, ok := access["dataset"]; ok { + // dataset does not map to an IAM member, use access instead + return "", fmt.Errorf("Failed to convert BigQuery Dataset access to IAM member. To use views with a dataset, please use dataset_access") + } if member, ok := access["userByEmail"]; ok { // service accounts have "gservice" in their email. This is best guess due to lost information if strings.Contains(member.(string), "gserviceaccount") { diff --git a/google-beta/resource_bigquery_dataset.go b/google-beta/resource_bigquery_dataset.go index b0e9fba3b2..ac4a29d28d 100644 --- a/google-beta/resource_bigquery_dataset.go +++ b/google-beta/resource_bigquery_dataset.go @@ -219,6 +219,45 @@ milliseconds since the epoch.`, func bigqueryDatasetAccessSchema() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ + "dataset": { + Type: schema.TypeList, + Optional: true, + Description: `Grants all resources of particular types in a particular dataset read access to the current dataset.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dataset": { + Type: schema.TypeList, + Required: true, + Description: `The dataset this entry applies to`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dataset_id": { + Type: schema.TypeString, + Required: true, + Description: `The ID of the dataset containing this table.`, + }, + "project_id": { + Type: schema.TypeString, + Required: true, + Description: `The ID of the project containing this table.`, + }, + }, + }, + }, + "target_types": { + Type: schema.TypeList, + Required: true, + Description: `Which resources in the dataset this entry applies to. Currently, only views are supported, +but additional target types may be added in the future. Possible values: VIEWS`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, "domain": { Type: schema.TypeString, Optional: true, @@ -666,6 +705,7 @@ func flattenBigQueryDatasetAccess(v interface{}, d *schema.ResourceData, config "special_group": flattenBigQueryDatasetAccessSpecialGroup(original["specialGroup"], d, config), "user_by_email": flattenBigQueryDatasetAccessUserByEmail(original["userByEmail"], d, config), "view": flattenBigQueryDatasetAccessView(original["view"], d, config), + "dataset": flattenBigQueryDatasetAccessDataset(original["dataset"], d, config), }) } return transformed @@ -719,6 +759,48 @@ func flattenBigQueryDatasetAccessViewTableId(v interface{}, d *schema.ResourceDa return v } +func flattenBigQueryDatasetAccessDataset(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["dataset"] = + flattenBigQueryDatasetAccessDatasetDataset(original["dataset"], d, config) + transformed["target_types"] = + flattenBigQueryDatasetAccessDatasetTargetTypes(original["targetTypes"], d, config) + return []interface{}{transformed} +} +func flattenBigQueryDatasetAccessDatasetDataset(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["dataset_id"] = + flattenBigQueryDatasetAccessDatasetDatasetDatasetId(original["datasetId"], d, config) + transformed["project_id"] = + flattenBigQueryDatasetAccessDatasetDatasetProjectId(original["projectId"], d, config) + return []interface{}{transformed} +} +func flattenBigQueryDatasetAccessDatasetDatasetDatasetId(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigQueryDatasetAccessDatasetDatasetProjectId(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigQueryDatasetAccessDatasetTargetTypes(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func flattenBigQueryDatasetCreationTime(v interface{}, d *schema.ResourceData, config *Config) interface{} { // Handles the string fixed64 format if strVal, ok := v.(string); ok { @@ -900,6 +982,13 @@ func expandBigQueryDatasetAccess(v interface{}, d TerraformResourceData, config transformed["view"] = transformedView } + transformedDataset, err := expandBigQueryDatasetAccessDataset(original["dataset"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDataset); val.IsValid() && !isEmptyValue(val) { + transformed["dataset"] = transformedDataset + } + req = append(req, transformed) } return req, nil @@ -970,6 +1059,70 @@ func expandBigQueryDatasetAccessViewTableId(v interface{}, d TerraformResourceDa return v, nil } +func expandBigQueryDatasetAccessDataset(v interface{}, d TerraformResourceData, config *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{}) + + transformedDataset, err := expandBigQueryDatasetAccessDatasetDataset(original["dataset"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDataset); val.IsValid() && !isEmptyValue(val) { + transformed["dataset"] = transformedDataset + } + + transformedTargetTypes, err := expandBigQueryDatasetAccessDatasetTargetTypes(original["target_types"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTargetTypes); val.IsValid() && !isEmptyValue(val) { + transformed["targetTypes"] = transformedTargetTypes + } + + return transformed, nil +} + +func expandBigQueryDatasetAccessDatasetDataset(v interface{}, d TerraformResourceData, config *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{}) + + transformedDatasetId, err := expandBigQueryDatasetAccessDatasetDatasetDatasetId(original["dataset_id"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDatasetId); val.IsValid() && !isEmptyValue(val) { + transformed["datasetId"] = transformedDatasetId + } + + transformedProjectId, err := expandBigQueryDatasetAccessDatasetDatasetProjectId(original["project_id"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedProjectId); val.IsValid() && !isEmptyValue(val) { + transformed["projectId"] = transformedProjectId + } + + return transformed, nil +} + +func expandBigQueryDatasetAccessDatasetDatasetDatasetId(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigQueryDatasetAccessDatasetDatasetProjectId(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigQueryDatasetAccessDatasetTargetTypes(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandBigQueryDatasetDatasetReference(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { transformed := make(map[string]interface{}) transformedDatasetId, err := expandBigQueryDatasetDatasetReferenceDatasetId(d.Get("dataset_id"), d, config) diff --git a/google-beta/resource_bigquery_dataset_access.go b/google-beta/resource_bigquery_dataset_access.go index 412a60bd88..5d0a52dec9 100644 --- a/google-beta/resource_bigquery_dataset_access.go +++ b/google-beta/resource_bigquery_dataset_access.go @@ -160,6 +160,51 @@ func resourceBigQueryDatasetAccess() *schema.Resource { must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_). The maximum length is 1,024 characters.`, }, + "dataset": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `Grants all resources of particular types in a particular dataset read access to the current dataset.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dataset": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + Description: `The dataset this entry applies to`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dataset_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The ID of the dataset containing this table.`, + }, + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The ID of the project containing this table.`, + }, + }, + }, + }, + "target_types": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + Description: `Which resources in the dataset this entry applies to. Currently, only views are supported, +but additional target types may be added in the future. Possible values: VIEWS`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view", "dataset"}, + }, "domain": { Type: schema.TypeString, Optional: true, @@ -167,7 +212,7 @@ underscores (_). The maximum length is 1,024 characters.`, DiffSuppressFunc: resourceBigQueryDatasetAccessIamMemberDiffSuppress, Description: `A domain to grant access to. Any users signed in with the domain specified will be granted the specified access`, - ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view"}, + ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view", "dataset"}, }, "group_by_email": { Type: schema.TypeString, @@ -175,7 +220,7 @@ domain specified will be granted the specified access`, ForceNew: true, DiffSuppressFunc: resourceBigQueryDatasetAccessIamMemberDiffSuppress, Description: `An email address of a Google Group to grant access to.`, - ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view"}, + ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view", "dataset"}, }, "iam_member": { Type: schema.TypeString, @@ -184,7 +229,7 @@ domain specified will be granted the specified access`, DiffSuppressFunc: resourceBigQueryDatasetAccessIamMemberDiffSuppress, Description: `Some other type of member that appears in the IAM Policy but isn't a user, group, domain, or special group. For example: 'allUsers'`, - ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view"}, + ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view", "dataset"}, }, "role": { Type: schema.TypeString, @@ -216,7 +261,7 @@ post-create. See * 'allAuthenticatedUsers': All authenticated BigQuery users.`, - ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view"}, + ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view", "dataset"}, }, "user_by_email": { Type: schema.TypeString, @@ -225,7 +270,7 @@ post-create. See DiffSuppressFunc: resourceBigQueryDatasetAccessIamMemberDiffSuppress, Description: `An email address of a user to grant access to. For example: fred@example.com`, - ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view"}, + ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view", "dataset"}, }, "view": { Type: schema.TypeList, @@ -261,7 +306,7 @@ is 1,024 characters.`, }, }, }, - ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view"}, + ExactlyOneOf: []string{"user_by_email", "group_by_email", "domain", "special_group", "iam_member", "view", "dataset"}, }, "api_updated_member": { Type: schema.TypeBool, @@ -335,6 +380,12 @@ func resourceBigQueryDatasetAccessCreate(d *schema.ResourceData, meta interface{ } else if v, ok := d.GetOkExists("view"); !isEmptyValue(reflect.ValueOf(viewProp)) && (ok || !reflect.DeepEqual(v, viewProp)) { obj["view"] = viewProp } + datasetProp, err := expandNestedBigQueryDatasetAccessDataset(d.Get("dataset"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("dataset"); !isEmptyValue(reflect.ValueOf(datasetProp)) && (ok || !reflect.DeepEqual(v, datasetProp)) { + obj["dataset"] = datasetProp + } lockName, err := replaceVars(d, config, "{{dataset_id}}") if err != nil { @@ -479,6 +530,9 @@ func resourceBigQueryDatasetAccessRead(d *schema.ResourceData, meta interface{}) if err := d.Set("view", flattenNestedBigQueryDatasetAccessView(res["view"], d, config)); err != nil { return fmt.Errorf("Error reading DatasetAccess: %s", err) } + if err := d.Set("dataset", flattenNestedBigQueryDatasetAccessDataset(res["dataset"], d, config)); err != nil { + return fmt.Errorf("Error reading DatasetAccess: %s", err) + } return nil } @@ -585,6 +639,48 @@ func flattenNestedBigQueryDatasetAccessViewTableId(v interface{}, d *schema.Reso return v } +func flattenNestedBigQueryDatasetAccessDataset(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["dataset"] = + flattenNestedBigQueryDatasetAccessDatasetDataset(original["dataset"], d, config) + transformed["target_types"] = + flattenNestedBigQueryDatasetAccessDatasetTargetTypes(original["targetTypes"], d, config) + return []interface{}{transformed} +} +func flattenNestedBigQueryDatasetAccessDatasetDataset(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["dataset_id"] = + flattenNestedBigQueryDatasetAccessDatasetDatasetDatasetId(original["datasetId"], d, config) + transformed["project_id"] = + flattenNestedBigQueryDatasetAccessDatasetDatasetProjectId(original["projectId"], d, config) + return []interface{}{transformed} +} +func flattenNestedBigQueryDatasetAccessDatasetDatasetDatasetId(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNestedBigQueryDatasetAccessDatasetDatasetProjectId(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNestedBigQueryDatasetAccessDatasetTargetTypes(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func expandNestedBigQueryDatasetAccessDatasetId(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -665,6 +761,70 @@ func expandNestedBigQueryDatasetAccessViewTableId(v interface{}, d TerraformReso return v, nil } +func expandNestedBigQueryDatasetAccessDataset(v interface{}, d TerraformResourceData, config *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{}) + + transformedDataset, err := expandNestedBigQueryDatasetAccessDatasetDataset(original["dataset"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDataset); val.IsValid() && !isEmptyValue(val) { + transformed["dataset"] = transformedDataset + } + + transformedTargetTypes, err := expandNestedBigQueryDatasetAccessDatasetTargetTypes(original["target_types"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTargetTypes); val.IsValid() && !isEmptyValue(val) { + transformed["targetTypes"] = transformedTargetTypes + } + + return transformed, nil +} + +func expandNestedBigQueryDatasetAccessDatasetDataset(v interface{}, d TerraformResourceData, config *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{}) + + transformedDatasetId, err := expandNestedBigQueryDatasetAccessDatasetDatasetDatasetId(original["dataset_id"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDatasetId); val.IsValid() && !isEmptyValue(val) { + transformed["datasetId"] = transformedDatasetId + } + + transformedProjectId, err := expandNestedBigQueryDatasetAccessDatasetDatasetProjectId(original["project_id"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedProjectId); val.IsValid() && !isEmptyValue(val) { + transformed["projectId"] = transformedProjectId + } + + return transformed, nil +} + +func expandNestedBigQueryDatasetAccessDatasetDatasetDatasetId(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNestedBigQueryDatasetAccessDatasetDatasetProjectId(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNestedBigQueryDatasetAccessDatasetTargetTypes(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func flattenNestedBigQueryDatasetAccess(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) { var v interface{} var ok bool @@ -727,6 +887,11 @@ func resourceBigQueryDatasetAccessFindNestedObjectInList(d *schema.ResourceData, return -1, nil, err } expectedFlattenedView := flattenNestedBigQueryDatasetAccessView(expectedView, d, meta.(*Config)) + expectedDataset, err := expandNestedBigQueryDatasetAccessDataset(d.Get("dataset"), d, meta.(*Config)) + if err != nil { + return -1, nil, err + } + expectedFlattenedDataset := flattenNestedBigQueryDatasetAccessDataset(expectedDataset, d, meta.(*Config)) // Search list for this resource. for idx, itemRaw := range items { @@ -777,6 +942,12 @@ func resourceBigQueryDatasetAccessFindNestedObjectInList(d *schema.ResourceData, log.Printf("[DEBUG] Skipping item with view= %#v, looking for %#v)", itemView, expectedFlattenedView) continue } + itemDataset := flattenNestedBigQueryDatasetAccessDataset(item["dataset"], d, meta.(*Config)) + // isEmptyValue check so that if one is nil and the other is "", that's considered a match + if !(isEmptyValue(reflect.ValueOf(itemDataset)) && isEmptyValue(reflect.ValueOf(expectedFlattenedDataset))) && !reflect.DeepEqual(itemDataset, expectedFlattenedDataset) { + log.Printf("[DEBUG] Skipping item with dataset= %#v, looking for %#v)", itemDataset, expectedFlattenedDataset) + continue + } log.Printf("[DEBUG] Found item for resource %q: %#v)", d.Id(), item) return idx, item, nil } diff --git a/google-beta/resource_bigquery_dataset_access_test.go b/google-beta/resource_bigquery_dataset_access_test.go index 608c86549b..0aa0010cb2 100644 --- a/google-beta/resource_bigquery_dataset_access_test.go +++ b/google-beta/resource_bigquery_dataset_access_test.go @@ -68,6 +68,38 @@ func TestAccBigQueryDatasetAccess_view(t *testing.T) { }) } +func TestAccBigQueryDatasetAccess_authorizedDataset(t *testing.T) { + t.Parallel() + + datasetID := fmt.Sprintf("tf_test_%s", randString(t, 10)) + datasetID2 := fmt.Sprintf("tf_test_%s", randString(t, 10)) + + expected := map[string]interface{}{ + "dataset": map[string]interface{}{ + "dataset": map[string]interface{}{ + "projectId": getTestProjectFromEnv(), + "datasetId": datasetID2, + }, + "targetTypes": []interface{}{"VIEWS"}, + }, + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccBigQueryDatasetAccess_authorizedDataset(datasetID, datasetID2), + Check: testAccCheckBigQueryDatasetAccessPresent(t, "google_bigquery_dataset.private", expected), + }, + { + Config: testAccBigQueryDatasetAccess_destroy(datasetID, "private"), + Check: testAccCheckBigQueryDatasetAccessAbsent(t, "google_bigquery_dataset.private", expected), + }, + }, + }) +} + func TestAccBigQueryDatasetAccess_multiple(t *testing.T) { // Multiple fine-grained resources skipIfVcr(t) @@ -303,6 +335,29 @@ resource "google_bigquery_table" "public" { `, datasetID, datasetID2, tableID, "SELECT state FROM `lookerdata.cdc.project_tycho_reports`") } +func testAccBigQueryDatasetAccess_authorizedDataset(datasetID, datasetID2 string) string { + return fmt.Sprintf(` +resource "google_bigquery_dataset_access" "access" { + dataset_id = google_bigquery_dataset.private.dataset_id + dataset { + dataset{ + project_id = google_bigquery_dataset.public.project + dataset_id = google_bigquery_dataset.public.dataset_id + } + target_types = ["VIEWS"] + } +} + +resource "google_bigquery_dataset" "private" { + dataset_id = "%s" +} + +resource "google_bigquery_dataset" "public" { + dataset_id = "%s" +} +`, datasetID, datasetID2) +} + func testAccBigQueryDatasetAccess_multiple(datasetID string) string { return fmt.Sprintf(` resource "google_bigquery_dataset_access" "access" { diff --git a/google-beta/resource_bigquery_dataset_generated_test.go b/google-beta/resource_bigquery_dataset_generated_test.go index a89efc6eec..2912ed0c78 100644 --- a/google-beta/resource_bigquery_dataset_generated_test.go +++ b/google-beta/resource_bigquery_dataset_generated_test.go @@ -77,6 +77,92 @@ resource "google_service_account" "bqowner" { `, context) } +func TestAccBigQueryDataset_bigqueryDatasetAuthorizedDatasetExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBigQueryDatasetDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccBigQueryDataset_bigqueryDatasetAuthorizedDatasetExample(context), + }, + { + ResourceName: "google_bigquery_dataset.dataset", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccBigQueryDataset_bigqueryDatasetAuthorizedDatasetExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_dataset" "public" { + dataset_id = "public%{random_suffix}" + friendly_name = "test" + description = "This dataset is public" + location = "EU" + default_table_expiration_ms = 3600000 + + labels = { + env = "default" + } + + access { + role = "OWNER" + user_by_email = google_service_account.bqowner.email + } + + access { + role = "READER" + domain = "hashicorp.com" + } +} + +resource "google_bigquery_dataset" "dataset" { + dataset_id = "private%{random_suffix}" + friendly_name = "test" + description = "This dataset is private" + location = "EU" + default_table_expiration_ms = 3600000 + + labels = { + env = "default" + } + + access { + role = "OWNER" + user_by_email = google_service_account.bqowner.email + } + + access { + role = "READER" + domain = "hashicorp.com" + } + + access { + dataset { + dataset { + project_id = google_bigquery_dataset.public.project + dataset_id = google_bigquery_dataset.public.dataset_id + } + target_types = ["VIEWS"] + } + } +} + +resource "google_service_account" "bqowner" { + account_id = "bqowner%{random_suffix}" +} +`, context) +} + func testAccCheckBigQueryDatasetDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/website/docs/r/bigquery_dataset.html.markdown b/website/docs/r/bigquery_dataset.html.markdown index 05b8d8ee53..ad28e6b3c6 100644 --- a/website/docs/r/bigquery_dataset.html.markdown +++ b/website/docs/r/bigquery_dataset.html.markdown @@ -96,6 +96,73 @@ resource "google_kms_key_ring" "key_ring" { location = "us" } ``` + +## Example Usage - Bigquery Dataset Authorized Dataset + + +```hcl +resource "google_bigquery_dataset" "public" { + dataset_id = "public" + friendly_name = "test" + description = "This dataset is public" + location = "EU" + default_table_expiration_ms = 3600000 + + labels = { + env = "default" + } + + access { + role = "OWNER" + user_by_email = google_service_account.bqowner.email + } + + access { + role = "READER" + domain = "hashicorp.com" + } +} + +resource "google_bigquery_dataset" "dataset" { + dataset_id = "private" + friendly_name = "test" + description = "This dataset is private" + location = "EU" + default_table_expiration_ms = 3600000 + + labels = { + env = "default" + } + + access { + role = "OWNER" + user_by_email = google_service_account.bqowner.email + } + + access { + role = "READER" + domain = "hashicorp.com" + } + + access { + dataset { + dataset { + project_id = google_bigquery_dataset.public.project + dataset_id = google_bigquery_dataset.public.dataset_id + } + target_types = ["VIEWS"] + } + } +} + +resource "google_service_account" "bqowner" { + account_id = "bqowner" +} +``` ## Argument Reference @@ -235,6 +302,11 @@ destroying the resource will fail if tables are present. needs to be granted again via an update operation. Structure is [documented below](#nested_view). +* `dataset` - + (Optional) + Grants all resources of particular types in a particular dataset read access to the current dataset. + Structure is [documented below](#nested_dataset). + The `view` block supports: @@ -252,6 +324,29 @@ destroying the resource will fail if tables are present. A-Z), numbers (0-9), or underscores (_). The maximum length is 1,024 characters. +The `dataset` block supports: + +* `dataset` - + (Required) + The dataset this entry applies to + Structure is [documented below](#nested_dataset). + +* `target_types` - + (Required) + Which resources in the dataset this entry applies to. Currently, only views are supported, + but additional target types may be added in the future. Possible values: VIEWS + + +The `dataset` block supports: + +* `dataset_id` - + (Required) + The ID of the dataset containing this table. + +* `project_id` - + (Required) + The ID of the project containing this table. + The `default_encryption_configuration` block supports: * `kms_key_name` - diff --git a/website/docs/r/bigquery_dataset_access.html.markdown b/website/docs/r/bigquery_dataset_access.html.markdown index 706cdf65b6..4bb2c98407 100644 --- a/website/docs/r/bigquery_dataset_access.html.markdown +++ b/website/docs/r/bigquery_dataset_access.html.markdown @@ -91,6 +91,29 @@ resource "google_bigquery_table" "public" { } } ``` +## Example Usage - Bigquery Dataset Access Authorized Dataset + + +```hcl +resource "google_bigquery_dataset_access" "access" { + dataset_id = google_bigquery_dataset.private.dataset_id + dataset { + dataset{ + project_id = google_bigquery_dataset.public.project + dataset_id = google_bigquery_dataset.public.dataset_id + } + target_types = ["VIEWS"] + } +} + +resource "google_bigquery_dataset" "private" { + dataset_id = "private" +} + +resource "google_bigquery_dataset" "public" { + dataset_id = "public" +} +``` ## Argument Reference @@ -156,6 +179,11 @@ The following arguments are supported: needs to be granted again via an update operation. Structure is [documented below](#nested_view). +* `dataset` - + (Optional) + Grants all resources of particular types in a particular dataset read access to the current dataset. + Structure is [documented below](#nested_dataset). + * `project` - (Optional) The ID of the project in which the resource belongs. If it is not provided, the provider project is used. @@ -176,6 +204,29 @@ The following arguments are supported: A-Z), numbers (0-9), or underscores (_). The maximum length is 1,024 characters. +The `dataset` block supports: + +* `dataset` - + (Required) + The dataset this entry applies to + Structure is [documented below](#nested_dataset). + +* `target_types` - + (Required) + Which resources in the dataset this entry applies to. Currently, only views are supported, + but additional target types may be added in the future. Possible values: VIEWS + + +The `dataset` block supports: + +* `dataset_id` - + (Required) + The ID of the dataset containing this table. + +* `project_id` - + (Required) + The ID of the project containing this table. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: