diff --git a/modules/fleet/README.md b/modules/fleet/README.md new file mode 100644 index 0000000000..e4aa567b80 --- /dev/null +++ b/modules/fleet/README.md @@ -0,0 +1,60 @@ + +# Terraform Kubernetes Engine Fleet submodule + +GKE submodule to manage GKE's fleets + +With the two mandatory parameters, the module will create a fleet on the specified project. it requires `gkehub.googleapis.com` api only. +The other parameters are Anthos service features. So, if you set or enable any of them, the `anthos.googleapis.com` api will be enabled. + +## Usage + +```tf +module "hub" { + source = "terraform-google-modules/kubernetes-engine/google//modules/fleet" + + project_id = "fleet-host-project" + display_name = "GKE Fleet - Staging" +} +``` + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3 | +| [google](#requirement\_google) | ~> 6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [google](#provider\_google) | ~> 6.0 | + +## Resources + +| Name | Type | +|------|------| +| [google_gke_hub_fleet.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/gke_hub_fleet) | resource | +| [google_project_service.anthos](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_service) | resource | +| [google_project_service.gkehub](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_service) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [binary\_authorization\_evaluation\_mode](#input\_binary\_authorization\_evaluation\_mode) | Mode of operation for binauthz policy evaluation. Set to null to omit the attribute and use provider/API default if the block is rendered. Possible values: "DISABLED", "PROJECT\_SINGLETON\_POLICY\_ENFORCE". | `string` | `"DISABLED"` | no | +| [binary\_authorization\_policy\_bindings](#input\_binary\_authorization\_policy\_bindings) | A list of binauthz policy bindings. Each binding has a 'name' attribute. |
list(object({
name = string # Name is technically optional in API, but required for a useful binding here.
})) | `[]` | no |
+| [display\_name](#input\_display\_name) | A user-assigned display name of the Fleet. | `string` | n/a | yes |
+| [project\_id](#input\_project\_id) | The ID of the project in which the Fleet resource belongs. If it is not provided, the provider project is used. | `string` | n/a | yes |
+| [security\_posture\_mode](#input\_security\_posture\_mode) | Sets the mode for Security Posture features on the cluster. Set to null to omit the attribute. Possible values: "DISABLED", "BASIC", "ENTERPRISE". | `string` | `"DISABLED"` | no |
+| [security\_posture\_vulnerability\_mode](#input\_security\_posture\_vulnerability\_mode) | Sets the mode for Vulnerability Scanning. Set to null to omit the attribute. Possible values: "VULNERABILITY\_DISABLED", "VULNERABILITY\_BASIC", "VULNERABILITY\_ENTERPRISE". | `string` | `"VULNERABILITY_DISABLED"` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [fleet\_id](#output\_fleet\_id) | the Fleet identifier |
+| [fleet\_state](#output\_fleet\_state) | The state of the fleet resource |
+| [fleet\_uid](#output\_fleet\_uid) | Unique UID across all Fleet resources |
+
diff --git a/modules/fleet/apis.tf b/modules/fleet/apis.tf
new file mode 100644
index 0000000000..397d19e3cd
--- /dev/null
+++ b/modules/fleet/apis.tf
@@ -0,0 +1,19 @@
+# --- Enable GKE HUB API ---
+
+resource "google_project_service" "gkehub" {
+ project = var.project_id
+ service = "gkehub.googleapis.com"
+
+ disable_on_destroy = false
+}
+
+# --- Enable Anthos API ---
+
+resource "google_project_service" "anthos" {
+ count = ((var.security_posture_mode != "DISABLED" || var.security_posture_vulnerability_mode != "VULNERABILITY_DISABLED") || (var.binary_authorization_evaluation_mode != "DISABLED" || length(var.binary_authorization_policy_bindings) > 0)) ? 1 : 0
+
+ project = var.project_id
+ service = "anthos.googleapis.com"
+
+ disable_on_destroy = false
+}
diff --git a/modules/fleet/main.tf b/modules/fleet/main.tf
new file mode 100644
index 0000000000..2ec68be73c
--- /dev/null
+++ b/modules/fleet/main.tf
@@ -0,0 +1,35 @@
+resource "google_gke_hub_fleet" "this" {
+ project = var.project_id
+ display_name = var.display_name
+
+ dynamic "default_cluster_config" {
+ for_each = ((var.security_posture_mode != "DISABLED" || var.security_posture_vulnerability_mode != "VULNERABILITY_DISABLED") || (var.binary_authorization_evaluation_mode != "DISABLED" || length(var.binary_authorization_policy_bindings) > 0)) ? [1] : []
+
+ content {
+ dynamic "binary_authorization_config" {
+ for_each = (var.binary_authorization_evaluation_mode != null || length(var.binary_authorization_policy_bindings) > 0) ? [1] : []
+ content {
+ evaluation_mode = var.binary_authorization_evaluation_mode
+ dynamic "policy_bindings" {
+ for_each = var.binary_authorization_policy_bindings
+ content {
+ name = policy_bindings.value.name
+ }
+ }
+ }
+ }
+
+ dynamic "security_posture_config" {
+ for_each = (var.security_posture_mode != null || var.security_posture_vulnerability_mode != null) ? [1] : []
+ content {
+ mode = var.security_posture_mode
+ vulnerability_mode = var.security_posture_vulnerability_mode
+ }
+ }
+ }
+ }
+
+ depends_on = [
+ google_project_service.gkehub
+ ]
+}
diff --git a/modules/fleet/outputs.tf b/modules/fleet/outputs.tf
new file mode 100644
index 0000000000..f35995a5c7
--- /dev/null
+++ b/modules/fleet/outputs.tf
@@ -0,0 +1,14 @@
+output "fleet_id" {
+ description = "the Fleet identifier"
+ value = google_gke_hub_fleet.this.id
+}
+
+output "fleet_state" {
+ description = "The state of the fleet resource"
+ value = google_gke_hub_fleet.this.state[0].code
+}
+
+output "fleet_uid" {
+ description = "Unique UID across all Fleet resources"
+ value = google_gke_hub_fleet.this.uid
+}
diff --git a/modules/fleet/variables.tf b/modules/fleet/variables.tf
new file mode 100644
index 0000000000..244e7e47aa
--- /dev/null
+++ b/modules/fleet/variables.tf
@@ -0,0 +1,55 @@
+variable "project_id" {
+ description = "The ID of the project in which the Fleet resource belongs. If it is not provided, the provider project is used."
+ type = string
+}
+
+variable "display_name" {
+ description = "A user-assigned display name of the Fleet."
+ type = string
+}
+
+# variable "manage_default_cluster_config" {
+# description = "Set to true to manage default_cluster_config. If false, the entire default_cluster_config block will be omitted."
+# type = bool
+# default = true
+# }
+
+# Variables for default_cluster_config.binary_authorization_config
+variable "binary_authorization_evaluation_mode" {
+ description = "Mode of operation for binauthz policy evaluation. Set to null to omit the attribute and use provider/API default if the block is rendered. Possible values: \"DISABLED\", \"PROJECT_SINGLETON_POLICY_ENFORCE\"."
+ type = string
+ default = "DISABLED" # Provider default
+ validation {
+ condition = var.binary_authorization_evaluation_mode == null || can(regex("^(DISABLED|PROJECT_SINGLETON_POLICY_ENFORCE)$", var.binary_authorization_evaluation_mode))
+ error_message = "Invalid binary_authorization_evaluation_mode. Must be one of: DISABLED, PROJECT_SINGLETON_POLICY_ENFORCE, or null."
+ }
+}
+
+variable "binary_authorization_policy_bindings" {
+ description = "A list of binauthz policy bindings. Each binding has a 'name' attribute."
+ type = list(object({
+ name = string # Name is technically optional in API, but required for a useful binding here.
+ }))
+ default = [] # Default is no bindings
+}
+
+# Variables for default_cluster_config.security_posture_config
+variable "security_posture_mode" {
+ description = "Sets the mode for Security Posture features on the cluster. Set to null to omit the attribute. Possible values: \"DISABLED\", \"BASIC\", \"ENTERPRISE\"."
+ type = string
+ default = "DISABLED" # Matches original and provider default
+ validation {
+ condition = var.security_posture_mode == null || can(regex("^(DISABLED|BASIC|ENTERPRISE)$", var.security_posture_mode))
+ error_message = "Invalid security_posture_mode. Must be one of: DISABLED, BASIC, ENTERPRISE, or null."
+ }
+}
+
+variable "security_posture_vulnerability_mode" {
+ description = "Sets the mode for Vulnerability Scanning. Set to null to omit the attribute. Possible values: \"VULNERABILITY_DISABLED\", \"VULNERABILITY_BASIC\", \"VULNERABILITY_ENTERPRISE\"."
+ type = string
+ default = "VULNERABILITY_DISABLED" # Matches original and provider default
+ validation {
+ condition = var.security_posture_vulnerability_mode == null || can(regex("^(VULNERABILITY_DISABLED|VULNERABILITY_BASIC|VULNERABILITY_ENTERPRISE)$", var.security_posture_vulnerability_mode))
+ error_message = "Invalid security_posture_vulnerability_mode. Must be one of: VULNERABILITY_DISABLED, VULNERABILITY_BASIC, VULNERABILITY_ENTERPRISE, or null."
+ }
+}
diff --git a/modules/fleet/version.tf b/modules/fleet/version.tf
new file mode 100644
index 0000000000..0aab52b6bc
--- /dev/null
+++ b/modules/fleet/version.tf
@@ -0,0 +1,10 @@
+terraform {
+ required_version = ">= 1.3"
+
+ required_providers {
+ google = {
+ source = "hashicorp/google"
+ version = "~> 6.0"
+ }
+ }
+}