diff --git a/mmv1/products/iamworkforcepool/OauthClient.yaml b/mmv1/products/iamworkforcepool/OauthClient.yaml index bbb1154877f9..aadf9ba78f52 100644 --- a/mmv1/products/iamworkforcepool/OauthClient.yaml +++ b/mmv1/products/iamworkforcepool/OauthClient.yaml @@ -34,7 +34,6 @@ timeouts: update_minutes: 20 delete_minutes: 20 custom_code: - constants: "templates/terraform/constants/iam_oauth_client.go.tmpl" decoder: "templates/terraform/decoders/treat_deleted_state_as_gone.go.tmpl" test_check_destroy: "templates/terraform/custom_check_destroy/iam_oauth_client.go.tmpl" post_create: "templates/terraform/post_create/sleep.go.tmpl" diff --git a/mmv1/products/iamworkforcepool/OauthClientCredential.yaml b/mmv1/products/iamworkforcepool/OauthClientCredential.yaml new file mode 100644 index 000000000000..af9e302f0ba3 --- /dev/null +++ b/mmv1/products/iamworkforcepool/OauthClientCredential.yaml @@ -0,0 +1,107 @@ +# Copyright 2024 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +name: OauthClientCredential +description: | + Represents an OAuth Client Credential. Used to authenticate an OAuth Client + while accessing Google Cloud resources on behalf of a Workforce Identity Federation user + by using OAuth 2.0 Protocol. +references: + guides: + "Managing OAuth clients": "https://cloud.google.com/iam/docs/workforce-manage-oauth-app#manage-clients" + api: "https://cloud.google.com/iam/docs/reference/rest/v1/projects.locations.oauthClients.credentials" +base_url: projects/{{project}}/locations/{{location}}/oauthClients/{{oauthclient}}/credentials +update_mask: true +self_link: projects/{{project}}/locations/{{location}}/oauthClients/{{oauthclient}}/credentials/{{oauth_client_credential_id}} +create_url: projects/{{project}}/locations/{{location}}/oauthClients/{{oauthclient}}/credentials?oauthClientCredentialId={{oauth_client_credential_id}} +update_verb: PATCH +id_format: projects/{{project}}/locations/{{location}}/oauthClients/{{oauthclient}}/credentials/{{oauth_client_credential_id}} +import_format: + - projects/{{project}}/locations/{{location}}/oauthClients/{{oauthclient}}/credentials/{{oauth_client_credential_id}} +timeouts: + insert_minutes: 20 + update_minutes: 20 + delete_minutes: 20 +custom_code: + decoder: "templates/terraform/decoders/treat_deleted_state_as_gone.go.tmpl" + test_check_destroy: "templates/terraform/custom_check_destroy/iam_oauth_client_credential.go.tmpl" + post_create: "templates/terraform/post_create/sleep.go.tmpl" + post_update: "templates/terraform/post_create/sleep.go.tmpl" + post_delete: "templates/terraform/post_create/sleep.go.tmpl" +exclude_sweeper: true +examples: + - name: "iam_oauth_client_credential_basic" + primary_resource_id: "example" + vars: + oauth_client_id: "example-client-id" + oauth_client_credential_id: "cred-id" + - name: "iam_oauth_client_credential_full" + primary_resource_id: "example" + vars: + oauth_client_id: "example-client-id" + oauth_client_credential_id: "cred-id" +parameters: + - name: location + type: String + description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122. + immutable: true + url_param_only: true + required: true + - name: oauthclient + type: String + description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122. + immutable: true + url_param_only: true + required: true + - name: oauthClientCredentialId + type: String + description: |- + Required. The ID to use for the OauthClientCredential, which becomes the + final component of the resource name. This value should be 4-32 characters, + and may contain the characters [a-z0-9-]. The prefix `gcp-` is + reserved for use by Google, and may not be specified. + immutable: true + url_param_only: true + required: true +properties: + - name: disabled + type: Boolean + description: |- + Optional. Whether the OauthClientCredential is disabled. You cannot use a + disabled OauthClientCredential. + - name: clientSecret + type: String + description: |- + Output only. The system-generated OAuth client secret. + + The client secret must be stored securely. If the client secret is + leaked, you must delete and re-create the client credential. To learn + more, see [OAuth client and credential security risks and + mitigations](https://cloud.google.com/iam/docs/workforce-oauth-app#security) + output: true + - name: displayName + type: String + description: |- + Optional. A user-specified display name of the OauthClientCredential. + + Cannot exceed 32 characters. + - name: name + type: String + description: |- + Immutable. Identifier. The resource name of the OauthClientCredential. + + Format: + `projects/{project}/locations/{location}/oauthClients/{oauth_client}/credentials/{credential}` + output: true + immutable: true diff --git a/mmv1/templates/terraform/constants/iam_oauth_client.go.tmpl b/mmv1/templates/terraform/constants/iam_oauth_client.go.tmpl deleted file mode 100644 index 0be3c5e309c9..000000000000 --- a/mmv1/templates/terraform/constants/iam_oauth_client.go.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -const oauthClientIdRegexp = `^[a-z][a-z0-9-]{4,61}[a-z0-9]$` - -func ValidateOauthClientId(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - - if strings.HasPrefix(value, "gcp-") { - errors = append(errors, fmt.Errorf( - "%q (%q) can not start with \"gcp-\". " + - "The prefix `gcp-` is reserved for use by Google, and may not be specified.", k, value)) - } - - if !regexp.MustCompile(oauthClientIdRegexp).MatchString(value) { - errors = append(errors, fmt.Errorf( - "%q (%q) must contain only lowercase letters [a-z], digits [0-9], and hyphens " + - "[-]. The OauthClient ID must be between 6 and 63 characters, begin " + - "with a letter, and cannot have a trailing hyphen.", k, value)) - } - - return -} diff --git a/mmv1/templates/terraform/custom_check_destroy/iam_oauth_client.go.tmpl b/mmv1/templates/terraform/custom_check_destroy/iam_oauth_client.go.tmpl index d233441bf7ef..651b6b029a0c 100644 --- a/mmv1/templates/terraform/custom_check_destroy/iam_oauth_client.go.tmpl +++ b/mmv1/templates/terraform/custom_check_destroy/iam_oauth_client.go.tmpl @@ -1,6 +1,6 @@ config := acctest.GoogleProviderConfig(t) -url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{"{{"}}IAMWorkforcePoolBasePath{{"}}"}}projects/{{"{{"}}project{{"}}"}}/locations/global/oauthClients/{{"{{"}}oauth_client_id{{"}}"}}") +url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{"{{"}}IAMWorkforcePoolBasePath{{"}}"}}projects/{{"{{"}}project{{"}}"}}/locations/{{"{{"}}location{{"}}"}}/oauthClients/{{"{{"}}oauth_client_id{{"}}"}}") if err != nil { return err } @@ -19,4 +19,4 @@ if v := res["state"]; v == "DELETED" { return nil } -return fmt.Errorf("IAMOAuthCLient still exists at %s", url) +return fmt.Errorf("IAMOAuthClient still exists at %s", url) diff --git a/mmv1/templates/terraform/custom_check_destroy/iam_oauth_client_credential.go.tmpl b/mmv1/templates/terraform/custom_check_destroy/iam_oauth_client_credential.go.tmpl new file mode 100644 index 000000000000..9560ffe0c06b --- /dev/null +++ b/mmv1/templates/terraform/custom_check_destroy/iam_oauth_client_credential.go.tmpl @@ -0,0 +1,25 @@ +config := acctest.GoogleProviderConfig(t) + +url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{"{{"}}IAMWorkforcePoolBasePath{{"}}"}}projects/{{"{{"}}project{{"}}"}}/locations/{{"{{"}}location{{"}}"}}/oauthClients/{{"{{"}}oauthclient{{"}}"}}/credentials/{{"{{"}}oauth_client_credential_id{{"}}"}}") +if err != nil { + return err +} + +_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + RawURL: url, + UserAgent: config.UserAgent, +}) +if err != nil { + e := err.(*googleapi.Error) + // credential or parent client is expected to be deleted. + if (e.Code == 404) { + return nil + } + + // Return err in all other cases + return err +} + +return fmt.Errorf("IAMOAuthClientCredential still exists at %s", url) diff --git a/mmv1/templates/terraform/examples/iam_oauth_client_credential_basic.tf.tmpl b/mmv1/templates/terraform/examples/iam_oauth_client_credential_basic.tf.tmpl new file mode 100644 index 000000000000..220660893897 --- /dev/null +++ b/mmv1/templates/terraform/examples/iam_oauth_client_credential_basic.tf.tmpl @@ -0,0 +1,15 @@ +resource "google_iam_oauth_client" "oauth_client" { + oauth_client_id = "{{index $.Vars "oauth_client_id"}}" + location = "global" + allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"] + allowed_redirect_uris = ["https://www.example.com"] + allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + client_type = "CONFIDENTIAL_CLIENT" +} + +resource "google_iam_oauth_client_credential" "{{$.PrimaryResourceId}}" { + oauthclient = google_iam_oauth_client.oauth_client.oauth_client_id + location = google_iam_oauth_client.oauth_client.location + oauth_client_credential_id = "{{index $.Vars "oauth_client_credential_id"}}" + disabled = true +} diff --git a/mmv1/templates/terraform/examples/iam_oauth_client_credential_full.tf.tmpl b/mmv1/templates/terraform/examples/iam_oauth_client_credential_full.tf.tmpl new file mode 100644 index 000000000000..b8499bd25428 --- /dev/null +++ b/mmv1/templates/terraform/examples/iam_oauth_client_credential_full.tf.tmpl @@ -0,0 +1,16 @@ +resource "google_iam_oauth_client" "oauth_client" { + oauth_client_id = "{{index $.Vars "oauth_client_id"}}" + location = "global" + allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"] + allowed_redirect_uris = ["https://www.example.com"] + allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + client_type = "CONFIDENTIAL_CLIENT" +} + +resource "google_iam_oauth_client_credential" "{{$.PrimaryResourceId}}" { + oauthclient = google_iam_oauth_client.oauth_client.oauth_client_id + location = google_iam_oauth_client.oauth_client.location + oauth_client_credential_id = "{{index $.Vars "oauth_client_credential_id"}}" + disabled = true + display_name = "Display Name of credential" +} diff --git a/mmv1/third_party/terraform/services/iamworkforcepool/resource_iam_oauth_client_credential_test.go.tmpl b/mmv1/third_party/terraform/services/iamworkforcepool/resource_iam_oauth_client_credential_test.go.tmpl new file mode 100644 index 000000000000..1fd8b21b7e6e --- /dev/null +++ b/mmv1/third_party/terraform/services/iamworkforcepool/resource_iam_oauth_client_credential_test.go.tmpl @@ -0,0 +1,160 @@ +package iamworkforcepool_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccIAMWorkforcePoolOauthClientCredential_basic(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckIAMWorkforcePoolOauthClientCredentialDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccIAMWorkforcePoolOauthClientCredential_basic(context), + }, + { + ResourceName: "google_iam_oauth_client_credential.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "oauth_client_credential_id", "oauthclient"}, + }, + { + Config: testAccIAMWorkforcePoolOauthClientCredential_basic_update(context), + }, + { + ResourceName: "google_iam_oauth_client_credential.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "oauth_client_credential_id", "oauthclient"}, + }, + }, + }) +} + +func testAccIAMWorkforcePoolOauthClientCredential_basic(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_iam_oauth_client" "oauth_client" { + oauth_client_id = "tf-test-example-client-id%{random_suffix}" + location = "global" + allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"] + allowed_redirect_uris = ["https://www.example.com"] + allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + client_type = "CONFIDENTIAL_CLIENT" +} + +resource "google_iam_oauth_client_credential" "example" { + oauthclient = google_iam_oauth_client.oauth_client.oauth_client_id + location = google_iam_oauth_client.oauth_client.location + oauth_client_credential_id = "tf-test-cred-id%{random_suffix}" + disabled = true +} +`, context) +} + +func testAccIAMWorkforcePoolOauthClientCredential_basic_update(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_iam_oauth_client" "oauth_client" { + oauth_client_id = "tf-test-example-client-id%{random_suffix}" + location = "global" + allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"] + allowed_redirect_uris = ["https://www.example.com"] + allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + client_type = "CONFIDENTIAL_CLIENT" +} + +resource "google_iam_oauth_client_credential" "example" { + oauthclient = google_iam_oauth_client.oauth_client.oauth_client_id + location = google_iam_oauth_client.oauth_client.location + oauth_client_credential_id = "tf-test-cred-id%{random_suffix}" + disabled = true +} +`, context) +} + + +func TestAccIAMWorkforcePoolOauthClientCredential_full(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckIAMWorkforcePoolOauthClientCredentialDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccIAMWorkforcePoolOauthClientCredential_full(context), + }, + { + ResourceName: "google_iam_oauth_client_credential.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "oauth_client_credential_id", "oauthclient"}, + }, + { + Config: testAccIAMWorkforcePoolOauthClientCredential_full_update(context), + }, + { + ResourceName: "google_iam_oauth_client_credential.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "oauth_client_credential_id", "oauthclient"}, + }, + }, + }) +} + +func testAccIAMWorkforcePoolOauthClientCredential_full(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_iam_oauth_client" "oauth_client" { + oauth_client_id = "tf-test-example-client-id%{random_suffix}" + location = "global" + allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"] + allowed_redirect_uris = ["https://www.example.com"] + allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + client_type = "CONFIDENTIAL_CLIENT" +} + +resource "google_iam_oauth_client_credential" "example" { + oauthclient = google_iam_oauth_client.oauth_client.oauth_client_id + location = google_iam_oauth_client.oauth_client.location + oauth_client_credential_id = "tf-test-cred-id%{random_suffix}" + disabled = true + display_name = "Display Name of credential" +} +`, context) +} + +func testAccIAMWorkforcePoolOauthClientCredential_full_update(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_iam_oauth_client" "oauth_client" { + oauth_client_id = "tf-test-example-client-id%{random_suffix}" + location = "global" + allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"] + allowed_redirect_uris = ["https://www.example.com"] + allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + client_type = "CONFIDENTIAL_CLIENT" +} + +resource "google_iam_oauth_client_credential" "example" { + oauthclient = google_iam_oauth_client.oauth_client.oauth_client_id + location = google_iam_oauth_client.oauth_client.location + oauth_client_credential_id = "tf-test-cred-id%{random_suffix}" + disabled = true + display_name = "Updated displayName" +} +`, context) +} \ No newline at end of file