Skip to content

Commit de065bd

Browse files
plus-1smelinath
authored andcommitted
Add new resource IAM OAuth Client Credential for Workforce Identity Federation (GoogleCloudPlatform#13231)
Co-authored-by: Stephen Lewis (Burrows) <[email protected]>
1 parent 9716568 commit de065bd

File tree

4 files changed

+277
-0
lines changed

4 files changed

+277
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright 2025 Google Inc.
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
---
15+
name: OauthClientCredential
16+
description: |
17+
Represents an OAuth Client Credential. Used to authenticate an OAuth Client
18+
while accessing Google Cloud resources on behalf of a Workforce Identity Federation user
19+
by using OAuth 2.0 Protocol.
20+
references:
21+
guides:
22+
"Managing OAuth clients": "https://cloud.google.com/iam/docs/workforce-manage-oauth-app#manage-clients"
23+
api: "https://cloud.google.com/iam/docs/reference/rest/v1/projects.locations.oauthClients.credentials"
24+
base_url: projects/{{project}}/locations/{{location}}/oauthClients/{{oauthclient}}/credentials
25+
update_mask: true
26+
self_link: projects/{{project}}/locations/{{location}}/oauthClients/{{oauthclient}}/credentials/{{oauth_client_credential_id}}
27+
create_url: projects/{{project}}/locations/{{location}}/oauthClients/{{oauthclient}}/credentials?oauthClientCredentialId={{oauth_client_credential_id}}
28+
update_verb: PATCH
29+
id_format: projects/{{project}}/locations/{{location}}/oauthClients/{{oauthclient}}/credentials/{{oauth_client_credential_id}}
30+
import_format:
31+
- projects/{{project}}/locations/{{location}}/oauthClients/{{oauthclient}}/credentials/{{oauth_client_credential_id}}
32+
timeouts:
33+
insert_minutes: 20
34+
update_minutes: 20
35+
delete_minutes: 20
36+
custom_code:
37+
decoder: "templates/terraform/decoders/treat_deleted_state_as_gone.go.tmpl"
38+
test_check_destroy: "templates/terraform/custom_check_destroy/iam_oauth_client_credential.go.tmpl"
39+
post_create: "templates/terraform/post_create/sleep.go.tmpl"
40+
post_update: "templates/terraform/post_create/sleep.go.tmpl"
41+
post_delete: "templates/terraform/post_create/sleep.go.tmpl"
42+
exclude_sweeper: true
43+
examples:
44+
- name: "iam_oauth_client_credential_full"
45+
primary_resource_id: "example"
46+
vars:
47+
oauth_client_id: "example-client-id"
48+
oauth_client_credential_id: "cred-id"
49+
parameters:
50+
- name: location
51+
type: String
52+
description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122.
53+
immutable: true
54+
url_param_only: true
55+
required: true
56+
- name: oauthclient
57+
type: String
58+
description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122.
59+
immutable: true
60+
url_param_only: true
61+
required: true
62+
- name: oauthClientCredentialId
63+
type: String
64+
description: |-
65+
Required. The ID to use for the OauthClientCredential, which becomes the
66+
final component of the resource name. This value should be 4-32 characters,
67+
and may contain the characters [a-z0-9-]. The prefix `gcp-` is
68+
reserved for use by Google, and may not be specified.
69+
immutable: true
70+
url_param_only: true
71+
required: true
72+
properties:
73+
- name: disabled
74+
type: Boolean
75+
description: |-
76+
Whether the OauthClientCredential is disabled. You cannot use a
77+
disabled OauthClientCredential.
78+
- name: clientSecret
79+
type: String
80+
description: |-
81+
The system-generated OAuth client secret.
82+
83+
The client secret must be stored securely. If the client secret is
84+
leaked, you must delete and re-create the client credential. To learn
85+
more, see [OAuth client and credential security risks and
86+
mitigations](https://cloud.google.com/iam/docs/workforce-oauth-app#security)
87+
output: true
88+
- name: displayName
89+
type: String
90+
description: |-
91+
A user-specified display name of the OauthClientCredential.
92+
93+
Cannot exceed 32 characters.
94+
- name: name
95+
type: String
96+
description: |-
97+
Immutable. Identifier. The resource name of the OauthClientCredential.
98+
99+
Format:
100+
`projects/{project}/locations/{location}/oauthClients/{oauth_client}/credentials/{credential}`
101+
output: true
102+
immutable: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
config := acctest.GoogleProviderConfig(t)
2+
3+
url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{"{{"}}IAMWorkforcePoolBasePath{{"}}"}}projects/{{"{{"}}project{{"}}"}}/locations/{{"{{"}}location{{"}}"}}/oauthClients/{{"{{"}}oauthclient{{"}}"}}/credentials/{{"{{"}}oauth_client_credential_id{{"}}"}}")
4+
if err != nil {
5+
return err
6+
}
7+
8+
_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
9+
Config: config,
10+
Method: "GET",
11+
RawURL: url,
12+
UserAgent: config.UserAgent,
13+
})
14+
if err != nil {
15+
e := err.(*googleapi.Error)
16+
// credential or parent client is expected to be deleted.
17+
if (e.Code == 404) {
18+
return nil
19+
}
20+
21+
// Return err in all other cases
22+
return err
23+
}
24+
25+
return fmt.Errorf("IAMOAuthClientCredential still exists at %s", url)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
resource "google_iam_oauth_client" "oauth_client" {
2+
oauth_client_id = "{{index $.Vars "oauth_client_id"}}"
3+
location = "global"
4+
allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"]
5+
allowed_redirect_uris = ["https://www.example.com"]
6+
allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
7+
client_type = "CONFIDENTIAL_CLIENT"
8+
}
9+
10+
resource "google_iam_oauth_client_credential" "{{$.PrimaryResourceId}}" {
11+
oauthclient = google_iam_oauth_client.oauth_client.oauth_client_id
12+
location = google_iam_oauth_client.oauth_client.location
13+
oauth_client_credential_id = "{{index $.Vars "oauth_client_credential_id"}}"
14+
disabled = true
15+
display_name = "Display Name of credential"
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package iamworkforcepool_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
7+
"github.com/hashicorp/terraform-plugin-testing/plancheck"
8+
9+
"github.com/hashicorp/terraform-provider-google/google/acctest"
10+
)
11+
12+
13+
func TestAccIAMWorkforcePoolOauthClientCredential_full(t *testing.T) {
14+
t.Parallel()
15+
16+
context := map[string]interface{}{
17+
"random_suffix": acctest.RandString(t, 10),
18+
}
19+
20+
acctest.VcrTest(t, resource.TestCase{
21+
PreCheck: func() { acctest.AccTestPreCheck(t) },
22+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
23+
CheckDestroy: testAccCheckIAMWorkforcePoolOauthClientCredentialDestroyProducer(t),
24+
Steps: []resource.TestStep{
25+
{
26+
Config: testAccIAMWorkforcePoolOauthClientCredential_full(context),
27+
},
28+
{
29+
ResourceName: "google_iam_oauth_client_credential.example",
30+
ImportState: true,
31+
ImportStateVerify: true,
32+
ImportStateVerifyIgnore: []string{"location", "oauth_client_credential_id", "oauthclient"},
33+
},
34+
{
35+
Config: testAccIAMWorkforcePoolOauthClientCredential_full_update(context),
36+
ConfigPlanChecks: resource.ConfigPlanChecks{
37+
PreApply: []plancheck.PlanCheck{
38+
plancheck.ExpectResourceAction("google_iam_oauth_client_credential.example", plancheck.ResourceActionUpdate),
39+
},
40+
},
41+
},
42+
{
43+
ResourceName: "google_iam_oauth_client_credential.example",
44+
ImportState: true,
45+
ImportStateVerify: true,
46+
ImportStateVerifyIgnore: []string{"location", "oauth_client_credential_id", "oauthclient"},
47+
},
48+
{
49+
Config: testAccIAMWorkforcePoolOauthClientCredential_full_cleanOptionalFields(context),
50+
ConfigPlanChecks: resource.ConfigPlanChecks{
51+
PreApply: []plancheck.PlanCheck{
52+
plancheck.ExpectResourceAction("google_iam_oauth_client_credential.example", plancheck.ResourceActionUpdate),
53+
},
54+
},
55+
},
56+
{
57+
ResourceName: "google_iam_oauth_client_credential.example",
58+
ImportState: true,
59+
ImportStateVerify: true,
60+
ImportStateVerifyIgnore: []string{"location", "oauth_client_credential_id", "oauthclient"},
61+
},
62+
// Set disabled to `true` so the client credential can be deleted
63+
{
64+
Config: testAccIAMWorkforcePoolOauthClientCredential_full_update(context),
65+
ConfigPlanChecks: resource.ConfigPlanChecks{
66+
PreApply: []plancheck.PlanCheck{
67+
plancheck.ExpectResourceAction("google_iam_oauth_client_credential.example", plancheck.ResourceActionUpdate),
68+
},
69+
},
70+
},
71+
},
72+
})
73+
}
74+
75+
func testAccIAMWorkforcePoolOauthClientCredential_full(context map[string]interface{}) string {
76+
return acctest.Nprintf(`
77+
resource "google_iam_oauth_client" "oauth_client" {
78+
oauth_client_id = "tf-test-example-client-id%{random_suffix}"
79+
location = "global"
80+
allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"]
81+
allowed_redirect_uris = ["https://www.example.com"]
82+
allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
83+
client_type = "CONFIDENTIAL_CLIENT"
84+
}
85+
86+
resource "google_iam_oauth_client_credential" "example" {
87+
oauthclient = google_iam_oauth_client.oauth_client.oauth_client_id
88+
location = google_iam_oauth_client.oauth_client.location
89+
oauth_client_credential_id = "tf-test-cred-id%{random_suffix}"
90+
disabled = true
91+
display_name = "Display Name of credential"
92+
}
93+
`, context)
94+
}
95+
96+
func testAccIAMWorkforcePoolOauthClientCredential_full_update(context map[string]interface{}) string {
97+
return acctest.Nprintf(`
98+
resource "google_iam_oauth_client" "oauth_client" {
99+
oauth_client_id = "tf-test-example-client-id%{random_suffix}"
100+
location = "global"
101+
allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"]
102+
allowed_redirect_uris = ["https://www.example.com"]
103+
allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
104+
client_type = "CONFIDENTIAL_CLIENT"
105+
}
106+
107+
resource "google_iam_oauth_client_credential" "example" {
108+
oauthclient = google_iam_oauth_client.oauth_client.oauth_client_id
109+
location = google_iam_oauth_client.oauth_client.location
110+
oauth_client_credential_id = "tf-test-cred-id%{random_suffix}"
111+
disabled = true
112+
display_name = "Updated displayName"
113+
}
114+
`, context)
115+
}
116+
117+
func testAccIAMWorkforcePoolOauthClientCredential_full_cleanOptionalFields(context map[string]interface{}) string {
118+
return acctest.Nprintf(`
119+
resource "google_iam_oauth_client" "oauth_client" {
120+
oauth_client_id = "tf-test-example-client-id%{random_suffix}"
121+
location = "global"
122+
allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"]
123+
allowed_redirect_uris = ["https://www.example.com"]
124+
allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
125+
client_type = "CONFIDENTIAL_CLIENT"
126+
}
127+
128+
resource "google_iam_oauth_client_credential" "example" {
129+
oauthclient = google_iam_oauth_client.oauth_client.oauth_client_id
130+
location = google_iam_oauth_client.oauth_client.location
131+
oauth_client_credential_id = "tf-test-cred-id%{random_suffix}"
132+
}
133+
`, context)
134+
}

0 commit comments

Comments
 (0)