diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 351a6bf..e88517a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,7 +2,9 @@ version: 2 updates: - package-ecosystem: "terraform" directories: - - "/modules/*" + - "/src" + - "/src/modules/*" + - "/stackit-verified-modules/*" schedule: interval: "weekly" day: "sunday" diff --git a/.github/images/stackit-logo.svg b/.github/images/stackit-logo.svg new file mode 100644 index 0000000..6823680 --- /dev/null +++ b/.github/images/stackit-logo.svg @@ -0,0 +1,43 @@ + + \ No newline at end of file diff --git a/.terraform-docs.yml b/.terraform-docs.yml deleted file mode 100644 index 3647d5e..0000000 --- a/.terraform-docs.yml +++ /dev/null @@ -1,17 +0,0 @@ -formatter: markdown table - -output: - file: README.md - mode: inject - -sort: - enabled: true - by: required - -settings: - anchor: true - default: true - escape: true - indent: 3 - required: true - type: true diff --git a/.tflint.hcl b/.tflint.hcl deleted file mode 100644 index c3f1a2b..0000000 --- a/.tflint.hcl +++ /dev/null @@ -1,20 +0,0 @@ -plugin "terraform" { - enabled = true - preset = "recommended" -} - -config { - call_module_type = "all" -} - -rule "terraform_required_providers" { - enabled = true -} - -rule "terraform_required_version" { - enabled = true -} - -rule "terraform_unused_declarations" { - enabled = true -} \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS index 7bf8e5c..0993d48 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,3 +1,2 @@ -* david.wenzel@stackit.cloud @mahauber -docs/* @lweberru -scripts/* @lweberru \ No newline at end of file +* @dweezl @mahauber @simpe00 +docs/* @lweberru \ No newline at end of file diff --git a/README.md b/README.md index 3e832e1..7af5d00 100644 --- a/README.md +++ b/README.md @@ -1,91 +1,22 @@ -# 🚀 STACKIT Landing Zone Accelerator +
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
deleted file mode 100644
index 09d9f04..0000000
--- a/examples/01-standalone/main.tf
+++ /dev/null
@@ -1,89 +0,0 @@
-################
-## GOVERNANCE ##
-################
-
-module "governance" {
- source = "../../modules/governance"
-
- owner_email = var.owner_email
- organization_id = var.organization_id
- 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 = []
- }
- }
-}
-
-################
-## MANAGEMENT ##
-################
-
-module "management" {
- source = "../../modules/management"
-
- 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
-}
-
-###############
-## SANDBOXES ##
-###############
-
-module "sandboxes" {
- source = "../../modules/sandboxes"
-
- naming_prefix = "${var.company_code}-sbx"
- parent_container_id = module.governance.folder_container_ids["sandboxes"]
- sandboxes = var.sandboxes
-}
-
-###################
-## LANDING ZONES ##
-###################
-
-module "landing_zone" {
- source = "../../modules/landing-zone"
- for_each = var.landing_zones
-
- organization_id = var.organization_id
- 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
- role_assignments = each.value.role_assignments
- network_prefix_length = each.value.network_prefix_length
- custom_roles = each.value.custom_roles
-}
diff --git a/examples/01-standalone/outputs.tf b/examples/01-standalone/outputs.tf
deleted file mode 100644
index 1b8d42d..0000000
--- a/examples/01-standalone/outputs.tf
+++ /dev/null
@@ -1,33 +0,0 @@
-#############
-## OUTPUTS ##
-#############
-
-output "governance_folder_ids" {
- description = "Map of governance folder names to their container IDs."
- value = module.governance.folder_container_ids
-}
-
-output "devops_project_id" {
- description = "The project ID of the DevOps project."
- value = module.devops.project_id
-}
-
-output "management_project_id" {
- description = "The project ID of the Management project."
- value = module.management.project_id
-}
-
-output "sandbox_projects" {
- description = "The created sandbox projects."
- value = module.sandboxes.projects
-}
-
-output "landing_zone_projects" {
- description = "Map of landing zone project IDs."
- value = {
- for k, v in module.landing_zone : k => {
- project_id = v.project_id
- project_name = v.project_name
- }
- }
-}
\ No newline at end of file
diff --git a/examples/01-standalone/terraform.tfvars b/examples/01-standalone/terraform.tfvars
deleted file mode 100644
index 04e0844..0000000
--- a/examples/01-standalone/terraform.tfvars
+++ /dev/null
@@ -1,58 +0,0 @@
-# Email of the technical owner registered in STACKIT
-owner_email = "platform-team@example.com"
-
-# Company name used for folder naming in the resource manager
-company_name = "Example Corp"
-
-# Short company code used as prefix in resource naming (e.g. project names, service accounts)
-company_code = "exc"
-
-# Root organization container ID from STACKIT resource manager
-organization_id = "org-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-
-region = "eu01"
-
-# Labels applied to all resources for cost tracking / filtering
-labels = {
- managed_by = "opentofu"
-}
-
-# Users with full organization-level owner permissions
-organization_owners = [
- "org-owner@example.com"
-]
-
-# Users with read-only audit access at the organization level
-organization_auditors = [
- "auditor@example.com"
-]
-
-# Users with admin access to the Platform folder (DevOps, Management)
-platform_admins = [
- "platform-admin@example.com"
-]
-
-# Sandbox projects for experimentation / PoCs
-sandboxes = [
- {
- project_name = "Sandbox Team Alpha"
- project_owner_email = "alpha-lead@example.com"
- owner_emails = ["dev1@example.com", "dev2@example.com"]
- }
-]
-
-# Landing zones keyed by a unique identifier
-landing_zones = {
- "fe-dev" = {
- project_code = "fe"
- owner_email = "frontend-team@example.com"
- env = "dev"
-
- role_assignments = [
- {
- role = "project.owner"
- subject = "frontend-lead@example.com"
- }
- ]
- }
-}
diff --git a/examples/01-standalone/variables.tf b/examples/01-standalone/variables.tf
deleted file mode 100644
index 5432b70..0000000
--- a/examples/01-standalone/variables.tf
+++ /dev/null
@@ -1,83 +0,0 @@
-###############
-## VARIABLES ##
-###############
-
-variable "owner_email" {
- type = string
- description = "Email address of the owner. Required for STACKIT resource manager."
-}
-
-variable "company_name" {
- type = string
- description = "Name of the company."
-}
-
-variable "company_code" {
- type = string
- description = "Company code used in resource naming conventions."
-}
-
-variable "organization_id" {
- type = string
- description = "Container ID of the root organization."
-}
-
-variable "region" {
- type = string
- description = "STACKIT region for regional resources."
- default = "eu01"
-}
-
-variable "labels" {
- type = map(string)
- description = "Additional labels to apply to all resources."
- default = {}
-}
-
-variable "organization_owners" {
- type = list(string)
- description = "List of organization owners."
- default = []
-}
-
-variable "organization_auditors" {
- type = list(string)
- description = "List of organization auditors."
- default = []
-}
-
-variable "platform_admins" {
- type = list(string)
- description = "List of platform administrators."
- default = []
-}
-
-variable "sandboxes" {
- type = list(object({
- project_name = string
- owner_emails = optional(list(string))
- project_owner_email = string
- }))
- description = "List of sandboxes to create."
- default = []
-}
-
-variable "landing_zones" {
- type = map(object({
- 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)
- })), [])
- }))
- description = "Map of landing zones to create (public, without network area)."
- default = {}
-}
\ No newline at end of file
diff --git a/examples/02-hub-spoke/README.md b/examples/02-hub-spoke/README.md
deleted file mode 100644
index fd286fa..0000000
--- a/examples/02-hub-spoke/README.md
+++ /dev/null
@@ -1,65 +0,0 @@
-
-## 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
deleted file mode 100644
index eae1e4d..0000000
--- a/examples/02-hub-spoke/main.tf
+++ /dev/null
@@ -1,129 +0,0 @@
-################
-## GOVERNANCE ##
-################
-
-module "governance" {
- source = "../../modules/governance"
-
- owner_email = var.owner_email
- organization_id = var.organization_id
- 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 = []
- }
- }
-}
-
-################
-## MANAGEMENT ##
-################
-
-module "management" {
- source = "../../modules/management"
-
- 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
-}
-
-###########################
-## CONNECTIVITY - GLOBAL ##
-###########################
-
-module "connectivity_global" {
- source = "../../modules/connectivity-global"
-
- organization_id = var.organization_id
- labels = var.labels
- network_areas = var.network_areas
-}
-
-#############################
-## CONNECTIVITY - REGIONAL ##
-#############################
-
-module "connectivity_regional" {
- source = "../../modules/connectivity-regional"
-
- owner_email = var.owner_email
- 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
- 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
-}
-
-############
-## 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
-}
-
-###############
-## SANDBOXES ##
-###############
-
-module "sandboxes" {
- source = "../../modules/sandboxes"
-
- naming_prefix = "${var.company_code}-sbx"
- parent_container_id = module.governance.folder_container_ids["sandboxes"]
- sandboxes = var.sandboxes
-}
-
-###################
-## LANDING ZONES ##
-###################
-
-module "landing_zone" {
- source = "../../modules/landing-zone"
- for_each = var.landing_zones
-
- organization_id = var.organization_id
- 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
- network_prefix_length = each.value.network_prefix_length
- custom_roles = each.value.custom_roles
- firewall_next_hop_ip = module.connectivity_regional.firewall_next_hop_ip
-}
diff --git a/examples/02-hub-spoke/outputs.tf b/examples/02-hub-spoke/outputs.tf
deleted file mode 100644
index a4e8b41..0000000
--- a/examples/02-hub-spoke/outputs.tf
+++ /dev/null
@@ -1,48 +0,0 @@
-#############
-## OUTPUTS ##
-#############
-
-output "governance_folder_ids" {
- description = "Map of governance folder names to their container IDs."
- value = module.governance.folder_container_ids
-}
-
-output "devops_project_id" {
- description = "The project ID of the DevOps project."
- value = module.devops.project_id
-}
-
-output "management_project_id" {
- description = "The project ID of the Management project."
- value = module.management.project_id
-}
-
-output "connectivity_global_network_area_ids" {
- description = "Map of network area names to their IDs."
- value = module.connectivity_global.network_area_ids
-}
-
-output "connectivity_regional_project_id" {
- description = "The project ID of the regional connectivity project."
- value = module.connectivity_regional.project_id
-}
-
-output "connectivity_regional_firewall_public_ip" {
- description = "The public IP of the firewall."
- value = module.connectivity_regional.firewall_public_ip
-}
-
-output "sandbox_projects" {
- description = "The created sandbox projects."
- value = module.sandboxes.projects
-}
-
-output "landing_zone_projects" {
- description = "Map of landing zone project IDs."
- value = {
- for k, v in module.landing_zone : k => {
- project_id = v.project_id
- project_name = v.project_name
- }
- }
-}
diff --git a/examples/02-hub-spoke/pfsense.qcow2 b/examples/02-hub-spoke/pfsense.qcow2
deleted file mode 100644
index 311c8dd..0000000
--- a/examples/02-hub-spoke/pfsense.qcow2
+++ /dev/null
@@ -1 +0,0 @@
-PLACEHOLDER
\ No newline at end of file
diff --git a/examples/02-hub-spoke/providers.tf b/examples/02-hub-spoke/providers.tf
deleted file mode 100644
index 0dda673..0000000
--- a/examples/02-hub-spoke/providers.tf
+++ /dev/null
@@ -1,17 +0,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 = module.management.secretsmanager_username
- password = module.management.secretsmanager_password
- }
-}
-
-provider "time" {}
\ No newline at end of file
diff --git a/examples/02-hub-spoke/terraform.tf b/examples/02-hub-spoke/terraform.tf
deleted file mode 100644
index 0cdc026..0000000
--- a/examples/02-hub-spoke/terraform.tf
+++ /dev/null
@@ -1,18 +0,0 @@
-terraform {
- required_version = ">= 1.10"
-
- required_providers {
- stackit = {
- source = "stackitcloud/stackit"
- version = "0.93.0"
- }
- time = {
- source = "hashicorp/time"
- version = "0.13.1"
- }
- vault = {
- source = "hashicorp/vault"
- version = "5.7.0"
- }
- }
-}
\ No newline at end of file
diff --git a/examples/02-hub-spoke/terraform.tfvars b/examples/02-hub-spoke/terraform.tfvars
deleted file mode 100644
index caccdc9..0000000
--- a/examples/02-hub-spoke/terraform.tfvars
+++ /dev/null
@@ -1,157 +0,0 @@
-# Email of the technical owner registered in STACKIT
-owner_email = "platform-team@example.com"
-
-# Company name used for folder naming in the resource manager
-company_name = "Example Corp"
-
-# Short company code used as prefix in resource naming (e.g. project names, service accounts)
-company_code = "exc"
-
-# Root organization container ID from STACKIT resource manager
-organization_id = "org-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-
-region = "eu01"
-
-# Labels applied to all resources for cost tracking / filtering
-labels = {
- managed_by = "opentofu"
- environment = "production"
-}
-
-# Users with full organization-level owner permissions
-organization_owners = [
- "org-owner@example.com"
-]
-
-# Users with read-only audit access at the organization level
-organization_auditors = [
- "auditor@example.com"
-]
-
-# Users with admin access to the Platform folder (DevOps, Management, Connectivity)
-platform_admins = [
- "platform-admin@example.com"
-]
-
-###########################
-## 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
-network_areas = [
- {
- name = "corporate"
- # IP ranges that will be sliced into per-project subnets
- network_ranges = [
- { prefix = "10.1.0.0/16" },
- { prefix = "10.2.0.0/16" }
- ]
- # Transfer network used for routing between projects in this area
- transfer_network_range = "10.255.0.0/24"
- # Controls the subnet sizes assigned to individual projects
- min_prefix_length = 24
- max_prefix_length = 28
- default_prefix_length = 25
- }
-]
-
-#############################
-## CONNECTIVITY - REGIONAL ##
-#############################
-
-# Must match a name from the network_areas list above
-connectivity_regional_network_area = "corporate"
-
-# Availability zone for the firewall VM
-firewall_zone = "eu01-m"
-
-# VM flavor for the pfSense firewall
-firewall_flavor = "c1.2"
-
-# CIDR range for the connectivity project's own VNet (firewall LAN side)
-connectivity_vnet_range = "10.0.0.0/24"
-
-# Static LAN IP of the pfSense firewall (used as default gateway)
-firewall_ip = "10.0.0.220"
-
-###############
-## SANDBOXES ##
-###############
-
-# Sandbox projects for experimentation / PoCs
-sandboxes = [
- {
- project_name = "Sandbox Team Alpha"
- project_owner_email = "alpha-lead@example.com"
- owner_emails = ["dev1@example.com", "dev2@example.com"]
- },
- {
- project_name = "Sandbox Data Science"
- project_owner_email = "ds-lead@example.com"
- }
-]
-
-##################
-## LANDING ZONE ##
-##################
-
-# Landing zones keyed by a unique identifier
-# Set corporate = true for network area connectivity, false for public internet
-landing_zones = {
- "app-backend" = {
- project_name = "Backend Services"
- project_code = "be"
- owner_email = "backend-team@example.com"
- env = "prod"
- corporate = true
-
- # Subnet size assigned from the network area (/25 = 128 addresses)
- network_prefix_length = 25
-
- role_assignments = [
- {
- role = "project.owner"
- subject = "backend-lead@example.com"
- },
- {
- role = "project.member"
- subject = "backend-dev@example.com"
- }
- ]
-
- custom_roles = [
- {
- name = "deployer"
- description = "Can deploy workloads"
- permissions = ["project.resources.read", "project.resources.write"]
- }
- ]
- }
-
- "data-platform" = {
- project_name = "Data Platform"
- project_code = "data"
- owner_email = "data-team@example.com"
- env = "prod"
- corporate = true
-
- network_prefix_length = 24
- }
-
- # Public landing zone — no network area, uses STACKIT's default public networking
- "external-api" = {
- project_name = "External API Gateway"
- project_code = "api"
- owner_email = "api-team@example.com"
- env = "prod"
- corporate = false
-
- role_assignments = [
- {
- role = "project.owner"
- subject = "api-lead@example.com"
- }
- ]
- }
-}
diff --git a/examples/02-hub-spoke/variables.tf b/examples/02-hub-spoke/variables.tf
deleted file mode 100644
index abc18e3..0000000
--- a/examples/02-hub-spoke/variables.tf
+++ /dev/null
@@ -1,144 +0,0 @@
-###############
-## VARIABLES ##
-###############
-
-variable "owner_email" {
- type = string
- description = "Email address of the owner. Required for STACKIT resource manager."
-}
-
-variable "company_name" {
- type = string
- description = "Name of the company."
-}
-
-variable "company_code" {
- type = string
- description = "Company code used in resource naming conventions."
-}
-
-variable "organization_id" {
- type = string
- description = "Container ID of the root organization."
-}
-
-variable "region" {
- type = string
- description = "STACKIT region for regional resources."
- default = "eu01"
-}
-
-variable "labels" {
- type = map(string)
- description = "Additional labels to apply to all resources."
- default = {}
-}
-
-variable "organization_owners" {
- type = list(string)
- description = "List of organization owners."
- default = []
-}
-
-variable "organization_auditors" {
- type = list(string)
- description = "List of organization auditors."
- default = []
-}
-
-variable "platform_admins" {
- type = list(string)
- description = "List of platform administrators."
- default = []
-}
-
-###########################
-## CONNECTIVITY - GLOBAL ##
-###########################
-
-variable "network_areas" {
- type = 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)
- }))
- description = "List of network areas to create with their IP ranges and configuration."
-}
-
-#############################
-## CONNECTIVITY - REGIONAL ##
-#############################
-
-variable "connectivity_regional_network_area" {
- type = string
- description = "Name key of the network area (from network_areas) to use for the regional connectivity project."
-}
-
-variable "firewall_zone" {
- type = string
- description = "STACKIT Availability Zone for the firewall VM."
- default = "eu01-m"
-}
-
-variable "firewall_flavor" {
- type = string
- description = "Firewall VM flavor."
- default = "c1.2"
-}
-
-variable "connectivity_vnet_range" {
- type = string
- description = "CIDR range for the connectivity project VNet."
- default = "10.0.0.0/24"
-}
-
-variable "firewall_ip" {
- type = string
- description = "Static IP address for the firewall LAN interface."
- default = "10.0.0.220"
-}
-
-###############
-## SANDBOXES ##
-###############
-
-variable "sandboxes" {
- type = list(object({
- project_name = string
- owner_emails = optional(list(string))
- project_owner_email = string
- }))
- description = "List of sandboxes to create."
- default = []
-}
-
-##################
-## LANDING ZONE ##
-##################
-
-variable "landing_zones" {
- type = 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)
- })), [])
- }))
- description = "Map of landing zones to create. Set corporate = true for network area connectivity, false for public."
- default = {}
-}
diff --git a/mise.toml b/mise.toml
new file mode 100644
index 0000000..46d8a0b
--- /dev/null
+++ b/mise.toml
@@ -0,0 +1,6 @@
+[tools]
+opentofu = "1.11.6"
+"github:stackitcloud/stackit-cli" = "0.61.0"
+
+[env]
+STACKIT_SERVICE_ACCOUNT_KEY_PATH = "{{env.HOME}}/.stackit/credentials.json"
\ No newline at end of file
diff --git a/modules/connectivity-global/1-network-area.tf b/modules/connectivity-global/1-network-area.tf
deleted file mode 100644
index db4103f..0000000
--- a/modules/connectivity-global/1-network-area.tf
+++ /dev/null
@@ -1,34 +0,0 @@
-##################
-## NETWORK AREA ##
-##################
-
-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 = each.key
- labels = merge(local.project_labels, { "preview/routingtables" = "true" })
-}
-
-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.this[each.key].network_area_id
-
- ipv4 = {
- network_ranges = each.value.network_ranges
- transfer_network = each.value.transfer_network_range
- max_prefix_length = each.value.max_prefix_length
- min_prefix_length = each.value.min_prefix_length
- default_prefix_length = each.value.default_prefix_length
- default_nameservers = each.value.default_nameservers
- }
-}
\ No newline at end of file
diff --git a/modules/connectivity-global/README.md b/modules/connectivity-global/README.md
deleted file mode 100644
index bae0828..0000000
--- a/modules/connectivity-global/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-
-### Requirements
-
-| Name | Version |
-|------|---------|
-| [terraform](#requirement\_terraform) | >= 1.10 |
-| [stackit](#requirement\_stackit) | >=0.88.0 |
-
-### Providers
-
-| Name | Version |
-|------|---------|
-| [stackit](#provider\_stackit) | 0.88.0 |
-
-### Modules
-
-No modules.
-
-### Resources
-
-| Name | Type |
-|------|------|
-| [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 |
-|------|-------------|------|---------|:--------:|
-| [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 |
-| [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 |
-| [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
-
-| Name | Description |
-|------|-------------|
-| [network\_area\_ids](#output\_network\_area\_ids) | Map of network area names to their IDs. |
-
\ No newline at end of file
diff --git a/modules/connectivity-global/outputs.tf b/modules/connectivity-global/outputs.tf
deleted file mode 100644
index 30b15f5..0000000
--- a/modules/connectivity-global/outputs.tf
+++ /dev/null
@@ -1,4 +0,0 @@
-output "network_area_ids" {
- description = "Map of network area names to their IDs."
- 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
deleted file mode 100644
index 9b65a85..0000000
--- a/modules/connectivity-global/terraform.tf
+++ /dev/null
@@ -1,10 +0,0 @@
-terraform {
- required_version = ">= 1.10"
-
- required_providers {
- stackit = {
- source = "stackitcloud/stackit"
- version = ">=0.93.0"
- }
- }
-}
diff --git a/modules/connectivity-global/variables.tf b/modules/connectivity-global/variables.tf
deleted file mode 100644
index 3549cdc..0000000
--- a/modules/connectivity-global/variables.tf
+++ /dev/null
@@ -1,29 +0,0 @@
-variable "labels" {
- type = map(string)
- description = "Additional labels to apply to all folders."
- default = {}
-}
-
-variable "network_area_id" {
- type = string
- description = "Network Area ID to deploy resources into. Required if network is enabled."
- default = null
-}
-
-variable "network_areas" {
- type = 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), ["1.0.0.1", "1.1.1.1"])
- }))
- description = "List of network areas to create, each with its own name, ranges, and configuration."
-}
-
-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/3-internal-network.tf b/modules/connectivity-regional/3-internal-network.tf
deleted file mode 100644
index 254076d..0000000
--- a/modules/connectivity-regional/3-internal-network.tf
+++ /dev/null
@@ -1,16 +0,0 @@
-#############
-## NETWORK ##
-#############
-
-resource "stackit_network" "lan" {
- project_id = stackit_resourcemanager_project.this.project_id
- name = "lan_network"
- routed = true
-}
-
-resource "stackit_network_interface" "lan" {
- name = "vtnet1_lan"
- project_id = stackit_resourcemanager_project.this.project_id
- network_id = stackit_network.lan.network_id
- security = false
-}
\ No newline at end of file
diff --git a/modules/connectivity-regional/4-firewall.tf b/modules/connectivity-regional/4-firewall.tf
deleted file mode 100644
index cc043fc..0000000
--- a/modules/connectivity-regional/4-firewall.tf
+++ /dev/null
@@ -1,64 +0,0 @@
-#####################
-## PFSENSE - IMAGE ##
-#####################
-
-# resource "terraform_data" "pfsense_image_file" {
-# triggers_replace = [
-# timestamp()
-# ]
-
-# provisioner "local-exec" {
-# command = "curl -o pfsense.qcow2 https://pfsense.object.storage.eu01.onstackit.cloud/pfsense-ce-2.7.2-amd64-10-12-2024.qcow2"
-# }
-# }
-
-resource "stackit_image" "pfsense_image" {
- project_id = stackit_resourcemanager_project.this.project_id
- name = "pfsense-2.7.2-amd64-image"
- local_file_path = "./pfsense.qcow2"
- disk_format = "qcow2"
- min_disk_size = 10
- min_ram = 2
- config = {
- uefi = false
- }
-
- # depends_on = [terraform_data.pfsense_image_file]
-}
-
-############
-## VOLUME ##
-############
-
-resource "stackit_volume" "pfsense_vol" {
- 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
- type = "image"
- }
-}
-
-############
-## SERVER ##
-############
-
-# after rollout: https://docs.stackit.cloud/products/quick-deployments/pfsense-firewall/tutorials/configure-pfsense/
-resource "stackit_server" "pfsense_Server" {
- project_id = stackit_resourcemanager_project.this.project_id
- name = "pfSense"
- boot_volume = {
- source_type = "volume"
- source_id = stackit_volume.pfsense_vol.volume_id
- }
- availability_zone = var.firewall_zone
- machine_type = var.firewall_flavor
-
- network_interfaces = [
- stackit_network_interface.wan.network_interface_id, # vtnet0 = WAN
- stackit_network_interface.lan.network_interface_id # vtnet1 = LAN
- ]
-}
\ No newline at end of file
diff --git a/modules/connectivity-regional/README.md b/modules/connectivity-regional/README.md
deleted file mode 100644
index 8e062df..0000000
--- a/modules/connectivity-regional/README.md
+++ /dev/null
@@ -1,107 +0,0 @@
-# STACKIT pfSense Deployment
-
-Terraform script to deploy an pfSense firewall into STACKIT Cloud.
-
-Deployment overview:
-
-
-The Terraform deployment consists of:
-+ WAN Network
-+ LAN Network
-+ pfSense firewall VM + disk volume
-+ FloatingIP for firewall VM
-+ deactivating port security on firewall ports
-
-## Setup
-**Requirements:**
-+ Terraform installed
-+ Access to a STACKIT project
-+ STACKIT Service Account Key
-
-### Installation
-1. Clone Repo
-1. Set Project ID in `01-config.tf`
-1. Create & Save a STACKIT Service Account Token and place it in the `secrets.json` file.
-1. Run Terraform `terraform apply`
-
-## Default Configuration
-
-### Interfaces
-1. `vtnet0` WAN
-1. `vtnet1` LAN
-
-### NAT
-Masqurade (Outbound NAT) Traffic from `LAN` to `WAN`
-
-### Dashboard
-Customized Widgets and CSS settings
-
-### Password
-Set default password for admin to STACKIT123!
-
-### Interface Access
-Disabled Referer-Check
-Enable allow all wan adresses to connect to the WebUI
-
-Now you can enter the WebUI via the FloatingIP on port 443 the default login is admin:STACKIT123!
-
-### Requirements
-
-| Name | Version |
-|------|---------|
-| [terraform](#requirement\_terraform) | >= 1.10 |
-| [stackit](#requirement\_stackit) | >=0.88.0 |
-
-### Providers
-
-| Name | Version |
-|------|---------|
-| [stackit](#provider\_stackit) | 0.88.0 |
-
-### Modules
-
-No modules.
-
-### Resources
-
-| Name | Type |
-|------|------|
-| [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 |
-|------|-------------|------|---------|:--------:|
-| [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 |
-| [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 |
-| [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 |
-
-### Outputs
-
-| Name | Description |
-|------|-------------|
-| [pfsense\_public\_ip](#output\_pfsense\_public\_ip) | The public IP address of the pfSense firewall WAN interface. |
-| [pfsense\_wan\_ip](#output\_pfsense\_wan\_ip) | The internal network area IP of the pfSense WAN interface (used as next hop in routes). |
-| [project\_container\_id](#output\_project\_container\_id) | The container ID of the created STACKIT project. |
-| [project\_id](#output\_project\_id) | The project ID of the created STACKIT project. |
-| [project\_name](#output\_project\_name) | The name of the created STACKIT project. |
-
\ No newline at end of file
diff --git a/modules/connectivity-regional/outputs.tf b/modules/connectivity-regional/outputs.tf
deleted file mode 100644
index b27b2ce..0000000
--- a/modules/connectivity-regional/outputs.tf
+++ /dev/null
@@ -1,24 +0,0 @@
-output "firewall_public_ip" {
- description = "The public IP address of the pfSense firewall WAN interface."
- value = stackit_public_ip.wan-ip.ip
-}
-
-output "firewall_next_hop_ip" {
- description = "The IP address to be used as next hop for the default route in the landing zones (pfSense WAN IP)."
- value = stackit_network_interface.lan.ipv4
-}
-
-output "project_container_id" {
- description = "The container ID of the created STACKIT project."
- 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.this.name
-}
diff --git a/modules/connectivity-regional/pfsense.qcow2 b/modules/connectivity-regional/pfsense.qcow2
deleted file mode 100644
index 311c8dd..0000000
--- a/modules/connectivity-regional/pfsense.qcow2
+++ /dev/null
@@ -1 +0,0 @@
-PLACEHOLDER
\ No newline at end of file
diff --git a/modules/connectivity-regional/variables.tf b/modules/connectivity-regional/variables.tf
deleted file mode 100644
index e1cf112..0000000
--- a/modules/connectivity-regional/variables.tf
+++ /dev/null
@@ -1,74 +0,0 @@
-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 "firewall_ip" {
- type = string
- description = "IP address of the firewall"
- default = "10.0.0.220"
-}
-
-variable "firewall_zone" {
- type = string
- description = "STACKIT Availability Zone"
- default = "eu01-m"
-}
-
-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 "network_area_id" {
- type = string
- description = "Network Area ID to deploy resources into. Required if network is enabled."
-}
-
-variable "organization_id" {
- type = string
- description = "Organization ID, required for network area route configuration."
-}
-
-variable "owner_email" {
- type = string
- description = "Email address of the owner for the folders. Required for STACKIT resource manager."
-}
-
-variable "parent_container_id" {
- type = string
- description = "Parent container ID (folder or organization) where the project will be created."
-}
-
-variable "project_name" {
- type = string
- description = "Name of the STACKIT project to create."
- 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 "vnet_range" {
- type = string
- description = "CIDR range for the project VNet. Required if network is enabled."
- default = "10.0.0.0/24"
-}
diff --git a/modules/landing-zone/4-secrets-manager.tf b/modules/landing-zone/4-secrets-manager.tf
deleted file mode 100644
index b69242d..0000000
--- a/modules/landing-zone/4-secrets-manager.tf
+++ /dev/null
@@ -1,17 +0,0 @@
-#####################
-## SECRETS MANAGER ##
-#####################
-
-resource "stackit_secretsmanager_instance" "this" {
- 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
-}
-
-# used for vault provider to access the secrets manager instance
-# resource "stackit_secretsmanager_user" "default" {
-# 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
-# }
\ No newline at end of file
diff --git a/modules/landing-zone/outputs.tf b/modules/landing-zone/outputs.tf
deleted file mode 100644
index d88bbcb..0000000
--- a/modules/landing-zone/outputs.tf
+++ /dev/null
@@ -1,14 +0,0 @@
-output "project_container_id" {
- description = "The container ID of the created STACKIT project."
- 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.this.name
-}
\ No newline at end of file
diff --git a/modules/management/5-observability.tf b/modules/management/5-observability.tf
deleted file mode 100644
index 9ab399f..0000000
--- a/modules/management/5-observability.tf
+++ /dev/null
@@ -1,34 +0,0 @@
-###################
-## OBSERVABILITY ##
-###################
-
-# resource "stackit_observability_instance" "this" {
-# project_id = stackit_resourcemanager_project.project.project_id
-# name = local.naming_pattern
-# plan_name = "Observability-Starter-EU01"
-# # acl = ["1.1.1.1/32", "2.2.2.2/32"]
-# logs_retention_days = 30
-# traces_retention_days = 30
-# metrics_retention_days = 90
-# metrics_retention_days_5m_downsampling = 90
-# metrics_retention_days_1h_downsampling = 90
-# }
-
-# resource "stackit_observability_credential" "this" {
-# project_id = stackit_resourcemanager_project.project.project_id
-# instance_id = stackit_observability_instance.this.instance_id
-# description = "Default credential for accessing the Observability Instance"
-# }
-
-# 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 = jsonencode(
-# {
-# USERNAME = "${stackit_observability_credential.this.username}",
-# PASSWORD = "${stackit_observability_credential.this.password}"
-# }
-# )
-# }
\ No newline at end of file
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..2e26940
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,61 @@
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.10 |
+| [stackit](#requirement\_stackit) | 0.93.0 |
+| [time](#requirement\_time) | 0.13.1 |
+| [vault](#requirement\_vault) | 5.7.0 |
+
+## Providers
+
+No providers.
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [connectivity](#module\_connectivity) | ./modules/connectivity | 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](#input\_connectivity) | Connectivity configuration including DNS zones, network area, and firewall. Set firewall/network\_area to null to skip deployment. | object({
dns_zones = optional(map(object({
dns_name = string
name = optional(string, null)
contact_email = optional(string, null)
type = optional(string, "primary")
acl = optional(string, null)
description = optional(string, null)
default_ttl = optional(number, 3600)
})), {})
network_area = optional(object({
ranges = list(string)
transfer_network = string
min_prefix_length = optional(number, 24)
max_prefix_length = optional(number, 28)
default_prefix_length = optional(number, 28)
}), null)
firewall = optional(object({
zone = string
flavor = string
name = string
volume_performance_class = optional(string, "storage_premium_perf4")
volume_size = optional(number, 16)
lan_network_range = string
wan_network_range = string
lan_ip = optional(string, null)
wan_ip = optional(string, null)
}), null)
}) | `null` | no |
+| [devops](#input\_devops) | DevOps module configuration. Set to null to skip deployment. | object({
git_flavor = optional(string, null)
allowed_network_ranges = optional(list(string), ["0.0.0.0/0"])
}) | `null` | no |
+| [labels](#input\_labels) | Additional labels to apply to all resources. | `map(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)
})), [])
})) | `{}` | no |
+| [observability](#input\_observability) | Observability instance configuration for the management module. Set to null to skip observability deployment. | object({
plan_name = optional(string, "Observability-Starter-EU01")
acl = optional(list(string), [])
logs_retention_days = optional(number, 30)
traces_retention_days = optional(number, 30)
metrics_retention_days = optional(number, 90)
metrics_retention_days_5m_downsampling = optional(number, 90)
metrics_retention_days_1h_downsampling = optional(number, 90)
}) | `null` | 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 |
+| [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no |
+| [rm\_folders](#input\_rm\_folders) | Map of resource manager folders to create under the root organization. | map(object({
name = string
description = optional(string, null)
owner_emails = list(string)
reader_emails = list(string)
})) | {
"landing_zones_corporate": {
"name": "Landing Zones - Corporate 3",
"owner_emails": [],
"reader_emails": []
},
"landing_zones_public": {
"name": "Landing Zones - Public 3",
"owner_emails": [],
"reader_emails": []
},
"platform": {
"name": "Platform 3",
"owner_emails": [],
"reader_emails": []
},
"sandboxes": {
"name": "Sandboxes 3",
"owner_emails": [],
"reader_emails": []
}
} | 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\_firewall\_public\_ip](#output\_connectivity\_firewall\_public\_ip) | The public IP of the firewall. |
+| [connectivity\_network\_area\_id](#output\_connectivity\_network\_area\_id) | The network area ID created by the regional module. |
+| [connectivity\_project\_id](#output\_connectivity\_project\_id) | The project ID of the 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/src/backend.tf b/src/backend.tf
new file mode 100644
index 0000000..c9266ff
--- /dev/null
+++ b/src/backend.tf
@@ -0,0 +1,14 @@
+# terraform {
+# backend "s3" {
+# bucket = "map(object({
dns_name = string
name = optional(string, null)
contact_email = optional(string, null)
type = optional(string, "primary")
acl = optional(string, null)
description = optional(string, null)
default_ttl = optional(number, 3600)
})) | `{}` | no |
+| [firewall](#input\_firewall) | Firewall configuration. Set to null to skip firewall deployment (network area and routing are still created). lan\_network\_range and wan\_network\_range must be CIDRs within the network area range. lan\_ip and wan\_ip are optional; when omitted, the 5th address of the respective prefix is used (STACKIT reserves the first usable address as the gateway). | object({
zone = string
flavor = string
name = string
volume_performance_class = optional(string, "storage_premium_perf4")
volume_size = optional(number, 16)
lan_network_range = string
wan_network_range = string
lan_ip = optional(string, null)
wan_ip = optional(string, null)
}) | `null` | no |
+| [labels](#input\_labels) | Additional labels to apply to all resources. | `map(string)` | `{}` | no |
+| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-hub-prod". | `string` | n/a | yes |
+| [network\_area](#input\_network\_area) | Network area configuration including IP ranges, transfer network, and prefix length settings. | object({
ranges = list(string)
transfer_network = string
min_prefix_length = optional(number, 24)
max_prefix_length = optional(number, 28)
default_prefix_length = optional(number, 28)
default_nameservers = optional(list(string), ["1.0.0.1", "1.1.1.1"])
}) | n/a | yes |
+| [network\_area\_name](#input\_network\_area\_name) | Name of the network area to create for this region. | `string` | `null` | no |
+| [organization\_id](#input\_organization\_id) | Organization ID, required for network area and route configuration. | `string` | n/a | yes |
+| [owner\_email](#input\_owner\_email) | Email address of the owner for the project. 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\_name](#input\_project\_name) | Name of the STACKIT project to create. Falls back to naming\_pattern if not set. | `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
+
+| Name | Description |
+|------|-------------|
+| [dns\_zone\_dns\_names](#output\_dns\_zone\_dns\_names) | Map of DNS zone keys to their DNS names |
+| [dns\_zone\_ids](#output\_dns\_zone\_ids) | Map of DNS zone keys to their zone IDs |
+| [firewall\_next\_hop\_ip](#output\_firewall\_next\_hop\_ip) | The IP address to be used as next hop for the default route in the landing zones (firewall WAN IP). |
+| [firewall\_public\_ip](#output\_firewall\_public\_ip) | The public IP address of the firewall WAN interface. |
+| [network\_area\_id](#output\_network\_area\_id) | The ID of the created network area. |
+| [project\_container\_id](#output\_project\_container\_id) | The container ID of the created STACKIT project. |
+| [project\_id](#output\_project\_id) | The project ID of the created STACKIT project. |
+| [project\_name](#output\_project\_name) | The name of the created STACKIT project. |
+
\ No newline at end of file
diff --git a/src/modules/connectivity/outputs.tf b/src/modules/connectivity/outputs.tf
new file mode 100644
index 0000000..025db01
--- /dev/null
+++ b/src/modules/connectivity/outputs.tf
@@ -0,0 +1,39 @@
+output "dns_zone_dns_names" {
+ description = "Map of DNS zone keys to their DNS names"
+ value = { for k, z in stackit_dns_zone.this : k => z.dns_name }
+}
+
+output "dns_zone_ids" {
+ description = "Map of DNS zone keys to their zone IDs"
+ value = { for k, z in stackit_dns_zone.this : k => z.zone_id }
+}
+
+output "firewall_next_hop_ip" {
+ description = "The IP address to be used as next hop for the default route in the landing zones (firewall WAN IP)."
+ value = var.firewall != null ? stackit_network_interface.lan[0].ipv4 : null
+}
+
+output "firewall_public_ip" {
+ description = "The public IP address of the firewall WAN interface."
+ value = var.firewall != null ? stackit_public_ip.wan-ip[0].ip : null
+}
+
+output "network_area_id" {
+ description = "The ID of the created network area."
+ value = stackit_network_area.this.network_area_id
+}
+
+output "project_container_id" {
+ description = "The container ID of the created STACKIT project."
+ 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.this.name
+}
diff --git a/modules/governance/terraform.tf b/src/modules/connectivity/terraform.tf
similarity index 66%
rename from modules/governance/terraform.tf
rename to src/modules/connectivity/terraform.tf
index 851721e..7932297 100644
--- a/modules/governance/terraform.tf
+++ b/src/modules/connectivity/terraform.tf
@@ -6,5 +6,9 @@ terraform {
source = "stackitcloud/stackit"
version = ">=0.93.0"
}
+ time = {
+ source = "hashicorp/time"
+ version = ">= 0.13.0"
+ }
}
}
\ No newline at end of file
diff --git a/src/modules/connectivity/variables.tf b/src/modules/connectivity/variables.tf
new file mode 100644
index 0000000..15ba03b
--- /dev/null
+++ b/src/modules/connectivity/variables.tf
@@ -0,0 +1,93 @@
+variable "dns_zones" {
+ type = map(object({
+ dns_name = string
+ name = optional(string, null)
+ contact_email = optional(string, null)
+ type = optional(string, "primary")
+ acl = optional(string, null)
+ description = optional(string, null)
+ default_ttl = optional(number, 3600)
+ }))
+ description = "Map of DNS zone keys to DNS zone configuration. Name defaults to dns_name if not set."
+ default = {}
+}
+
+variable "firewall" {
+ type = object({
+ zone = string
+ flavor = string
+ name = string
+ volume_performance_class = optional(string, "storage_premium_perf4")
+ volume_size = optional(number, 16)
+ lan_network_range = string
+ wan_network_range = string
+ lan_ip = optional(string, null)
+ wan_ip = optional(string, null)
+ })
+ description = "Firewall configuration. Set to null to skip firewall deployment (network area and routing are still created). lan_network_range and wan_network_range must be CIDRs within the network area range. lan_ip and wan_ip are optional; when omitted, the 5th address of the respective prefix is used (STACKIT reserves the first usable address as the gateway)."
+ default = null
+
+ validation {
+ condition = var.firewall == null || 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 "labels" {
+ type = map(string)
+ description = "Additional labels to apply to all resources."
+ default = {}
+}
+
+variable "naming_pattern" {
+ type = string
+ description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-hub-prod\"."
+}
+
+variable "network_area" {
+ type = object({
+ ranges = list(string)
+ transfer_network = string
+ min_prefix_length = optional(number, 24)
+ max_prefix_length = optional(number, 28)
+ default_prefix_length = optional(number, 28)
+ default_nameservers = optional(list(string), ["1.0.0.1", "1.1.1.1"])
+ })
+ description = "Network area configuration including IP ranges, transfer network, and prefix length settings."
+}
+
+variable "network_area_name" {
+ type = string
+ description = "Name of the network area to create for this region."
+ default = null
+}
+
+variable "organization_id" {
+ type = string
+ description = "Organization ID, required for network area and route configuration."
+}
+
+variable "owner_email" {
+ type = string
+ description = "Email address of the owner for the project. Required for STACKIT resource manager."
+}
+
+variable "parent_container_id" {
+ type = string
+ description = "Parent container ID (folder or organization) where the project will be created."
+}
+
+variable "project_name" {
+ type = string
+ description = "Name of the STACKIT project to create. Falls back to naming_pattern if not set."
+ 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 = []
+}
\ No newline at end of file
diff --git a/modules/devops/1-project.tf b/src/modules/devops/1-project.tf
similarity index 100%
rename from modules/devops/1-project.tf
rename to src/modules/devops/1-project.tf
diff --git a/modules/devops/2-git.tf b/src/modules/devops/2-git.tf
similarity index 100%
rename from modules/devops/2-git.tf
rename to src/modules/devops/2-git.tf
diff --git a/modules/devops/README.md b/src/modules/devops/README.md
similarity index 95%
rename from modules/devops/README.md
rename to src/modules/devops/README.md
index c2deca5..6577001 100644
--- a/modules/devops/README.md
+++ b/src/modules/devops/README.md
@@ -1,22 +1,22 @@
-### Requirements
+## Requirements
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.10 |
-| [stackit](#requirement\_stackit) | >=0.88.0 |
+| [stackit](#requirement\_stackit) | >=0.93.0 |
-### Providers
+## Providers
| Name | Version |
|------|---------|
| [stackit](#provider\_stackit) | 0.88.0 |
-### Modules
+## Modules
No modules.
-### Resources
+## Resources
| Name | Type |
|------|------|
@@ -24,22 +24,22 @@ No modules.
| [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
+## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
-| [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 |
-| [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 |
| [allowed\_network\_ranges](#input\_allowed\_network\_ranges) | List of allowed network ranges for Git instance ACL. | `list(string)` | [| no | +| [company\_name](#input\_company\_name) | Name of the company folder to create. | `string` | n/a | yes | | [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 | +| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-hub-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` | `null` | no | +| [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\_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. |
"0.0.0.0/0"
]
list(object({
role = string
subject = string
})) | `[]` | no |
-### Outputs
+## Outputs
| Name | Description |
|------|-------------|
diff --git a/modules/devops/outputs.tf b/src/modules/devops/outputs.tf
similarity index 100%
rename from modules/devops/outputs.tf
rename to src/modules/devops/outputs.tf
diff --git a/modules/connectivity-regional/terraform.tf b/src/modules/devops/terraform.tf
similarity index 100%
rename from modules/connectivity-regional/terraform.tf
rename to src/modules/devops/terraform.tf
diff --git a/modules/devops/variables.tf b/src/modules/devops/variables.tf
similarity index 98%
rename from modules/devops/variables.tf
rename to src/modules/devops/variables.tf
index 631f67f..87deb26 100644
--- a/modules/devops/variables.tf
+++ b/src/modules/devops/variables.tf
@@ -50,7 +50,7 @@ variable "project_name" {
variable "naming_pattern" {
type = string
- description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-net-prod\"."
+ description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-hub-prod\"."
}
variable "role_assignments" {
diff --git a/modules/governance/1-rm-folders.tf b/src/modules/governance/1-rm-folders.tf
similarity index 90%
rename from modules/governance/1-rm-folders.tf
rename to src/modules/governance/1-rm-folders.tf
index 3e5f758..df3800e 100644
--- a/modules/governance/1-rm-folders.tf
+++ b/src/modules/governance/1-rm-folders.tf
@@ -32,7 +32,11 @@ resource "stackit_resourcemanager_folder" "this" {
name = each.value.name
parent_container_id = var.organization_id
owner_email = var.owner_email
- labels = length(var.labels) > 0 ? var.labels : null # provider bug: empty map becomes null after apply
+ # labels = length(var.labels) > 0 ? var.labels : null # provider bug: empty map becomes null after apply
+
+ lifecycle {
+ ignore_changes = [labels]
+ }
}
################################
diff --git a/modules/governance/2-custom-roles.tf b/src/modules/governance/2-custom-roles.tf
similarity index 100%
rename from modules/governance/2-custom-roles.tf
rename to src/modules/governance/2-custom-roles.tf
diff --git a/modules/governance/3-organization-roles.tf b/src/modules/governance/3-organization-roles.tf
similarity index 100%
rename from modules/governance/3-organization-roles.tf
rename to src/modules/governance/3-organization-roles.tf
diff --git a/modules/governance/README.md b/src/modules/governance/README.md
similarity index 95%
rename from modules/governance/README.md
rename to src/modules/governance/README.md
index 537d4a2..2bfd363 100644
--- a/modules/governance/README.md
+++ b/src/modules/governance/README.md
@@ -1,22 +1,23 @@
-### Requirements
+## Requirements
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.10 |
-| [stackit](#requirement\_stackit) | >=0.88.0 |
+| [stackit](#requirement\_stackit) | >=0.93.0 |
+| [time](#requirement\_time) | >= 0.13.0 |
-### Providers
+## Providers
| Name | Version |
|------|---------|
-| [stackit](#provider\_stackit) | 0.88.0 |
+| [stackit](#provider\_stackit) | 0.93.0 |
-### Modules
+## Modules
No modules.
-### Resources
+## Resources
| Name | Type |
|------|------|
@@ -27,19 +28,19 @@ No modules.
| [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
+## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
-| [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 |
| [organization\_auditors](#input\_organization\_auditors) | List of organization role assignments for organization auditors. | `list(string)` | `[]` | no |
+| [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 |
| [organization\_owners](#input\_organization\_owners) | List of organization role assignments for organization owners. | `list(string)` | `[]` | no |
+| [owner\_email](#input\_owner\_email) | Email address of the owner for the folders. Required for STACKIT resource manager. | `string` | n/a | yes |
| [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
+## Outputs
| Name | Description |
|------|-------------|
diff --git a/modules/governance/outputs.tf b/src/modules/governance/outputs.tf
similarity index 100%
rename from modules/governance/outputs.tf
rename to src/modules/governance/outputs.tf
diff --git a/modules/sandboxes/terraform.tf b/src/modules/governance/terraform.tf
similarity index 66%
rename from modules/sandboxes/terraform.tf
rename to src/modules/governance/terraform.tf
index 851721e..7932297 100644
--- a/modules/sandboxes/terraform.tf
+++ b/src/modules/governance/terraform.tf
@@ -6,5 +6,9 @@ terraform {
source = "stackitcloud/stackit"
version = ">=0.93.0"
}
+ time = {
+ source = "hashicorp/time"
+ version = ">= 0.13.0"
+ }
}
}
\ No newline at end of file
diff --git a/modules/governance/variables.tf b/src/modules/governance/variables.tf
similarity index 100%
rename from modules/governance/variables.tf
rename to src/modules/governance/variables.tf
diff --git a/modules/landing-zone/1-project.tf b/src/modules/landing-zone/1-project.tf
similarity index 100%
rename from modules/landing-zone/1-project.tf
rename to src/modules/landing-zone/1-project.tf
diff --git a/modules/landing-zone/2-rbac.tf b/src/modules/landing-zone/2-rbac.tf
similarity index 100%
rename from modules/landing-zone/2-rbac.tf
rename to src/modules/landing-zone/2-rbac.tf
diff --git a/modules/landing-zone/3-network.tf b/src/modules/landing-zone/3-network.tf
similarity index 65%
rename from modules/landing-zone/3-network.tf
rename to src/modules/landing-zone/3-network.tf
index d4d05da..57ff21e 100644
--- a/modules/landing-zone/3-network.tf
+++ b/src/modules/landing-zone/3-network.tf
@@ -2,7 +2,8 @@
## ROUTING ##
#############
resource "stackit_routing_table" "this" {
- count = var.network_area_id != null ? 1 : 0
+ count = var.corporate && var.firewall_next_hop_ip != null ? 1 : 0
+
organization_id = var.organization_id
network_area_id = var.network_area_id
name = var.naming_pattern
@@ -12,11 +13,11 @@ resource "stackit_routing_table" "this" {
}
resource "stackit_routing_table_route" "this" {
- count = var.network_area_id != null ? 1 : 0
- routing_table_id = stackit_routing_table.this[0].routing_table_id
+ count = var.corporate && var.firewall_next_hop_ip != null ? 1 : 0
- organization_id = var.organization_id
- network_area_id = var.network_area_id
+ routing_table_id = stackit_routing_table.this[0].routing_table_id
+ organization_id = var.organization_id
+ network_area_id = var.network_area_id
destination = {
type = "cidrv4"
@@ -35,14 +36,14 @@ resource "stackit_routing_table_route" "this" {
## NETWORK ##
#############
resource "stackit_network" "this" {
- count = var.network_area_id != null ? 1 : 0
- project_id = stackit_resourcemanager_project.this.project_id
+ count = var.corporate ? 1 : 0
name = "${var.naming_pattern}-routed"
+ project_id = stackit_resourcemanager_project.this.project_id
ipv4_prefix_length = var.network_prefix_length
routed = true
ipv4_nameservers = var.ipv4_nameservers
- routing_table_id = stackit_routing_table.this[0].routing_table_id
+ routing_table_id = var.firewall_next_hop_ip != null ? stackit_routing_table.this[0].routing_table_id : null
labels = local.labels
}
diff --git a/src/modules/landing-zone/4-secrets-manager.tf b/src/modules/landing-zone/4-secrets-manager.tf
new file mode 100644
index 0000000..ea454bf
--- /dev/null
+++ b/src/modules/landing-zone/4-secrets-manager.tf
@@ -0,0 +1,9 @@
+#####################
+## SECRETS MANAGER ##
+#####################
+
+resource "stackit_secretsmanager_instance" "this" {
+ project_id = stackit_resourcemanager_project.this.project_id
+ name = "${var.naming_pattern}-default"
+ acls = length(var.secretsmanager_acls) > 0 ? var.secretsmanager_acls : null
+}
\ No newline at end of file
diff --git a/modules/landing-zone/5-bucket.tf b/src/modules/landing-zone/5-bucket.tf
similarity index 67%
rename from modules/landing-zone/5-bucket.tf
rename to src/modules/landing-zone/5-bucket.tf
index 5f9733e..1cc34f3 100644
--- a/modules/landing-zone/5-bucket.tf
+++ b/src/modules/landing-zone/5-bucket.tf
@@ -29,17 +29,4 @@ resource "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
-}
-
-# 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
+}
\ No newline at end of file
diff --git a/modules/landing-zone/6-service-account.tf b/src/modules/landing-zone/6-service-account.tf
similarity index 63%
rename from modules/landing-zone/6-service-account.tf
rename to src/modules/landing-zone/6-service-account.tf
index 07fbddb..58e2aef 100644
--- a/modules/landing-zone/6-service-account.tf
+++ b/src/modules/landing-zone/6-service-account.tf
@@ -19,12 +19,4 @@ resource "stackit_service_account_key" "automation" {
rotate_when_changed = {
rotation = time_rotating.key_rotate.id
}
-}
-
-# 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
+}
\ No newline at end of file
diff --git a/src/modules/landing-zone/7-dns-zone.tf b/src/modules/landing-zone/7-dns-zone.tf
new file mode 100644
index 0000000..f5c6bbe
--- /dev/null
+++ b/src/modules/landing-zone/7-dns-zone.tf
@@ -0,0 +1,13 @@
+##############
+## DNS ZONE ##
+##############
+
+resource "stackit_dns_zone" "this" {
+ count = var.dns_zone_name != null ? 1 : 0
+
+ project_id = stackit_resourcemanager_project.this.project_id
+ name = var.dns_zone_name
+ dns_name = var.dns_zone_name
+ type = "primary"
+ default_ttl = 3600
+}
diff --git a/modules/landing-zone/README.md b/src/modules/landing-zone/README.md
similarity index 67%
rename from modules/landing-zone/README.md
rename to src/modules/landing-zone/README.md
index 21d0012..a340f21 100644
--- a/modules/landing-zone/README.md
+++ b/src/modules/landing-zone/README.md
@@ -1,60 +1,72 @@
-### Requirements
+## Requirements
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.5 |
-| [stackit](#requirement\_stackit) | >=0.88.0 |
+| [stackit](#requirement\_stackit) | >=0.93.0 |
| [time](#requirement\_time) | >=0.13.1 |
-### Providers
+## Providers
| Name | Version |
|------|---------|
-| [stackit](#provider\_stackit) | 0.88.0 |
+| [stackit](#provider\_stackit) | 0.93.0 |
| [time](#provider\_time) | 0.13.1 |
-### Modules
+## Modules
No modules.
-### Resources
+## Resources
| Name | Type |
|------|------|
| [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_dns_zone.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/dns_zone) | 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_routing_table.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/routing_table) | resource |
+| [stackit_routing_table_route.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/routing_table_route) | 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
+## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
+| [corporate](#input\_corporate) | Whether this landing zone uses corporate networking (network area + routing). Set to false for public internet. | `bool` | `false` | no |
| [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 |
+| [dns\_zone\_name](#input\_dns\_zone\_name) | Full DNS zone domain name for this landing zone. Set to null to skip DNS zone creation. | `string` | `null` | no |
+| [firewall\_next\_hop\_ip](#input\_firewall\_next\_hop\_ip) | IP address of the firewall next hop. | `string` | `null` | no |
+| [ipv4\_nameservers](#input\_ipv4\_nameservers) | List of IPv4 nameservers for the network. | `list(string)` | `null` | 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 |
+| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-hub-prod". | `string` | n/a | yes |
+| [network\_area\_id](#input\_network\_area\_id) | Network Area ID to deploy resources into. Required if corporate is true. | `string` | `null` | no |
| [network\_prefix\_length](#input\_network\_prefix\_length) | CIDR block prefix length for the project's network range. | `number` | `null` | no |
+| [organization\_id](#input\_organization\_id) | Container ID of the root organization. | `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\_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 |
+| [secretsmanager\_acls](#input\_secretsmanager\_acls) | List of ACL rules for the Secrets Manager instance. Set to empty list for no ACLs or null to skip Secrets Manager creation. | `list(string)` | `[]` | no |
-### Outputs
+## Outputs
| Name | Description |
|------|-------------|
+| [connected\_network\_area\_id](#output\_connected\_network\_area\_id) | The ID of the connected network area. |
+| [dns\_zone\_dns\_name](#output\_dns\_zone\_dns\_name) | The DNS name of the landing zone's child DNS zone. |
+| [dns\_zone\_id](#output\_dns\_zone\_id) | The ID of the landing zone's child DNS zone. |
+| [landing\_zone\_type](#output\_landing\_zone\_type) | The type of the landing zone, either 'corporate' or 'public'. |
| [project\_container\_id](#output\_project\_container\_id) | The container ID of the created STACKIT project. |
| [project\_id](#output\_project\_id) | The project ID of the created STACKIT project. |
| [project\_name](#output\_project\_name) | The name of the created STACKIT project. |
diff --git a/src/modules/landing-zone/outputs.tf b/src/modules/landing-zone/outputs.tf
new file mode 100644
index 0000000..64741fc
--- /dev/null
+++ b/src/modules/landing-zone/outputs.tf
@@ -0,0 +1,34 @@
+output "project_container_id" {
+ description = "The container ID of the created STACKIT project."
+ 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.this.name
+}
+
+output "dns_zone_dns_name" {
+ description = "The DNS name of the landing zone's child DNS zone."
+ value = var.dns_zone_name != null ? stackit_dns_zone.this[0].dns_name : null
+}
+
+output "dns_zone_id" {
+ description = "The ID of the landing zone's child DNS zone."
+ value = var.dns_zone_name != null ? stackit_dns_zone.this[0].zone_id : null
+}
+
+output "connected_network_area_id" {
+ description = "The ID of the connected network area."
+ value = try(var.network_area_id, null)
+}
+
+output "landing_zone_type" {
+ description = "The type of the landing zone, either 'corporate' or 'public'."
+ value = var.corporate ? "corporate" : "public"
+}
\ No newline at end of file
diff --git a/modules/landing-zone/terraform.tf b/src/modules/landing-zone/terraform.tf
similarity index 100%
rename from modules/landing-zone/terraform.tf
rename to src/modules/landing-zone/terraform.tf
diff --git a/modules/landing-zone/variables.tf b/src/modules/landing-zone/variables.tf
similarity index 73%
rename from modules/landing-zone/variables.tf
rename to src/modules/landing-zone/variables.tf
index 30d04df..797f6e3 100644
--- a/modules/landing-zone/variables.tf
+++ b/src/modules/landing-zone/variables.tf
@@ -14,7 +14,7 @@ variable "custom_roles" {
variable "naming_pattern" {
type = string
- description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-net-prod\"."
+ description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-hub-prod\"."
}
variable "labels" {
@@ -25,10 +25,16 @@ variable "labels" {
variable "network_area_id" {
type = string
- description = "Network Area ID to deploy resources into. Required if network is enabled."
+ description = "Network Area ID to deploy resources into. Required if corporate is true."
default = null
}
+variable "corporate" {
+ type = bool
+ description = "Whether this landing zone uses corporate networking (network area + routing). Set to false for public internet."
+ default = false
+}
+
variable "network_prefix_length" {
type = number
description = "CIDR block prefix length for the project's network range."
@@ -70,4 +76,16 @@ variable "ipv4_nameservers" {
type = list(string)
description = "List of IPv4 nameservers for the network."
default = null
+}
+
+variable "dns_zone_name" {
+ type = string
+ description = "Full DNS zone domain name for this landing zone. Set to null to skip DNS zone creation."
+ default = null
+}
+
+variable "secretsmanager_acls" {
+ type = list(string)
+ description = "List of ACL rules for the Secrets Manager instance. Set to empty list for no ACLs or null to skip Secrets Manager creation."
+ default = []
}
\ No newline at end of file
diff --git a/modules/management/1-project.tf b/src/modules/management/1-project.tf
similarity index 100%
rename from modules/management/1-project.tf
rename to src/modules/management/1-project.tf
diff --git a/modules/management/2-secrets-manager.tf b/src/modules/management/2-secrets-manager.tf
similarity index 100%
rename from modules/management/2-secrets-manager.tf
rename to src/modules/management/2-secrets-manager.tf
diff --git a/modules/management/3-bucket.tf b/src/modules/management/3-bucket.tf
similarity index 93%
rename from modules/management/3-bucket.tf
rename to src/modules/management/3-bucket.tf
index b9117c5..c0a731e 100644
--- a/modules/management/3-bucket.tf
+++ b/src/modules/management/3-bucket.tf
@@ -33,7 +33,7 @@ resource "stackit_objectstorage_credential" "this" {
resource "vault_kv_secret_v2" "object_storage_credentials" {
mount = stackit_secretsmanager_instance.this.instance_id
- name = "service_account_key_${stackit_service_account.automation.name}"
+ name = "object_storage_credentials_${replace(var.naming_pattern, "-", "_")}"
cas = 1
delete_all_versions = true
data_json = jsonencode(
diff --git a/modules/management/4-service-account.tf b/src/modules/management/4-service-account.tf
similarity index 92%
rename from modules/management/4-service-account.tf
rename to src/modules/management/4-service-account.tf
index 24c3f77..96d66b8 100644
--- a/modules/management/4-service-account.tf
+++ b/src/modules/management/4-service-account.tf
@@ -29,7 +29,7 @@ resource "stackit_authorization_organization_role_assignment" "sa_owner" {
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}"
+ name = "service_account_key_${replace(var.naming_pattern, "-", "_")}"
cas = 1
delete_all_versions = true
data_json = stackit_service_account_key.automation.json
diff --git a/src/modules/management/5-observability.tf b/src/modules/management/5-observability.tf
new file mode 100644
index 0000000..9e54ebc
--- /dev/null
+++ b/src/modules/management/5-observability.tf
@@ -0,0 +1,40 @@
+###################
+## OBSERVABILITY ##
+###################
+
+resource "stackit_observability_instance" "this" {
+ count = var.observability != null ? 1 : 0
+
+ project_id = stackit_resourcemanager_project.this.project_id
+ name = var.naming_pattern
+ plan_name = var.observability.plan_name
+ acl = var.observability.acl
+ logs_retention_days = var.observability.logs_retention_days
+ traces_retention_days = var.observability.traces_retention_days
+ metrics_retention_days = var.observability.metrics_retention_days
+ metrics_retention_days_5m_downsampling = var.observability.metrics_retention_days_5m_downsampling
+ metrics_retention_days_1h_downsampling = var.observability.metrics_retention_days_1h_downsampling
+}
+
+resource "stackit_observability_credential" "this" {
+ count = var.observability != null ? 1 : 0
+
+ project_id = stackit_resourcemanager_project.this.project_id
+ instance_id = stackit_observability_instance.this[0].instance_id
+ description = "Default credential for accessing the Observability Instance"
+}
+
+resource "vault_kv_secret_v2" "observability" {
+ count = var.observability != null ? 1 : 0
+
+ mount = stackit_secretsmanager_instance.this.instance_id
+ name = "observability_credentials_${replace(var.naming_pattern, "-", "_")}"
+ cas = 1
+ delete_all_versions = true
+ data_json = jsonencode(
+ {
+ USERNAME = stackit_observability_credential.this[0].username,
+ PASSWORD = stackit_observability_credential.this[0].password
+ }
+ )
+}
\ No newline at end of file
diff --git a/modules/management/README.md b/src/modules/management/README.md
similarity index 67%
rename from modules/management/README.md
rename to src/modules/management/README.md
index c2094cd..5e9411d 100644
--- a/modules/management/README.md
+++ b/src/modules/management/README.md
@@ -1,24 +1,26 @@
-### Requirements
+## Requirements
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.10 |
-| [stackit](#requirement\_stackit) | >=0.88.0 |
+| [stackit](#requirement\_stackit) | >=0.93.0 |
| [time](#requirement\_time) | >=0.13.1 |
+| [vault](#requirement\_vault) | >=5.7.0 |
-### Providers
+## Providers
| Name | Version |
|------|---------|
-| [stackit](#provider\_stackit) | 0.88.0 |
+| [stackit](#provider\_stackit) | 0.93.0 |
| [time](#provider\_time) | 0.13.1 |
+| [vault](#provider\_vault) | 5.9.0 |
-### Modules
+## Modules
No modules.
-### Resources
+## Resources
| Name | Type |
|------|------|
@@ -28,31 +30,39 @@ No modules.
| [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_observability_credential.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/observability_credential) | resource |
+| [stackit_observability_instance.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/observability_instance) | 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 |
+| [vault_kv_secret_v2.object_storage_credentials](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/resources/kv_secret_v2) | resource |
+| [vault_kv_secret_v2.observability](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/resources/kv_secret_v2) | resource |
+| [vault_kv_secret_v2.service_account_key_automation](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/resources/kv_secret_v2) | resource |
-### Inputs
+## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
-| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-net-prod". | `string` | n/a | yes |
+| [labels](#input\_labels) | Additional labels to apply to all folders. | `map(string)` | `{}` | no |
+| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-hub-prod". | `string` | n/a | yes |
+| [observability](#input\_observability) | Observability instance configuration. Set to null to skip observability deployment. | object({
plan_name = optional(string, "Observability-Starter-EU01")
acl = optional(list(string), [])
logs_retention_days = optional(number, 30)
traces_retention_days = optional(number, 30)
metrics_retention_days = optional(number, 90)
metrics_retention_days_5m_downsampling = optional(number, 90)
metrics_retention_days_1h_downsampling = optional(number, 90)
}) | `null` | no |
| [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 |
-| [labels](#input\_labels) | Additional labels to apply to all folders. | `map(string)` | `{}` | 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
+## Outputs
| Name | Description |
|------|-------------|
| [project\_container\_id](#output\_project\_container\_id) | The container ID of the created STACKIT project. |
| [project\_id](#output\_project\_id) | The project ID of the created STACKIT project. |
| [project\_name](#output\_project\_name) | The name of the created STACKIT project. |
+| [secretsmanager\_password](#output\_secretsmanager\_password) | The password of the default Secrets Manager user. |
+| [secretsmanager\_username](#output\_secretsmanager\_username) | The username of the default Secrets Manager user. |
| [service\_account\_email](#output\_service\_account\_email) | The email of the created service account. |
\ No newline at end of file
diff --git a/modules/management/outputs.tf b/src/modules/management/outputs.tf
similarity index 86%
rename from modules/management/outputs.tf
rename to src/modules/management/outputs.tf
index 7946dda..345a4c1 100644
--- a/modules/management/outputs.tf
+++ b/src/modules/management/outputs.tf
@@ -27,4 +27,9 @@ output "secretsmanager_password" {
description = "The password of the default Secrets Manager user."
value = stackit_secretsmanager_user.default.password
sensitive = true
+}
+
+output "bucket_name_tfstate" {
+ description = "The name of the tfstate object storage bucket."
+ value = stackit_objectstorage_bucket.tfstate.name
}
\ No newline at end of file
diff --git a/modules/management/terraform.tf b/src/modules/management/terraform.tf
similarity index 100%
rename from modules/management/terraform.tf
rename to src/modules/management/terraform.tf
diff --git a/modules/management/variables.tf b/src/modules/management/variables.tf
similarity index 60%
rename from modules/management/variables.tf
rename to src/modules/management/variables.tf
index 2fd3874..e536fd2 100644
--- a/modules/management/variables.tf
+++ b/src/modules/management/variables.tf
@@ -6,7 +6,7 @@ variable "project_name" {
variable "naming_pattern" {
type = string
- description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-net-prod\"."
+ description = "Naming prefix for all resources in this module, e.g. \"myco-pltfm-hub-prod\"."
}
variable "labels" {
@@ -30,6 +30,20 @@ variable "parent_container_id" {
description = "Parent container ID (folder or organization) where the project will be created."
}
+variable "observability" {
+ type = object({
+ plan_name = optional(string, "Observability-Starter-EU01")
+ acl = optional(list(string), [])
+ logs_retention_days = optional(number, 30)
+ traces_retention_days = optional(number, 30)
+ metrics_retention_days = optional(number, 90)
+ metrics_retention_days_5m_downsampling = optional(number, 90)
+ metrics_retention_days_1h_downsampling = optional(number, 90)
+ })
+ description = "Observability instance configuration. Set to null to skip observability deployment."
+ default = null
+}
+
variable "role_assignments" {
type = list(object({
role = string
diff --git a/modules/sandboxes/README.md b/src/modules/sandboxes/README.md
similarity index 77%
rename from modules/sandboxes/README.md
rename to src/modules/sandboxes/README.md
index aca42a5..9f87852 100644
--- a/modules/sandboxes/README.md
+++ b/src/modules/sandboxes/README.md
@@ -1,38 +1,37 @@
-### Requirements
+## Requirements
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.10 |
-| [stackit](#requirement\_stackit) | >=0.88.0 |
+| [stackit](#requirement\_stackit) | >=0.93.0 |
-### Providers
+## Providers
| Name | Version |
|------|---------|
-| [stackit](#provider\_stackit) | 0.88.0 |
+| [stackit](#provider\_stackit) | 0.93.0 |
-### Modules
+## Modules
No modules.
-### Resources
+## Resources
| Name | Type |
|------|------|
| [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
+## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
-| [naming\_pattern](#input\_naming\_pattern) | Naming prefix for all resources in this module, e.g. "myco-pltfm-net-prod". | `string` | n/a | yes |
+| [naming\_prefix](#input\_naming\_prefix) | Naming prefix for all resources in this module. | `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\_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
+## Outputs
| Name | Description |
|------|-------------|
diff --git a/modules/sandboxes/main.tf b/src/modules/sandboxes/main.tf
similarity index 100%
rename from modules/sandboxes/main.tf
rename to src/modules/sandboxes/main.tf
diff --git a/modules/sandboxes/outputs.tf b/src/modules/sandboxes/outputs.tf
similarity index 100%
rename from modules/sandboxes/outputs.tf
rename to src/modules/sandboxes/outputs.tf
diff --git a/modules/devops/terraform.tf b/src/modules/sandboxes/terraform.tf
similarity index 100%
rename from modules/devops/terraform.tf
rename to src/modules/sandboxes/terraform.tf
diff --git a/modules/sandboxes/variables.tf b/src/modules/sandboxes/variables.tf
similarity index 100%
rename from modules/sandboxes/variables.tf
rename to src/modules/sandboxes/variables.tf
diff --git a/src/outputs.tf b/src/outputs.tf
new file mode 100644
index 0000000..647d98e
--- /dev/null
+++ b/src/outputs.tf
@@ -0,0 +1,56 @@
+#############
+## OUTPUTS ##
+#############
+
+output "governance_folder_ids" {
+ description = "Map of governance folder names to their container IDs."
+ value = module.governance.folder_container_ids
+}
+
+output "devops_project_id" {
+ description = "The project ID of the DevOps project."
+ value = length(module.devops) > 0 ? module.devops[0].project_id : null
+}
+
+output "management_project_id" {
+ description = "The project ID of the Management project."
+ value = module.management.project_id
+}
+
+output "management_bucket_name_tfstate" {
+ description = "The name of the Management tfstate object storage bucket."
+ value = module.management.bucket_name_tfstate
+}
+
+output "connectivity_network_area_id" {
+ description = "The network area ID created by the regional module."
+ value = try(module.connectivity[0].network_area_id, null)
+}
+
+output "connectivity_project_id" {
+ description = "The project ID of the connectivity project."
+ value = try(module.connectivity[0].project_id, null)
+}
+
+output "connectivity_firewall_public_ip" {
+ description = "The public IP of the firewall."
+ value = try(module.connectivity[0].firewall_public_ip, null)
+}
+
+output "sandbox_projects" {
+ description = "The created sandbox projects."
+ value = length(module.sandboxes) > 0 ? module.sandboxes[0].projects : {}
+}
+
+output "landing_zone_projects" {
+ description = "Map of landing zone project IDs."
+ value = {
+ for k, v in module.landing_zone : k => {
+ project_id = v.project_id
+ project_name = v.project_name
+ dns_zone_name = v.dns_zone_dns_name
+ landing_zone_type = v.landing_zone_type
+ connected_network_area_id = v.connected_network_area_id == null ? "" : v.connected_network_area_id
+ }
+ }
+}
diff --git a/examples/01-standalone/providers.tf b/src/providers.tf
similarity index 100%
rename from examples/01-standalone/providers.tf
rename to src/providers.tf
diff --git a/examples/01-standalone/terraform.tf b/src/terraform.tf
similarity index 100%
rename from examples/01-standalone/terraform.tf
rename to src/terraform.tf
diff --git a/src/tests/hub_spoke.tftest.hcl b/src/tests/hub_spoke.tftest.hcl
new file mode 100644
index 0000000..227857b
--- /dev/null
+++ b/src/tests/hub_spoke.tftest.hcl
@@ -0,0 +1,102 @@
+variables {
+ owner_email = "matthias.hauber@prodyna.com"
+ company_name = "Test Corp"
+ company_code = "tst"
+ organization_id = "b76b54b6-f55d-41a1-b3c3-30252f8b97cc"
+ region = "eu01"
+
+ labels = {
+ managed_by = "opentofu"
+ environment = "test"
+ }
+
+ rm_folders = {
+ platform = {
+ name = "Platform - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ landing_zones_corporate = {
+ name = "Landing Zones - Corporate - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ landing_zones_public = {
+ name = "Landing Zones - Public - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ sandboxes = {
+ name = "Sandboxes - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ }
+
+ devops = {
+ git_flavor = "git-10"
+ allowed_network_ranges = ["0.0.0.0/0"]
+ }
+
+ connectivity = {
+ dns_zones = {
+ "test-corp" = {
+ dns_name = "test-corp.stackit.run"
+ }
+ }
+ network_area = {
+ ranges = ["10.0.0.0/16"]
+ transfer_network = "10.255.0.0/24"
+ min_prefix_length = 24
+ max_prefix_length = 28
+ default_prefix_length = 25
+ }
+ }
+
+ sandboxes = []
+
+ landing_zones = {
+ "test-corporate" = {
+ project_name = "Test Corporate LZ"
+ project_code = "tcorp"
+ owner_email = "matthias.hauber@prodyna.com"
+ env = "test"
+ corporate = true
+ network_prefix_length = 25
+ }
+ "test-public" = {
+ project_name = "Test Public LZ"
+ project_code = "tpub"
+ owner_email = "matthias.hauber@prodyna.com"
+ env = "test"
+ corporate = false
+ }
+ }
+}
+
+# Validates hub-spoke without firewall: connectivity module is created, no firewall.
+# Resource-computed outputs (network_area_id, project_id) are unknown during plan
+# and cannot be asserted — a successful plan is the primary validation.
+run "hub_spoke_plan" {
+ command = plan
+
+ assert {
+ condition = output.connectivity_firewall_public_ip == null
+ error_message = "Firewall public IP must be null when no firewall is configured."
+ }
+
+ assert {
+ condition = length(output.landing_zone_projects) == 2
+ error_message = "Expected 2 landing zones to be created."
+ }
+
+ assert {
+ condition = output.landing_zone_projects["test-corporate"].landing_zone_type == "corporate"
+ error_message = "test-corporate must be a corporate landing zone."
+ }
+
+ assert {
+ condition = output.landing_zone_projects["test-public"].landing_zone_type == "public"
+ error_message = "test-public must be a public landing zone."
+ }
+}
\ No newline at end of file
diff --git a/src/tests/hub_spoke_firewall.tftest.hcl b/src/tests/hub_spoke_firewall.tftest.hcl
new file mode 100644
index 0000000..de1a06c
--- /dev/null
+++ b/src/tests/hub_spoke_firewall.tftest.hcl
@@ -0,0 +1,113 @@
+variables {
+ owner_email = "matthias.hauber@prodyna.com"
+ company_name = "Test Corp"
+ company_code = "tst"
+ organization_id = "b76b54b6-f55d-41a1-b3c3-30252f8b97cc"
+ region = "eu01"
+
+ labels = {
+ managed_by = "opentofu"
+ environment = "test"
+ }
+
+ rm_folders = {
+ platform = {
+ name = "Platform - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ landing_zones_corporate = {
+ name = "Landing Zones - Corporate - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ landing_zones_public = {
+ name = "Landing Zones - Public - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ sandboxes = {
+ name = "Sandboxes - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ }
+
+ devops = {
+ git_flavor = "git-10"
+ allowed_network_ranges = ["0.0.0.0/0"]
+ }
+
+ observability = {
+ plan_name = "Observability-Starter-EU01"
+ }
+
+ connectivity = {
+ dns_zones = {
+ "test-corp" = {
+ dns_name = "test-corp.stackit.run"
+ }
+ }
+ network_area = {
+ ranges = ["10.0.0.0/16"]
+ transfer_network = "10.255.0.0/24"
+ min_prefix_length = 24
+ max_prefix_length = 28
+ default_prefix_length = 25
+ }
+ firewall = {
+ zone = "eu01-m"
+ flavor = "c1.2"
+ lan_network_range = "10.0.0.0/28"
+ wan_network_range = "10.0.0.16/28"
+ name = "pfsense-2.7.2"
+ }
+ }
+
+ sandboxes = [
+ {
+ project_name = "Test Sandbox"
+ project_owner_email = "matthias.hauber@prodyna.com"
+ }
+ ]
+
+ landing_zones = {
+ "test-corporate" = {
+ project_name = "Test Corporate LZ"
+ project_code = "tcorp"
+ owner_email = "matthias.hauber@prodyna.com"
+ env = "test"
+ corporate = true
+ network_prefix_length = 25
+ }
+ "test-public" = {
+ project_name = "Test Public LZ"
+ project_code = "tpub"
+ owner_email = "matthias.hauber@prodyna.com"
+ env = "test"
+ corporate = false
+ }
+ }
+}
+
+# Validates hub-spoke-firewall variant. Resource-computed outputs (network_area_id,
+# project_id, firewall_public_ip) are unknown during plan and cannot be asserted —
+# a successful plan is the primary validation.
+run "hub_spoke_firewall_plan" {
+ command = plan
+
+ assert {
+ condition = length(output.landing_zone_projects) == 2
+ error_message = "Expected 2 landing zones to be created."
+ }
+
+ assert {
+ condition = output.landing_zone_projects["test-corporate"].landing_zone_type == "corporate"
+ error_message = "test-corporate must be a corporate landing zone."
+ }
+
+ assert {
+ condition = output.landing_zone_projects["test-public"].landing_zone_type == "public"
+ error_message = "test-public must be a public landing zone."
+ }
+}
diff --git a/src/tests/standalone.tftest.hcl b/src/tests/standalone.tftest.hcl
new file mode 100644
index 0000000..60c2081
--- /dev/null
+++ b/src/tests/standalone.tftest.hcl
@@ -0,0 +1,89 @@
+variables {
+ owner_email = "matthias.hauber@prodyna.com"
+ company_name = "Test Corp"
+ company_code = "tst"
+ organization_id = "b76b54b6-f55d-41a1-b3c3-30252f8b97cc"
+ region = "eu01"
+
+ labels = {
+ managed_by = "opentofu"
+ environment = "test"
+ }
+
+ rm_folders = {
+ platform = {
+ name = "Platform - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ landing_zones_corporate = {
+ name = "Landing Zones - Corporate - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ landing_zones_public = {
+ name = "Landing Zones - Public - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ sandboxes = {
+ name = "Sandboxes - TST"
+ owner_emails = []
+ reader_emails = []
+ }
+ }
+
+ devops = {
+ git_flavor = "git-10"
+ allowed_network_ranges = ["0.0.0.0/0"]
+ }
+
+ # No connectivity — standalone flavour has no network area or firewall
+ connectivity = null
+
+ sandboxes = [
+ {
+ project_name = "Test Sandbox"
+ project_owner_email = "matthias.hauber@prodyna.com"
+ }
+ ]
+
+ landing_zones = {
+ "test-public" = {
+ project_name = "Test Public LZ"
+ project_code = "tpub"
+ owner_email = "matthias.hauber@prodyna.com"
+ env = "test"
+ corporate = false
+ }
+ }
+}
+
+run "standalone_plan" {
+ command = plan
+
+ assert {
+ condition = output.connectivity_network_area_id == null
+ error_message = "Network area must be null in standalone configuration."
+ }
+
+ assert {
+ condition = output.connectivity_project_id == null
+ error_message = "Connectivity project must not be created in standalone configuration."
+ }
+
+ assert {
+ condition = output.connectivity_firewall_public_ip == null
+ error_message = "Firewall public IP must be null in standalone configuration."
+ }
+
+ assert {
+ condition = length(output.landing_zone_projects) == 1
+ error_message = "Expected 1 landing zone to be created."
+ }
+
+ assert {
+ condition = output.landing_zone_projects["test-public"].landing_zone_type == "public"
+ error_message = "test-public must be a public landing zone."
+ }
+}
diff --git a/src/variables.tf b/src/variables.tf
new file mode 100644
index 0000000..ae6c2d0
--- /dev/null
+++ b/src/variables.tf
@@ -0,0 +1,181 @@
+#############
+## GENERAL ##
+#############
+
+variable "owner_email" {
+ type = string
+ description = "Email address of the owner. Required for STACKIT resource manager."
+}
+
+variable "company_name" {
+ type = string
+ description = "Name of the company."
+}
+
+variable "company_code" {
+ type = string
+ description = "Company code used in resource naming conventions."
+}
+
+variable "organization_id" {
+ type = string
+ description = "Container ID of the root organization."
+}
+
+variable "region" {
+ type = string
+ description = "STACKIT region for regional resources."
+ default = "eu01"
+}
+
+variable "labels" {
+ type = map(string)
+ description = "Additional labels to apply to all resources."
+ default = {}
+}
+
+variable "organization_owners" {
+ type = list(string)
+ description = "List of organization owners."
+ default = []
+}
+
+variable "organization_auditors" {
+ type = list(string)
+ description = "List of organization auditors."
+ default = []
+}
+
+variable "devops" {
+ type = object({
+ git_flavor = optional(string, null)
+ allowed_network_ranges = optional(list(string), ["0.0.0.0/0"])
+ })
+ description = "DevOps module configuration. Set to null to skip deployment."
+ default = null
+}
+
+variable "observability" {
+ type = object({
+ plan_name = optional(string, "Observability-Starter-EU01")
+ acl = optional(list(string), [])
+ logs_retention_days = optional(number, 30)
+ traces_retention_days = optional(number, 30)
+ metrics_retention_days = optional(number, 90)
+ metrics_retention_days_5m_downsampling = optional(number, 90)
+ metrics_retention_days_1h_downsampling = optional(number, 90)
+ })
+ description = "Observability instance configuration for the management module. Set to null to skip observability deployment."
+ default = null
+}
+
+variable "rm_folders" {
+ type = map(object({
+ name = string
+ description = optional(string, null)
+ owner_emails = list(string)
+ reader_emails = list(string)
+ }))
+ description = "Map of resource manager folders to create under the root organization."
+ default = {
+ platform = {
+ name = "Platform 3"
+ owner_emails = []
+ reader_emails = []
+ }
+ landing_zones_corporate = {
+ name = "Landing Zones - Corporate 3"
+ owner_emails = []
+ reader_emails = []
+ }
+ landing_zones_public = {
+ name = "Landing Zones - Public 3"
+ owner_emails = []
+ reader_emails = []
+ }
+ sandboxes = {
+ name = "Sandboxes 3"
+ owner_emails = []
+ reader_emails = []
+ }
+ }
+}
+
+##################
+## CONNECTIVITY ##
+##################
+
+variable "connectivity" {
+ type = object({
+ dns_zones = optional(map(object({
+ dns_name = string
+ name = optional(string, null)
+ contact_email = optional(string, null)
+ type = optional(string, "primary")
+ acl = optional(string, null)
+ description = optional(string, null)
+ default_ttl = optional(number, 3600)
+ })), {})
+ network_area = optional(object({
+ ranges = list(string)
+ transfer_network = string
+ min_prefix_length = optional(number, 24)
+ max_prefix_length = optional(number, 28)
+ default_prefix_length = optional(number, 28)
+ }), null)
+ firewall = optional(object({
+ zone = string
+ flavor = string
+ name = string
+ volume_performance_class = optional(string, "storage_premium_perf4")
+ volume_size = optional(number, 16)
+ lan_network_range = string
+ wan_network_range = string
+ lan_ip = optional(string, null)
+ wan_ip = optional(string, null)
+ }), null)
+ })
+ description = "Connectivity configuration including DNS zones, network area, and firewall. Set firewall/network_area to null to skip deployment."
+ default = null
+}
+
+###############
+## SANDBOXES ##
+###############
+
+variable "sandboxes" {
+ type = list(object({
+ project_name = string
+ owner_emails = optional(list(string))
+ project_owner_email = string
+ }))
+ description = "List of sandboxes to create."
+ default = []
+}
+
+##################
+## LANDING ZONE ##
+##################
+
+variable "landing_zones" {
+ type = 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)
+ })), [])
+ }))
+ description = "Map of landing zones to create. Set corporate = true for network area connectivity, false for public."
+ default = {}
+}