1
+ import * as pulumi from "@pulumi/pulumi" ;
2
+ import * as gcp from "@pulumi/gcp" ;
3
+ import * as random from "@pulumi/random" ;
4
+ import * as pcloud from "@pulumi/pulumiservice" ;
5
+
6
+ const config = new pulumi . Config ( ) ;
7
+ const gcpConfig = new pulumi . Config ( "gcp" ) ;
8
+
9
+ const gcpProjectName = gcpConfig . require ( "project" ) ;
10
+
11
+ // In most cases, it's safe to assume that this stack is run in the same Pulumi
12
+ // org in which the OIDC environment is being configured. If not, set the
13
+ // escEnvOrg config to the name of the org where the environment is going to be
14
+ // configured.
15
+ const escEnvOrg = config . get ( "escEnvOrg" ) || pulumi . getOrganization ( ) ;
16
+ const escEnvProject = config . get ( "escEnvProject" ) || `gcloud` ;
17
+ const escEnvName = config . get ( "escEnvName" ) || `${ gcpProjectName } -admin` ;
18
+
19
+ // We use a shorter name for the Workload Identity Pool and Service Account IDs
20
+ // because they have character limits of 30 and 32 respectively, and the Google
21
+ // Cloud project name is redundant in this context anyway, since we already know
22
+ // what Google Cloud project we are in:
23
+ const workloadIdentityPoolId = `${ escEnvOrg } -admin` ;
24
+ const serviceAccountId = workloadIdentityPoolId . replace ( "-" , "" ) ;
25
+
26
+ const randomSuffix = new random . RandomString ( `random-suffix` , {
27
+ length : 5 ,
28
+ lower : true ,
29
+ upper : false ,
30
+ special : false
31
+ } ) ;
32
+
33
+ // The Workload Identity Pool id uses a random suffix so that this stack can be
34
+ // brought up and down repeatably: Workload Identity Pools only soft deletes and
35
+ // will auto-purge after 30 days. It is not possible to force a hard delete:
36
+ const identityPool = new gcp . iam . WorkloadIdentityPool ( `identity-pool` , {
37
+ workloadIdentityPoolId : pulumi . interpolate `${ workloadIdentityPoolId } -${ randomSuffix . result } `
38
+ } ) ;
39
+
40
+ const oidcProvider = new gcp . iam . WorkloadIdentityPoolProvider ( `identity-pool-provider` , {
41
+ workloadIdentityPoolId : identityPool . workloadIdentityPoolId ,
42
+ workloadIdentityPoolProviderId : `pulumi-cloud-${ pulumi . getOrganization ( ) } -oidc` ,
43
+ oidc : {
44
+ issuerUri : "https://api.pulumi.com/oidc" ,
45
+ allowedAudiences : [ `gcp:${ pulumi . getOrganization ( ) } ` ]
46
+ } ,
47
+ attributeMapping : {
48
+ "google.subject" : "assertion.sub"
49
+ }
50
+ } ) ;
51
+
52
+ const serviceAccount = new gcp . serviceaccount . Account ( "service-account" , {
53
+ accountId : serviceAccountId ,
54
+ project : gcpProjectName
55
+ } ) ;
56
+
57
+ new gcp . projects . IAMMember ( "service-account" , {
58
+ member : pulumi . interpolate `serviceAccount:${ serviceAccount . email } ` ,
59
+ role : "roles/admin" ,
60
+ project : gcpProjectName
61
+ } ) ;
62
+
63
+ new gcp . serviceaccount . IAMBinding ( "service-account" , {
64
+ serviceAccountId : serviceAccount . id ,
65
+ role : "roles/iam.workloadIdentityUser" ,
66
+ members : [ pulumi . interpolate `principalSet://iam.googleapis.com/${ identityPool . name } /*` ]
67
+ } ) ;
68
+
69
+ // fn::open::gcp-login requires project number instead of project name:
70
+ const projectNumber = gcp . projects . getProjectOutput ( {
71
+ filter : `name:${ gcpProjectName } `
72
+ } ) . projects [ 0 ] . number
73
+ . apply ( projectNumber => + projectNumber ) ; // this casts it from string to a number
74
+
75
+ const envYaml = pulumi . interpolate `
76
+ values:
77
+ gcp:
78
+ login:
79
+ fn::open::gcp-login:
80
+ project: ${ projectNumber }
81
+ oidc:
82
+ workloadPoolId: ${ oidcProvider . workloadIdentityPoolId }
83
+ providerId: ${ oidcProvider . workloadIdentityPoolProviderId }
84
+ serviceAccount: ${ serviceAccount . email }
85
+ subjectAttributes:
86
+ - currentEnvironment.name
87
+ pulumiConfig:
88
+ gpc:project: \${gcp.login.project}
89
+ environmentVariables:
90
+ # The Google Cloud SDK (which is used by the Pulumi provider) requires the project to be set by number:
91
+ GOOGLE_CLOUD_PROJECT: \${gcp.login.project}
92
+ # The gcloud CLI requires the project be set by name, and via a different env var.
93
+ # See: https://cloud.google.com/sdk/docs/properties#setting_properties_using_environment_variables
94
+ CLOUDSDK_CORE_PROJECT: ${ gcpProjectName }
95
+ GOOGLE_OAUTH_ACCESS_TOKEN: \${gcp.login.accessToken}
96
+ CLOUDSDK_AUTH_ACCESS_TOKEN: \${gcp.login.accessToken}
97
+ USE_GKE_GCLOUD_AUTH_PLUGIN: True
98
+ ` ;
99
+
100
+ const environment = new pcloud . Environment ( "environment" , {
101
+ organization : escEnvOrg ,
102
+ project : escEnvProject ,
103
+ name : escEnvName ,
104
+ yaml : envYaml ,
105
+ } ) ;
106
+
107
+ export const escEnvId = environment . id ;
0 commit comments