Skip to content

Commit b208d5c

Browse files
authored
fix: WI conditionally invoke data source if using external GSA (#974)
* fix: conditionally invoke datasource for custom gsa * add an example for custom gsa * refactor tests * fmt
1 parent a861a2d commit b208d5c

File tree

7 files changed

+115
-33
lines changed

7 files changed

+115
-33
lines changed

examples/workload_identity/README.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ Read more about [workload identity in the docs](https://cloud.google.com/kuberne
2424
| ca\_certificate | n/a |
2525
| client\_token | n/a |
2626
| cluster\_name | Cluster name |
27-
| k8s\_service\_account\_email | K8S GCP service account. |
28-
| k8s\_service\_account\_name | K8S GCP service name |
27+
| default\_wi\_email | GCP service account. |
28+
| default\_wi\_ksa\_name | K8S SA name |
29+
| existing\_gsa\_email | GCP service account. |
30+
| existing\_gsa\_name | K8S SA name |
31+
| existing\_ksa\_email | GCP service account. |
32+
| existing\_ksa\_name | K8S SA name |
2933
| kubernetes\_endpoint | n/a |
3034
| location | Cluster location (zones) |
3135
| project\_id | Project id where GKE cluster is created. |

examples/workload_identity/main.tf

+15-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ module "workload_identity" {
6262
use_existing_k8s_sa = false
6363
}
6464

65-
6665
# example with existing KSA
6766
resource "kubernetes_service_account" "test" {
6867
metadata {
@@ -83,3 +82,18 @@ module "workload_identity_existing_ksa" {
8382
use_existing_k8s_sa = true
8483
k8s_sa_name = kubernetes_service_account.test.metadata.0.name
8584
}
85+
86+
# example with existing GSA
87+
resource "google_service_account" "custom" {
88+
account_id = "custom-gsa"
89+
project = var.project_id
90+
}
91+
92+
module "workload_identity_existing_gsa" {
93+
source = "../../modules/workload-identity"
94+
project_id = var.project_id
95+
name = google_service_account.custom.account_id
96+
use_existing_gcp_sa = true
97+
# wait till custom GSA is created to force module data source read during apply
98+
depends_on = [google_service_account.custom]
99+
}

examples/workload_identity/outputs.tf

+28-5
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,35 @@ output "cluster_name" {
5353
value = module.gke.name
5454
}
5555

56-
output "k8s_service_account_email" {
57-
description = "K8S GCP service account."
56+
# Default instantiation of WI module
57+
output "default_wi_email" {
58+
description = "GCP service account."
5859
value = module.workload_identity.gcp_service_account_email
5960
}
6061

61-
output "k8s_service_account_name" {
62-
description = "K8S GCP service name"
63-
value = module.workload_identity.gcp_service_account_name
62+
output "default_wi_ksa_name" {
63+
description = "K8S SA name"
64+
value = module.workload_identity.k8s_service_account_name
65+
}
66+
67+
# Existing KSA instantiation of WI module
68+
output "existing_ksa_email" {
69+
description = "GCP service account."
70+
value = module.workload_identity_existing_ksa.gcp_service_account_email
71+
}
72+
73+
output "existing_ksa_name" {
74+
description = "K8S SA name"
75+
value = module.workload_identity_existing_ksa.k8s_service_account_name
76+
}
77+
78+
# Existing GSA instantiation of WI module
79+
output "existing_gsa_email" {
80+
description = "GCP service account."
81+
value = google_service_account.custom.email
82+
}
83+
84+
output "existing_gsa_name" {
85+
description = "K8S SA name"
86+
value = module.workload_identity_existing_gsa.k8s_service_account_name
6487
}

modules/workload-identity/main.tf

+5-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ locals {
1818
# GCP service account ids must be < 30 chars matching regex ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$
1919
# KSAs do not have this naming restriction.
2020
gcp_given_name = var.gcp_sa_name != null ? var.gcp_sa_name : substr(var.name, 0, 30)
21-
gcp_sa_email = data.google_service_account.cluster_service_account.email
21+
gcp_sa_email = var.use_existing_gcp_sa ? data.google_service_account.cluster_service_account[0].email : google_service_account.cluster_service_account[0].email
2222
gcp_sa_fqn = "serviceAccount:${local.gcp_sa_email}"
2323

2424
# This will cause Terraform to block returning outputs until the service account is created
@@ -30,8 +30,9 @@ locals {
3030
}
3131

3232
data "google_service_account" "cluster_service_account" {
33-
# This will cause Terraform to block looking up details until the service account is created
34-
account_id = var.use_existing_gcp_sa ? local.gcp_given_name : google_service_account.cluster_service_account[0].account_id
33+
count = var.use_existing_gcp_sa ? 1 : 0
34+
35+
account_id = var.name
3536
project = var.project_id
3637
}
3738

@@ -72,7 +73,7 @@ module "annotate-sa" {
7273
}
7374

7475
resource "google_service_account_iam_member" "main" {
75-
service_account_id = data.google_service_account.cluster_service_account.name
76+
service_account_id = var.use_existing_gcp_sa ? data.google_service_account.cluster_service_account[0].name : google_service_account.cluster_service_account[0].name
7677
role = "roles/iam.workloadIdentityUser"
7778
member = local.k8s_sa_gcp_derived_name
7879
}

test/fixtures/workload_identity/outputs.tf

+29-5
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,36 @@ output "cluster_name" {
7575
value = module.example.cluster_name
7676
}
7777

78-
output "k8s_service_account_email" {
79-
description = "K8S GCP service account."
80-
value = module.example.k8s_service_account_email
78+
# Default instantiation of WI module
79+
output "default_wi_email" {
80+
description = "GCP service account."
81+
value = module.example.default_wi_email
8182
}
8283

83-
output "k8s_service_account_name" {
84+
output "default_wi_ksa_name" {
8485
description = "K8S GCP service account name."
85-
value = module.example.k8s_service_account_name
86+
value = module.example.default_wi_ksa_name
8687
}
88+
89+
# Existing KSA instantiation of WI module
90+
output "existing_ksa_email" {
91+
description = "GCP service account."
92+
value = module.example.existing_ksa_email
93+
}
94+
95+
output "existing_ksa_name" {
96+
description = "K8S GCP service name"
97+
value = module.example.existing_ksa_name
98+
}
99+
100+
# Existing GSA instantiation of WI module
101+
output "existing_gsa_email" {
102+
description = "GCP service account."
103+
value = module.example.existing_gsa_email
104+
}
105+
106+
output "existing_gsa_name" {
107+
description = "K8S GCP service name"
108+
value = module.example.existing_gsa_name
109+
}
110+

test/integration/workload_identity/controls/gcloud.rb

+18-14
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
project_id = attribute('project_id')
1616
location = attribute('location')
1717
cluster_name = attribute('cluster_name')
18-
k8s_service_account_email = attribute('k8s_service_account_email')
19-
k8s_service_account_name = attribute('k8s_service_account_name')
18+
wi_gsa_to_k8s_sa = {
19+
attribute('default_wi_email') => attribute('default_wi_ksa_name'),
20+
attribute('existing_ksa_email') => attribute('existing_ksa_name'),
21+
attribute('existing_gsa_email') => attribute('existing_gsa_name')
22+
}
2023

2124
control "gcloud" do
2225
title "Google Compute Engine GKE configuration"
@@ -57,20 +60,21 @@
5760
end
5861
end
5962
end
63+
wi_gsa_to_k8s_sa.each do |gsa_email,ksa_name|
64+
describe command("gcloud iam service-accounts get-iam-policy #{gsa_email} --format=json") do
65+
its(:exit_status) { should eq 0 }
66+
its(:stderr) { should eq '' }
6067

61-
describe command("gcloud iam service-accounts get-iam-policy #{k8s_service_account_email} --format=json") do
62-
its(:exit_status) { should eq 0 }
63-
its(:stderr) { should eq '' }
64-
65-
let!(:iam) do
66-
if subject.exit_status == 0
67-
JSON.parse(subject.stdout)
68-
else
69-
{}
68+
let!(:iam) do
69+
if subject.exit_status == 0
70+
JSON.parse(subject.stdout)
71+
else
72+
{}
73+
end
74+
end
75+
it "has expected workload identity user roles" do
76+
expect(iam['bindings'][0]).to include("members" => ["serviceAccount:#{project_id}.svc.id.goog[default/#{ksa_name}]"], "role" => "roles/iam.workloadIdentityUser")
7077
end
71-
end
72-
it "has expected workload identity user roles" do
73-
expect(iam['bindings'][0]).to include("members" => [k8s_service_account_name], "role" => "roles/iam.workloadIdentityUser")
7478
end
7579
end
7680
end

test/integration/workload_identity/inspec.yml

+14-2
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,21 @@ attributes:
2323
- name: project_id
2424
required: true
2525
type: string
26-
- name: k8s_service_account_email
26+
- name: default_wi_email
2727
required: true
2828
type: string
29-
- name: k8s_service_account_name
29+
- name: default_wi_ksa_name
30+
required: true
31+
type: string
32+
- name: existing_ksa_email
33+
required: true
34+
type: string
35+
- name: existing_ksa_name
36+
required: true
37+
type: string
38+
- name: existing_gsa_email
39+
required: true
40+
type: string
41+
- name: existing_gsa_name
3042
required: true
3143
type: string

0 commit comments

Comments
 (0)