From 0937f2bbdb3b03cace77ca5a28afc6a65b871d5b Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Wed, 18 Oct 2023 15:25:20 +0000 Subject: [PATCH] Add `google_cloud_identity_group_lookup` data source (#9163) * Add initial version of `google_cloud_identity_group_lookup` data source and acceptance test * Add `google_cloud_identity_group_lookup` to provider * Add dependency * Update `google_cloud_identity_group_lookup` test to check that the group name matches between resource and datasource * Add documentation for `google_cloud_identity_group_lookup` data source * Fix docs description, add link to EntityKey description * Add second test case testing `google_cloud_identity_group_lookup` for lookup via alias * Format imports * Fix mixture of tabs/spaces in config strings for cloud identity group tests * Remove unneeded data source from acc test step * Remove inter-datasource comparison check [upstream:91231a153e48efbc72d28a58447cafc0ba56042d] Signed-off-by: Modular Magician --- .changelog/9163.txt | 3 + google/provider/provider.go | 1 + ...data_source_cloud_identity_group_lookup.go | 103 ++++++++++++++++++ ...source_cloud_identity_group_lookup_test.go | 102 +++++++++++++++++ .../data_source_cloud_identity_groups.go | 3 +- ...ce_cloud_identity_group_membership_test.go | 6 +- .../resource_cloud_identity_group_test.go | 3 +- .../cloud_identity_group_lookup.html.markdown | 51 +++++++++ 8 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 .changelog/9163.txt create mode 100644 google/services/cloudidentity/data_source_cloud_identity_group_lookup.go create mode 100644 google/services/cloudidentity/data_source_cloud_identity_group_lookup_test.go create mode 100644 website/docs/d/cloud_identity_group_lookup.html.markdown diff --git a/.changelog/9163.txt b/.changelog/9163.txt new file mode 100644 index 00000000000..89b61ae48f6 --- /dev/null +++ b/.changelog/9163.txt @@ -0,0 +1,3 @@ +```release-note:new-datasource +`google_cloud_identity_group_lookup` +``` diff --git a/google/provider/provider.go b/google/provider/provider.go index 55ba01ac859..bcdfa632ded 100644 --- a/google/provider/provider.go +++ b/google/provider/provider.go @@ -801,6 +801,7 @@ func DatasourceMapWithErrors() (map[string]*schema.Resource, error) { "google_cloudfunctions2_function": cloudfunctions2.DataSourceGoogleCloudFunctions2Function(), "google_cloud_identity_groups": cloudidentity.DataSourceGoogleCloudIdentityGroups(), "google_cloud_identity_group_memberships": cloudidentity.DataSourceGoogleCloudIdentityGroupMemberships(), + "google_cloud_identity_group_lookup": cloudidentity.DataSourceGoogleCloudIdentityGroupLookup(), "google_cloud_run_locations": cloudrun.DataSourceGoogleCloudRunLocations(), "google_cloud_run_service": cloudrun.DataSourceGoogleCloudRunService(), "google_cloud_run_v2_job": cloudrunv2.DataSourceGoogleCloudRunV2Job(), diff --git a/google/services/cloudidentity/data_source_cloud_identity_group_lookup.go b/google/services/cloudidentity/data_source_cloud_identity_group_lookup.go new file mode 100644 index 00000000000..ce4975b7c4e --- /dev/null +++ b/google/services/cloudidentity/data_source_cloud_identity_group_lookup.go @@ -0,0 +1,103 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package cloudidentity + +import ( + "fmt" + "time" + + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceGoogleCloudIdentityGroupLookup() *schema.Resource { + + return &schema.Resource{ + Read: dataSourceGoogleCloudIdentityGroupLookupRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + Description: `The [resource name](https://cloud.google.com/apis/design/resource_names) of the looked-up Group.`, + }, + "group_key": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Description: `The EntityKey of the Group to lookup. A unique identifier for an entity in the Cloud Identity Groups API. +An entity can represent either a group with an optional namespace or a user without a namespace. +The combination of id and namespace must be unique; however, the same id can be used with different namespaces.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + Description: `The ID of the entity. For Google-managed entities, the id should be the email address of an existing group or user. +For external-identity-mapped entities, the id must be a string conforming to the Identity Source's requirements. +Must be unique within a namespace.`, + }, + "namespace": { + Type: schema.TypeString, + Optional: true, + Description: `The namespace in which the entity exists. If not specified, the EntityKey represents a Google-managed entity such as a Google user or a Google Group. +If specified, the EntityKey represents an external-identity-mapped group. The namespace must correspond to an identity source created in Admin Console and must be in the form of identitysources/{identity_source}.`, + }, + }, + }, + }, + }, + } +} + +func dataSourceGoogleCloudIdentityGroupLookupRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + gkId, ok := d.GetOk("group_key.0.id") + if !ok { + return fmt.Errorf("error getting group key id") + } + id := gkId.(string) + + groupsLookupCall := config.NewCloudIdentityClient(userAgent).Groups.Lookup().GroupKeyId(id) + + gkNamespace, ok := d.GetOk("group_key.0.namespace") + if ok { + // If optional namespace argument provided, add as param to API call + namespace := gkNamespace.(string) + groupsLookupCall = groupsLookupCall.GroupKeyNamespace(namespace) + } + + if config.UserProjectOverride { + billingProject := "" + // err may be nil - project isn't required for this resource + if project, err := tpgresource.GetProject(d, config); err == nil { + billingProject = project + } + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + if billingProject != "" { + groupsLookupCall.Header().Set("X-Goog-User-Project", billingProject) + } + } + resp, err := groupsLookupCall.Do() + if err != nil { + return transport_tpg.HandleDataSourceNotFoundError(err, d, fmt.Sprintf("CloudIdentityGroups %q", d.Id()), "Groups") + } + + if err := d.Set("name", resp.Name); err != nil { + return fmt.Errorf("error setting group lookup name: %s", err) + } + d.SetId(time.Now().UTC().String()) + return nil +} diff --git a/google/services/cloudidentity/data_source_cloud_identity_group_lookup_test.go b/google/services/cloudidentity/data_source_cloud_identity_group_lookup_test.go new file mode 100644 index 00000000000..838d1d6c84c --- /dev/null +++ b/google/services/cloudidentity/data_source_cloud_identity_group_lookup_test.go @@ -0,0 +1,102 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package cloudidentity_test + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func testAccDataSourceCloudIdentityGroupLookup_basicTest(t *testing.T) { + + context := map[string]interface{}{ + "org_domain": envvar.GetTestOrgDomainFromEnv(t), + "cust_id": envvar.GetTestCustIdFromEnv(t), + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Create group and look it up via its group key, i.e. email we set + Config: testAccCloudIdentityGroupLookupConfig_groupKeyLookup(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr("data.google_cloud_identity_group_lookup.email", + "name", regexp.MustCompile("^groups/.*$")), + resource.TestCheckResourceAttrPair("data.google_cloud_identity_group_lookup.email", "name", + "google_cloud_identity_group.cloud_identity_group_basic", "name"), + ), + }, + { + // Look up group via an API-generated 'additional group key' + Config: testAccCloudIdentityGroupLookupConfig_additionalGroupKeyLookup(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair("data.google_cloud_identity_group_lookup.additional-groupkey", "name", + "google_cloud_identity_group.cloud_identity_group_basic", "name"), + ), + }, + }, + }) +} + +func testAccCloudIdentityGroupLookupConfig_groupKeyLookup(context map[string]interface{}) string { + return acctest.Nprintf(` +# config matching testAccCloudIdentityGroup_cloudIdentityGroupsBasicExample +resource "google_cloud_identity_group" "cloud_identity_group_basic" { + display_name = "tf-test-my-identity-group%{random_suffix}" + initial_group_config = "WITH_INITIAL_OWNER" + + parent = "customers/%{cust_id}" + + group_key { + id = "tf-test-my-identity-group%{random_suffix}@%{org_domain}" + } + + labels = { + "cloudidentity.googleapis.com/groups.discussion_forum" = "" + } +} + +data "google_cloud_identity_group_lookup" "email" { + group_key { + id = google_cloud_identity_group.cloud_identity_group_basic.group_key[0].id + } +} +`, context) +} + +func testAccCloudIdentityGroupLookupConfig_additionalGroupKeyLookup(context map[string]interface{}) string { + return acctest.Nprintf(` +# config matching testAccCloudIdentityGroup_cloudIdentityGroupsBasicExample +resource "google_cloud_identity_group" "cloud_identity_group_basic" { + display_name = "tf-test-my-identity-group%{random_suffix}" + initial_group_config = "WITH_INITIAL_OWNER" + + parent = "customers/%{cust_id}" + + group_key { + id = "tf-test-my-identity-group%{random_suffix}@%{org_domain}" + } + + labels = { + "cloudidentity.googleapis.com/groups.discussion_forum" = "" + } +} + +data "google_cloud_identity_group_lookup" "additional-groupkey" { + group_key { + # This value is an automatically created 'additionalGroupKeys' value + id = "tf-test-my-identity-group%{random_suffix}@%{org_domain}.test-google-a.com" + } + depends_on = [ + google_cloud_identity_group.cloud_identity_group_basic, + ] +} +`, context) +} diff --git a/google/services/cloudidentity/data_source_cloud_identity_groups.go b/google/services/cloudidentity/data_source_cloud_identity_groups.go index 026bc69541d..65ce6da53ce 100644 --- a/google/services/cloudidentity/data_source_cloud_identity_groups.go +++ b/google/services/cloudidentity/data_source_cloud_identity_groups.go @@ -4,9 +4,10 @@ package cloudidentity import ( "fmt" + "time" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" - "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "google.golang.org/api/cloudidentity/v1" diff --git a/google/services/cloudidentity/resource_cloud_identity_group_membership_test.go b/google/services/cloudidentity/resource_cloud_identity_group_membership_test.go index 8bd594d6d28..891624d6f72 100644 --- a/google/services/cloudidentity/resource_cloud_identity_group_membership_test.go +++ b/google/services/cloudidentity/resource_cloud_identity_group_membership_test.go @@ -297,7 +297,7 @@ resource "google_cloud_identity_group" "group" { parent = "customers/%{cust_id}" group_key { - id = "tf-test-my-identity-group%{random_suffix}@%{org_domain}" + id = "tf-test-my-identity-group%{random_suffix}@%{org_domain}" } labels = { @@ -311,7 +311,7 @@ resource "google_cloud_identity_group" "child-group" { parent = "customers/%{cust_id}" group_key { - id = "tf-test-my-identity-group%{random_suffix}-child@%{org_domain}" + id = "tf-test-my-identity-group%{random_suffix}-child@%{org_domain}" } labels = { @@ -327,7 +327,7 @@ resource "google_cloud_identity_group_membership" "cloud_identity_group_membersh } roles { - name = "MEMBER" + name = "MEMBER" } } `, context) diff --git a/google/services/cloudidentity/resource_cloud_identity_group_test.go b/google/services/cloudidentity/resource_cloud_identity_group_test.go index d3e40b48fff..0592d39247c 100644 --- a/google/services/cloudidentity/resource_cloud_identity_group_test.go +++ b/google/services/cloudidentity/resource_cloud_identity_group_test.go @@ -31,6 +31,7 @@ func TestAccCloudIdentityGroup(t *testing.T) { "membership_user": testAccCloudIdentityGroupMembership_cloudIdentityGroupMembershipUserExampleTest, "data_source_basic": testAccDataSourceCloudIdentityGroups_basicTest, "data_source_membership_basic": testAccDataSourceCloudIdentityGroupMemberships_basicTest, + "data_source_group_lookup": testAccDataSourceCloudIdentityGroupLookup_basicTest, } for name, tc := range testCases { @@ -125,7 +126,7 @@ resource "google_cloud_identity_group" "cloud_identity_group_basic" { parent = "customers/%{cust_id}" group_key { - id = "tf-test-my-identity-group%{random_suffix}@%{org_domain}" + id = "tf-test-my-identity-group%{random_suffix}@%{org_domain}" } labels = { diff --git a/website/docs/d/cloud_identity_group_lookup.html.markdown b/website/docs/d/cloud_identity_group_lookup.html.markdown new file mode 100644 index 00000000000..b51478ce1bc --- /dev/null +++ b/website/docs/d/cloud_identity_group_lookup.html.markdown @@ -0,0 +1,51 @@ +--- +subcategory: "Cloud Identity" +description: |- + Look up a Cloud Identity Group using its email and namespace. +--- + +# google_cloud_identity_group_lookup + +Use this data source to look up the resource name of a Cloud Identity Group by its [EntityKey](https://cloud.google.com/identity/docs/reference/rest/v1/EntityKey), i.e. the group's email. + +https://cloud.google.com/identity/docs/concepts/overview#groups + +## Example Usage + +```tf +data "google_cloud_identity_group_lookup" "group" { + group_key { + id = "my-group@example.com" + } +} +``` + +## Argument Reference + +* `group_key` - (Required) The EntityKey of the Group to lookup. A unique identifier for an entity in the Cloud Identity Groups API. +An entity can represent either a group with an optional namespace or a user without a namespace. +The combination of id and namespace must be unique; however, the same id can be used with different namespaces. Structure is [documented below](#nested_group_key). + +The `group_key` block supports: + +* `id` - + (Required) The ID of the entity. + For Google-managed entities, the id is the email address of an existing group or user. + For external-identity-mapped entities, the id is a string conforming + to the Identity Source's requirements. + +* `namespace` - + (Optional) The namespace in which the entity exists. + If not populated, the EntityKey represents a Google-managed entity + such as a Google user or a Google Group. + If populated, the EntityKey represents an external-identity-mapped group. + The namespace must correspond to an identity source created in Admin Console + and must be in the form of `identitysources/{identity_source_id}`. + + +## Attributes Reference + +In addition to the arguments listed above, the following attributes are exported: + +* `name` - + Resource name of the Group in the format: groups/{group_id}, where `group_id` is the unique ID assigned to the Group. \ No newline at end of file