From fb6365f03a0712d0053334bf87a90630785182aa Mon Sep 17 00:00:00 2001 From: Matthias Hauber Date: Mon, 23 Mar 2026 23:26:09 +0100 Subject: [PATCH 1/3] Refactor governance and landing zone modules - Updated organization role assignments for clarity in naming. - Removed redundant folder role assignments and main configuration files in governance module. - Enhanced outputs in governance module to include custom role IDs and organization role assignments. - Introduced a new terraform configuration file for governance module with updated provider version. - Refactored variables in governance module to streamline folder configurations and custom roles. - Updated landing zone module to use consistent resource naming conventions and improved variable definitions. - Removed deprecated Kubernetes cluster resource from landing zone module. - Refactored management module to align with new project naming conventions and removed unused variables. - Updated sandboxes module to improve project creation logic and resource naming. - Added new terraform configuration files across modules to ensure compatibility with updated provider versions. --- .github/copilot-instructions.md | 207 ++++++++++ README.md | 27 +- examples/01-standalone/README.md | 52 +++ examples/01-standalone/main.tf | 63 ++-- examples/01-standalone/provider.tf | 16 - examples/01-standalone/providers.tf | 5 + examples/01-standalone/terraform.tf | 10 + examples/01-standalone/terraform.tfvars | 38 +- examples/01-standalone/variables.tf | 53 +-- examples/02-hub-spoke/README.md | 65 ++++ examples/02-hub-spoke/main.tf | 65 +--- examples/02-hub-spoke/pfsense.qcow2 | 1 + examples/02-hub-spoke/provider.tf | 16 - examples/02-hub-spoke/providers.tf | 5 + examples/02-hub-spoke/terraform.tf | 10 + examples/02-hub-spoke/terraform.tfvars | 47 +-- modules/connectivity-global/1-network-area.tf | 33 +- modules/connectivity-global/README.md | 22 +- modules/connectivity-global/main.tf | 24 -- modules/connectivity-global/outputs.tf | 2 +- modules/connectivity-global/terraform.tf | 10 + modules/connectivity-global/variables.tf | 96 ++--- modules/connectivity-regional/1-project.tf | 15 +- modules/connectivity-regional/2-network.tf | 16 +- modules/connectivity-regional/3-firewall.tf | 18 +- modules/connectivity-regional/README.md | 34 +- modules/connectivity-regional/main.tf | 24 -- modules/connectivity-regional/outputs.tf | 30 +- modules/connectivity-regional/terraform.tf | 10 + modules/connectivity-regional/variables.tf | 84 ++--- modules/devops/1-project.tf | 15 +- modules/devops/2-git.tf | 2 +- modules/devops/README.md | 20 +- modules/devops/main.tf | 24 -- modules/devops/outputs.tf | 14 +- modules/devops/terraform.tf | 10 + modules/devops/variables.tf | 83 ++-- modules/governance/1-rm-folders.tf | 67 ++-- modules/governance/2-custom-roles.tf | 355 +----------------- modules/governance/3-organization-roles.tf | 6 +- modules/governance/4-rm-folders-roles.tf | 27 -- modules/governance/README.md | 29 +- modules/governance/main.tf | 20 - modules/governance/outputs.tf | 22 +- modules/governance/terraform.tf | 10 + modules/governance/variables.tf | 76 ++-- modules/landing-zone/1-project.tf | 4 +- modules/landing-zone/2-rbac.tf | 8 +- modules/landing-zone/3-network.tf | 5 +- modules/landing-zone/4-secrets-manager.tf | 6 +- modules/landing-zone/5-bucket.tf | 38 +- modules/landing-zone/6-service-account.tf | 6 +- modules/landing-zone/7-kubernetes.tf | 25 -- modules/landing-zone/README.md | 47 +-- modules/landing-zone/main.tf | 38 -- modules/landing-zone/outputs.tf | 14 +- modules/landing-zone/terraform.tf | 14 + modules/landing-zone/variables.tf | 122 ++---- modules/management/1-project.tf | 8 +- modules/management/2-secrets-manager.tf | 6 +- modules/management/3-bucket.tf | 38 +- modules/management/4-service-account.tf | 20 +- modules/management/README.md | 43 +-- modules/management/main.tf | 38 -- modules/management/outputs.tf | 14 +- modules/management/terraform.tf | 14 + modules/management/variables.tf | 57 +-- modules/sandboxes/1-project.tf | 33 -- modules/sandboxes/README.md | 13 +- modules/sandboxes/main.tf | 39 +- modules/sandboxes/outputs.tf | 2 +- modules/sandboxes/terraform.tf | 10 + modules/sandboxes/variables.tf | 18 +- 73 files changed, 1028 insertions(+), 1530 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 examples/01-standalone/README.md delete mode 100644 examples/01-standalone/provider.tf create mode 100644 examples/01-standalone/providers.tf create mode 100644 examples/01-standalone/terraform.tf create mode 100644 examples/02-hub-spoke/README.md create mode 100644 examples/02-hub-spoke/pfsense.qcow2 delete mode 100644 examples/02-hub-spoke/provider.tf create mode 100644 examples/02-hub-spoke/providers.tf create mode 100644 examples/02-hub-spoke/terraform.tf delete mode 100644 modules/connectivity-global/main.tf create mode 100644 modules/connectivity-global/terraform.tf delete mode 100644 modules/connectivity-regional/main.tf create mode 100644 modules/connectivity-regional/terraform.tf delete mode 100644 modules/devops/main.tf create mode 100644 modules/devops/terraform.tf delete mode 100644 modules/governance/4-rm-folders-roles.tf delete mode 100644 modules/governance/main.tf create mode 100644 modules/governance/terraform.tf delete mode 100644 modules/landing-zone/7-kubernetes.tf delete mode 100644 modules/landing-zone/main.tf create mode 100644 modules/landing-zone/terraform.tf delete mode 100644 modules/management/main.tf create mode 100644 modules/management/terraform.tf delete mode 100644 modules/sandboxes/1-project.tf create mode 100644 modules/sandboxes/terraform.tf diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..100a02f --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,207 @@ +# Terraform Style Guide + +This file defines the Terraform style conventions for this repository, based on the [HashiCorp Terraform Style Guide](https://developer.hashicorp.com/terraform/language/style). All Terraform code in this workspace MUST conform to these rules. When reviewing or generating Terraform code, enforce every rule below. + +--- + +## Code Formatting + +- Indent **two spaces** per nesting level. +- Align `=` signs when multiple single-line arguments appear on consecutive lines at the same nesting level: + ```hcl + ami = "abc123" + instance_type = "t2.micro" + ``` +- Place all arguments at the **top** of a block, then nested blocks **below**, separated by one blank line. +- Use **empty lines** to separate logical groups of arguments within a block. +- **Meta-arguments first**: list meta-arguments (`count`, `for_each`) at the top of a resource block, separated from other arguments by one blank line. +- **Meta-argument blocks last**: place `lifecycle`, `depends_on` blocks at the bottom, separated from other blocks by one blank line. + ```hcl + resource "aws_instance" "example" { + # meta-argument first + count = 2 + + ami = "abc123" + instance_type = "t2.micro" + + network_interface { + # ... + } + + # meta-argument block last + lifecycle { + create_before_destroy = true + } + } + ``` +- Separate **top-level blocks** with exactly one blank line. +- Separate **nested blocks** with blank lines, except when grouping related blocks of the same type. +- Run `terraform fmt` before committing. Use `-recursive` to format subdirectories. + +## Code Validation + +- Run `terraform validate` before committing to check syntax and internal consistency. + +## Comments + +- Use `#` for **all** comments (single-line and multi-line). Do NOT use `//` or `/* */`. +- Write self-explanatory code; only add comments when necessary to clarify complexity. + +## Resource Naming + +- Use a **descriptive noun** for every resource name. +- Separate words with **underscores** (`_`), not hyphens or camelCase. +- Do NOT include the resource type in the resource name (the address already contains it). +- Wrap both resource type and name in **double quotes**. + + **Bad:** + ```hcl + resource aws_instance webAPI-aws-instance {...} + ``` + **Good:** + ```hcl + resource "aws_instance" "web_api" {...} + ``` + +## Resource Order + +- Define **data sources before** the resources that reference them so code "builds on itself". +- Within a resource block, order parameters as follows: + 1. `count` or `for_each` meta-argument + 2. Resource-specific non-block parameters + 3. Resource-specific block parameters + 4. `lifecycle` block (if required) + 5. `depends_on` (if required) + +## Variables + +- Every variable MUST have a `type` and a `description`. +- Provide a `default` for optional variables. +- Set `sensitive = true` for passwords, private keys, and other secrets. +- Use `validation` blocks only when values have uniquely restrictive requirements. +- Order variable parameters: + 1. `type` + 2. `description` + 3. `default` (optional) + 4. `sensitive` (optional) + 5. `validation` blocks + + ```hcl + variable "db_disk_size" { + type = number + description = "Disk size for the API database" + default = 100 + } + + variable "db_password" { + type = string + description = "Database password" + sensitive = true + } + ``` + +## Outputs + +- Every output MUST have a `description`. +- Order output parameters: + 1. `description` + 2. `value` + 3. `sensitive` (optional) + + ```hcl + output "web_public_ip" { + description = "Public IP of the web instance" + value = aws_instance.web.public_ip + } + ``` + +## Local Values + +- Use local values **sparingly**; overuse makes code harder to understand. +- If referenced in multiple files, define locals in a `locals.tf` file. +- If specific to one file, define locals at the **top** of that file. +- Use descriptive nouns with underscores for local value names. + +## Provider Configuration + +- Always include a **default provider configuration** (without `alias`). +- Define **all providers** in the same file. +- If multiple instances of a provider exist, define the **default first**. +- For non-default providers, the `alias` parameter must be the **first** parameter in the block. + +## Dynamic Resource Count (`count` / `for_each`) + +- Use `count` and `for_each` **sparingly**; they add complexity. +- Use `count` when resources are almost identical. +- Use `for_each` when arguments need distinct values not derivable from an integer. +- A common pattern for conditional resources: `count = var.condition ? 1 : 0`. +- If the effect of a meta-argument is not immediately obvious, add a comment. + +## File Naming Conventions + +- `main.tf` — resource and data source blocks (or split by logical group as the codebase grows). +- `variables.tf` — all variable blocks, in **alphabetical order**. +- `outputs.tf` — all output blocks, in **alphabetical order**. +- `providers.tf` — all `provider` blocks and configuration. +- `terraform.tf` — single `terraform` block with `required_version` and `required_providers`. +- `backend.tf` — backend configuration. +- `locals.tf` — local values (if shared across files). +- `override.tf` — override definitions (use sparingly, comment the original resource). +- When the codebase grows, split resources into logically named files (e.g., `network.tf`, `storage.tf`, `compute.tf`). It should be immediately clear where to find any resource. + +## Version Pinning + +- Pin **provider versions** in `required_providers`. +- Pin **module versions** to a specific major and minor version. +- Set a minimum `required_version` for the Terraform binary in the `terraform` block. + ```hcl + terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "5.34.0" + } + } + required_version = ">= 1.7" + } + ``` +- For registry modules, use the `version` parameter in the `module` block. + +## Module Structure + +- Group logically related resources into modules. +- Store local (child) modules in `./modules/`. +- Follow the [standard module structure](https://developer.hashicorp.com/terraform/language/modules/develop/structure). +- Name module repositories `terraform--` if publishing to a registry. + +## .gitignore + +Do NOT commit: +- `terraform.tfstate` and `terraform.tfstate.*` backup files. +- `.terraform.tfstate.lock.info`. +- `.terraform/` directory. +- Saved plan files (from `terraform plan -out`). +- `.tfvars` files containing sensitive information. + +Always commit: +- All `.tf` code files. +- `.terraform.lock.hcl` dependency lock file. +- `.gitignore`. +- `README.md`. + +## Secrets Management + +- Never store secrets in plain-text Terraform files. +- Use provider-specific environment variables for credentials. +- Use a secrets manager (e.g., HashiCorp Vault) where possible. +- Mark sensitive variables with `sensitive = true`. + +## Testing + +- Write tests for Terraform modules. +- Run tests as pre-merge checks in pull requests or as CI/CD pipeline steps. + +## Linting + +- Use a linter such as [TFLint](https://github.com/terraform-linters/tflint) to enforce coding standards. +- Run `terraform fmt` and `terraform validate` before every commit. diff --git a/README.md b/README.md index 3e832e1..a5805a3 100644 --- a/README.md +++ b/README.md @@ -93,4 +93,29 @@ Contributions are welcome! Please feel free to submit a Pull Request. ## 📄 License -This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details. \ No newline at end of file +This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details. + +### Requirements + +No requirements. + +### Providers + +No providers. + +### Modules + +No modules. + +### Resources + +No resources. + +### Inputs + +No inputs. + +### Outputs + +No outputs. + \ No newline at end of file diff --git a/examples/01-standalone/README.md b/examples/01-standalone/README.md new file mode 100644 index 0000000..4a690a2 --- /dev/null +++ b/examples/01-standalone/README.md @@ -0,0 +1,52 @@ + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.10 | +| [stackit](#requirement\_stackit) | 0.88.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [devops](#module\_devops) | ../../modules/devops | n/a | +| [governance](#module\_governance) | ../../modules/governance | n/a | +| [landing\_zone](#module\_landing\_zone) | ../../modules/landing-zone | n/a | +| [management](#module\_management) | ../../modules/management | n/a | +| [sandboxes](#module\_sandboxes) | ../../modules/sandboxes | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [company\_code](#input\_company\_code) | Company code used in resource naming conventions. | `string` | n/a | yes | +| [company\_name](#input\_company\_name) | Name of the company. | `string` | n/a | yes | +| [labels](#input\_labels) | Additional labels to apply to all resources. | `map(string)` | `{}` | no | +| [landing\_zones](#input\_landing\_zones) | Map of landing zones to create (public, without network area). |
map(object({
project_name = string
project_code = string
owner_email = string
env = optional(string, "dev")
role_assignments = optional(list(object({
role = string
subject = string
})), [])
network_prefix_length = optional(number, null)
custom_roles = optional(list(object({
name = string
description = string
permissions = list(string)
})), [])
}))
| `{}` | no | +| [organization\_auditors](#input\_organization\_auditors) | List of organization auditors. | `list(string)` | `[]` | no | +| [organization\_id](#input\_organization\_id) | Container ID of the root organization. | `string` | n/a | yes | +| [organization\_owners](#input\_organization\_owners) | List of organization owners. | `list(string)` | `[]` | no | +| [owner\_email](#input\_owner\_email) | Email address of the owner. Required for STACKIT resource manager. | `string` | n/a | yes | +| [platform\_admins](#input\_platform\_admins) | List of platform administrators. | `list(string)` | `[]` | no | +| [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no | +| [sandboxes](#input\_sandboxes) | List of sandboxes to create. |
list(object({
project_name = string
owner_emails = optional(list(string))
project_owner_email = string
}))
| `[]` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [devops\_project\_id](#output\_devops\_project\_id) | The project ID of the DevOps project. | +| [governance\_folder\_ids](#output\_governance\_folder\_ids) | Map of governance folder names to their container IDs. | +| [landing\_zone\_projects](#output\_landing\_zone\_projects) | Map of landing zone project IDs. | +| [management\_project\_id](#output\_management\_project\_id) | The project ID of the Management project. | +| [sandbox\_projects](#output\_sandbox\_projects) | The created sandbox projects. | + \ No newline at end of file diff --git a/examples/01-standalone/main.tf b/examples/01-standalone/main.tf index a901902..d76fd43 100644 --- a/examples/01-standalone/main.tf +++ b/examples/01-standalone/main.tf @@ -6,32 +6,8 @@ module "governance" { source = "../../modules/governance" owner_email = var.owner_email - company_name = var.company_name organization_id = var.organization_id labels = var.labels - region = var.region - organization_owners = var.organization_owners - organization_auditors = var.organization_auditors - platform_admins = var.platform_admins - landing_zone_admins = var.landing_zone_admins -} - -############ -## DEVOPS ## -############ - -module "devops" { - source = "../../modules/devops" - - owner_email = var.owner_email - project_name = "${var.company_name} DevOps" - project_code = "devops" - company_name = var.company_name - company_code = var.company_code - parent_container_id = module.governance.folder_container_ids.platform - organization_id = var.organization_id - labels = var.labels - region = var.region organization_owners = var.organization_owners organization_auditors = var.organization_auditors } @@ -43,15 +19,25 @@ module "devops" { module "management" { source = "../../modules/management" - owner_email = var.owner_email - project_code = "mgmt" - company_code = var.company_code - parent_container_id = module.governance.folder_container_ids.platform - organization_id = var.organization_id - labels = var.labels - region = var.region - organization_owners = var.organization_owners - organization_auditors = var.organization_auditors + owner_email = var.owner_email + naming_pattern = "${var.company_code}-pltfm-mgmt-prod" + parent_container_id = module.governance.folder_container_ids.platform + organization_id = var.organization_id + labels = var.labels +} + +############ +## DEVOPS ## +############ + +module "devops" { + source = "../../modules/devops" + + owner_email = var.owner_email + naming_pattern = "${var.company_code}-pltfm-devops-prod" + company_name = var.company_name + parent_container_id = module.governance.folder_container_ids.platform + labels = var.labels } ############### @@ -61,10 +47,8 @@ module "management" { module "sandboxes" { source = "../../modules/sandboxes" - company_code = var.company_code + naming_pattern = "${var.company_code}-sbx" parent_container_id = module.governance.folder_container_ids.sandbox - labels = var.labels - region = var.region sandboxes = var.sandboxes } @@ -76,16 +60,11 @@ module "landing_zone" { source = "../../modules/landing-zone" for_each = var.landing_zones - project_name = each.value.project_name - project_code = each.value.project_code - company_code = var.company_code parent_container_id = module.governance.folder_container_ids.landing_zones_public + naming_pattern = "${var.company_code}-lz-${each.value.project_code}-${each.value.env}" owner_email = each.value.owner_email labels = var.labels - region = var.region - env = each.value.env role_assignments = each.value.role_assignments network_prefix_length = each.value.network_prefix_length custom_roles = each.value.custom_roles - kubernetes_clusters = each.value.kubernetes_clusters } diff --git a/examples/01-standalone/provider.tf b/examples/01-standalone/provider.tf deleted file mode 100644 index 36a65b3..0000000 --- a/examples/01-standalone/provider.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = ">= 1.10" - - required_providers { - stackit = { - source = "stackitcloud/stackit" - version = "0.83.0" - } - } -} - -provider "stackit" { - default_region = var.region - enable_beta_resources = true - experiments = ["iam", "routing-tables", "network"] -} \ No newline at end of file diff --git a/examples/01-standalone/providers.tf b/examples/01-standalone/providers.tf new file mode 100644 index 0000000..cdea654 --- /dev/null +++ b/examples/01-standalone/providers.tf @@ -0,0 +1,5 @@ +provider "stackit" { + default_region = var.region + enable_beta_resources = true + experiments = ["iam", "routing-tables", "network"] +} \ No newline at end of file diff --git a/examples/01-standalone/terraform.tf b/examples/01-standalone/terraform.tf new file mode 100644 index 0000000..c51ea6f --- /dev/null +++ b/examples/01-standalone/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.10" + + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = "0.88.0" + } + } +} \ No newline at end of file diff --git a/examples/01-standalone/terraform.tfvars b/examples/01-standalone/terraform.tfvars index 4ed33d4..04e0844 100644 --- a/examples/01-standalone/terraform.tfvars +++ b/examples/01-standalone/terraform.tfvars @@ -32,11 +32,6 @@ platform_admins = [ "platform-admin@example.com" ] -# Users with admin access to the Landing Zones folders -landing_zone_admins = [ - "lz-admin@example.com" -] - # Sandbox projects for experimentation / PoCs sandboxes = [ { @@ -48,8 +43,7 @@ sandboxes = [ # Landing zones keyed by a unique identifier landing_zones = { - "app-frontend" = { - project_name = "Frontend App" + "fe-dev" = { project_code = "fe" owner_email = "frontend-team@example.com" env = "dev" @@ -60,35 +54,5 @@ landing_zones = { subject = "frontend-lead@example.com" } ] - - custom_roles = [ - { - name = "deployer" - description = "Can deploy workloads" - permissions = ["project.resources.read", "project.resources.write"] - } - ] - - # Omit kubernetes_clusters to skip SKE provisioning, or define clusters: - kubernetes_clusters = { - "main" = { - kubernetes_version = "1.31" - node_pools = [ - { - name = "default" - machine_type = "c1.3" - availability_zones = ["eu01-m"] - minimum = 2 - maximum = 4 - } - ] - extensions = { - acl = { - enabled = true - allowed_cidrs = ["0.0.0.0/0"] - } - } - } - } } } diff --git a/examples/01-standalone/variables.tf b/examples/01-standalone/variables.tf index f7ee3ec..5432b70 100644 --- a/examples/01-standalone/variables.tf +++ b/examples/01-standalone/variables.tf @@ -52,12 +52,6 @@ variable "platform_admins" { default = [] } -variable "landing_zone_admins" { - type = list(string) - description = "List of landing zone administrators." - default = [] -} - variable "sandboxes" { type = list(object({ project_name = string @@ -70,10 +64,9 @@ variable "sandboxes" { variable "landing_zones" { type = map(object({ - project_name = string - project_code = string - owner_email = string - env = optional(string, "dev") + project_code = string + owner_email = string + env = optional(string, "dev") role_assignments = optional(list(object({ role = string subject = string @@ -84,46 +77,6 @@ variable "landing_zones" { description = string permissions = list(string) })), []) - kubernetes_clusters = optional(map(object({ - kubernetes_version = string - enable_kubernetes_version_updates = optional(bool, true) - enable_machine_image_version_updates = optional(bool, true) - hibernations = optional(list(object({ - start = string - end = string - timezone = optional(string, "Europe/Berlin") - })), []) - node_pools = list(object({ - name = string - machine_type = string - availability_zones = list(string) - os_version_min = optional(string) - minimum = number - maximum = number - max_surge = optional(number) - max_unavailable = optional(number) - labels = optional(map(string)) - taints = optional(list(object({ - key = string - value = string - effect = string - }))) - })) - extensions = optional(object({ - acl = optional(object({ - allowed_cidrs = list(string) - enabled = bool - })) - dns = optional(object({ - enabled = bool - zones = optional(list(string)) - })) - observability = optional(object({ - enabled = bool - instance_id = optional(string) - })) - })) - })), {}) })) description = "Map of landing zones to create (public, without network area)." default = {} diff --git a/examples/02-hub-spoke/README.md b/examples/02-hub-spoke/README.md new file mode 100644 index 0000000..fd286fa --- /dev/null +++ b/examples/02-hub-spoke/README.md @@ -0,0 +1,65 @@ + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.10 | +| [stackit](#requirement\_stackit) | 0.88.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [connectivity\_global](#module\_connectivity\_global) | ../../modules/connectivity-global | n/a | +| [connectivity\_regional](#module\_connectivity\_regional) | ../../modules/connectivity-regional | n/a | +| [devops](#module\_devops) | ../../modules/devops | n/a | +| [governance](#module\_governance) | ../../modules/governance | n/a | +| [landing\_zone](#module\_landing\_zone) | ../../modules/landing-zone | n/a | +| [management](#module\_management) | ../../modules/management | n/a | +| [sandboxes](#module\_sandboxes) | ../../modules/sandboxes | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [company\_code](#input\_company\_code) | Company code used in resource naming conventions. | `string` | n/a | yes | +| [company\_name](#input\_company\_name) | Name of the company. | `string` | n/a | yes | +| [connectivity\_regional\_network\_area](#input\_connectivity\_regional\_network\_area) | Name key of the network area (from network\_areas) to use for the regional connectivity project. | `string` | n/a | yes | +| [connectivity\_vnet\_range](#input\_connectivity\_vnet\_range) | CIDR range for the connectivity project VNet. | `string` | `"10.0.0.0/24"` | no | +| [firewall\_flavor](#input\_firewall\_flavor) | Firewall VM flavor. | `string` | `"c1.2"` | no | +| [firewall\_ip](#input\_firewall\_ip) | Static IP address for the firewall LAN interface. | `string` | `"10.0.0.220"` | no | +| [firewall\_zone](#input\_firewall\_zone) | STACKIT Availability Zone for the firewall VM. | `string` | `"eu01-m"` | no | +| [labels](#input\_labels) | Additional labels to apply to all resources. | `map(string)` | `{}` | no | +| [landing\_zone\_admins](#input\_landing\_zone\_admins) | List of landing zone administrators. | `list(string)` | `[]` | no | +| [landing\_zones](#input\_landing\_zones) | Map of landing zones to create. Set corporate = true for network area connectivity, false for public. |
map(object({
project_name = string
project_code = string
owner_email = string
# Set to true for corporate landing zones (connected to network area), false for public
corporate = optional(bool, true)
env = optional(string, "dev")
role_assignments = optional(list(object({
role = string
subject = string
})), [])
network_prefix_length = optional(number, null)
custom_roles = optional(list(object({
name = string
description = string
permissions = list(string)
})), [])
kubernetes_clusters = optional(map(object({
kubernetes_version = string
enable_kubernetes_version_updates = optional(bool, true)
enable_machine_image_version_updates = optional(bool, true)
hibernations = optional(list(object({
start = string
end = string
timezone = optional(string, "Europe/Berlin")
})), [])
node_pools = list(object({
name = string
machine_type = string
availability_zones = list(string)
os_version_min = optional(string)
minimum = number
maximum = number
max_surge = optional(number)
max_unavailable = optional(number)
labels = optional(map(string))
taints = optional(list(object({
key = string
value = string
effect = string
})))
}))
extensions = optional(object({
acl = optional(object({
allowed_cidrs = list(string)
enabled = bool
}))
dns = optional(object({
enabled = bool
zones = optional(list(string))
}))
observability = optional(object({
enabled = bool
instance_id = optional(string)
}))
}))
})), {})
}))
| `{}` | no | +| [network\_areas](#input\_network\_areas) | List of network areas to create with their IP ranges and configuration. |
list(object({
name = string
network_ranges = list(object({ prefix = string }))
transfer_network_range = string
max_prefix_length = optional(number, 28)
min_prefix_length = optional(number, 24)
default_prefix_length = optional(number, 28)
default_nameservers = optional(list(string), null)
}))
| n/a | yes | +| [organization\_auditors](#input\_organization\_auditors) | List of organization auditors. | `list(string)` | `[]` | no | +| [organization\_id](#input\_organization\_id) | Container ID of the root organization. | `string` | n/a | yes | +| [organization\_owners](#input\_organization\_owners) | List of organization owners. | `list(string)` | `[]` | no | +| [owner\_email](#input\_owner\_email) | Email address of the owner. Required for STACKIT resource manager. | `string` | n/a | yes | +| [platform\_admins](#input\_platform\_admins) | List of platform administrators. | `list(string)` | `[]` | no | +| [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no | +| [sandboxes](#input\_sandboxes) | List of sandboxes to create. |
list(object({
project_name = string
owner_emails = optional(list(string))
project_owner_email = string
}))
| `[]` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [connectivity\_global\_network\_area\_ids](#output\_connectivity\_global\_network\_area\_ids) | Map of network area names to their IDs. | +| [connectivity\_regional\_pfsense\_public\_ip](#output\_connectivity\_regional\_pfsense\_public\_ip) | The public IP of the pfSense firewall. | +| [connectivity\_regional\_pfsense\_wan\_ip](#output\_connectivity\_regional\_pfsense\_wan\_ip) | The internal WAN IP of the pfSense firewall (used as next hop). | +| [connectivity\_regional\_project\_id](#output\_connectivity\_regional\_project\_id) | The project ID of the regional connectivity project. | +| [devops\_project\_id](#output\_devops\_project\_id) | The project ID of the DevOps project. | +| [governance\_folder\_ids](#output\_governance\_folder\_ids) | Map of governance folder names to their container IDs. | +| [landing\_zone\_projects](#output\_landing\_zone\_projects) | Map of landing zone project IDs. | +| [management\_project\_id](#output\_management\_project\_id) | The project ID of the Management project. | +| [sandbox\_projects](#output\_sandbox\_projects) | The created sandbox projects. | + \ No newline at end of file diff --git a/examples/02-hub-spoke/main.tf b/examples/02-hub-spoke/main.tf index 00278c0..06d6063 100644 --- a/examples/02-hub-spoke/main.tf +++ b/examples/02-hub-spoke/main.tf @@ -6,14 +6,10 @@ module "governance" { source = "../../modules/governance" owner_email = var.owner_email - company_name = var.company_name organization_id = var.organization_id labels = var.labels - region = var.region organization_owners = var.organization_owners organization_auditors = var.organization_auditors - platform_admins = var.platform_admins - landing_zone_admins = var.landing_zone_admins } ################ @@ -23,15 +19,11 @@ module "governance" { module "management" { source = "../../modules/management" - owner_email = var.owner_email - project_code = "mgmt" - company_code = var.company_code - parent_container_id = module.governance.folder_container_ids.platform - organization_id = var.organization_id - labels = var.labels - region = var.region - organization_owners = var.organization_owners - organization_auditors = var.organization_auditors + owner_email = var.owner_email + naming_pattern = "${var.company_code}-pltfm-mgmt-prod" + parent_container_id = module.governance.folder_container_ids.platform + organization_id = var.organization_id + labels = var.labels } ########################### @@ -41,17 +33,8 @@ module "management" { module "connectivity_global" { source = "../../modules/connectivity-global" - owner_email = var.owner_email - project_name = "${var.company_name} Connectivity" - project_code = "conn" - company_name = var.company_name - company_code = var.company_code - parent_container_id = module.governance.folder_container_ids.platform organization_id = var.organization_id labels = var.labels - region = var.region - organization_owners = var.organization_owners - organization_auditors = var.organization_auditors network_areas = var.network_areas } @@ -63,19 +46,17 @@ module "connectivity_regional" { source = "../../modules/connectivity-regional" owner_email = var.owner_email - project_name = "${var.company_name} Connectivity Regional" - project_code = "conn-reg" - company_name = var.company_name - company_code = var.company_code + naming_pattern = "${var.company_code}-pltfm-hub-prod" parent_container_id = module.governance.folder_container_ids.platform organization_id = var.organization_id network_area_id = module.connectivity_global.network_area_ids[var.connectivity_regional_network_area] labels = var.labels - region = var.region firewall_zone = var.firewall_zone firewall_flavor = var.firewall_flavor vnet_range = var.connectivity_vnet_range firewall_ip = var.firewall_ip + + # for multiple regions define alias } ############ @@ -85,17 +66,11 @@ module "connectivity_regional" { module "devops" { source = "../../modules/devops" - owner_email = var.owner_email - project_name = "${var.company_name} DevOps" - project_code = "devops" - company_name = var.company_name - company_code = var.company_code - parent_container_id = module.governance.folder_container_ids.platform - organization_id = var.organization_id - labels = var.labels - region = var.region - organization_owners = var.organization_owners - organization_auditors = var.organization_auditors + owner_email = var.owner_email + naming_pattern = "${var.company_code}-pltfm-devops-prod" + company_name = var.company_name + parent_container_id = module.governance.folder_container_ids.platform + labels = var.labels } ############### @@ -105,10 +80,8 @@ module "devops" { module "sandboxes" { source = "../../modules/sandboxes" - company_code = var.company_code + naming_pattern = "${var.company_code}-sbx" parent_container_id = module.governance.folder_container_ids.sandbox - labels = var.labels - region = var.region sandboxes = var.sandboxes } @@ -120,17 +93,11 @@ module "landing_zone" { source = "../../modules/landing-zone" for_each = var.landing_zones - project_name = each.value.project_name - project_code = each.value.project_code - company_code = var.company_code - parent_container_id = each.value.corporate ? module.governance.folder_container_ids.landing_zones_corporate : module.governance.folder_container_ids.landing_zones_public + parent_container_id = module.governance.folder_container_ids.landing_zones_public + naming_pattern = "${var.company_code}-lz-${each.value.project_code}-${each.value.env}" owner_email = each.value.owner_email labels = var.labels - region = var.region - network_area_id = each.value.corporate ? module.connectivity_global.network_area_ids[var.connectivity_regional_network_area] : null - env = each.value.env role_assignments = each.value.role_assignments network_prefix_length = each.value.network_prefix_length custom_roles = each.value.custom_roles - kubernetes_clusters = each.value.kubernetes_clusters } diff --git a/examples/02-hub-spoke/pfsense.qcow2 b/examples/02-hub-spoke/pfsense.qcow2 new file mode 100644 index 0000000..311c8dd --- /dev/null +++ b/examples/02-hub-spoke/pfsense.qcow2 @@ -0,0 +1 @@ +PLACEHOLDER \ No newline at end of file diff --git a/examples/02-hub-spoke/provider.tf b/examples/02-hub-spoke/provider.tf deleted file mode 100644 index c0605f6..0000000 --- a/examples/02-hub-spoke/provider.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = ">= 1.10" - - required_providers { - stackit = { - source = "stackitcloud/stackit" - version = "0.83.0" - } - } -} - -provider "stackit" { - default_region = var.region - enable_beta_resources = true - experiments = ["iam", "routing-tables", "network"] -} diff --git a/examples/02-hub-spoke/providers.tf b/examples/02-hub-spoke/providers.tf new file mode 100644 index 0000000..25839dc --- /dev/null +++ b/examples/02-hub-spoke/providers.tf @@ -0,0 +1,5 @@ +provider "stackit" { + default_region = var.region + enable_beta_resources = true + experiments = ["iam", "routing-tables", "network"] +} diff --git a/examples/02-hub-spoke/terraform.tf b/examples/02-hub-spoke/terraform.tf new file mode 100644 index 0000000..c51ea6f --- /dev/null +++ b/examples/02-hub-spoke/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.10" + + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = "0.88.0" + } + } +} \ No newline at end of file diff --git a/examples/02-hub-spoke/terraform.tfvars b/examples/02-hub-spoke/terraform.tfvars index f8959a7..cfff7e0 100644 --- a/examples/02-hub-spoke/terraform.tfvars +++ b/examples/02-hub-spoke/terraform.tfvars @@ -38,9 +38,9 @@ landing_zone_admins = [ "lz-admin@example.com" ] -############################## -## CONNECTIVITY - GLOBAL ## -############################## +########################### +## CONNECTIVITY - GLOBAL ## +########################### # Network areas define the overall IP address space available for projects # Each area gets a transfer network (for inter-project routing) and one or more ranges @@ -61,9 +61,9 @@ network_areas = [ } ] -################################ -## CONNECTIVITY - REGIONAL ## -################################ +############################# +## CONNECTIVITY - REGIONAL ## +############################# # Must match a name from the network_areas list above connectivity_regional_network_area = "corporate" @@ -132,38 +132,6 @@ landing_zones = { permissions = ["project.resources.read", "project.resources.write"] } ] - - kubernetes_clusters = { - "main" = { - kubernetes_version = "1.31" - node_pools = [ - { - name = "system" - machine_type = "c1.3" - availability_zones = ["eu01-m"] - minimum = 2 - maximum = 5 - }, - { - name = "workers" - machine_type = "c1.4" - availability_zones = ["eu01-m"] - minimum = 3 - maximum = 10 - labels = { "workload" = "general" } - } - ] - extensions = { - acl = { - enabled = true - allowed_cidrs = ["10.0.0.0/8"] - } - dns = { - enabled = true - } - } - } - } } "data-platform" = { @@ -174,9 +142,6 @@ landing_zones = { corporate = true network_prefix_length = 24 - - # No Kubernetes — just a project with network connectivity - kubernetes_clusters = {} } # Public landing zone — no network area, uses STACKIT's default public networking diff --git a/modules/connectivity-global/1-network-area.tf b/modules/connectivity-global/1-network-area.tf index 99815c2..d9ee281 100644 --- a/modules/connectivity-global/1-network-area.tf +++ b/modules/connectivity-global/1-network-area.tf @@ -2,19 +2,26 @@ ## NETWORK AREA ## ################## -resource "stackit_network_area" "areas" { +locals { + project_labels = merge( + var.network_area_id != null ? { "networkArea" = var.network_area_id } : {}, + var.labels + ) +} + +resource "stackit_network_area" "this" { for_each = { for na in var.network_areas : na.name => na } organization_id = var.organization_id - name = "${local.naming_pattern}-${each.key}" + name = each.key labels = length(local.project_labels) > 0 ? local.project_labels : null } -resource "stackit_network_area_region" "areas" { +resource "stackit_network_area_region" "this" { for_each = { for na in var.network_areas : na.name => na } organization_id = var.organization_id - network_area_id = stackit_network_area.areas[each.key].network_area_id + network_area_id = stackit_network_area.this[each.key].network_area_id ipv4 = { network_ranges = each.value.network_ranges @@ -30,12 +37,12 @@ resource "stackit_network_area_region" "areas" { ## NETWORK AREA - ROUTING ## ############################ -# resource "stackit_network_area_route" "main" { -# organization_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -# network_area_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -# prefix = "192.168.0.0/24" -# next_hop = "192.168.0.0" -# labels = { -# "key" = "value" -# } -# } \ No newline at end of file +resource "stackit_network_area_route" "this" { + for_each = { for r in var.network_area_routes : r.name => r } + + organization_id = var.organization_id + network_area_id = stackit_network_area.this[each.value.network_area_name].network_area_id + + destination = each.value.destination + next_hop = each.value.next_hop +} \ No newline at end of file diff --git a/modules/connectivity-global/README.md b/modules/connectivity-global/README.md index f493969..bae0828 100644 --- a/modules/connectivity-global/README.md +++ b/modules/connectivity-global/README.md @@ -4,13 +4,13 @@ | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.10 | -| [stackit](#requirement\_stackit) | 0.83.0 | +| [stackit](#requirement\_stackit) | >=0.88.0 | ### Providers | Name | Version | |------|---------| -| [stackit](#provider\_stackit) | 0.83.0 | +| [stackit](#provider\_stackit) | 0.88.0 | ### Modules @@ -20,29 +20,19 @@ No modules. | Name | Type | |------|------| -| [stackit_network_area.areas](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/network_area) | resource | -| [stackit_network_area_region.areas](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/network_area_region) | resource | +| [stackit_network_area.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/network_area) | resource | +| [stackit_network_area_region.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/network_area_region) | resource | +| [stackit_network_area_route.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/network_area_route) | resource | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [company\_code](#input\_company\_code) | Company code used in resource naming conventions. | `string` | n/a | yes | -| [company\_name](#input\_company\_name) | Name of the company folder to create. | `string` | n/a | yes | | [network\_areas](#input\_network\_areas) | List of network areas to create, each with its own name, ranges, and configuration. |
list(object({
name = string
network_ranges = list(object({ prefix = string }))
transfer_network_range = string
max_prefix_length = optional(number, 28)
min_prefix_length = optional(number, 24)
default_prefix_length = optional(number, 28)
default_nameservers = optional(list(string), null)
}))
| n/a | yes | | [organization\_id](#input\_organization\_id) | Container ID of the root folder or organization under which the company folder will be created. | `string` | n/a | yes | -| [owner\_email](#input\_owner\_email) | Email address of the owner for the folders. Required for STACKIT resource manager. | `string` | n/a | yes | -| [parent\_container\_id](#input\_parent\_container\_id) | Parent container ID (folder or organization) where the project will be created. | `string` | n/a | yes | -| [project\_code](#input\_project\_code) | Optional project code for the STACKIT project. | `string` | n/a | yes | -| [project\_name](#input\_project\_name) | Name of the STACKIT project to create. | `string` | n/a | yes | -| [allowed\_network\_ranges](#input\_allowed\_network\_ranges) | List of allowed network ranges for Git instance ACL. | `list(string)` |
[
"0.0.0.0/0"
]
| no | -| [env](#input\_env) | Environment identifier (e.g., dev, staging, prod) used in resource naming conventions. | `string` | `"dev"` | no | | [labels](#input\_labels) | Additional labels to apply to all folders. | `map(string)` | `{}` | no | | [network\_area\_id](#input\_network\_area\_id) | Network Area ID to deploy resources into. Required if network is enabled. | `string` | `null` | no | -| [organization\_auditors](#input\_organization\_auditors) | List of organization role assignments for organization auditors. | `list(string)` | `[]` | no | -| [organization\_owners](#input\_organization\_owners) | List of organization role assignments for organization owners. | `list(string)` | `[]` | no | -| [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no | -| [role\_assignments](#input\_role\_assignments) | List of role assignments for the project. Subject can be a user email or service account email. |
list(object({
role = string
subject = string
}))
| `[]` | no | +| [network\_area\_routes](#input\_network\_area\_routes) | List of static routes to create within network areas. Each route references a network area by name. |
list(object({
name = string
network_area_name = string
destination = object({
type = string
value = string
})
next_hop = object({
type = string
value = optional(string)
})
}))
| `[]` | no | ### Outputs diff --git a/modules/connectivity-global/main.tf b/modules/connectivity-global/main.tf deleted file mode 100644 index e78da80..0000000 --- a/modules/connectivity-global/main.tf +++ /dev/null @@ -1,24 +0,0 @@ -terraform { - required_version = ">= 1.10" - - required_providers { - stackit = { - source = "stackitcloud/stackit" - version = "0.85.0" - } - } -} - -# provider "stackit" { -# default_region = var.region -# enable_beta_resources = true -# experiments = ["iam", "routing-tables", "network"] -# } - -locals { - naming_pattern = "${var.company_code}-pltfm-${var.project_code}-${var.env}" - project_labels = merge( - var.network_area_id != null ? { "networkArea" = var.network_area_id } : {}, - var.labels - ) -} \ No newline at end of file diff --git a/modules/connectivity-global/outputs.tf b/modules/connectivity-global/outputs.tf index f8ebb08..30b15f5 100644 --- a/modules/connectivity-global/outputs.tf +++ b/modules/connectivity-global/outputs.tf @@ -1,4 +1,4 @@ output "network_area_ids" { description = "Map of network area names to their IDs." - value = { for name, area in stackit_network_area.areas : name => area.network_area_id } + value = { for name, area in stackit_network_area.this : name => area.network_area_id } } \ No newline at end of file diff --git a/modules/connectivity-global/terraform.tf b/modules/connectivity-global/terraform.tf new file mode 100644 index 0000000..3900fd4 --- /dev/null +++ b/modules/connectivity-global/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.10" + + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = ">=0.88.0" + } + } +} diff --git a/modules/connectivity-global/variables.tf b/modules/connectivity-global/variables.tf index 5f43b8b..5c02424 100644 --- a/modules/connectivity-global/variables.tf +++ b/modules/connectivity-global/variables.tf @@ -1,89 +1,15 @@ -variable "owner_email" { - type = string - description = "Email address of the owner for the folders. Required for STACKIT resource manager." -} - -variable "project_name" { - type = string - description = "Name of the STACKIT project to create." -} - -variable "project_code" { - type = string - description = "Optional project code for the STACKIT project." -} - -variable "company_name" { - type = string - description = "Name of the company folder to create." -} - -variable "company_code" { - type = string - description = "Company code used in resource naming conventions." -} - -variable "parent_container_id" { - type = string - description = "Parent container ID (folder or organization) where the project will be created." -} - -variable "organization_id" { - type = string - description = "Container ID of the root folder or organization under which the company folder will be created." -} - variable "labels" { type = map(string) description = "Additional labels to apply to all folders." default = {} } -variable "region" { - type = string - description = "STACKIT region for regional resources." - default = "eu01" -} - -variable "organization_owners" { - type = list(string) - description = "List of organization role assignments for organization owners." - default = [] -} - -variable "organization_auditors" { - type = list(string) - description = "List of organization role assignments for organization auditors." - default = [] -} - -variable "allowed_network_ranges" { - type = list(string) - description = "List of allowed network ranges for Git instance ACL." - default = ["0.0.0.0/0"] -} - variable "network_area_id" { type = string description = "Network Area ID to deploy resources into. Required if network is enabled." default = null } -variable "role_assignments" { - type = list(object({ - role = string - subject = string - })) - description = "List of role assignments for the project. Subject can be a user email or service account email." - default = [] -} - -variable "env" { - type = string - description = "Environment identifier (e.g., dev, staging, prod) used in resource naming conventions." - default = "dev" -} - variable "network_areas" { type = list(object({ name = string @@ -95,4 +21,26 @@ variable "network_areas" { default_nameservers = optional(list(string), null) })) description = "List of network areas to create, each with its own name, ranges, and configuration." +} + +variable "network_area_routes" { + type = list(object({ + name = string + network_area_name = string + destination = object({ + type = string + value = string + }) + next_hop = object({ + type = string + value = optional(string) + }) + })) + description = "List of static routes to create within network areas. Each route references a network area by name." + default = [] +} + +variable "organization_id" { + type = string + description = "Container ID of the root folder or organization under which the company folder will be created." } \ No newline at end of file diff --git a/modules/connectivity-regional/1-project.tf b/modules/connectivity-regional/1-project.tf index feb2339..d16e004 100644 --- a/modules/connectivity-regional/1-project.tf +++ b/modules/connectivity-regional/1-project.tf @@ -2,17 +2,24 @@ ## PROJECT ## ############# -resource "stackit_resourcemanager_project" "project" { +locals { + project_labels = merge( + var.network_area_id != null ? { "networkArea" = var.network_area_id } : {}, + var.labels + ) +} + +resource "stackit_resourcemanager_project" "this" { parent_container_id = var.parent_container_id - name = local.naming_pattern + name = var.project_name != null ? var.project_name : var.naming_pattern owner_email = var.owner_email labels = length(local.project_labels) > 0 ? local.project_labels : null # provider bug: empty map becomes null after apply } -resource "stackit_authorization_project_role_assignment" "assignments" { +resource "stackit_authorization_project_role_assignment" "this" { for_each = { for assignment in var.role_assignments : "${assignment.role}-${assignment.subject}" => assignment } - resource_id = stackit_resourcemanager_project.project.project_id + resource_id = stackit_resourcemanager_project.this.project_id role = each.value.role subject = each.value.subject } diff --git a/modules/connectivity-regional/2-network.tf b/modules/connectivity-regional/2-network.tf index bb3e6e0..c2a6786 100644 --- a/modules/connectivity-regional/2-network.tf +++ b/modules/connectivity-regional/2-network.tf @@ -4,8 +4,8 @@ # will get auto assigned a free range from the network area (needs to be, because for a public ip it needs to be routed) resource "stackit_network" "wan" { - project_id = stackit_resourcemanager_project.project.project_id - name = "wan_network" + project_id = stackit_resourcemanager_project.this.project_id + name = "wan" #ipv4_nameservers = ["208.67.222.222", "9.9.9.9"] provider bug routed = true @@ -16,8 +16,8 @@ resource "stackit_network" "wan" { # the wan interface actually handles the internal and external traffic # actually lan should be reachable internally and wan not resource "stackit_network" "lan" { - project_id = stackit_resourcemanager_project.project.project_id - name = "lan_network" + project_id = stackit_resourcemanager_project.this.project_id + name = "lan" ipv4_nameservers = ["208.67.222.222", "9.9.9.9"] ipv4_prefix = var.vnet_range ipv4_gateway = var.firewall_ip @@ -30,13 +30,13 @@ resource "stackit_network" "lan" { # the firewall will see a private ip, because stackit handles NAT (public ip is not attached directly) resource "stackit_network_interface" "wan" { - project_id = stackit_resourcemanager_project.project.project_id + project_id = stackit_resourcemanager_project.this.project_id network_id = stackit_network.wan.network_id security = false } resource "stackit_network_interface" "lan" { - project_id = stackit_resourcemanager_project.project.project_id + project_id = stackit_resourcemanager_project.this.project_id network_id = stackit_network.lan.network_id ipv4 = var.firewall_ip security = false @@ -46,7 +46,7 @@ resource "stackit_network_interface" "lan" { ## PUBLIC IP ## ############### -resource "stackit_public_ip" "wan-ip" { - project_id = stackit_resourcemanager_project.project.project_id +resource "stackit_public_ip" "wan" { + project_id = stackit_resourcemanager_project.this.project_id network_interface_id = stackit_network_interface.wan.network_interface_id } diff --git a/modules/connectivity-regional/3-firewall.tf b/modules/connectivity-regional/3-firewall.tf index 353f1cf..3f20c3a 100644 --- a/modules/connectivity-regional/3-firewall.tf +++ b/modules/connectivity-regional/3-firewall.tf @@ -12,8 +12,8 @@ # } # } -resource "stackit_image" "pfsense_image" { - project_id = stackit_resourcemanager_project.project.project_id +resource "stackit_image" "pfsense" { + project_id = stackit_resourcemanager_project.this.project_id name = "pfsense-2.7.2-amd64-image" local_file_path = "./pfsense.qcow2" disk_format = "qcow2" @@ -23,21 +23,21 @@ resource "stackit_image" "pfsense_image" { uefi = false } - # depends_on = [terraform_data.pfsense_image_file] + # depends_on = [terraform_data.pfsense_image_file] } ############ ## VOLUME ## ############ -resource "stackit_volume" "pfsense_vol" { - project_id = stackit_resourcemanager_project.project.project_id +resource "stackit_volume" "pfsense" { + project_id = stackit_resourcemanager_project.this.project_id name = "pfsense-2.7.2-root" availability_zone = var.firewall_zone size = 16 performance_class = "storage_premium_perf4" source = { - id = stackit_image.pfsense_image.image_id + id = stackit_image.pfsense.image_id type = "image" } } @@ -47,12 +47,12 @@ resource "stackit_volume" "pfsense_vol" { ############ # after rollout: https://docs.stackit.cloud/products/quick-deployments/pfsense-firewall/tutorials/configure-pfsense/ -resource "stackit_server" "pfsense_Server" { - project_id = stackit_resourcemanager_project.project.project_id +resource "stackit_server" "pfsense" { + project_id = stackit_resourcemanager_project.this.project_id name = "pfSense" boot_volume = { source_type = "volume" - source_id = stackit_volume.pfsense_vol.volume_id + source_id = stackit_volume.pfsense.volume_id } availability_zone = var.firewall_zone machine_type = var.firewall_flavor diff --git a/modules/connectivity-regional/README.md b/modules/connectivity-regional/README.md index 1e26d30..8e062df 100644 --- a/modules/connectivity-regional/README.md +++ b/modules/connectivity-regional/README.md @@ -50,13 +50,13 @@ Now you can enter the WebUI via the FloatingIP on port 443 the default login is | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.10 | -| [stackit](#requirement\_stackit) | 0.83.0 | +| [stackit](#requirement\_stackit) | >=0.88.0 | ### Providers | Name | Version | |------|---------| -| [stackit](#provider\_stackit) | 0.83.0 | +| [stackit](#provider\_stackit) | 0.88.0 | ### Modules @@ -66,36 +66,32 @@ No modules. | Name | Type | |------|------| -| [stackit_authorization_project_role_assignment.assignments](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_project_role_assignment) | resource | -| [stackit_image.pfsense_image](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/image) | resource | -| [stackit_network.lan](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/network) | resource | -| [stackit_network.wan](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/network) | resource | -| [stackit_network_area_route.default](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/network_area_route) | resource | -| [stackit_network_interface.lan](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/network_interface) | resource | -| [stackit_network_interface.wan](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/network_interface) | resource | -| [stackit_public_ip.wan-ip](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/public_ip) | resource | -| [stackit_resourcemanager_project.project](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/resourcemanager_project) | resource | -| [stackit_server.pfsense_Server](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/server) | resource | -| [stackit_volume.pfsense_vol](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/volume) | resource | +| [stackit_authorization_project_role_assignment.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_project_role_assignment) | resource | +| [stackit_image.pfsense](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/image) | resource | +| [stackit_network.lan](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/network) | resource | +| [stackit_network.wan](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/network) | resource | +| [stackit_network_area_route.default](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/network_area_route) | resource | +| [stackit_network_interface.lan](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/network_interface) | resource | +| [stackit_network_interface.wan](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/network_interface) | resource | +| [stackit_public_ip.wan](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/public_ip) | resource | +| [stackit_resourcemanager_project.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/resourcemanager_project) | resource | +| [stackit_server.pfsense](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/server) | resource | +| [stackit_volume.pfsense](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/volume) | resource | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [company\_code](#input\_company\_code) | Company code used in resource naming conventions. | `string` | n/a | yes | -| [company\_name](#input\_company\_name) | Name of the company folder to create. | `string` | n/a | yes | +| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-net-prod". | `string` | n/a | yes | | [network\_area\_id](#input\_network\_area\_id) | Network Area ID to deploy resources into. Required if network is enabled. | `string` | n/a | yes | | [organization\_id](#input\_organization\_id) | Organization ID, required for network area route configuration. | `string` | n/a | yes | | [owner\_email](#input\_owner\_email) | Email address of the owner for the folders. Required for STACKIT resource manager. | `string` | n/a | yes | | [parent\_container\_id](#input\_parent\_container\_id) | Parent container ID (folder or organization) where the project will be created. | `string` | n/a | yes | -| [project\_code](#input\_project\_code) | Optional project code for the STACKIT project. | `string` | n/a | yes | -| [project\_name](#input\_project\_name) | Name of the STACKIT project to create. | `string` | n/a | yes | -| [env](#input\_env) | Environment identifier (e.g., dev, staging, prod) used in resource naming conventions. | `string` | `"dev"` | no | | [firewall\_flavor](#input\_firewall\_flavor) | Firewall VM Flavor | `string` | `"c1.2"` | no | | [firewall\_ip](#input\_firewall\_ip) | IP address of the firewall | `string` | `"10.0.0.220"` | no | | [firewall\_zone](#input\_firewall\_zone) | STACKIT Availability Zone | `string` | `"eu01-m"` | no | | [labels](#input\_labels) | Additional labels to apply to all folders. | `map(string)` | `{}` | no | -| [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no | +| [project\_name](#input\_project\_name) | Name of the STACKIT project to create. | `string` | `null` | no | | [role\_assignments](#input\_role\_assignments) | List of role assignments for the project. Subject can be a user email or service account email. |
list(object({
role = string
subject = string
}))
| `[]` | no | | [vnet\_range](#input\_vnet\_range) | CIDR range for the project VNet. Required if network is enabled. | `string` | `"10.0.0.0/24"` | no | diff --git a/modules/connectivity-regional/main.tf b/modules/connectivity-regional/main.tf deleted file mode 100644 index 4816768..0000000 --- a/modules/connectivity-regional/main.tf +++ /dev/null @@ -1,24 +0,0 @@ -terraform { - required_version = ">= 1.10" - - required_providers { - stackit = { - source = "stackitcloud/stackit" - version = "0.85.0" - } - } -} - -# provider "stackit" { -# default_region = var.region -# enable_beta_resources = true -# experiments = ["iam", "routing-tables", "network"] -# } - -locals { - naming_pattern = "${var.company_code}-pltfm-${var.project_code}-${var.region}-${var.env}" - project_labels = merge( - var.network_area_id != null ? { "networkArea" = var.network_area_id } : {}, - var.labels - ) -} \ No newline at end of file diff --git a/modules/connectivity-regional/outputs.tf b/modules/connectivity-regional/outputs.tf index 2780584..c005c50 100644 --- a/modules/connectivity-regional/outputs.tf +++ b/modules/connectivity-regional/outputs.tf @@ -1,24 +1,24 @@ -output "project_id" { - description = "The project ID of the created STACKIT project." - value = stackit_resourcemanager_project.project.project_id +output "pfsense_public_ip" { + description = "The public IP address of the pfSense firewall WAN interface." + value = stackit_public_ip.wan.ip } -output "project_container_id" { - description = "The container ID of the created STACKIT project." - value = stackit_resourcemanager_project.project.container_id +output "pfsense_wan_ip" { + description = "The internal network area IP of the pfSense WAN interface (used as next hop in routes)." + value = stackit_network_interface.wan.ipv4 } -output "project_name" { - description = "The name of the created STACKIT project." - value = stackit_resourcemanager_project.project.name +output "project_container_id" { + description = "The container ID of the created STACKIT project." + value = stackit_resourcemanager_project.this.container_id } -output "pfsense_public_ip" { - description = "The public IP address of the pfSense firewall WAN interface." - value = stackit_public_ip.wan-ip.ip +output "project_id" { + description = "The project ID of the created STACKIT project." + value = stackit_resourcemanager_project.this.project_id } -output "pfsense_wan_ip" { - description = "The internal network area IP of the pfSense WAN interface (used as next hop in routes)." - value = stackit_network_interface.wan.ipv4 +output "project_name" { + description = "The name of the created STACKIT project." + value = stackit_resourcemanager_project.this.name } diff --git a/modules/connectivity-regional/terraform.tf b/modules/connectivity-regional/terraform.tf new file mode 100644 index 0000000..33d151a --- /dev/null +++ b/modules/connectivity-regional/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.10" + + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = ">=0.88.0" + } + } +} \ No newline at end of file diff --git a/modules/connectivity-regional/variables.tf b/modules/connectivity-regional/variables.tf index 06e708e..e1cf112 100644 --- a/modules/connectivity-regional/variables.tf +++ b/modules/connectivity-regional/variables.tf @@ -1,47 +1,35 @@ -variable "owner_email" { - type = string - description = "Email address of the owner for the folders. Required for STACKIT resource manager." -} - -variable "project_name" { +variable "firewall_flavor" { type = string - description = "Name of the STACKIT project to create." -} + description = "Firewall VM Flavor" + default = "c1.2" -variable "project_code" { - type = string - description = "Optional project code for the STACKIT project." + validation { + condition = can(regex("^[a-z][0-9]+\\.[0-9]+$", var.firewall_flavor)) + error_message = "firewall_flavor must match STACKIT machine type format (e.g. c1.2). Validate available flavors with: stackit server machine-type list" + } } -variable "company_name" { +variable "firewall_ip" { type = string - description = "Name of the company folder to create." + description = "IP address of the firewall" + default = "10.0.0.220" } -variable "company_code" { +variable "firewall_zone" { type = string - description = "Company code used in resource naming conventions." -} - -variable "role_assignments" { - type = list(object({ - role = string - subject = string - })) - description = "List of role assignments for the project. Subject can be a user email or service account email." - default = [] + description = "STACKIT Availability Zone" + default = "eu01-m" } -variable "region" { - type = string - description = "STACKIT region for regional resources." - default = "eu01" +variable "labels" { + type = map(string) + description = "Additional labels to apply to all folders." + default = {} } -variable "env" { +variable "naming_pattern" { type = string - description = "Environment identifier (e.g., dev, staging, prod) used in resource naming conventions." - default = "dev" + description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-net-prod\"." } variable "network_area_id" { @@ -54,10 +42,9 @@ variable "organization_id" { description = "Organization ID, required for network area route configuration." } -variable "labels" { - type = map(string) - description = "Additional labels to apply to all folders." - default = {} +variable "owner_email" { + type = string + description = "Email address of the owner for the folders. Required for STACKIT resource manager." } variable "parent_container_id" { @@ -65,21 +52,19 @@ variable "parent_container_id" { description = "Parent container ID (folder or organization) where the project will be created." } -variable "firewall_zone" { +variable "project_name" { type = string - description = "STACKIT Availability Zone" - default = "eu01-m" + description = "Name of the STACKIT project to create." + default = null } -variable "firewall_flavor" { - type = string - description = "Firewall VM Flavor" - default = "c1.2" - - validation { - condition = can(regex("^[a-z][0-9]+\\.[0-9]+$", var.firewall_flavor)) - error_message = "firewall_flavor must match STACKIT machine type format (e.g. c1.2). Validate available flavors with: stackit server machine-type list" - } +variable "role_assignments" { + type = list(object({ + role = string + subject = string + })) + description = "List of role assignments for the project. Subject can be a user email or service account email." + default = [] } variable "vnet_range" { @@ -87,8 +72,3 @@ variable "vnet_range" { description = "CIDR range for the project VNet. Required if network is enabled." default = "10.0.0.0/24" } -variable "firewall_ip" { - type = string - description = "IP address of the firewall" - default = "10.0.0.220" -} diff --git a/modules/devops/1-project.tf b/modules/devops/1-project.tf index ce1129c..8472290 100644 --- a/modules/devops/1-project.tf +++ b/modules/devops/1-project.tf @@ -2,17 +2,24 @@ ## PROJECT ## ############# -resource "stackit_resourcemanager_project" "project" { +locals { + project_labels = merge( + var.network_area_id != null ? { "networkArea" = var.network_area_id } : {}, + var.labels + ) +} + +resource "stackit_resourcemanager_project" "this" { parent_container_id = var.parent_container_id - name = local.naming_pattern + name = var.project_name != null ? var.project_name : var.naming_pattern owner_email = var.owner_email labels = length(local.project_labels) > 0 ? local.project_labels : null # provider bug: empty map becomes null after apply } -resource "stackit_authorization_project_role_assignment" "assignments" { +resource "stackit_authorization_project_role_assignment" "this" { for_each = { for assignment in var.role_assignments : "${assignment.role}-${assignment.subject}" => assignment } - resource_id = stackit_resourcemanager_project.project.project_id + resource_id = stackit_resourcemanager_project.this.project_id role = each.value.role subject = each.value.subject } \ No newline at end of file diff --git a/modules/devops/2-git.tf b/modules/devops/2-git.tf index 6052ccc..64cf326 100644 --- a/modules/devops/2-git.tf +++ b/modules/devops/2-git.tf @@ -5,7 +5,7 @@ resource "stackit_git" "git" { count = var.git_flavor != null ? 1 : 0 - project_id = stackit_resourcemanager_project.project.project_id + project_id = stackit_resourcemanager_project.this.project_id name = replace(lower(substr(var.company_name, 0, 31)), " ", "-") acl = var.allowed_network_ranges flavor = var.git_flavor diff --git a/modules/devops/README.md b/modules/devops/README.md index 1f27904..c2deca5 100644 --- a/modules/devops/README.md +++ b/modules/devops/README.md @@ -4,13 +4,13 @@ | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.10 | -| [stackit](#requirement\_stackit) | 0.83.0 | +| [stackit](#requirement\_stackit) | >=0.88.0 | ### Providers | Name | Version | |------|---------| -| [stackit](#provider\_stackit) | 0.83.0 | +| [stackit](#provider\_stackit) | 0.88.0 | ### Modules @@ -20,29 +20,23 @@ No modules. | Name | Type | |------|------| -| [stackit_authorization_project_role_assignment.assignments](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_project_role_assignment) | resource | -| [stackit_git.git](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/git) | resource | -| [stackit_resourcemanager_project.project](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/resourcemanager_project) | resource | +| [stackit_authorization_project_role_assignment.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_project_role_assignment) | resource | +| [stackit_git.git](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/git) | resource | +| [stackit_resourcemanager_project.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/resourcemanager_project) | resource | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [company\_code](#input\_company\_code) | Company code used in resource naming conventions. | `string` | n/a | yes | | [company\_name](#input\_company\_name) | Name of the company folder to create. | `string` | n/a | yes | -| [organization\_id](#input\_organization\_id) | Container ID of the root folder or organization under which the company folder will be created. | `string` | n/a | yes | +| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-net-prod". | `string` | n/a | yes | | [owner\_email](#input\_owner\_email) | Email address of the owner for the folders. Required for STACKIT resource manager. | `string` | n/a | yes | | [parent\_container\_id](#input\_parent\_container\_id) | Parent container ID (folder or organization) where the project will be created. | `string` | n/a | yes | -| [project\_code](#input\_project\_code) | Optional project code for the STACKIT project. | `string` | n/a | yes | -| [project\_name](#input\_project\_name) | Name of the STACKIT project to create. | `string` | n/a | yes | | [allowed\_network\_ranges](#input\_allowed\_network\_ranges) | List of allowed network ranges for Git instance ACL. | `list(string)` |
[
"0.0.0.0/0"
]
| no | -| [env](#input\_env) | Environment identifier (e.g., dev, staging, prod) used in resource naming conventions. | `string` | `"dev"` | no | | [git\_flavor](#input\_git\_flavor) | The flavor of the Git instance. | `string` | `null` | no | | [labels](#input\_labels) | Additional labels to apply to all folders. | `map(string)` | `{}` | no | | [network\_area\_id](#input\_network\_area\_id) | Network Area ID to deploy resources into. Required if network is enabled. | `string` | `null` | no | -| [organization\_auditors](#input\_organization\_auditors) | List of organization role assignments for organization auditors. | `list(string)` | `[]` | no | -| [organization\_owners](#input\_organization\_owners) | List of organization role assignments for organization owners. | `list(string)` | `[]` | no | -| [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no | +| [project\_name](#input\_project\_name) | Name of the STACKIT project to create. | `string` | `null` | no | | [role\_assignments](#input\_role\_assignments) | List of role assignments for the project. Subject can be a user email or service account email. |
list(object({
role = string
subject = string
}))
| `[]` | no | ### Outputs diff --git a/modules/devops/main.tf b/modules/devops/main.tf deleted file mode 100644 index 1191091..0000000 --- a/modules/devops/main.tf +++ /dev/null @@ -1,24 +0,0 @@ -terraform { - required_version = ">= 1.10" - - required_providers { - stackit = { - source = "stackitcloud/stackit" - version = "0.85.0" - } - } -} - -provider "stackit" { - default_region = var.region - enable_beta_resources = true - experiments = ["iam", "routing-tables", "network"] -} - -locals { - naming_pattern = "${var.company_code}-pltfm-${var.project_code}-${var.env}" - project_labels = merge( - var.network_area_id != null ? { "networkArea" = var.network_area_id } : {}, - var.labels - ) -} \ No newline at end of file diff --git a/modules/devops/outputs.tf b/modules/devops/outputs.tf index 723cca0..d95455e 100644 --- a/modules/devops/outputs.tf +++ b/modules/devops/outputs.tf @@ -1,14 +1,14 @@ -output "project_id" { - description = "The project ID of the created STACKIT project." - value = stackit_resourcemanager_project.project.project_id -} - output "project_container_id" { description = "The container ID of the created STACKIT project." - value = stackit_resourcemanager_project.project.container_id + value = stackit_resourcemanager_project.this.container_id +} + +output "project_id" { + description = "The project ID of the created STACKIT project." + value = stackit_resourcemanager_project.this.project_id } output "project_name" { description = "The name of the created STACKIT project." - value = stackit_resourcemanager_project.project.name + value = stackit_resourcemanager_project.this.name } diff --git a/modules/devops/terraform.tf b/modules/devops/terraform.tf new file mode 100644 index 0000000..33d151a --- /dev/null +++ b/modules/devops/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.10" + + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = ">=0.88.0" + } + } +} \ No newline at end of file diff --git a/modules/devops/variables.tf b/modules/devops/variables.tf index 4a8f21f..631f67f 100644 --- a/modules/devops/variables.tf +++ b/modules/devops/variables.tf @@ -1,16 +1,7 @@ -variable "owner_email" { - type = string - description = "Email address of the owner for the folders. Required for STACKIT resource manager." -} - -variable "project_name" { - type = string - description = "Name of the STACKIT project to create." -} - -variable "project_code" { - type = string - description = "Optional project code for the STACKIT project." +variable "allowed_network_ranges" { + type = list(string) + description = "List of allowed network ranges for Git instance ACL." + default = ["0.0.0.0/0"] } variable "company_name" { @@ -18,19 +9,15 @@ variable "company_name" { description = "Name of the company folder to create." } -variable "company_code" { - type = string - description = "Company code used in resource naming conventions." -} - -variable "parent_container_id" { +variable "git_flavor" { type = string - description = "Parent container ID (folder or organization) where the project will be created." -} + description = "The flavor of the Git instance." + default = null # "git-100", git-10 -variable "organization_id" { - type = string - description = "Container ID of the root folder or organization under which the company folder will be created." + validation { + condition = var.git_flavor == null || can(regex("^git-[0-9]+$", var.git_flavor)) + error_message = "git_flavor must match STACKIT Git flavor format (e.g. git-10 or git-100). Validate available flavors in the STACKIT Git API documentation." + } } variable "labels" { @@ -39,45 +26,31 @@ variable "labels" { default = {} } -variable "region" { +variable "network_area_id" { type = string - description = "STACKIT region for regional resources." - default = "eu01" -} - -variable "organization_owners" { - type = list(string) - description = "List of organization role assignments for organization owners." - default = [] + description = "Network Area ID to deploy resources into. Required if network is enabled." + default = null } -variable "organization_auditors" { - type = list(string) - description = "List of organization role assignments for organization auditors." - default = [] +variable "owner_email" { + type = string + description = "Email address of the owner for the folders. Required for STACKIT resource manager." } -variable "git_flavor" { +variable "parent_container_id" { type = string - description = "The flavor of the Git instance." - default = null # "git-100", git-10 - - validation { - condition = var.git_flavor == null || can(regex("^git-[0-9]+$", var.git_flavor)) - error_message = "git_flavor must match STACKIT Git flavor format (e.g. git-10 or git-100). Validate available flavors in the STACKIT Git API documentation." - } + description = "Parent container ID (folder or organization) where the project will be created." } -variable "allowed_network_ranges" { - type = list(string) - description = "List of allowed network ranges for Git instance ACL." - default = ["0.0.0.0/0"] +variable "project_name" { + type = string + description = "Name of the STACKIT project to create." + default = null } -variable "network_area_id" { +variable "naming_pattern" { type = string - description = "Network Area ID to deploy resources into. Required if network is enabled." - default = null + description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-net-prod\"." } variable "role_assignments" { @@ -87,10 +60,4 @@ variable "role_assignments" { })) description = "List of role assignments for the project. Subject can be a user email or service account email." default = [] -} - -variable "env" { - type = string - description = "Environment identifier (e.g., dev, staging, prod) used in resource naming conventions." - default = "dev" } \ No newline at end of file diff --git a/modules/governance/1-rm-folders.tf b/modules/governance/1-rm-folders.tf index 00a153a..3e5f758 100644 --- a/modules/governance/1-rm-folders.tf +++ b/modules/governance/1-rm-folders.tf @@ -1,35 +1,56 @@ +locals { + folder_owners = merge([ + for folder_key, folder in var.rm_folders : { + for email in folder.owner_emails : + "${folder_key}:${email}" => { + folder_key = folder_key + subject = email + } + if !contains(var.organization_owners, email) # skip duplicates already covered by org-level role + } + ]...) + + folder_readers = merge([ + for folder_key, folder in var.rm_folders : { + for email in folder.reader_emails : + "${folder_key}:${email}" => { + folder_key = folder_key + subject = email + } + if !contains(var.organization_owners, email) # skip duplicates already covered by org-level role + } + ]...) +} + ############################## ## RESOURCE MANAGER FOLDERS ## ############################## -########### -# Level 1 # -########### +resource "stackit_resourcemanager_folder" "this" { + for_each = var.rm_folders -resource "stackit_resourcemanager_folder" "platform" { - name = "Platform" + name = each.value.name parent_container_id = var.organization_id owner_email = var.owner_email - labels = local.labels + labels = length(var.labels) > 0 ? var.labels : null # provider bug: empty map becomes null after apply } -resource "stackit_resourcemanager_folder" "landing_zones_corporate" { - name = "Landing Zones - Corporate" - parent_container_id = var.organization_id - owner_email = var.owner_email - labels = local.labels -} +################################ +## RM FOLDER ROLE ASSIGNMENTS ## +################################ -resource "stackit_resourcemanager_folder" "landing_zones_public" { - name = "Landing Zones - Public" - parent_container_id = var.organization_id - owner_email = var.owner_email - labels = local.labels +resource "stackit_authorization_folder_role_assignment" "owners" { + for_each = local.folder_owners + + resource_id = stackit_resourcemanager_folder.this[each.value.folder_key].folder_id + role = "owner" + subject = each.value.subject } -resource "stackit_resourcemanager_folder" "sandbox" { - name = "Sandboxes" - parent_container_id = var.organization_id - owner_email = var.owner_email - labels = local.labels -} \ No newline at end of file +resource "stackit_authorization_folder_role_assignment" "readers" { + for_each = local.folder_readers + + resource_id = stackit_resourcemanager_folder.this[each.value.folder_key].folder_id + role = "auditor" + subject = each.value.subject +} \ No newline at end of file diff --git a/modules/governance/2-custom-roles.tf b/modules/governance/2-custom-roles.tf index 0cf884e..0b3b77e 100644 --- a/modules/governance/2-custom-roles.tf +++ b/modules/governance/2-custom-roles.tf @@ -1,347 +1,12 @@ -locals { - custom_roles = [ - { - name = "organization.reader" - description = "Custom role for organization readers with read-only access to all services." - permissions = [ - "airflow.instance.get", - "airflow.instance.list", - "alb.certificateservice.certificate.get", - "alb.certificateservice.certificate.list", - "alb.certificateservice.ping", - "alb.credentials.get", - "alb.credentials.list", - "alb.loadbalancer.get", - "alb.loadbalancer.list", - "alb.ping", - "alb.quota.get", - "alb.waf.crs.get", - "alb.waf.crs.list", - "alb.waf.get", - "alb.waf.list", - "alb.waf.ping", - "alb.waf.rules.get", - "alb.waf.rules.list", - "archiving.credential.get", - "archiving.flavor.list", - "archiving.instance.get", - "archiving.instance.list", - "argus.grafana.view", - "argus.instance.get", - "argus.instance.list", - "argus.plan.list", - "audit-log.entry.get", - "audit-log.resource.entry.get", - "billing-account.billing-details.get", - "cdn.distribution.get", - "cost-management.billing.get", - "cost-management.credit.get", - "cost-management.partner-cost.get", - "cspm.alert.get", - "cspm.alert.list", - "cspm.override.get", - "cspm.override.list", - "cspm.standard.get", - "cspm.standard.list", - "customer.billing-account.get", - "customer.billing-account.list", - "customer.company-profile.get", - "customer.customer.get", - "customer.linkable-billing-account.list", - "customer.operation.list", - "customer.organization.customer.get", - "customer.organization.list", - "customer.payment-method.list", - "customer.project.billing-account.get", - "customer.project.billing-account.list", - "dns.zone.get", - "dns.zone.list", - "dns.zone.rrset.get", - "dns.zone.rrset.list", - "dremio.instance.get", - "dremio.instance.list", - "dremio.user.get", - "dremio.user.list", - "edge.instances.get", - "edge.instances.list", - "edge.plans.list", - "file-storage.resource-pool.list", - "file-storage.resource-pool.snapshot.list", - "file-storage.share.list", - "file-storage.share-export-policy.list", - "functions.function.get", - "functions.function.list", - "functions.function.logs", - "functions.function.metrics", - "functions.label.get", - "git.authentication.source.get", - "git.authentication.source.list", - "git.flavor.list", - "git.instance.authentication-source.get", - "git.instance.authentication-source.list", - "git.instance.get", - "git.instance.list", - "git.runner.get", - "iaas.affinity-group.get", - "iaas.affinity-group.list", - "iaas.backup.get", - "iaas.backup.list", - "iaas.image.get", - "iaas.image.list", - "iaas.image.share.get", - "iaas.keypair.get", - "iaas.keypair.list", - "iaas.machine-type.get", - "iaas.machine-type.list", - "iaas.network-area.get", - "iaas.network-area.list", - "iaas.network-area.project.list", - "iaas.network-area.range.get", - "iaas.network-area.range.list", - "iaas.network-area.route.get", - "iaas.network-area.route.list", - "iaas.network-area.rt.get", - "iaas.network-area.rt.list", - "iaas.network-area.rt.route.get", - "iaas.network-area.rt.route.list", - "iaas.network.get", - "iaas.network.list", - "iaas.nic.get", - "iaas.nic.list", - "iaas.openstack-network.get", - "iaas.project.get", - "iaas.public-ip.get", - "iaas.public-ip.list", - "iaas.quota.get", - "iaas.regional-network-area.get", - "iaas.regional-network-area.list", - "iaas.request.get", - "iaas.resource.request.get", - "iaas.security-group.get", - "iaas.security-group.list", - "iaas.security-group.rule.get", - "iaas.security-group.rule.list", - "iaas.server.console-log.get", - "iaas.server.console-url.get", - "iaas.server.get", - "iaas.server.list", - "iaas.server.metadata.get", - "iaas.server.nic.list", - "iaas.server.service-account.list", - "iaas.server.volume.get", - "iaas.server.volume.list", - "iaas.snapshot.get", - "iaas.snapshot.list", - "iaas.virtual-ip.get", - "iaas.virtual-ip.list", - "iaas.volume.get", - "iaas.volume.list", - "iaas.volume-performance-class.get", - "iaas.volume-performance-class.list", - "iam.member.get", - "iam.resource.member.get", - "iam.resource.role.get", - "iam.role.get", - "iam.role.list", - "iam.service-account.get", - "iam.service-account.list", - "iam.service-account-federation.get", - "iam.service-account-federation.list", - "iam.service-account-key.get", - "iam.service-account-key.list", - "iam.service-account-token.list", - "intake.intake.get", - "intake.intake.list", - "kms.keyring.get", - "kms.keyring.list", - "logme.credential.get", - "logme.credential.list", - "logme.instance.get", - "logme.instance.list", - "logme.offerings.list", - "logme.proxy.read", - "logs.instance.get", - "logs.instance.list", - "logs.instance.credentials.get", - "logs.instance.credentials.list", - "mariadb.credential.get", - "mariadb.credential.list", - "mariadb.instance.get", - "mariadb.instance.list", - "mariadb.offerings.list", - "mariadb.proxy.read", - "marketplace.asset.get", - "marketplace.asset.list", - "marketplace.catalog.get", - "marketplace.catalog.list", - "marketplace.subscription.get", - "marketplace.subscription.list", - "mlflow.instance.get", - "mlflow.instance.list", - "mlflow.token.get", - "mlflow.token.list", - "model-experiments.instance.get", - "model-experiments.instance.list", - "model-experiments.token.get", - "model-experiments.token.list", - "model-serving.instance.get", - "model-serving.instance.list", - "model-serving.token.get", - "model-serving.token.list", - "mongodb-flex.advisor.list", - "mongodb-flex.backup.get", - "mongodb-flex.backup.list", - "mongodb-flex.backup.restore-list", - "mongodb-flex.flavor.list", - "mongodb-flex.instance.get", - "mongodb-flex.instance.list", - "mongodb-flex.metric.get", - "mongodb-flex.storage.list", - "mongodb-flex.user.get", - "mongodb-flex.user.list", - "mongodb-flex.version.get", - "nlb.credentials.get", - "nlb.credentials.list", - "nlb.loadbalancer.get", - "nlb.loadbalancer.list", - "nlb.migration.get", - "nlb.ping", - "nlb.quota.get", - "notebooks.instance.get", - "notebooks.instance.list", - "object-storage.access-key.list", - "object-storage.bucket.list", - "object-storage.credentials-group.list", - "object-storage.service-account.list", - "object-storage.service.list", - "opensearch.credential.get", - "opensearch.credential.list", - "opensearch.instance.get", - "opensearch.instance.list", - "opensearch.offerings.list", - "opensearch.proxy.read", - "postgres-flex.backup.get", - "postgres-flex.backup.list", - "postgres-flex.collation.list", - "postgres-flex.database.get", - "postgres-flex.database.list", - "postgres-flex.flavor.list", - "postgres-flex.instance.get", - "postgres-flex.instance.list", - "postgres-flex.metric.list", - "postgres-flex.metrics-advanced.get", - "postgres-flex.metrics-basic.get", - "postgres-flex.parameter.list", - "postgres-flex.role.list", - "postgres-flex.storage.list", - "postgres-flex.user.get", - "postgres-flex.user.list", - "postgres-flex.version.list", - "private-endpoint.instance.info.list", - "private-endpoint.network-range.get", - "private-endpoint.network-range.list", - "prometheus-proxy.mongodb.metric.get", - "prometheus-proxy.postgres.metric.get", - "prometheus-proxy.sqlserver.metric.get", - "rabbitmq.credential.get", - "rabbitmq.credential.list", - "rabbitmq.instance.get", - "rabbitmq.instance.list", - "rabbitmq.offerings.list", - "rabbitmq.proxy.read", - "redis.credential.get", - "redis.credential.list", - "redis.instance.get", - "redis.instance.list", - "redis.offerings.list", - "redis.proxy.read", - "resource-manager.folder.get", - "resource-manager.folder.list", - "resource-manager.organization.get", - "resource-manager.project.get", - "resource-manager.project.list", - "resource-manager.resource.project.get", - "scf.org.get", - "scf.org.list", - "scf.org.manager.get", - "scf.org.quota.get", - "scf.org.quota.list", - "scf.org.usage.get", - "scf.platform.get", - "scf.platform.list", - "scf.platform.quota.get", - "scf.platform.quota.list", - "scf.space.get", - "scf.space.list", - "secrets-manager.instance.get", - "secrets-manager.instance.list", - "secrets-manager.secret.get", - "secrets-manager.secret.list", - "server-backup.backup.get", - "server-backup.backup.list", - "server-backup.backup-schedule.get", - "server-backup.backup-schedule.list", - "server-backup.policy.list", - "server-backup.service.get", - "server-update.policy.list", - "server-update.service.get", - "server-update.update.get", - "server-update.update.list", - "server-update.update-schedule.get", - "server-update.update-schedule.list", - "service-enablement.service-state.get", - "ske.cluster.get", - "ske.cluster.list", - "sqlserver-flex.backup.get", - "sqlserver-flex.backup.list", - "sqlserver-flex.collation.list", - "sqlserver-flex.compatlevel.list", - "sqlserver-flex.database.get", - "sqlserver-flex.database.list", - "sqlserver-flex.flavor.list", - "sqlserver-flex.instance.get", - "sqlserver-flex.instance.list", - "sqlserver-flex.metric.list", - "sqlserver-flex.restore.list", - "sqlserver-flex.role.list", - "sqlserver-flex.storage.list", - "sqlserver-flex.user.get", - "sqlserver-flex.user.list", - "sqlserver-flex.version.list", - "support.assume-role.read", - "support.ticket.get", - "support.ticket.list", - "support.ticket-attachment.get", - "support.ticket-attachment.list", - "telemetry-link.instance.get", - "telemetry-router.access-token.get", - "telemetry-router.access-token.list", - "telemetry-router.destination.get", - "telemetry-router.destination.list", - "telemetry-router.instance.get", - "telemetry-router.instance.list", - "ufw.instance.list", - "ufw.provider-options.list", - "ufw.services.list", - "vpn.connection.get", - "vpn.connection.list", - "vpn.connection.status.get", - "vpn.gateway.get", - "vpn.gateway.list", - "vpn.gateway.status.get", - "vpn.quota.get", - "workflows.instance.get", - "workflows.instance.list", - ] - }, - ] -} +################## +## CUSTOM ROLES ## +################## -# resource "stackit_authorization_project_custom_role" "this" { -# for_each = { for role in local.custom_roles : role.name => role } +resource "stackit_authorization_project_custom_role" "this" { + for_each = { for role in var.custom_roles : role.name => role } -# resource_id = var.organization_id -# name = each.value.name -# description = each.value.description -# permissions = each.value.permissions -# } \ No newline at end of file + resource_id = var.organization_id + name = each.value.name + description = each.value.description + permissions = each.value.permissions +} \ No newline at end of file diff --git a/modules/governance/3-organization-roles.tf b/modules/governance/3-organization-roles.tf index cf17931..9b4807a 100644 --- a/modules/governance/3-organization-roles.tf +++ b/modules/governance/3-organization-roles.tf @@ -1,6 +1,6 @@ -########################## -## ORG ROLE ASSIGNMENTS ## -########################## +######################################### +## ORGANIZATION LEVEL ROLE ASSIGNMENTS ## +######################################### resource "stackit_authorization_organization_role_assignment" "owner" { for_each = toset(var.organization_owners) diff --git a/modules/governance/4-rm-folders-roles.tf b/modules/governance/4-rm-folders-roles.tf deleted file mode 100644 index 98282bd..0000000 --- a/modules/governance/4-rm-folders-roles.tf +++ /dev/null @@ -1,27 +0,0 @@ -################################ -## RM FOLDER ROLE ASSIGNMENTS ## -################################ - -resource "stackit_authorization_folder_role_assignment" "platform_admins" { - for_each = setsubtract(toset(var.platform_admins), toset(var.organization_owners)) # found a duplicate role assignment - - resource_id = stackit_resourcemanager_folder.platform.folder_id - role = "owner" - subject = each.value -} - -resource "stackit_authorization_folder_role_assignment" "landing_zones_corporate_admins" { - for_each = setsubtract(toset(var.landing_zone_admins), toset(var.organization_owners)) - - resource_id = stackit_resourcemanager_folder.landing_zones_corporate.folder_id - role = "owner" - subject = each.value -} - -resource "stackit_authorization_folder_role_assignment" "landing_zones_public_admins" { - for_each = setsubtract(toset(var.landing_zone_admins), toset(var.organization_owners)) - - resource_id = stackit_resourcemanager_folder.landing_zones_public.folder_id - role = "owner" - subject = each.value -} \ No newline at end of file diff --git a/modules/governance/README.md b/modules/governance/README.md index a8037a7..537d4a2 100644 --- a/modules/governance/README.md +++ b/modules/governance/README.md @@ -4,13 +4,13 @@ | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.10 | -| [stackit](#requirement\_stackit) | 0.83.0 | +| [stackit](#requirement\_stackit) | >=0.88.0 | ### Providers | Name | Version | |------|---------| -| [stackit](#provider\_stackit) | 0.83.0 | +| [stackit](#provider\_stackit) | 0.88.0 | ### Modules @@ -20,33 +20,30 @@ No modules. | Name | Type | |------|------| -| [stackit_authorization_folder_role_assignment.landing_zones_corporate_admins](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_folder_role_assignment) | resource | -| [stackit_authorization_folder_role_assignment.landing_zones_public_admins](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_folder_role_assignment) | resource | -| [stackit_authorization_folder_role_assignment.platform_admins](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_folder_role_assignment) | resource | -| [stackit_authorization_organization_role_assignment.auditor](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_organization_role_assignment) | resource | -| [stackit_authorization_organization_role_assignment.owner](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_organization_role_assignment) | resource | -| [stackit_resourcemanager_folder.landing_zones_corporate](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/resourcemanager_folder) | resource | -| [stackit_resourcemanager_folder.landing_zones_public](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/resourcemanager_folder) | resource | -| [stackit_resourcemanager_folder.platform](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/resourcemanager_folder) | resource | -| [stackit_resourcemanager_folder.sandbox](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/resourcemanager_folder) | resource | +| [stackit_authorization_folder_role_assignment.owners](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_folder_role_assignment) | resource | +| [stackit_authorization_folder_role_assignment.readers](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_folder_role_assignment) | resource | +| [stackit_authorization_organization_role_assignment.auditor](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_organization_role_assignment) | resource | +| [stackit_authorization_organization_role_assignment.owner](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_organization_role_assignment) | resource | +| [stackit_authorization_project_custom_role.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_project_custom_role) | resource | +| [stackit_resourcemanager_folder.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/resourcemanager_folder) | resource | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [company\_name](#input\_company\_name) | Name of the company folder to create. | `string` | n/a | yes | | [organization\_id](#input\_organization\_id) | Container ID of the root folder or organization under which the company folder will be created. | `string` | n/a | yes | | [owner\_email](#input\_owner\_email) | Email address of the owner for the folders. Required for STACKIT resource manager. | `string` | n/a | yes | +| [custom\_roles](#input\_custom\_roles) | List of custom roles to create at the organization level. |
list(object({
name = string
description = string
permissions = list(string)
}))
| `[]` | no | | [labels](#input\_labels) | Additional labels to apply to all folders. | `map(string)` | `{}` | no | -| [landing\_zone\_admins](#input\_landing\_zone\_admins) | List of landing zone administrators with elevated permissions. | `list(string)` | `[]` | no | | [organization\_auditors](#input\_organization\_auditors) | List of organization role assignments for organization auditors. | `list(string)` | `[]` | no | | [organization\_owners](#input\_organization\_owners) | List of organization role assignments for organization owners. | `list(string)` | `[]` | no | -| [platform\_admins](#input\_platform\_admins) | List of platform administrators with elevated permissions. | `list(string)` | `[]` | no | -| [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no | +| [rm\_folders](#input\_rm\_folders) | Map of folder keys to folder configuration. Each folder has a display name and optional lists of owner and reader subjects. |
map(object({
name = string
owner_emails = optional(list(string), [])
reader_emails = optional(list(string), [])
}))
|
{
"landing_zones_corporate": {
"name": "Landing Zones - Corporate",
"owner_emails": [],
"reader_emails": []
},
"landing_zones_public": {
"name": "Landing Zones - Public",
"owner_emails": [],
"reader_emails": []
},
"platform": {
"name": "Platform",
"owner_emails": [],
"reader_emails": []
},
"sandbox": {
"name": "Sandboxes",
"owner_emails": [],
"reader_emails": []
}
}
| no | ### Outputs | Name | Description | |------|-------------| -| [folder\_container\_ids](#output\_folder\_container\_ids) | Map of all folder names to their container IDs for easy reference | +| [custom\_role\_ids](#output\_custom\_role\_ids) | Map of custom role names to their role IDs | +| [folder\_container\_ids](#output\_folder\_container\_ids) | Map of all folder keys to their container IDs for easy reference | +| [organization\_role\_assignments](#output\_organization\_role\_assignments) | Map of organization-level role assignment subjects grouped by role | \ No newline at end of file diff --git a/modules/governance/main.tf b/modules/governance/main.tf deleted file mode 100644 index 821fe16..0000000 --- a/modules/governance/main.tf +++ /dev/null @@ -1,20 +0,0 @@ -terraform { - required_version = ">= 1.10" - - required_providers { - stackit = { - source = "stackitcloud/stackit" - version = "0.85.0" - } - } -} - -provider "stackit" { - default_region = var.region - enable_beta_resources = true - experiments = ["iam", "routing-tables", "network"] -} - -locals { - labels = length(var.labels) > 0 ? var.labels : null # provider bug: empty map becomes null after apply -} \ No newline at end of file diff --git a/modules/governance/outputs.tf b/modules/governance/outputs.tf index fdc8697..529d63d 100644 --- a/modules/governance/outputs.tf +++ b/modules/governance/outputs.tf @@ -1,10 +1,20 @@ output "folder_container_ids" { - description = "Map of all folder names to their container IDs for easy reference" + description = "Map of all folder keys to their container IDs for easy reference" + value = merge( + { root = var.organization_id }, + { for k, f in stackit_resourcemanager_folder.this : k => f.container_id } + ) +} + +output "custom_role_ids" { + description = "Map of custom role names to their role IDs" + value = { for k, r in stackit_authorization_project_custom_role.this : k => r.role_id } +} + +output "organization_role_assignments" { + description = "Map of organization-level role assignment subjects grouped by role" value = { - root = var.organization_id - platform = stackit_resourcemanager_folder.platform.container_id - landing_zones_public = stackit_resourcemanager_folder.landing_zones_public.container_id - landing_zones_corporate = stackit_resourcemanager_folder.landing_zones_corporate.container_id - sandbox = stackit_resourcemanager_folder.sandbox.container_id + owners = { for k, r in stackit_authorization_organization_role_assignment.owner : k => r.subject } + auditors = { for k, r in stackit_authorization_organization_role_assignment.auditor : k => r.subject } } } \ No newline at end of file diff --git a/modules/governance/terraform.tf b/modules/governance/terraform.tf new file mode 100644 index 0000000..33d151a --- /dev/null +++ b/modules/governance/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.10" + + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = ">=0.88.0" + } + } +} \ No newline at end of file diff --git a/modules/governance/variables.tf b/modules/governance/variables.tf index 3fa2bf6..61d6ed7 100644 --- a/modules/governance/variables.tf +++ b/modules/governance/variables.tf @@ -1,16 +1,42 @@ -variable "owner_email" { - type = string - description = "Email address of the owner for the folders. Required for STACKIT resource manager." -} - -variable "company_name" { - type = string - description = "Name of the company folder to create." +variable "custom_roles" { + type = list(object({ + name = string + description = string + permissions = list(string) + })) + description = "List of custom roles to create at the organization level." + default = [] } -variable "organization_id" { - type = string - description = "Container ID of the root folder or organization under which the company folder will be created." +variable "rm_folders" { + type = map(object({ + name = string + owner_emails = optional(list(string), []) + reader_emails = optional(list(string), []) + })) + description = "Map of folder keys to folder configuration. Each folder has a display name and optional lists of owner and reader subjects." + default = { + platform = { + name = "Platform" + owner_emails = [] + reader_emails = [] + } + landing_zones_corporate = { + name = "Landing Zones - Corporate" + owner_emails = [] + reader_emails = [] + } + landing_zones_public = { + name = "Landing Zones - Public" + owner_emails = [] + reader_emails = [] + } + sandbox = { + name = "Sandboxes" + owner_emails = [] + reader_emails = [] + } + } } variable "labels" { @@ -19,32 +45,24 @@ variable "labels" { default = {} } -variable "region" { - type = string - description = "STACKIT region for regional resources." - default = "eu01" -} - -variable "organization_owners" { - type = list(string) - description = "List of organization role assignments for organization owners." - default = [] -} - variable "organization_auditors" { type = list(string) description = "List of organization role assignments for organization auditors." default = [] } -variable "platform_admins" { - type = list(string) - description = "List of platform administrators with elevated permissions." - default = [] +variable "organization_id" { + type = string + description = "Container ID of the root folder or organization under which the company folder will be created." } -variable "landing_zone_admins" { +variable "organization_owners" { type = list(string) - description = "List of landing zone administrators with elevated permissions." + description = "List of organization role assignments for organization owners." default = [] +} + +variable "owner_email" { + type = string + description = "Email address of the owner for the folders. Required for STACKIT resource manager." } \ No newline at end of file diff --git a/modules/landing-zone/1-project.tf b/modules/landing-zone/1-project.tf index 665d7ae..e08652e 100644 --- a/modules/landing-zone/1-project.tf +++ b/modules/landing-zone/1-project.tf @@ -10,9 +10,9 @@ locals { labels = length(local.project_labels) > 0 ? local.project_labels : null # provider bug: empty map becomes null after apply } -resource "stackit_resourcemanager_project" "project" { +resource "stackit_resourcemanager_project" "this" { parent_container_id = var.parent_container_id - name = local.naming_pattern + name = var.project_name != null ? var.project_name : var.naming_pattern owner_email = var.owner_email labels = local.labels diff --git a/modules/landing-zone/2-rbac.tf b/modules/landing-zone/2-rbac.tf index 80daf48..4f597c8 100644 --- a/modules/landing-zone/2-rbac.tf +++ b/modules/landing-zone/2-rbac.tf @@ -5,7 +5,7 @@ resource "stackit_authorization_project_custom_role" "this" { for_each = { for role in var.custom_roles : role.name => role } - resource_id = stackit_resourcemanager_project.project.project_id + resource_id = stackit_resourcemanager_project.this.project_id name = each.value.name description = each.value.description permissions = each.value.permissions @@ -15,10 +15,10 @@ resource "stackit_authorization_project_custom_role" "this" { ## ROLE ASSIGNMENTS ## ###################### -resource "stackit_authorization_project_role_assignment" "assignments" { +resource "stackit_authorization_project_role_assignment" "this" { for_each = { for assignment in var.role_assignments : "${assignment.role}-${assignment.subject}" => assignment } - resource_id = stackit_resourcemanager_project.project.project_id + resource_id = stackit_resourcemanager_project.this.project_id role = each.value.role subject = each.value.subject @@ -26,7 +26,7 @@ resource "stackit_authorization_project_role_assignment" "assignments" { } resource "stackit_authorization_project_role_assignment" "sa_owner" { - resource_id = stackit_resourcemanager_project.project.project_id + resource_id = stackit_resourcemanager_project.this.project_id role = "owner" subject = stackit_service_account.automation.email } \ No newline at end of file diff --git a/modules/landing-zone/3-network.tf b/modules/landing-zone/3-network.tf index 48d9816..6ace280 100644 --- a/modules/landing-zone/3-network.tf +++ b/modules/landing-zone/3-network.tf @@ -1,11 +1,12 @@ ############# ## NETWORK ## ############# + resource "stackit_network" "this" { count = var.network_area_id != null ? 1 : 0 - project_id = stackit_resourcemanager_project.project.project_id - name = "${local.naming_pattern}-routed" + project_id = stackit_resourcemanager_project.this.project_id + name = "${var.naming_pattern}-routed" ipv4_prefix_length = var.network_prefix_length routed = true } \ No newline at end of file diff --git a/modules/landing-zone/4-secrets-manager.tf b/modules/landing-zone/4-secrets-manager.tf index e2ee01c..342816e 100644 --- a/modules/landing-zone/4-secrets-manager.tf +++ b/modules/landing-zone/4-secrets-manager.tf @@ -3,13 +3,13 @@ ##################### resource "stackit_secretsmanager_instance" "this" { - project_id = stackit_resourcemanager_project.project.project_id - name = "${local.naming_pattern}-default" + project_id = stackit_resourcemanager_project.this.project_id + name = "${var.naming_pattern}-default" # acls = length(var.secretsmanager_config.acls) > 0 ? var.secretsmanager_config.acls : null } resource "stackit_secretsmanager_user" "default" { - project_id = stackit_resourcemanager_project.project.project_id + project_id = stackit_resourcemanager_project.this.project_id instance_id = stackit_secretsmanager_instance.this.instance_id description = "Default user for accessing the Secrets Manager" write_enabled = true diff --git a/modules/landing-zone/5-bucket.tf b/modules/landing-zone/5-bucket.tf index 6711145..5f9733e 100644 --- a/modules/landing-zone/5-bucket.tf +++ b/modules/landing-zone/5-bucket.tf @@ -3,13 +3,13 @@ #################### resource "stackit_objectstorage_bucket" "default" { - name = "${local.naming_pattern}-default" - project_id = stackit_resourcemanager_project.project.project_id + name = "${var.naming_pattern}-default" + project_id = stackit_resourcemanager_project.this.project_id } resource "stackit_objectstorage_bucket" "tfstate" { - name = "${local.naming_pattern}-tfstate" - project_id = stackit_resourcemanager_project.project.project_id + name = "${var.naming_pattern}-tfstate" + project_id = stackit_resourcemanager_project.this.project_id depends_on = [ stackit_objectstorage_bucket.default, # "project.create_conflict","msg":"Two concurrent calls try to create the same project"}]} @@ -17,8 +17,8 @@ resource "stackit_objectstorage_bucket" "tfstate" { } resource "stackit_objectstorage_credentials_group" "this" { - project_id = stackit_resourcemanager_project.project.project_id - name = local.naming_pattern + project_id = stackit_resourcemanager_project.this.project_id + name = var.naming_pattern depends_on = [ stackit_objectstorage_bucket.default, @@ -27,19 +27,19 @@ resource "stackit_objectstorage_credentials_group" "this" { } resource "stackit_objectstorage_credential" "this" { - project_id = stackit_resourcemanager_project.project.project_id + project_id = stackit_resourcemanager_project.this.project_id credentials_group_id = stackit_objectstorage_credentials_group.this.credentials_group_id } -resource "vault_kv_secret_v2" "object_storage_credentials" { - mount = stackit_secretsmanager_instance.this.instance_id - name = "service_account_key_${stackit_service_account.automation.name}" - cas = 1 - delete_all_versions = true - data_json = jsonencode( - { - ACCESS_KEY = stackit_objectstorage_credential.this.access_key, - SECRET_ACCESS_KEY = stackit_objectstorage_credential.this.secret_access_key - } - ) -} \ No newline at end of file +# resource "vault_kv_secret_v2" "object_storage_credentials" { +# mount = stackit_secretsmanager_instance.this.instance_id +# name = "service_account_key_${stackit_service_account.automation.name}" +# cas = 1 +# delete_all_versions = true +# data_json = jsonencode( +# { +# ACCESS_KEY = stackit_objectstorage_credential.this.access_key, +# SECRET_ACCESS_KEY = stackit_objectstorage_credential.this.secret_access_key +# } +# ) +# } \ No newline at end of file diff --git a/modules/landing-zone/6-service-account.tf b/modules/landing-zone/6-service-account.tf index 0d8ddf2..07fbddb 100644 --- a/modules/landing-zone/6-service-account.tf +++ b/modules/landing-zone/6-service-account.tf @@ -3,8 +3,8 @@ ##################### resource "stackit_service_account" "automation" { - project_id = stackit_resourcemanager_project.project.project_id - name = substr(replace("${local.naming_pattern}-automation", "-", ""), 0, 20) + project_id = stackit_resourcemanager_project.this.project_id + name = substr(replace("${var.naming_pattern}-automation", "-", ""), 0, 20) } resource "time_rotating" "key_rotate" { @@ -12,7 +12,7 @@ resource "time_rotating" "key_rotate" { } resource "stackit_service_account_key" "automation" { - project_id = stackit_resourcemanager_project.project.project_id + project_id = stackit_resourcemanager_project.this.project_id service_account_email = stackit_service_account.automation.email ttl_days = 90 diff --git a/modules/landing-zone/7-kubernetes.tf b/modules/landing-zone/7-kubernetes.tf deleted file mode 100644 index 8fa0e14..0000000 --- a/modules/landing-zone/7-kubernetes.tf +++ /dev/null @@ -1,25 +0,0 @@ -######################### -## KUBERNETES CLUSTERS ## -######################### - -resource "stackit_ske_cluster" "this" { - for_each = var.kubernetes_clusters - - project_id = stackit_resourcemanager_project.project.project_id - name = each.key - kubernetes_version_min = each.value.kubernetes_version - node_pools = each.value.node_pools - - maintenance = { - enable_kubernetes_version_updates = each.value.enable_kubernetes_version_updates - enable_machine_image_version_updates = each.value.enable_machine_image_version_updates - start = "01:00:00Z" - end = "02:00:00Z" - } - - hibernations = each.value.hibernations - - extensions = each.value.extensions - - network = var.network_area_id != null ? { id = stackit_network.this[0].network_id } : null -} \ No newline at end of file diff --git a/modules/landing-zone/README.md b/modules/landing-zone/README.md index 33daafe..21d0012 100644 --- a/modules/landing-zone/README.md +++ b/modules/landing-zone/README.md @@ -4,17 +4,15 @@ | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5 | -| [stackit](#requirement\_stackit) | 0.83.0 | -| [time](#requirement\_time) | 0.13.1 | -| [vault](#requirement\_vault) | 5.6.0 | +| [stackit](#requirement\_stackit) | >=0.88.0 | +| [time](#requirement\_time) | >=0.13.1 | ### Providers | Name | Version | |------|---------| -| [stackit](#provider\_stackit) | 0.83.0 | +| [stackit](#provider\_stackit) | 0.88.0 | | [time](#provider\_time) | 0.13.1 | -| [vault](#provider\_vault) | 5.6.0 | ### Modules @@ -24,40 +22,33 @@ No modules. | Name | Type | |------|------| -| [stackit_authorization_project_custom_role.this](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_project_custom_role) | resource | -| [stackit_authorization_project_role_assignment.assignments](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_project_role_assignment) | resource | -| [stackit_authorization_project_role_assignment.sa_owner](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_project_role_assignment) | resource | -| [stackit_network.this](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/network) | resource | -| [stackit_objectstorage_bucket.default](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/objectstorage_bucket) | resource | -| [stackit_objectstorage_bucket.tfstate](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/objectstorage_bucket) | resource | -| [stackit_objectstorage_credential.this](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/objectstorage_credential) | resource | -| [stackit_objectstorage_credentials_group.this](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/objectstorage_credentials_group) | resource | -| [stackit_resourcemanager_project.project](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/resourcemanager_project) | resource | -| [stackit_secretsmanager_instance.this](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/secretsmanager_instance) | resource | -| [stackit_secretsmanager_user.default](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/secretsmanager_user) | resource | -| [stackit_service_account.automation](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/service_account) | resource | -| [stackit_service_account_key.automation](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/service_account_key) | resource | -| [stackit_ske_cluster.this](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/ske_cluster) | resource | -| [time_rotating.key_rotate](https://registry.terraform.io/providers/hashicorp/time/0.13.1/docs/resources/rotating) | resource | -| [vault_kv_secret_v2.object_storage_credentials](https://registry.terraform.io/providers/hashicorp/vault/5.6.0/docs/resources/kv_secret_v2) | resource | -| [vault_kv_secret_v2.service_account_key_automation](https://registry.terraform.io/providers/hashicorp/vault/5.6.0/docs/resources/kv_secret_v2) | resource | +| [stackit_authorization_project_custom_role.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_project_custom_role) | resource | +| [stackit_authorization_project_role_assignment.sa_owner](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_project_role_assignment) | resource | +| [stackit_authorization_project_role_assignment.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_project_role_assignment) | resource | +| [stackit_network.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/network) | resource | +| [stackit_objectstorage_bucket.default](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/objectstorage_bucket) | resource | +| [stackit_objectstorage_bucket.tfstate](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/objectstorage_bucket) | resource | +| [stackit_objectstorage_credential.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/objectstorage_credential) | resource | +| [stackit_objectstorage_credentials_group.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/objectstorage_credentials_group) | resource | +| [stackit_resourcemanager_project.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/resourcemanager_project) | resource | +| [stackit_secretsmanager_instance.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/secretsmanager_instance) | resource | +| [stackit_secretsmanager_user.default](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/secretsmanager_user) | resource | +| [stackit_service_account.automation](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/service_account) | resource | +| [stackit_service_account_key.automation](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/service_account_key) | resource | +| [time_rotating.key_rotate](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/rotating) | resource | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [company\_code](#input\_company\_code) | Company code used in resource naming conventions. | `string` | n/a | yes | | [custom\_roles](#input\_custom\_roles) | List of custom roles to create for the project. |
list(object({
name = string
description = string
permissions = list(string)
}))
| n/a | yes | +| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-net-prod". | `string` | n/a | yes | | [owner\_email](#input\_owner\_email) | Email address of the project owner. Required for project creation. | `string` | n/a | yes | | [parent\_container\_id](#input\_parent\_container\_id) | Parent container ID (folder or organization) where the project will be created. | `string` | n/a | yes | -| [project\_code](#input\_project\_code) | Optional project code for the STACKIT project. | `string` | n/a | yes | -| [project\_name](#input\_project\_name) | Name of the STACKIT project to create. | `string` | n/a | yes | -| [env](#input\_env) | Environment identifier (e.g., dev, staging, prod) used in resource naming conventions. | `string` | `"dev"` | no | -| [kubernetes\_clusters](#input\_kubernetes\_clusters) | Map of Kubernetes clusters to create. The key is used as a suffix for the cluster name. |
map(object({
kubernetes_version = string
enable_kubernetes_version_updates = optional(bool, true)
enable_machine_image_version_updates = optional(bool, true)
hibernations = optional(list(object({
start = string
end = string
timezone = optional(string, "Europe/Berlin")
})), [])
node_pools = list(object({
name = string
machine_type = string
availability_zones = list(string)
os_version_min = optional(string)
minimum = number
maximum = number
max_surge = optional(number)
max_unavailable = optional(number)
labels = optional(map(string))
taints = optional(list(object({
key = string
value = string
effect = string
})))
}))
extensions = optional(object({
acl = optional(object({
allowed_cidrs = list(string)
enabled = bool
}))
dns = optional(object({
enabled = bool
zones = optional(list(string))
}))
observability = optional(object({
enabled = bool
instance_id = optional(string)
}))
}))
}))
| `{}` | no | | [labels](#input\_labels) | Additional labels to apply to all resources. | `map(string)` | `{}` | no | | [network\_area\_id](#input\_network\_area\_id) | Network Area ID to deploy resources into. Required if network is enabled. | `string` | `null` | no | | [network\_prefix\_length](#input\_network\_prefix\_length) | CIDR block prefix length for the project's network range. | `number` | `null` | no | -| [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no | +| [project\_name](#input\_project\_name) | Name of the STACKIT project to create. | `string` | `null` | no | | [role\_assignments](#input\_role\_assignments) | List of role assignments for the project. Subject can be a user email or service account email. |
list(object({
role = string
subject = string
}))
| `[]` | no | ### Outputs diff --git a/modules/landing-zone/main.tf b/modules/landing-zone/main.tf deleted file mode 100644 index a37f38c..0000000 --- a/modules/landing-zone/main.tf +++ /dev/null @@ -1,38 +0,0 @@ -terraform { - required_version = ">= 1.5" - required_providers { - stackit = { - source = "stackitcloud/stackit" - version = "0.85.0" - } - time = { - source = "hashicorp/time" - version = "0.13.1" - } - # vault = { - # source = "hashicorp/vault" - # version = "5.7.0" - # } - } -} - -# provider "stackit" { -# default_region = var.region -# enable_beta_resources = true -# experiments = ["iam", "routing-tables", "network"] -# } - -# provider "vault" { -# address = "https://prod.sm.eu01.stackit.cloud" -# skip_child_token = true -# -# auth_login_userpass { -# username = stackit_secretsmanager_user.default.username -# password = stackit_secretsmanager_user.default.password -# } -# } - -locals { - lz_type = var.network_area_id == null ? "public" : "corp" - naming_pattern = "${var.company_code}-${local.lz_type}-${var.project_code}-${var.env}" -} \ No newline at end of file diff --git a/modules/landing-zone/outputs.tf b/modules/landing-zone/outputs.tf index b64c87f..d88bbcb 100644 --- a/modules/landing-zone/outputs.tf +++ b/modules/landing-zone/outputs.tf @@ -1,14 +1,14 @@ -output "project_id" { - description = "The project ID of the created STACKIT project." - value = stackit_resourcemanager_project.project.project_id -} - output "project_container_id" { description = "The container ID of the created STACKIT project." - value = stackit_resourcemanager_project.project.container_id + value = stackit_resourcemanager_project.this.container_id +} + +output "project_id" { + description = "The project ID of the created STACKIT project." + value = stackit_resourcemanager_project.this.project_id } output "project_name" { description = "The name of the created STACKIT project." - value = stackit_resourcemanager_project.project.name + value = stackit_resourcemanager_project.this.name } \ No newline at end of file diff --git a/modules/landing-zone/terraform.tf b/modules/landing-zone/terraform.tf new file mode 100644 index 0000000..55e5a55 --- /dev/null +++ b/modules/landing-zone/terraform.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.5" + + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = ">=0.88.0" + } + time = { + source = "hashicorp/time" + version = ">=0.13.1" + } + } +} \ No newline at end of file diff --git a/modules/landing-zone/variables.tf b/modules/landing-zone/variables.tf index 6a3350c..1f52b6b 100644 --- a/modules/landing-zone/variables.tf +++ b/modules/landing-zone/variables.tf @@ -1,21 +1,33 @@ -variable "project_name" { - type = string - description = "Name of the STACKIT project to create." +variable "custom_roles" { + type = list(object({ + name = string + description = string + permissions = list(string) + })) + description = "List of custom roles to create for the project." } -variable "project_code" { +variable "naming_pattern" { type = string - description = "Optional project code for the STACKIT project." + description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-net-prod\"." } -variable "company_code" { - type = string - description = "Company code used in resource naming conventions." +variable "labels" { + type = map(string) + description = "Additional labels to apply to all resources." + default = {} } -variable "parent_container_id" { +variable "network_area_id" { type = string - description = "Parent container ID (folder or organization) where the project will be created." + description = "Network Area ID to deploy resources into. Required if network is enabled." + default = null +} + +variable "network_prefix_length" { + type = number + description = "CIDR block prefix length for the project's network range." + default = null } variable "owner_email" { @@ -23,30 +35,17 @@ variable "owner_email" { description = "Email address of the project owner. Required for project creation." } -variable "labels" { - type = map(string) - description = "Additional labels to apply to all resources." - default = {} -} - -variable "region" { +variable "parent_container_id" { type = string - description = "STACKIT region for regional resources." - default = "eu01" + description = "Parent container ID (folder or organization) where the project will be created." } -variable "network_area_id" { +variable "project_name" { type = string - description = "Network Area ID to deploy resources into. Required if network is enabled." + description = "Name of the STACKIT project to create." default = null } -variable "env" { - type = string - description = "Environment identifier (e.g., dev, staging, prod) used in resource naming conventions." - default = "dev" -} - variable "role_assignments" { type = list(object({ role = string @@ -54,73 +53,4 @@ variable "role_assignments" { })) description = "List of role assignments for the project. Subject can be a user email or service account email." default = [] -} - -variable "network_prefix_length" { - type = number - description = "CIDR block prefix length for the project's network range." - default = null -} - -variable "kubernetes_clusters" { - type = map(object({ - kubernetes_version = string - enable_kubernetes_version_updates = optional(bool, true) - enable_machine_image_version_updates = optional(bool, true) - hibernations = optional(list(object({ - start = string - end = string - timezone = optional(string, "Europe/Berlin") - })), []) - node_pools = list(object({ - name = string - machine_type = string - availability_zones = list(string) - os_version_min = optional(string) - minimum = number - maximum = number - max_surge = optional(number) - max_unavailable = optional(number) - labels = optional(map(string)) - taints = optional(list(object({ - key = string - value = string - effect = string - }))) - })) - extensions = optional(object({ - acl = optional(object({ - allowed_cidrs = list(string) - enabled = bool - })) - dns = optional(object({ - enabled = bool - zones = optional(list(string)) - })) - observability = optional(object({ - enabled = bool - instance_id = optional(string) - })) - })) - })) - description = "Map of Kubernetes clusters to create. The key is used as a suffix for the cluster name." - default = {} - - validation { - condition = alltrue(flatten([ - for cluster in values(var.kubernetes_clusters) : [ - for node_pool in cluster.node_pools : can(regex("^[a-z][0-9]+\\.[0-9]+$", node_pool.machine_type)) - ] - ])) - error_message = "Each node_pools[*].machine_type must match STACKIT machine type format (e.g. c1.2). Validate available flavors with: stackit server machine-type list" - } -} - -variable "custom_roles" { - type = list(object({ - name = string - description = string - permissions = list(string) - })) - description = "List of custom roles to create for the project." } \ No newline at end of file diff --git a/modules/management/1-project.tf b/modules/management/1-project.tf index be03901..8e6a60f 100644 --- a/modules/management/1-project.tf +++ b/modules/management/1-project.tf @@ -2,17 +2,17 @@ ## PROJECT ## ############# -resource "stackit_resourcemanager_project" "project" { +resource "stackit_resourcemanager_project" "this" { parent_container_id = var.parent_container_id - name = local.naming_pattern + name = var.project_name != null ? var.project_name : var.naming_pattern owner_email = var.owner_email labels = length(var.labels) > 0 ? var.labels : null # provider bug: empty map becomes null after apply } -resource "stackit_authorization_project_role_assignment" "assignments" { +resource "stackit_authorization_project_role_assignment" "this" { for_each = { for assignment in var.role_assignments : "${assignment.role}-${assignment.subject}" => assignment } - resource_id = stackit_resourcemanager_project.project.project_id + resource_id = stackit_resourcemanager_project.this.project_id role = each.value.role subject = each.value.subject } \ No newline at end of file diff --git a/modules/management/2-secrets-manager.tf b/modules/management/2-secrets-manager.tf index e2ee01c..342816e 100644 --- a/modules/management/2-secrets-manager.tf +++ b/modules/management/2-secrets-manager.tf @@ -3,13 +3,13 @@ ##################### resource "stackit_secretsmanager_instance" "this" { - project_id = stackit_resourcemanager_project.project.project_id - name = "${local.naming_pattern}-default" + project_id = stackit_resourcemanager_project.this.project_id + name = "${var.naming_pattern}-default" # acls = length(var.secretsmanager_config.acls) > 0 ? var.secretsmanager_config.acls : null } resource "stackit_secretsmanager_user" "default" { - project_id = stackit_resourcemanager_project.project.project_id + project_id = stackit_resourcemanager_project.this.project_id instance_id = stackit_secretsmanager_instance.this.instance_id description = "Default user for accessing the Secrets Manager" write_enabled = true diff --git a/modules/management/3-bucket.tf b/modules/management/3-bucket.tf index 84dcb8b..0c57f03 100644 --- a/modules/management/3-bucket.tf +++ b/modules/management/3-bucket.tf @@ -3,13 +3,13 @@ #################### resource "stackit_objectstorage_bucket" "default" { - name = "${local.naming_pattern}-default" - project_id = stackit_resourcemanager_project.project.project_id + name = "${var.naming_pattern}-default" + project_id = stackit_resourcemanager_project.this.project_id } resource "stackit_objectstorage_bucket" "tfstate" { - name = "${local.naming_pattern}-tfstate" - project_id = stackit_resourcemanager_project.project.project_id + name = "${var.naming_pattern}-tfstate" + project_id = stackit_resourcemanager_project.this.project_id depends_on = [ stackit_objectstorage_bucket.default, # "project.create_conflict","msg":"Two concurrent calls try to create the same project"}]} @@ -17,8 +17,8 @@ resource "stackit_objectstorage_bucket" "tfstate" { } resource "stackit_objectstorage_credentials_group" "this" { - project_id = stackit_resourcemanager_project.project.project_id - name = local.naming_pattern + project_id = stackit_resourcemanager_project.this.project_id + name = var.naming_pattern depends_on = [ stackit_objectstorage_bucket.default, @@ -32,19 +32,19 @@ moved { } resource "stackit_objectstorage_credential" "this" { - project_id = stackit_resourcemanager_project.project.project_id + project_id = stackit_resourcemanager_project.this.project_id credentials_group_id = stackit_objectstorage_credentials_group.this.credentials_group_id } -resource "vault_kv_secret_v2" "object_storage_credentials" { - mount = stackit_secretsmanager_instance.this.instance_id - name = "service_account_key_${stackit_service_account.automation.name}" - cas = 1 - delete_all_versions = true - data_json = jsonencode( - { - ACCESS_KEY = stackit_objectstorage_credential.this.access_key, - SECRET_ACCESS_KEY = stackit_objectstorage_credential.this.secret_access_key - } - ) -} \ No newline at end of file +# resource "vault_kv_secret_v2" "object_storage_credentials" { +# mount = stackit_secretsmanager_instance.this.instance_id +# name = "service_account_key_${stackit_service_account.automation.name}" +# cas = 1 +# delete_all_versions = true +# data_json = jsonencode( +# { +# ACCESS_KEY = stackit_objectstorage_credential.this.access_key, +# SECRET_ACCESS_KEY = stackit_objectstorage_credential.this.secret_access_key +# } +# ) +# } \ No newline at end of file diff --git a/modules/management/4-service-account.tf b/modules/management/4-service-account.tf index 10f0374..e8851bf 100644 --- a/modules/management/4-service-account.tf +++ b/modules/management/4-service-account.tf @@ -3,8 +3,8 @@ ##################### resource "stackit_service_account" "automation" { - project_id = stackit_resourcemanager_project.project.project_id - name = substr(replace("${local.naming_pattern}-automation", "-", ""), 0, 20) + project_id = stackit_resourcemanager_project.this.project_id + name = substr(replace("${var.naming_pattern}-automation", "-", ""), 0, 20) } resource "time_rotating" "key_rotate" { @@ -12,7 +12,7 @@ resource "time_rotating" "key_rotate" { } resource "stackit_service_account_key" "automation" { - project_id = stackit_resourcemanager_project.project.project_id + project_id = stackit_resourcemanager_project.this.project_id service_account_email = stackit_service_account.automation.email ttl_days = 90 @@ -27,10 +27,10 @@ resource "stackit_authorization_organization_role_assignment" "sa_owner" { subject = stackit_service_account.automation.email } -resource "vault_kv_secret_v2" "service_account_key_automation" { - mount = stackit_secretsmanager_instance.this.instance_id - name = "service_account_key_${stackit_service_account.automation.name}" - cas = 1 - delete_all_versions = true - data_json = stackit_service_account_key.automation.json -} \ No newline at end of file +# resource "vault_kv_secret_v2" "service_account_key_automation" { +# mount = stackit_secretsmanager_instance.this.instance_id +# name = "service_account_key_${stackit_service_account.automation.name}" +# cas = 1 +# delete_all_versions = true +# data_json = stackit_service_account_key.automation.json +# } \ No newline at end of file diff --git a/modules/management/README.md b/modules/management/README.md index df30630..c2094cd 100644 --- a/modules/management/README.md +++ b/modules/management/README.md @@ -4,17 +4,15 @@ | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.10 | -| [stackit](#requirement\_stackit) | 0.83.0 | -| [time](#requirement\_time) | 0.13.1 | -| [vault](#requirement\_vault) | 5.6.0 | +| [stackit](#requirement\_stackit) | >=0.88.0 | +| [time](#requirement\_time) | >=0.13.1 | ### Providers | Name | Version | |------|---------| -| [stackit](#provider\_stackit) | 0.83.0 | +| [stackit](#provider\_stackit) | 0.88.0 | | [time](#provider\_time) | 0.13.1 | -| [vault](#provider\_vault) | 5.6.0 | ### Modules @@ -24,36 +22,29 @@ No modules. | Name | Type | |------|------| -| [stackit_authorization_organization_role_assignment.sa_owner](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_organization_role_assignment) | resource | -| [stackit_authorization_project_role_assignment.assignments](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_project_role_assignment) | resource | -| [stackit_objectstorage_bucket.default](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/objectstorage_bucket) | resource | -| [stackit_objectstorage_bucket.tfstate](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/objectstorage_bucket) | resource | -| [stackit_objectstorage_credential.this](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/objectstorage_credential) | resource | -| [stackit_objectstorage_credentials_group.this](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/objectstorage_credentials_group) | resource | -| [stackit_resourcemanager_project.project](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/resourcemanager_project) | resource | -| [stackit_secretsmanager_instance.this](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/secretsmanager_instance) | resource | -| [stackit_secretsmanager_user.default](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/secretsmanager_user) | resource | -| [stackit_service_account.automation](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/service_account) | resource | -| [stackit_service_account_key.automation](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/service_account_key) | resource | -| [time_rotating.key_rotate](https://registry.terraform.io/providers/hashicorp/time/0.13.1/docs/resources/rotating) | resource | -| [vault_kv_secret_v2.object_storage_credentials](https://registry.terraform.io/providers/hashicorp/vault/5.6.0/docs/resources/kv_secret_v2) | resource | -| [vault_kv_secret_v2.service_account_key_automation](https://registry.terraform.io/providers/hashicorp/vault/5.6.0/docs/resources/kv_secret_v2) | resource | +| [stackit_authorization_organization_role_assignment.sa_owner](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_organization_role_assignment) | resource | +| [stackit_authorization_project_role_assignment.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_project_role_assignment) | resource | +| [stackit_objectstorage_bucket.default](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/objectstorage_bucket) | resource | +| [stackit_objectstorage_bucket.tfstate](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/objectstorage_bucket) | resource | +| [stackit_objectstorage_credential.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/objectstorage_credential) | resource | +| [stackit_objectstorage_credentials_group.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/objectstorage_credentials_group) | resource | +| [stackit_resourcemanager_project.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/resourcemanager_project) | resource | +| [stackit_secretsmanager_instance.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/secretsmanager_instance) | resource | +| [stackit_secretsmanager_user.default](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/secretsmanager_user) | resource | +| [stackit_service_account.automation](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/service_account) | resource | +| [stackit_service_account_key.automation](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/service_account_key) | resource | +| [time_rotating.key_rotate](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/rotating) | resource | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [company\_code](#input\_company\_code) | Company code used in resource naming conventions. | `string` | n/a | yes | +| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-net-prod". | `string` | n/a | yes | | [organization\_id](#input\_organization\_id) | Container ID of the root folder or organization under which the company folder will be created. | `string` | n/a | yes | | [owner\_email](#input\_owner\_email) | Email address of the owner for the folders. Required for STACKIT resource manager. | `string` | n/a | yes | | [parent\_container\_id](#input\_parent\_container\_id) | Parent container ID (folder or organization) where the project will be created. | `string` | n/a | yes | -| [project\_code](#input\_project\_code) | Optional project code for the STACKIT project. | `string` | n/a | yes | -| [allowed\_network\_ranges](#input\_allowed\_network\_ranges) | List of allowed network ranges for Git instance ACL. | `list(string)` |
[
"0.0.0.0/0"
]
| no | -| [env](#input\_env) | Environment identifier (e.g., dev, staging, prod) used in resource naming conventions. | `string` | `"dev"` | no | | [labels](#input\_labels) | Additional labels to apply to all folders. | `map(string)` | `{}` | no | -| [organization\_auditors](#input\_organization\_auditors) | List of organization role assignments for organization auditors. | `list(string)` | `[]` | no | -| [organization\_owners](#input\_organization\_owners) | List of organization role assignments for organization owners. | `list(string)` | `[]` | no | -| [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no | +| [project\_name](#input\_project\_name) | Name of the STACKIT project to create. | `string` | `null` | no | | [role\_assignments](#input\_role\_assignments) | List of role assignments for the project. Subject can be a user email or service account email. |
list(object({
role = string
subject = string
}))
| `[]` | no | ### Outputs diff --git a/modules/management/main.tf b/modules/management/main.tf deleted file mode 100644 index 3c9c8a9..0000000 --- a/modules/management/main.tf +++ /dev/null @@ -1,38 +0,0 @@ -terraform { - required_version = ">= 1.10" - - required_providers { - stackit = { - source = "stackitcloud/stackit" - version = "0.85.0" - } - time = { - source = "hashicorp/time" - version = "0.13.1" - } - vault = { - source = "hashicorp/vault" - version = "5.7.0" - } - } -} - -provider "stackit" { - default_region = var.region - enable_beta_resources = true - experiments = ["iam", "routing-tables", "network"] -} - -provider "vault" { - address = "https://prod.sm.eu01.stackit.cloud" - skip_child_token = true - - auth_login_userpass { - username = stackit_secretsmanager_user.default.username - password = stackit_secretsmanager_user.default.password - } -} - -locals { - naming_pattern = "${var.company_code}-pltfm-${var.project_code}-${var.env}" -} \ No newline at end of file diff --git a/modules/management/outputs.tf b/modules/management/outputs.tf index 2dab2e5..d79d4ce 100644 --- a/modules/management/outputs.tf +++ b/modules/management/outputs.tf @@ -1,16 +1,16 @@ -output "project_id" { - description = "The project ID of the created STACKIT project." - value = stackit_resourcemanager_project.project.project_id -} - output "project_container_id" { description = "The container ID of the created STACKIT project." - value = stackit_resourcemanager_project.project.container_id + value = stackit_resourcemanager_project.this.container_id +} + +output "project_id" { + description = "The project ID of the created STACKIT project." + value = stackit_resourcemanager_project.this.project_id } output "project_name" { description = "The name of the created STACKIT project." - value = stackit_resourcemanager_project.project.name + value = stackit_resourcemanager_project.this.name } output "service_account_email" { diff --git a/modules/management/terraform.tf b/modules/management/terraform.tf new file mode 100644 index 0000000..064e04c --- /dev/null +++ b/modules/management/terraform.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.10" + + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = ">=0.88.0" + } + time = { + source = "hashicorp/time" + version = ">=0.13.1" + } + } +} \ No newline at end of file diff --git a/modules/management/variables.tf b/modules/management/variables.tf index eabdf26..2fd3874 100644 --- a/modules/management/variables.tf +++ b/modules/management/variables.tf @@ -1,60 +1,33 @@ -variable "owner_email" { - type = string - description = "Email address of the owner for the folders. Required for STACKIT resource manager." -} - - -variable "project_code" { - type = string - description = "Optional project code for the STACKIT project." -} - - - -variable "company_code" { +variable "project_name" { type = string - description = "Company code used in resource naming conventions." + description = "Name of the STACKIT project to create." + default = null } -variable "parent_container_id" { +variable "naming_pattern" { type = string - description = "Parent container ID (folder or organization) where the project will be created." + description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-net-prod\"." } - variable "labels" { type = map(string) description = "Additional labels to apply to all folders." default = {} } -variable "region" { +variable "organization_id" { type = string - description = "STACKIT region for regional resources." - default = "eu01" -} - -variable "organization_owners" { - type = list(string) - description = "List of organization role assignments for organization owners." - default = [] -} - -variable "organization_auditors" { - type = list(string) - description = "List of organization role assignments for organization auditors." - default = [] + description = "Container ID of the root folder or organization under which the company folder will be created." } -variable "allowed_network_ranges" { - type = list(string) - description = "List of allowed network ranges for Git instance ACL." - default = ["0.0.0.0/0"] +variable "owner_email" { + type = string + description = "Email address of the owner for the folders. Required for STACKIT resource manager." } -variable "organization_id" { +variable "parent_container_id" { type = string - description = "Container ID of the root folder or organization under which the company folder will be created." + description = "Parent container ID (folder or organization) where the project will be created." } variable "role_assignments" { @@ -64,10 +37,4 @@ variable "role_assignments" { })) description = "List of role assignments for the project. Subject can be a user email or service account email." default = [] -} - -variable "env" { - type = string - description = "Environment identifier (e.g., dev, staging, prod) used in resource naming conventions." - default = "dev" } \ No newline at end of file diff --git a/modules/sandboxes/1-project.tf b/modules/sandboxes/1-project.tf deleted file mode 100644 index a2098c7..0000000 --- a/modules/sandboxes/1-project.tf +++ /dev/null @@ -1,33 +0,0 @@ -############# -## PROJECT ## -############# - -resource "stackit_resourcemanager_project" "project" { - for_each = { for sandbox in var.sandboxes : sandbox.project_name => sandbox } - - parent_container_id = var.parent_container_id - name = each.value.project_name - owner_email = each.value.project_owner_email - - lifecycle { - ignore_changes = [labels] # provider bug - } -} - -resource "stackit_authorization_project_role_assignment" "assignments" { - for_each = { - for assignment in flatten([ - for sandbox in var.sandboxes : [ - for email in coalesce(sandbox.owner_emails, []) : { - key = "${sandbox.project_name}-${email}" - project_name = sandbox.project_name - email = email - } - ] - ]) : assignment.key => assignment - } - - resource_id = stackit_resourcemanager_project.project[each.value.project_name].project_id - role = "owner" - subject = each.value.email -} \ No newline at end of file diff --git a/modules/sandboxes/README.md b/modules/sandboxes/README.md index d4e3d53..aca42a5 100644 --- a/modules/sandboxes/README.md +++ b/modules/sandboxes/README.md @@ -4,13 +4,13 @@ | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.10 | -| [stackit](#requirement\_stackit) | 0.83.0 | +| [stackit](#requirement\_stackit) | >=0.88.0 | ### Providers | Name | Version | |------|---------| -| [stackit](#provider\_stackit) | 0.83.0 | +| [stackit](#provider\_stackit) | 0.88.0 | ### Modules @@ -20,17 +20,16 @@ No modules. | Name | Type | |------|------| -| [stackit_authorization_project_role_assignment.assignments](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/authorization_project_role_assignment) | resource | -| [stackit_resourcemanager_project.project](https://registry.terraform.io/providers/stackitcloud/stackit/0.83.0/docs/resources/resourcemanager_project) | resource | +| [stackit_authorization_project_role_assignment.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_project_role_assignment) | resource | +| [stackit_resourcemanager_project.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/resourcemanager_project) | resource | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [company\_code](#input\_company\_code) | Company code used in resource naming conventions. | `string` | n/a | yes | +| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-net-prod". | `string` | n/a | yes | | [parent\_container\_id](#input\_parent\_container\_id) | Parent container ID (folder or organization) where the project will be created. | `string` | n/a | yes | -| [labels](#input\_labels) | Additional labels to apply to all folders. | `map(string)` | `{}` | no | -| [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no | +| [project\_name](#input\_project\_name) | Name of the STACKIT project to create. | `string` | `null` | no | | [sandboxes](#input\_sandboxes) | List of sandboxes to create. |
list(object({
project_name = string
owner_emails = optional(list(string))
project_owner_email = string
}))
| `[]` | no | ### Outputs diff --git a/modules/sandboxes/main.tf b/modules/sandboxes/main.tf index 0a6f13d..a39ede0 100644 --- a/modules/sandboxes/main.tf +++ b/modules/sandboxes/main.tf @@ -1,16 +1,33 @@ -terraform { - required_version = ">= 1.10" +############# +## PROJECT ## +############# - required_providers { - stackit = { - source = "stackitcloud/stackit" - version = "0.85.0" - } +resource "stackit_resourcemanager_project" "this" { + for_each = { for sandbox in var.sandboxes : sandbox.project_name => sandbox } + + parent_container_id = var.parent_container_id + name = "${var.naming_pattern}-${each.value.project_name}" + owner_email = each.value.project_owner_email + + lifecycle { + ignore_changes = [labels] # provider bug } } -provider "stackit" { - default_region = var.region - enable_beta_resources = true - experiments = ["iam", "routing-tables", "network"] +resource "stackit_authorization_project_role_assignment" "this" { + for_each = { + for assignment in flatten([ + for sandbox in var.sandboxes : [ + for email in coalesce(sandbox.owner_emails, []) : { + key = "${sandbox.project_name}-${email}" + project_name = sandbox.project_name + email = email + } + ] + ]) : assignment.key => assignment + } + + resource_id = stackit_resourcemanager_project.this[each.value.project_name].project_id + role = "owner" + subject = each.value.email } \ No newline at end of file diff --git a/modules/sandboxes/outputs.tf b/modules/sandboxes/outputs.tf index 0373014..585e978 100644 --- a/modules/sandboxes/outputs.tf +++ b/modules/sandboxes/outputs.tf @@ -1,7 +1,7 @@ output "projects" { description = "The created STACKIT projects with their IDs, container IDs, and names." value = { - for k, v in stackit_resourcemanager_project.project : k => { + for k, v in stackit_resourcemanager_project.this : k => { project_id = v.project_id container_id = v.container_id name = v.name diff --git a/modules/sandboxes/terraform.tf b/modules/sandboxes/terraform.tf new file mode 100644 index 0000000..33d151a --- /dev/null +++ b/modules/sandboxes/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.10" + + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = ">=0.88.0" + } + } +} \ No newline at end of file diff --git a/modules/sandboxes/variables.tf b/modules/sandboxes/variables.tf index 11ad870..4c052ee 100644 --- a/modules/sandboxes/variables.tf +++ b/modules/sandboxes/variables.tf @@ -1,23 +1,17 @@ -variable "company_code" { - type = string - description = "Company code used in resource naming conventions." -} - variable "parent_container_id" { type = string description = "Parent container ID (folder or organization) where the project will be created." } -variable "labels" { - type = map(string) - description = "Additional labels to apply to all folders." - default = {} +variable "naming_pattern" { + type = string + description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-net-prod\"." } -variable "region" { +variable "project_name" { type = string - description = "STACKIT region for regional resources." - default = "eu01" + description = "Name of the STACKIT project to create." + default = null } variable "sandboxes" { From 1e1dc305473273f88c861912e9a9b20e3e4ae070 Mon Sep 17 00:00:00 2001 From: Matthias Hauber Date: Tue, 24 Mar 2026 08:36:15 +0100 Subject: [PATCH 2/3] chore: update naming conventions and clean up variables in sandboxes and examples --- CODEOWNERS | 3 ++ examples/01-standalone/main.tf | 2 +- examples/02-hub-spoke/main.tf | 11 +++--- examples/02-hub-spoke/variables.tf | 58 ++++-------------------------- modules/sandboxes/main.tf | 2 +- modules/sandboxes/variables.tf | 10 ++---- 6 files changed, 19 insertions(+), 67 deletions(-) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..7bf8e5c --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +* david.wenzel@stackit.cloud @mahauber +docs/* @lweberru +scripts/* @lweberru \ No newline at end of file diff --git a/examples/01-standalone/main.tf b/examples/01-standalone/main.tf index d76fd43..1fa231a 100644 --- a/examples/01-standalone/main.tf +++ b/examples/01-standalone/main.tf @@ -47,7 +47,7 @@ module "devops" { module "sandboxes" { source = "../../modules/sandboxes" - naming_pattern = "${var.company_code}-sbx" + naming_prefix = "${var.company_code}-sbx" parent_container_id = module.governance.folder_container_ids.sandbox sandboxes = var.sandboxes } diff --git a/examples/02-hub-spoke/main.tf b/examples/02-hub-spoke/main.tf index 06d6063..cef6c61 100644 --- a/examples/02-hub-spoke/main.tf +++ b/examples/02-hub-spoke/main.tf @@ -33,9 +33,9 @@ module "management" { module "connectivity_global" { source = "../../modules/connectivity-global" - organization_id = var.organization_id - labels = var.labels - network_areas = var.network_areas + organization_id = var.organization_id + labels = var.labels + network_areas = var.network_areas } ############################# @@ -80,7 +80,7 @@ module "devops" { module "sandboxes" { source = "../../modules/sandboxes" - naming_pattern = "${var.company_code}-sbx" + naming_prefix = "${var.company_code}-sbx" parent_container_id = module.governance.folder_container_ids.sandbox sandboxes = var.sandboxes } @@ -93,8 +93,9 @@ module "landing_zone" { source = "../../modules/landing-zone" for_each = var.landing_zones - parent_container_id = module.governance.folder_container_ids.landing_zones_public + parent_container_id = each.value.corporate ? module.governance.folder_container_ids.landing_zones_corporate : module.governance.folder_container_ids.landing_zones_public naming_pattern = "${var.company_code}-lz-${each.value.project_code}-${each.value.env}" + network_area_id = each.value.corporate ? module.connectivity_global.network_area_ids[var.connectivity_regional_network_area] : null owner_email = each.value.owner_email labels = var.labels role_assignments = each.value.role_assignments diff --git a/examples/02-hub-spoke/variables.tf b/examples/02-hub-spoke/variables.tf index 86ec0dc..abc18e3 100644 --- a/examples/02-hub-spoke/variables.tf +++ b/examples/02-hub-spoke/variables.tf @@ -52,15 +52,9 @@ variable "platform_admins" { default = [] } -variable "landing_zone_admins" { - type = list(string) - description = "List of landing zone administrators." - default = [] -} - -############################## -## CONNECTIVITY - GLOBAL ## -############################## +########################### +## CONNECTIVITY - GLOBAL ## +########################### variable "network_areas" { type = list(object({ @@ -75,9 +69,9 @@ variable "network_areas" { description = "List of network areas to create with their IP ranges and configuration." } -################################ -## CONNECTIVITY - REGIONAL ## -################################ +############################# +## CONNECTIVITY - REGIONAL ## +############################# variable "connectivity_regional_network_area" { type = string @@ -144,46 +138,6 @@ variable "landing_zones" { description = string permissions = list(string) })), []) - kubernetes_clusters = optional(map(object({ - kubernetes_version = string - enable_kubernetes_version_updates = optional(bool, true) - enable_machine_image_version_updates = optional(bool, true) - hibernations = optional(list(object({ - start = string - end = string - timezone = optional(string, "Europe/Berlin") - })), []) - node_pools = list(object({ - name = string - machine_type = string - availability_zones = list(string) - os_version_min = optional(string) - minimum = number - maximum = number - max_surge = optional(number) - max_unavailable = optional(number) - labels = optional(map(string)) - taints = optional(list(object({ - key = string - value = string - effect = string - }))) - })) - extensions = optional(object({ - acl = optional(object({ - allowed_cidrs = list(string) - enabled = bool - })) - dns = optional(object({ - enabled = bool - zones = optional(list(string)) - })) - observability = optional(object({ - enabled = bool - instance_id = optional(string) - })) - })) - })), {}) })) description = "Map of landing zones to create. Set corporate = true for network area connectivity, false for public." default = {} diff --git a/modules/sandboxes/main.tf b/modules/sandboxes/main.tf index a39ede0..a7146df 100644 --- a/modules/sandboxes/main.tf +++ b/modules/sandboxes/main.tf @@ -6,7 +6,7 @@ resource "stackit_resourcemanager_project" "this" { for_each = { for sandbox in var.sandboxes : sandbox.project_name => sandbox } parent_container_id = var.parent_container_id - name = "${var.naming_pattern}-${each.value.project_name}" + name = "${var.naming_prefix}-${each.value.project_name}" owner_email = each.value.project_owner_email lifecycle { diff --git a/modules/sandboxes/variables.tf b/modules/sandboxes/variables.tf index 4c052ee..d76a849 100644 --- a/modules/sandboxes/variables.tf +++ b/modules/sandboxes/variables.tf @@ -3,15 +3,9 @@ variable "parent_container_id" { description = "Parent container ID (folder or organization) where the project will be created." } -variable "naming_pattern" { +variable "naming_prefix" { type = string - description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-net-prod\"." -} - -variable "project_name" { - type = string - description = "Name of the STACKIT project to create." - default = null + description = "Naming prefix for all resources in this module." } variable "sandboxes" { From c607f787862365ba65cf4a0007cd7909af50fda0 Mon Sep 17 00:00:00 2001 From: Matthias Hauber Date: Tue, 24 Mar 2026 15:49:50 +0100 Subject: [PATCH 3/3] feat(governance): add rm_folders structure for better resource management refactor(management): update parent_container_id references for consistency refactor(devops): update parent_container_id references for consistency refactor(sandboxes): update parent_container_id references for consistency refactor(landing_zone): update parent_container_id references for consistency refactor(bucket): remove unnecessary moved block in object storage credentials --- examples/01-standalone/main.tf | 26 ++++++++++++++++++++++---- examples/02-hub-spoke/main.tf | 33 ++++++++++++++++++++++++++++----- modules/management/3-bucket.tf | 5 ----- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/examples/01-standalone/main.tf b/examples/01-standalone/main.tf index 1fa231a..0f5faed 100644 --- a/examples/01-standalone/main.tf +++ b/examples/01-standalone/main.tf @@ -10,6 +10,24 @@ module "governance" { labels = var.labels organization_owners = var.organization_owners organization_auditors = var.organization_auditors + + rm_folders = { + platform = { + name = "Platform" + owner_emails = [] + reader_emails = [] + } + landing_zones = { + name = "Landing Zones" + owner_emails = [] + reader_emails = [] + } + sandboxes = { + name = "Sandboxes" + owner_emails = [] + reader_emails = [] + } + } } ################ @@ -21,7 +39,7 @@ module "management" { owner_email = var.owner_email naming_pattern = "${var.company_code}-pltfm-mgmt-prod" - parent_container_id = module.governance.folder_container_ids.platform + parent_container_id = module.governance.folder_container_ids["platform"] organization_id = var.organization_id labels = var.labels } @@ -36,7 +54,7 @@ module "devops" { owner_email = var.owner_email naming_pattern = "${var.company_code}-pltfm-devops-prod" company_name = var.company_name - parent_container_id = module.governance.folder_container_ids.platform + parent_container_id = module.governance.folder_container_ids["platform"] labels = var.labels } @@ -48,7 +66,7 @@ module "sandboxes" { source = "../../modules/sandboxes" naming_prefix = "${var.company_code}-sbx" - parent_container_id = module.governance.folder_container_ids.sandbox + parent_container_id = module.governance.folder_container_ids["sandboxes"] sandboxes = var.sandboxes } @@ -60,7 +78,7 @@ module "landing_zone" { source = "../../modules/landing-zone" for_each = var.landing_zones - parent_container_id = module.governance.folder_container_ids.landing_zones_public + parent_container_id = module.governance.folder_container_ids["landing_zones"] naming_pattern = "${var.company_code}-lz-${each.value.project_code}-${each.value.env}" owner_email = each.value.owner_email labels = var.labels diff --git a/examples/02-hub-spoke/main.tf b/examples/02-hub-spoke/main.tf index cef6c61..6859101 100644 --- a/examples/02-hub-spoke/main.tf +++ b/examples/02-hub-spoke/main.tf @@ -10,6 +10,29 @@ module "governance" { labels = var.labels organization_owners = var.organization_owners organization_auditors = var.organization_auditors + + rm_folders = { + platform = { + name = "Platform" + owner_emails = [] + reader_emails = [] + } + landing_zones_corporate = { + name = "Landing Zones - Corporate" + owner_emails = [] + reader_emails = [] + } + landing_zones_public = { + name = "Landing Zones - Public" + owner_emails = [] + reader_emails = [] + } + sandboxes = { + name = "Sandboxes" + owner_emails = [] + reader_emails = [] + } + } } ################ @@ -21,7 +44,7 @@ module "management" { owner_email = var.owner_email naming_pattern = "${var.company_code}-pltfm-mgmt-prod" - parent_container_id = module.governance.folder_container_ids.platform + parent_container_id = module.governance.folder_container_ids["platform"] organization_id = var.organization_id labels = var.labels } @@ -47,7 +70,7 @@ module "connectivity_regional" { owner_email = var.owner_email naming_pattern = "${var.company_code}-pltfm-hub-prod" - parent_container_id = module.governance.folder_container_ids.platform + parent_container_id = module.governance.folder_container_ids["platform"] organization_id = var.organization_id network_area_id = module.connectivity_global.network_area_ids[var.connectivity_regional_network_area] labels = var.labels @@ -69,7 +92,7 @@ module "devops" { owner_email = var.owner_email naming_pattern = "${var.company_code}-pltfm-devops-prod" company_name = var.company_name - parent_container_id = module.governance.folder_container_ids.platform + parent_container_id = module.governance.folder_container_ids["platform"] labels = var.labels } @@ -81,7 +104,7 @@ module "sandboxes" { source = "../../modules/sandboxes" naming_prefix = "${var.company_code}-sbx" - parent_container_id = module.governance.folder_container_ids.sandbox + parent_container_id = module.governance.folder_container_ids["sandboxes"] sandboxes = var.sandboxes } @@ -93,7 +116,7 @@ module "landing_zone" { source = "../../modules/landing-zone" for_each = var.landing_zones - parent_container_id = each.value.corporate ? module.governance.folder_container_ids.landing_zones_corporate : module.governance.folder_container_ids.landing_zones_public + parent_container_id = each.value.corporate ? module.governance.folder_container_ids["landing_zones_corporate"] : module.governance.folder_container_ids["landing_zones_public"] naming_pattern = "${var.company_code}-lz-${each.value.project_code}-${each.value.env}" network_area_id = each.value.corporate ? module.connectivity_global.network_area_ids[var.connectivity_regional_network_area] : null owner_email = each.value.owner_email diff --git a/modules/management/3-bucket.tf b/modules/management/3-bucket.tf index 0c57f03..5f9733e 100644 --- a/modules/management/3-bucket.tf +++ b/modules/management/3-bucket.tf @@ -26,11 +26,6 @@ resource "stackit_objectstorage_credentials_group" "this" { ] } -moved { - from = stackit_objectstorage_credentials_group.main - to = stackit_objectstorage_credentials_group.this -} - resource "stackit_objectstorage_credential" "this" { project_id = stackit_resourcemanager_project.this.project_id credentials_group_id = stackit_objectstorage_credentials_group.this.credentials_group_id