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" + } + } +}