Skip to content

Commit 45ed5ac

Browse files
authored
feat: support volume access for gcp (#125)
1 parent 32b5b54 commit 45ed5ac

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

examples/gcp/volume-access/main.tf

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module "sn_managed_cloud_access_bucket" {
2+
source = "../../../modules/gcp/volume-access"
3+
4+
streamnative_org_id = "<your-org-id>"
5+
project = "<your-gcs-bucket-project-name>"
6+
7+
cluster_projects = [
8+
"<your-ursa-cluster-project-name>"
9+
]
10+
11+
account_id = "<your-google-service-account-id>"
12+
13+
buckets = [
14+
"<your-gcs-bucket-path>"
15+
]
16+
}

modules/gcp/volume-access/main.tf

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
locals {
2+
streamnative_gsa = distinct(var.streamnative_vendor_access_gsa)
3+
cluster_projects = distinct(var.cluster_projects)
4+
buckets_list = distinct([for item in var.buckets : "${split("/", item)[0]}"])
5+
buckets_path = distinct([for item in var.buckets : "${replace(item, "/(\\/|\\/\\*)+$/", "")}"])
6+
}
7+
8+
# Grant permissions to the project service account that will be impersonated by StreamNative Control Plane service account
9+
locals {
10+
impersonation_roles = [
11+
"roles/storage.objectUser",
12+
"roles/storage.objectViewer",
13+
]
14+
impersonation_iam_bindings = flatten([
15+
for role in local.impersonation_roles : [
16+
for bucket_path in local.buckets_path : {
17+
bucket : split("/", bucket_path)[0],
18+
role : role,
19+
expression : role == "roles/storage.objectViewer" ? "" : (length(split("/", bucket_path)) == 1 ? format("resource.name.startsWith(\"projects/_/buckets/%s/objects\")", split("/", bucket_path)[0]) : format("resource.name.startsWith(\"projects/_/buckets/%s/objects/%s/\")", split("/", bucket_path)[0], join("/", slice(split("/", bucket_path), 1, length(split("/", bucket_path))))))
20+
}
21+
]
22+
])
23+
24+
}
25+
resource "google_service_account" "gsa" {
26+
account_id = var.account_id
27+
project = var.project
28+
display_name = "StreamNative Cloud Control Plane access bucket service account."
29+
}
30+
31+
resource "google_storage_bucket_iam_member" "gcs" {
32+
count = length(local.impersonation_iam_bindings)
33+
bucket = local.impersonation_iam_bindings[count.index].bucket
34+
role = local.impersonation_iam_bindings[count.index].role
35+
member = "serviceAccount:${google_service_account.gsa.email}"
36+
37+
condition {
38+
title = "restrict_to_data_path"
39+
description = "Restrict gcs access"
40+
expression = local.impersonation_iam_bindings[count.index].expression
41+
}
42+
43+
depends_on = [google_service_account.gsa]
44+
}
45+
46+
resource "google_service_account_iam_member" "sn_control_plane" {
47+
count = length(local.streamnative_gsa)
48+
service_account_id = google_service_account.gsa.id
49+
role = "roles/iam.serviceAccountTokenCreator"
50+
member = "serviceAccount:${local.streamnative_gsa[count.index]}"
51+
depends_on = [google_service_account.gsa]
52+
}
53+
54+
data "google_project" "project" {
55+
count = length(local.cluster_projects)
56+
project_id = local.cluster_projects[count.index]
57+
}
58+
59+
resource "google_service_account_iam_member" "sn_data_plane" {
60+
count = length(local.cluster_projects)
61+
service_account_id = google_service_account.gsa.id
62+
role = "roles/iam.workloadIdentityUser"
63+
member = "principalSet://iam.googleapis.com/projects/${data.google_project.project[count.index].number}/locations/global/workloadIdentityPools/${local.cluster_projects[count.index]}.svc.id.goog/namespace/${var.streamnative_org_id}"
64+
depends_on = [google_service_account.gsa]
65+
66+
}
67+
68+
output "google_service_account" {
69+
value = google_service_account.gsa
70+
description = "Google Service Account for Access GCS Bucket"
71+
depends_on = [google_service_account.gsa]
72+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
variable "streamnative_org_id" {
2+
type = string
3+
description = "Your Organization ID within StreamNative Cloud. This will be the organization ID in the StreamNative console, e.g. \"o-xhopj\"."
4+
validation {
5+
condition = length(var.streamnative_org_id) <= 18
6+
error_message = "The organization ID must not exceed 18 characters. If you reach this limit, please contact StreamNative support."
7+
}
8+
}
9+
10+
variable "streamnative_vendor_access_gsa" {
11+
default = [
12+
"cloud-manager@sncloud-production.iam.gserviceaccount.com"
13+
]
14+
type = list(string)
15+
description = "The GSA will be used by StreamnNative cloud."
16+
}
17+
18+
variable "account_id" {
19+
type = string
20+
description = "Google Service Account ID, <id>@<your-project>.iam.gserviceaccount.com"
21+
}
22+
23+
variable "project" {
24+
type = string
25+
description = "Name of the project that your gcs bucket is located in"
26+
}
27+
28+
variable "cluster_projects" {
29+
type = list(string)
30+
description = "The name of the project your ursa cluster belongs to."
31+
}
32+
33+
variable "buckets" {
34+
description = "User bucket and path name"
35+
type = list(string)
36+
37+
validation {
38+
condition = alltrue([
39+
for s in var.buckets : length(split("/", s)) >= 2
40+
])
41+
error_message = "Invalid bucket path found, please make sure the bucket contains at least one path <bucket-name>/<bucket-path>"
42+
}
43+
}

0 commit comments

Comments
 (0)