diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
new file mode 100644
index 0000000..773e61d
--- /dev/null
+++ b/.github/workflows/lint.yaml
@@ -0,0 +1,38 @@
+name: Lint
+
+on:
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ tflint:
+ runs-on: ${{ matrix.os }}
+
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+
+ steps:
+ - uses: actions/checkout@v2
+ name: Checkout source code
+
+ - uses: actions/cache@v2
+ name: Cache plugin dir
+ with:
+ path: ~/.tflint.d/plugins
+ key: ${{ matrix.os }}-tflint-${{ hashFiles('.tflint.hcl') }}
+
+ - uses: terraform-linters/setup-tflint@v1
+ name: Setup TFLint
+ with:
+ tflint_version: v0.53.0
+
+ - name: Show version
+ run: tflint --version
+
+ - name: Init TFLint
+ run: tflint --init
+
+ - name: Run TFLint
+ run: tflint -f compact
diff --git a/README.md b/README.md
index 838b954..2679566 100644
--- a/README.md
+++ b/README.md
@@ -61,19 +61,26 @@ No modules.
| [google_project_iam_member.project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource |
| [google_project_iam_member.scoped_project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource |
| [google_project_iam_member.scoped_service_account_user](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource |
+| [google_project_iam_member.workload_identity_project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource |
+| [google_project_iam_member.workload_identity_scoped_project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource |
+| [google_project_iam_member.workload_identity_scoped_service_account_user](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource |
| [google_service_account.castai_service_account](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource |
| [google_service_account_key.castai_key](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account_key) | resource |
| [castai_gke_user_policies.gke](https://registry.terraform.io/providers/castai/castai/latest/docs/data-sources/gke_user_policies) | data source |
-| [google_project.project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/project) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
+| [cloud\_proxy\_service\_account\_name](#input\_cloud\_proxy\_service\_account\_name) | Name of the cloud-proxy Kubernetes Service Account | `string` | `"castai-cloud-proxy"` | no |
+| [cloud\_proxy\_service\_account\_namespace](#input\_cloud\_proxy\_service\_account\_namespace) | Namespace of the cloud-proxy Kubernetes Service Account | `string` | `"castai-agent"` | no |
| [compute\_manager\_project\_ids](#input\_compute\_manager\_project\_ids) | Projects list for shared sole tenancy nodes | `list(string)` | `[]` | no |
+| [create\_service\_account](#input\_create\_service\_account) | Whether an Service Account with private key should be created | `bool` | `true` | no |
| [gke\_cluster\_name](#input\_gke\_cluster\_name) | GKE cluster name for which to create IAM roles | `string` | n/a | yes |
| [project\_id](#input\_project\_id) | The project id from GCP | `string` | n/a | yes |
| [service\_accounts\_unique\_ids](#input\_service\_accounts\_unique\_ids) | Service Accounts' unique IDs used by node pools in the cluster | `list(string)` | `[]` | no |
+| [setup\_cloud\_proxy\_workload\_identity](#input\_setup\_cloud\_proxy\_workload\_identity) | Whether the workload identity for castai-cloud-proxy should be setup | `bool` | `false` | no |
+| [workload\_identity\_namespace](#input\_workload\_identity\_namespace) | Override workload identity namespace, default is .svc.id.goog | `string` | `""` | no |
## Outputs
diff --git a/main.tf b/main.tf
index cb45bd1..f542e1c 100644
--- a/main.tf
+++ b/main.tf
@@ -1,28 +1,16 @@
## IAM user required for CAST.AI
locals {
- service_account_id = "castai-gke-tf-${substr(sha1(var.gke_cluster_name), 0, 8)}"
- service_account_email = "${local.service_account_id}@${var.project_id}.iam.gserviceaccount.com"
- custom_role_id = "castai.gkeAccess.${substr(sha1(var.gke_cluster_name), 0, 8)}.tf"
- condition_expression = join("||", formatlist("resource.name.startsWith(\"projects/-/serviceAccounts/%s\")", var.service_accounts_unique_ids))
- default_permissions = ["roles/iam.serviceAccountUser", "projects/${var.project_id}/roles/${local.custom_role_id}"]
- scoped_permissions = ["projects/${var.project_id}/roles/${local.custom_role_id}"]
+ custom_role_id = "castai.gkeAccess.${substr(sha1(var.gke_cluster_name), 0, 8)}.tf"
+ condition_expression = join("||", formatlist("resource.name.startsWith(\"projects/-/serviceAccounts/%s\")", var.service_accounts_unique_ids))
+ default_permissions = ["roles/iam.serviceAccountUser", "projects/${var.project_id}/roles/${local.custom_role_id}"]
+ scoped_permissions = ["projects/${var.project_id}/roles/${local.custom_role_id}"]
compute_manager_project_ids = var.compute_manager_project_ids
}
-resource "google_service_account" "castai_service_account" {
- account_id = local.service_account_id
- display_name = "Service account to manage ${var.gke_cluster_name} cluster via CAST"
- project = var.project_id
-}
-
data "castai_gke_user_policies" "gke" {}
-data "google_project" "project" {
- project_id = var.project_id
-}
-
resource "google_project_iam_custom_role" "castai_role" {
role_id = local.custom_role_id
title = "Role to manage GKE cluster via CAST AI"
@@ -32,43 +20,6 @@ resource "google_project_iam_custom_role" "castai_role" {
stage = "GA"
}
-resource "google_project_iam_member" "project" {
- for_each = (
- length(var.service_accounts_unique_ids) == 0 ?
- { for permission in local.default_permissions : permission => permission } :
- {}
- )
-
- project = var.project_id
- role = each.key
- member = "serviceAccount:${local.service_account_email}"
-}
-
-resource "google_project_iam_member" "scoped_project" {
- for_each = (
- length(var.service_accounts_unique_ids) > 0 ?
- { for permission in local.scoped_permissions : permission => permission } :
- {}
- )
- project = var.project_id
- role = each.key
- member = "serviceAccount:${local.service_account_email}"
-}
-
-resource "google_project_iam_member" "scoped_service_account_user" {
- count = length(var.service_accounts_unique_ids) > 0 ? 1 : 0
- project = var.project_id
-
- role = "roles/iam.serviceAccountUser"
- member = "serviceAccount:${local.service_account_email}"
-
- condition {
- title = "iam_condition"
- description = "IAM condition with limited scope"
- expression = local.condition_expression
- }
-}
-
resource "google_project_iam_custom_role" "compute_manager_role" {
for_each = toset(local.compute_manager_project_ids)
@@ -86,10 +37,6 @@ resource "google_project_iam_binding" "compute_manager_binding" {
project = each.key
role = "projects/${each.key}/roles/castai.gkeAccess.${substr(sha1(each.key), 0, 8)}.tf"
- members = ["serviceAccount:${local.service_account_email}"]
+ members = compact(["serviceAccount:${local.service_account_email}", var.setup_cloud_proxy_workload_identity ? local.workload_identity_sa : null])
}
-resource "google_service_account_key" "castai_key" {
- service_account_id = google_service_account.castai_service_account.name
- public_key_type = "TYPE_X509_PEM_FILE"
-}
diff --git a/output.tf b/output.tf
index 015a69a..c10c948 100644
--- a/output.tf
+++ b/output.tf
@@ -1,5 +1,5 @@
output "private_key" {
- value = base64decode(google_service_account_key.castai_key.private_key)
+ value = var.create_service_account ? base64decode(google_service_account_key.castai_key[0].private_key) : ""
sensitive = true
depends_on = [
@@ -12,9 +12,10 @@ output "private_key" {
}
output "service_account_id" {
- value = google_service_account.castai_service_account.account_id
+ value = var.create_service_account ? google_service_account.castai_service_account[0].account_id : ""
}
output "service_account_email" {
- value = google_service_account.castai_service_account.email
+ value = var.create_service_account ? google_service_account.castai_service_account[0].email : ""
}
+
diff --git a/service_account.tf b/service_account.tf
new file mode 100644
index 0000000..2352c5c
--- /dev/null
+++ b/service_account.tf
@@ -0,0 +1,64 @@
+moved {
+ from = google_service_account.castai_service_account
+ to = google_service_account.castai_service_account[0]
+}
+
+moved {
+ from = google_service_account_key.castai_key
+ to = google_service_account_key.castai_key[0]
+}
+
+locals {
+ service_account_id = "castai-gke-tf-${substr(sha1(var.gke_cluster_name), 0, 8)}"
+ service_account_email = "${local.service_account_id}@${var.project_id}.iam.gserviceaccount.com"
+}
+
+resource "google_service_account" "castai_service_account" {
+ count = var.create_service_account ? 1 : 0
+ account_id = local.service_account_id
+ display_name = "Service account to manage ${var.gke_cluster_name} cluster via CAST"
+ project = var.project_id
+}
+
+resource "google_service_account_key" "castai_key" {
+ count = var.create_service_account ? 1 : 0
+ service_account_id = google_service_account.castai_service_account[0].name
+ public_key_type = "TYPE_X509_PEM_FILE"
+}
+
+resource "google_project_iam_member" "project" {
+ for_each = (
+ var.create_service_account && length(var.service_accounts_unique_ids) == 0 ?
+ { for permission in local.default_permissions : permission => permission } :
+ {}
+ )
+
+ project = var.project_id
+ role = each.key
+ member = "serviceAccount:${local.service_account_email}"
+}
+
+resource "google_project_iam_member" "scoped_project" {
+ for_each = (
+ var.create_service_account && length(var.service_accounts_unique_ids) > 0 ?
+ { for permission in local.scoped_permissions : permission => permission } :
+ {}
+ )
+ project = var.project_id
+ role = each.key
+ member = "serviceAccount:${local.service_account_email}"
+}
+
+resource "google_project_iam_member" "scoped_service_account_user" {
+ count = var.create_service_account && length(var.service_accounts_unique_ids) > 0 ? 1 : 0
+ project = var.project_id
+
+ role = "roles/iam.serviceAccountUser"
+ member = "serviceAccount:${local.service_account_email}"
+
+ condition {
+ title = "iam_condition"
+ description = "IAM condition with limited scope"
+ expression = local.condition_expression
+ }
+}
diff --git a/variables.tf b/variables.tf
index 4c8502d..34aba7b 100644
--- a/variables.tf
+++ b/variables.tf
@@ -19,3 +19,34 @@ variable "compute_manager_project_ids" {
description = "Projects list for shared sole tenancy nodes"
default = []
}
+
+variable "create_service_account" {
+ type = bool
+ description = "Whether an Service Account with private key should be created"
+ default = true
+}
+
+variable "setup_cloud_proxy_workload_identity" {
+ type = bool
+ description = "Whether the workload identity for castai-cloud-proxy should be setup"
+ default = false
+}
+
+variable "workload_identity_namespace" {
+ type = string
+ description = "Override workload identity namespace, default is .svc.id.goog"
+ default = ""
+}
+
+variable "cloud_proxy_service_account_namespace" {
+ type = string
+ description = "Namespace of the cloud-proxy Kubernetes Service Account"
+ default = "castai-agent"
+}
+
+
+variable "cloud_proxy_service_account_name" {
+ type = string
+ description = "Name of the cloud-proxy Kubernetes Service Account"
+ default = "castai-cloud-proxy"
+}
diff --git a/workload_identity.tf b/workload_identity.tf
new file mode 100644
index 0000000..bce5e05
--- /dev/null
+++ b/workload_identity.tf
@@ -0,0 +1,42 @@
+locals {
+ workload_identity_namespace = var.workload_identity_namespace != "" ? var.workload_identity_namespace : "${var.project_id}.svc.id.goog"
+ workload_identity_sa = "serviceAccount:${local.workload_identity_namespace}[${var.cloud_proxy_service_account_namespace}/${var.cloud_proxy_service_account_name}]"
+}
+
+resource "google_project_iam_member" "workload_identity_project" {
+ for_each = (
+ var.setup_cloud_proxy_workload_identity && length(var.service_accounts_unique_ids) == 0 ?
+ { for permission in local.default_permissions : permission => permission } :
+ {}
+ )
+
+ project = var.project_id
+ role = each.key
+ member = local.workload_identity_sa
+}
+
+resource "google_project_iam_member" "workload_identity_scoped_project" {
+ for_each = (
+ var.setup_cloud_proxy_workload_identity && length(var.service_accounts_unique_ids) > 0 ?
+ { for permission in local.scoped_permissions : permission => permission } :
+ {}
+ )
+ project = var.project_id
+ role = each.key
+ member = local.workload_identity_sa
+}
+
+resource "google_project_iam_member" "workload_identity_scoped_service_account_user" {
+ count = var.setup_cloud_proxy_workload_identity && length(var.service_accounts_unique_ids) > 0 ? 1 : 0
+ project = var.project_id
+
+ role = "roles/iam.serviceAccountUser"
+ member = local.workload_identity_sa
+
+ condition {
+ title = "iam_condition"
+ description = "IAM condition with limited scope"
+ expression = local.condition_expression
+ }
+}
+