diff --git a/.gitignore b/.gitignore index c6a173d4..4a826b80 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ __pycache__/ website/src/generated/ website/public/assets/building-block-logos/ website/public/assets/logos/ +website/public/assets/reference-architecture-logos/ website/public/assets/*.json .worktrees/ diff --git a/AGENTS.md b/AGENTS.md index 02196203..fa106ef6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -309,6 +309,9 @@ getting-started steps, and shared responsibility matrix. ### Conventions - File name: `-.md` (e.g. `azure-kubernetes.md`, `stackit-kubernetes.md`). +- Logo: a flat markdown architecture pairs with a sibling `.png` (e.g. `azure-kubernetes.png`); + a directory-based one may ship its own `logo.png` or simply reuse `buildingblock/logo.png`. + Without a logo the website falls back to the logos of the architecture's cloud providers. - `buildingBlocks[].path` must match a module path under `modules/` (e.g. `azure/aks`). - The Markdown body should include a **Mermaid diagram** showing how blocks relate. - Include a **shared responsibility matrix** (platform team vs. application team) with ✅ / ❌ emojis. @@ -318,6 +321,8 @@ getting-started steps, and shared responsibility matrix. - [ ] Markdown file in `reference-architectures/` with YAML front-matter - [ ] `name`, `description`, `cloudProviders`, and `buildingBlocks` fields present +- [ ] Logo present — sibling `.png` for a flat markdown architecture, `logo.png` or + `buildingblock/logo.png` for a directory-based one - [ ] Every `buildingBlocks[].path` references an existing module in `modules/` - [ ] Every `buildingBlocks[].role` has a one-sentence description - [ ] Body includes: overview, architecture diagram, how-it-works, getting started, shared responsibilities diff --git a/Taskfile.yml b/Taskfile.yml index fda4f116..ca8e1b6a 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -18,3 +18,10 @@ tasks: desc: Run scorecard across all modules cmds: - node tools/scorecard/scorecard.mjs {{.CLI_ARGS}} + + website:dev: + desc: Start the website dev server locally at http://localhost:4200/ + dir: website + cmds: + - yarn install + - yarn start {{.CLI_ARGS}} diff --git a/index.ts b/index.ts index bb701cc5..2052f1a0 100644 --- a/index.ts +++ b/index.ts @@ -237,6 +237,9 @@ export interface ReferenceArchitecture { cloudProviders: string[]; buildingBlocks: ReferenceArchitectureBuildingBlock[]; body: string; + // Public asset path of the architecture's own logo, null when it has none and consumers + // should fall back to the logos of its cloud providers. + logo: string | null; sourceUrl: string | null; // Set when the reference architecture ships its own meshstack_integration.tf and can be // imported into meshStack directly, the same way a building block is imported. @@ -245,6 +248,28 @@ export interface ReferenceArchitecture { modulePath: string | null; } +// Copies a reference architecture's logo into the website assets and returns its public path. +// A directory-based architecture may ship a logo.png of its own or simply reuse the one from its +// buildingblock/; a flat markdown architecture pairs with a sibling .png. +function copyReferenceArchitectureLogoToAssets(id: string, codeDir: string | null): string | null { + const candidates = codeDir + ? [path.join(codeDir, "logo.png"), path.join(codeDir, "buildingblock", "logo.png")] + : [path.join(refArchRoot, `${id}.png`)]; + + const sourcePath = candidates.find((candidate) => fs.existsSync(candidate)); + if (!sourcePath) return null; + + const refArchAssetsDir = path.resolve( + __dirname, + "website/public/assets/reference-architecture-logos" + ); + + fs.mkdirSync(refArchAssetsDir, { recursive: true }); + fs.copyFileSync(sourcePath, path.join(refArchAssetsDir, `${id}.png`)); + + return `assets/reference-architecture-logos/${id}.png`; +} + // Parses a reference architecture from its front-matter/body markdown file. // `codeDir`, when set, is the directory checked for a `meshstack_integration.tf` that makes // this reference architecture importable, the same way a building block is imported. @@ -284,6 +309,7 @@ function parseReferenceArchitecture(filePath: string, id: string, codeDir: strin cloudProviders: data.cloudProviders || [], buildingBlocks: data.buildingBlocks, body, + logo: copyReferenceArchitectureLogoToAssets(id, codeDir), sourceUrl, integrationSourceUrl, folderUrl, diff --git a/reference-architectures/azure-kubernetes.png b/reference-architectures/azure-kubernetes.png new file mode 100644 index 00000000..94c65032 Binary files /dev/null and b/reference-architectures/azure-kubernetes.png differ diff --git a/reference-architectures/stackit-hub-spoke/README.md b/reference-architectures/stackit-hub-spoke/README.md deleted file mode 100644 index 311cc25c..00000000 --- a/reference-architectures/stackit-hub-spoke/README.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -name: STACKIT Hub-and-Spoke Network -description: > - Bootstraps a STACKIT sandbox platform together with a hub-and-spoke network topology: - a shared network-area address plan (hub) and a self-service routed-network building - block (spoke) for application teams. -cloudProviders: - - stackit -buildingBlocks: - - path: stackit/project - role: Provisions a STACKIT Project with role assignments. - - path: stackit/network-area - role: Provisions the hub network area with the shared IPv4 address plan. - - path: stackit/network - role: Lets application teams self-service order routed spoke networks inside their projects. ---- - -# STACKIT Hub-and-Spoke Network - -## Overview - -The **STACKIT Hub-and-Spoke Network** reference architecture turns a bare STACKIT organization -into a self-service-ready meshStack platform with IPAM built in: a shared network-area address -plan (the hub) that all tenant projects draw from, and a self-service routed-network building -block (the spoke) application teams can order inside their own projects. It builds on the -[STACKIT Sandbox Landing Zone](../stackit-sandbox-landingzone/README.md) foundation. - -**Target audience:** - -- **Platform engineers** bootstrapping a new STACKIT organization who want network segmentation - from day one, without hand-wiring a network area and its address plan separately. -- **Application teams** who need a dedicated IPv4 subnet inside their STACKIT project without - manually coordinating CIDR ranges with the platform team. - -## Architecture Diagram - -```mermaid -flowchart LR - subgraph Bootstrap["Platform Bootstrap"] - PLAT[STACKIT Project Platform] - PLAT --> DEFLZ[Default Landing Zone] - PLAT --> NETLZ[Networked Landing Zone] - end - - NETLZ -->|new projects land in| HUB - - subgraph HUB["Hub"] - NA["Network Area
shared IPv4 address plan"] - end - - HUB --> TP1[Tenant Project A] - HUB --> TP2[Tenant Project B] - - subgraph SPOKES["Spokes (self-service)"] - TP1 -->|orders stackit/network| NET1[Routed Network] - TP2 -->|orders stackit/network| NET2[Routed Network] - end - - NET1 -.draws CIDR from.-> NA - NET2 -.draws CIDR from.-> NA -``` - -Application teams order a routed network into their own project via the self-service -`stackit/network` building block; each order draws its subnet from the hub's address plan, -so no two spokes can accidentally collide on CIDR ranges. - -## How It Works - -Running this reference architecture: - -1. Sources the [STACKIT Sandbox Landing Zone](../stackit-sandbox-landingzone/README.md) to - create the landing-zone folder, backplane project, and the **STACKIT Project** platform with - its default landing zone. -2. Registers the [`stackit/network-area`](../../modules/stackit/network-area) building block - definition and immediately orders **one instance** of it in the platform team's own - workspace — this is the hub's IPv4 address plan. -3. Registers the [`stackit/network`](../../modules/stackit/network) building block definition - (`TENANT_LEVEL`) so application teams can self-service order routed networks (spokes) inside - their STACKIT projects, drawing from the hub's address plan. -4. Provisions an additional **networked landing zone**, tagged with the hub's network area ID, - so new STACKIT projects created against it are placed in the hub's network area. - -## Getting Started - -### Prerequisites - -| Requirement | Description | -|-----------------------|------------------------------------------------------------------------------------| -| STACKIT organization | With a service account key that has `resource-manager.admin` on the organization. | -| CIDR plan | A non-overlapping IPv4 address plan chosen up front for the hub network ranges and transfer network. | - -### Deployment Order - -Order the **STACKIT Hub and Spoke Network** building block once per workspace. It creates the -platform, the hub network area instance, and registers the spoke `stackit/network` building -block in a single apply. Application teams can then order `stackit/network` inside their own -STACKIT projects once those projects exist. - -## Shared Responsibilities - -| Responsibility | Platform Team | Application Team | -|---------------------------------------------------------------------------|:---:|:---:| -| Provision the STACKIT platform and landing zones | ✅ | ❌ | -| Provision the hub network area and choose its address plan | ✅ | ❌ | -| Register the spoke `stackit/network` building block for self-service | ✅ | ❌ | -| Order spoke networks inside their STACKIT projects | ❌ | ✅ | -| Use the assigned subnet for their workloads | ❌ | ✅ | diff --git a/reference-architectures/stackit-hub-spoke/buildingblock/README.md b/reference-architectures/stackit-hub-spoke/buildingblock/README.md deleted file mode 100644 index c7e3803e..00000000 --- a/reference-architectures/stackit-hub-spoke/buildingblock/README.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -name: STACKIT Hub and Spoke Network -supportedPlatforms: - - stackit -description: Bootstraps a STACKIT sandbox platform together with a hub-and-spoke network topology, provisioning the hub network area and registering the self-service spoke network building block. ---- - -This building block composes three Hub modules into a single orderable offering: it sources -[`stackit-sandbox-landingzone`](../../stackit-sandbox-landingzone/buildingblock) to bootstrap the -STACKIT platform, registers [`modules/stackit/network-area`](../../../modules/stackit/network-area) -and immediately orders one instance of it as the hub address plan, and registers -[`modules/stackit/network`](../../../modules/stackit/network) so application teams can self-service -order routed spoke networks inside their STACKIT projects. New STACKIT projects are placed in the -hub's network area via an additional `networked` landing zone tagged with the hub's network area ID. - -The user-facing readme is maintained inline in the `readme` field of the -`meshstack_building_block_definition` in -[`../meshstack_integration.tf`](../meshstack_integration.tf). - - -## Requirements - -| Name | Version | -| ---- | ------- | -| [terraform](#requirement\_terraform) | >= 1.12.0 | -| [meshstack](#requirement\_meshstack) | >= 0.24.0 | -| [stackit](#requirement\_stackit) | >= 0.99.0 | - -## Modules - -| Name | Source | Version | -| ---- | ------ | ------- | -| [foundation](#module\_foundation) | github.com/meshcloud/meshstack-hub//reference-architectures/stackit-sandbox-landingzone/buildingblock | main | -| [network\_area\_integration](#module\_network\_area\_integration) | github.com/meshcloud/meshstack-hub//modules/stackit/network-area | main | -| [network\_integration](#module\_network\_integration) | github.com/meshcloud/meshstack-hub//modules/stackit/network | main | - -## Resources - -| Name | Type | -| ---- | ---- | -| [meshstack_building_block.network_area_hub](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block) | resource | -| [meshstack_landingzone.networked](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/landingzone) | resource | -| [meshstack_landingzone.foundation_default](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/data-sources/landingzone) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -| ---- | ----------- | ---- | ------- | :------: | -| [hub](#input\_hub) | `git_ref`: meshstack-hub reference used to source the nested foundation, network-area, and network integration modules. `const` so it can be interpolated into the module source at init time.

`bbd_draft`: Forwarded as-is to those nested integrations' own `hub.bbd_draft`, so their building block definition draft state tracks this building block's own release state. |
object({
git_ref = optional(string, "main")
bbd_draft = optional(bool, true)
})
|
{
"bbd_draft": true,
"git_ref": "main"
}
| no | -| [hub\_default\_nameservers](#input\_hub\_default\_nameservers) | Default IPv4 nameservers assigned to networks created within the hub network area. | `list(string)` | `[]` | no | -| [hub\_default\_prefix\_length](#input\_hub\_default\_prefix\_length) | Default prefix length used for networks created within the hub network area when none is specified. | `number` | `28` | no | -| [hub\_max\_prefix\_length](#input\_hub\_max\_prefix\_length) | Maximum prefix length allowed for networks created within the hub network area. | `number` | `28` | no | -| [hub\_min\_prefix\_length](#input\_hub\_min\_prefix\_length) | Minimum prefix length allowed for networks created within the hub network area. | `number` | `24` | no | -| [hub\_network\_area\_name](#input\_hub\_network\_area\_name) | Name of the hub STACKIT network area instance. | `string` | `"hub"` | no | -| [hub\_network\_ranges](#input\_hub\_network\_ranges) | List of IPv4 CIDR ranges available to projects within the hub network area. | `list(string)` |
[
"10.0.0.0/16"
]
| no | -| [hub\_transfer\_network](#input\_hub\_transfer\_network) | IPv4 CIDR range used as the transfer network between the hub network area and connected networks. | `string` | `"10.1.255.0/24"` | no | -| [network\_area\_tag\_name](#input\_network\_area\_tag\_name) | Name of the meshStack landing zone tag whose value is the hub network area's ID. Forwarded to the foundation's nested STACKIT Project integration (so it knows which tag to read) and set on the `networked` landing zone created here (with the hub's network area ID as its value). | `string` | `"StackitNetworkArea"` | no | -| [platform\_identifier](#input\_platform\_identifier) | Identifier for the STACKIT sandbox platform created in meshStack (letters, digits and dashes only). | `string` | n/a | yes | -| [role\_mapping](#input\_role\_mapping) | Default mapping from meshStack roles to STACKIT project roles for the nested STACKIT Project integration. Values can be built-in STACKIT roles or custom STACKIT role names. | `map(list(string))` |
{
"admin": [
"owner"
],
"reader": [
"reader"
],
"user": [
"editor"
]
}
| no | -| [stackit\_org](#input\_stackit\_org) | STACKIT organization UUID under which the landing-zone folder, backplane project and tenant projects are created. | `string` | n/a | yes | -| [stackit\_organization\_onboarding\_enabled](#input\_stackit\_organization\_onboarding\_enabled) | Whether the nested STACKIT Project integration adds meshStack project users to the STACKIT organization before applying project-level role assignments. Disable if organization membership is managed outside this landing zone. | `bool` | `true` | no | -| [stackit\_owner\_email](#input\_stackit\_owner\_email) | Owner email assigned to the STACKIT resourcemanager folder and backplane project. | `string` | n/a | yes | -| [stackit\_service\_account\_key](#input\_stackit\_service\_account\_key) | STACKIT service account key JSON with `resource-manager.admin` on the organization. Used to create the landing-zone folder and backplane project. | `string` | n/a | yes | -| [tags](#input\_tags) | Tags forwarded to the nested foundation, network-area, and network integrations. `landingzone` tags are applied to the created landing zones; `building_block` tags are applied to the nested building block definitions. |
object({
landingzone = map(list(string))
building_block = map(list(string))
})
|
{
"building_block": {},
"landingzone": {}
}
| no | -| [tenant\_network\_max\_prefix\_length](#input\_tenant\_network\_max\_prefix\_length) | Maximum allowed IPv4 prefix length for the spoke network BBD's prefix length input, offered to application teams ordering spoke networks. | `number` | `28` | no | -| [tenant\_network\_min\_prefix\_length](#input\_tenant\_network\_min\_prefix\_length) | Minimum allowed IPv4 prefix length for the spoke network BBD's prefix length input, offered to application teams ordering spoke networks. | `number` | `24` | no | -| [use\_global\_location](#input\_use\_global\_location) | Use the global location instead of creating a dedicated location for this platform. | `bool` | `false` | no | -| [workspace](#input\_workspace) | Identifier of the meshStack workspace that will own the created platform, location, landing zones, and the hub network-area instance. | `string` | n/a | yes | - -## Outputs - -| Name | Description | -| ---- | ----------- | -| [backplane\_project\_id](#output\_backplane\_project\_id) | Project ID of the STACKIT backplane project that hosts the service account used for tenant project creation. | -| [backplane\_project\_url](#output\_backplane\_project\_url) | Deep link to the backplane project in the STACKIT portal. | -| [lz\_folder\_container\_id](#output\_lz\_folder\_container\_id) | Container ID of the STACKIT resourcemanager folder created for the landing zone. Tenant projects are created inside this folder. | - diff --git a/reference-architectures/stackit-hub-spoke/buildingblock/SUMMARY.md.tftpl b/reference-architectures/stackit-hub-spoke/buildingblock/SUMMARY.md.tftpl deleted file mode 100644 index fbfd8db6..00000000 --- a/reference-architectures/stackit-hub-spoke/buildingblock/SUMMARY.md.tftpl +++ /dev/null @@ -1,14 +0,0 @@ -# STACKIT Hub and Spoke Network: **${platform_identifier}** - -## Details - -| Property | Value | -|----------|-------| -| **Backplane Project** | [Open in STACKIT Portal](${backplane_project_url}) (`${backplane_project_id}`) | -| **Landing Zone Folder** | `${lz_folder_container_id}` | -| **Networked Landing Zone** | `${networked_landingzone_name}` | -| **Hub Network Area** | @buildingblock[${network_area_hub_uuid}] | - -Application teams can now order the **STACKIT Network** building block inside their own projects in -the `${networked_landingzone_name}` landing zone to get a routed spoke network drawn from the hub's -address plan. diff --git a/reference-architectures/stackit-hub-spoke/buildingblock/logo.png b/reference-architectures/stackit-hub-spoke/buildingblock/logo.png deleted file mode 100644 index f346453f..00000000 Binary files a/reference-architectures/stackit-hub-spoke/buildingblock/logo.png and /dev/null differ diff --git a/reference-architectures/stackit-hub-spoke/buildingblock/main.tf b/reference-architectures/stackit-hub-spoke/buildingblock/main.tf deleted file mode 100644 index 8e9de323..00000000 --- a/reference-architectures/stackit-hub-spoke/buildingblock/main.tf +++ /dev/null @@ -1,104 +0,0 @@ -module "foundation" { - source = "github.com/meshcloud/meshstack-hub//reference-architectures/stackit-sandbox-landingzone/buildingblock?ref=${var.hub.git_ref}" - - workspace = var.workspace - use_global_location = var.use_global_location - stackit_org = var.stackit_org - stackit_owner_email = var.stackit_owner_email - stackit_service_account_key = var.stackit_service_account_key - platform_identifier = var.platform_identifier - tags = var.tags - role_mapping = var.role_mapping - stackit_organization_onboarding_enabled = var.stackit_organization_onboarding_enabled - network_area_tag_name = var.network_area_tag_name - hub = var.hub -} - -module "network_area_integration" { - source = "github.com/meshcloud/meshstack-hub//modules/stackit/network-area?ref=${var.hub.git_ref}" - - stackit_organization_id = var.stackit_org - stackit_project_id = module.foundation.backplane_project_id - - meshstack = { owning_workspace_identifier = var.workspace, tags = var.tags.building_block } - hub = var.hub -} - -module "network_integration" { - source = "github.com/meshcloud/meshstack-hub//modules/stackit/network?ref=${var.hub.git_ref}" - - stackit_organization_id = var.stackit_org - stackit_project_id = module.foundation.backplane_project_id - stackit_network_min_prefix_length = var.tenant_network_min_prefix_length - stackit_network_max_prefix_length = var.tenant_network_max_prefix_length - - meshstack = { owning_workspace_identifier = var.workspace, tags = var.tags.building_block } - hub = var.hub -} - -resource "meshstack_building_block" "network_area_hub" { - wait_for_completion = true - depends_on = [module.network_area_integration] - - spec = { - building_block_definition_version_ref = { - uuid = module.network_area_integration.building_block_definition.version_ref.uuid - } - display_name = "Hub Network Area" - target_ref = { kind = "meshWorkspace", name = var.workspace } - - inputs = { - network_area_name = { - value = jsonencode(var.hub_network_area_name) - } - network_ranges = { - value = jsonencode(jsonencode(var.hub_network_ranges)) - } - transfer_network = { - value = jsonencode(var.hub_transfer_network) - } - min_prefix_length = { - value = jsonencode(var.hub_min_prefix_length) - } - max_prefix_length = { - value = jsonencode(var.hub_max_prefix_length) - } - default_prefix_length = { - value = jsonencode(var.hub_default_prefix_length) - } - default_nameservers = { - value = jsonencode(jsonencode(var.hub_default_nameservers)) - } - } - } -} - -# Looks up the default landing zone that `module.foundation` already registered (via the nested -# `modules/stackit` platform integration), without needing new outputs threaded through -# sandbox-landingzone. Used below only as an input into the independent `networked` landing zone — -# never fed back into `module.foundation` itself, which would create a dependency cycle. -data "meshstack_landingzone" "foundation_default" { - metadata = { name = "${var.platform_identifier}-default" } - depends_on = [module.foundation] -} - -resource "meshstack_landingzone" "networked" { - metadata = { - name = "${var.platform_identifier}-networked" - owned_by_workspace = var.workspace - tags = merge(var.tags.landingzone, { - (var.network_area_tag_name) = [jsondecode(meshstack_building_block.network_area_hub.status.outputs["network_area_id"].value)] - }) - } - - spec = { - display_name = "STACKIT Networked" - description = "STACKIT landing zone whose projects are placed in the hub network area." - automate_deletion_approval = true - automate_deletion_replication = true - - platform_ref = data.meshstack_landingzone.foundation_default.spec.platform_ref - platform_properties = { custom = {} } - mandatory_building_block_refs = data.meshstack_landingzone.foundation_default.spec.mandatory_building_block_refs - } -} diff --git a/reference-architectures/stackit-hub-spoke/buildingblock/outputs.tf b/reference-architectures/stackit-hub-spoke/buildingblock/outputs.tf deleted file mode 100644 index 84aac60d..00000000 --- a/reference-architectures/stackit-hub-spoke/buildingblock/outputs.tf +++ /dev/null @@ -1,26 +0,0 @@ -output "lz_folder_container_id" { - value = module.foundation.lz_folder_container_id - description = "Container ID of the STACKIT resourcemanager folder created for the landing zone. Tenant projects are created inside this folder." -} - -output "backplane_project_id" { - value = module.foundation.backplane_project_id - description = "Project ID of the STACKIT backplane project that hosts the service account used for tenant project creation." -} - -output "backplane_project_url" { - value = module.foundation.backplane_project_url - description = "Deep link to the backplane project in the STACKIT portal." -} - -output "summary" { - description = "Summary of the meshStack resources created by this reference architecture." - value = templatefile("${path.module}/SUMMARY.md.tftpl", { - platform_identifier = var.platform_identifier - backplane_project_id = module.foundation.backplane_project_id - backplane_project_url = module.foundation.backplane_project_url - lz_folder_container_id = module.foundation.lz_folder_container_id - networked_landingzone_name = meshstack_landingzone.networked.metadata.name - network_area_hub_uuid = meshstack_building_block.network_area_hub.metadata.uuid - }) -} diff --git a/reference-architectures/stackit-hub-spoke/buildingblock/variables.tf b/reference-architectures/stackit-hub-spoke/buildingblock/variables.tf deleted file mode 100644 index 28562f6e..00000000 --- a/reference-architectures/stackit-hub-spoke/buildingblock/variables.tf +++ /dev/null @@ -1,141 +0,0 @@ -variable "workspace" { - type = string - nullable = false - description = "Identifier of the meshStack workspace that will own the created platform, location, landing zones, and the hub network-area instance." -} - -variable "use_global_location" { - type = bool - nullable = false - description = "Use the global location instead of creating a dedicated location for this platform." -} - -variable "stackit_org" { - type = string - nullable = false - description = "STACKIT organization UUID under which the landing-zone folder, backplane project and tenant projects are created." - - validation { - condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", var.stackit_org)) - error_message = "stackit_org must be a valid UUID." - } -} - -variable "stackit_owner_email" { - type = string - nullable = false - description = "Owner email assigned to the STACKIT resourcemanager folder and backplane project." -} - -variable "stackit_service_account_key" { - type = string - sensitive = true - nullable = false - description = "STACKIT service account key JSON with `resource-manager.admin` on the organization. Used to create the landing-zone folder and backplane project." -} - -variable "platform_identifier" { - type = string - nullable = false - description = "Identifier for the STACKIT sandbox platform created in meshStack (letters, digits and dashes only)." - - validation { - condition = can(regex("^[a-zA-Z0-9-]+$", var.platform_identifier)) - error_message = "platform_identifier must only contain letters, digits, and dashes." - } -} - -variable "tags" { - type = object({ - landingzone = map(list(string)) - building_block = map(list(string)) - }) - nullable = false - description = "Tags forwarded to the nested foundation, network-area, and network integrations. `landingzone` tags are applied to the created landing zones; `building_block` tags are applied to the nested building block definitions." -} - -variable "role_mapping" { - type = map(list(string)) - nullable = false - description = "Default mapping from meshStack roles to STACKIT project roles for the nested STACKIT Project integration. Values can be built-in STACKIT roles or custom STACKIT role names." -} - -variable "stackit_organization_onboarding_enabled" { - type = bool - nullable = false - description = "Whether the nested STACKIT Project integration adds meshStack project users to the STACKIT organization before applying project-level role assignments. Disable if organization membership is managed outside this landing zone." -} - -variable "network_area_tag_name" { - type = string - nullable = false - description = "Name of the meshStack landing zone tag whose value is the hub network area's ID. Forwarded to the foundation's nested STACKIT Project integration (so it knows which tag to read) and set on the `networked` landing zone created here (with the hub's network area ID as its value)." -} - -variable "hub_network_area_name" { - type = string - nullable = false - description = "Name of the hub STACKIT network area instance." -} - -variable "hub_network_ranges" { - type = list(string) - nullable = false - description = "List of IPv4 CIDR ranges available to projects within the hub network area." -} - -variable "hub_transfer_network" { - type = string - nullable = false - description = "IPv4 CIDR range used as the transfer network between the hub network area and connected networks. Must not overlap with `hub_network_ranges`." -} - -variable "hub_min_prefix_length" { - type = number - nullable = false - description = "Minimum prefix length allowed for networks created within the hub network area." -} - -variable "hub_max_prefix_length" { - type = number - nullable = false - description = "Maximum prefix length allowed for networks created within the hub network area." -} - -variable "hub_default_prefix_length" { - type = number - nullable = false - description = "Default prefix length used for networks created within the hub network area when none is specified." -} - -variable "hub_default_nameservers" { - type = list(string) - nullable = false - description = "Default IPv4 nameservers assigned to networks created within the hub network area." -} - -variable "tenant_network_min_prefix_length" { - type = number - nullable = false - description = "Minimum allowed IPv4 prefix length for the spoke network BBD's prefix length input, offered to application teams ordering spoke networks." -} - -variable "tenant_network_max_prefix_length" { - type = number - nullable = false - description = "Maximum allowed IPv4 prefix length for the spoke network BBD's prefix length input, offered to application teams ordering spoke networks." -} - -variable "hub" { - type = object({ - git_ref = optional(string, "main") - bbd_draft = optional(bool, true) - }) - const = true - default = { git_ref = "main", bbd_draft = true } - - description = <<-EOT - `git_ref`: meshstack-hub reference used to source the nested foundation, network-area, and network integration modules. `const` so it can be interpolated into the module source at init time. - `bbd_draft`: Forwarded as-is to those nested integrations' own `hub.bbd_draft`, so their building block definition draft state tracks this building block's own release state. - EOT -} diff --git a/reference-architectures/stackit-hub-spoke/meshstack_integration.tf b/reference-architectures/stackit-hub-spoke/meshstack_integration.tf deleted file mode 100644 index 86a36999..00000000 --- a/reference-architectures/stackit-hub-spoke/meshstack_integration.tf +++ /dev/null @@ -1,348 +0,0 @@ -variable "meshstack" { - type = object({ - owning_workspace_identifier = string - tags = optional(map(list(string)), {}) - }) - description = "Shared meshStack context. Tags are optional and propagated to building block definition metadata." -} - -variable "hub" { - type = object({ - git_ref = optional(string, "main") - bbd_draft = optional(bool, true) - }) - const = true - - default = { - git_ref = "main" - bbd_draft = true - } - - description = <<-EOT - `git_ref`: Hub release reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of the meshstack-hub repo. - `bbd_draft`: If true, the building block definition version is kept in draft mode. - EOT -} - -output "building_block_definition" { - description = "BBD is consumed in building block compositions." - value = { - uuid = meshstack_building_block_definition.this.metadata.uuid - version_ref = var.hub.bbd_draft ? meshstack_building_block_definition.this.version_latest : meshstack_building_block_definition.this.version_latest_release - } -} - -resource "meshstack_building_block_definition" "this" { - metadata = { - owned_by_workspace = var.meshstack.owning_workspace_identifier - tags = var.meshstack.tags - } - - spec = { - display_name = "STACKIT Hub and Spoke Network" - symbol = "https://raw.githubusercontent.com/meshcloud/meshstack-hub/${var.hub.git_ref}/reference-architectures/stackit-hub-spoke/buildingblock/logo.png" - description = "Bootstraps a STACKIT sandbox platform together with a hub-and-spoke network topology: a shared network-area address plan and a self-service routed-network building block for application teams." - support_url = "https://portal.stackit.cloud" - target_type = "WORKSPACE_LEVEL" - run_transparency = true - - readme = chomp(<<-EOT - Bootstraps a STACKIT sandbox platform together with a hub-and-spoke network topology: it - provisions a shared network-area address plan (the hub) and registers a self-service - routed-network building block (the spoke) that application teams can order inside their - own STACKIT projects. - - ## 🎯 When to use it - - Use this building block when you: - - want a STACKIT platform where all tenant projects draw from a single, non-overlapping - IPv4 address plan instead of STACKIT's default flat networking. - - need application teams to self-service order routed subnets inside their projects - without manually coordinating CIDR ranges with the platform team. - - are bootstrapping a new STACKIT organization and want the network foundation (hub) - provisioned in the same step as the platform itself. - - ## 💡 Usage examples - - **Example 1: Bootstrap a new STACKIT sandbox with hub-and-spoke networking** - A platform engineer orders this building block once for a workspace. It creates the STACKIT - platform, provisions the hub network area with a chosen CIDR plan, and registers the - **STACKIT Network** building block so application teams can request spokes. - - **Example 2: Order a spoke network** - Once the platform is bootstrapped, an application team orders the **STACKIT Network** - building block inside their own STACKIT project to get a routed subnet drawn from the hub's - address plan — no manual IPAM coordination needed. - - ## 📊 Shared Responsibility - - | Responsibility | Platform Team | Application Team | - |---|:---:|:---:| - | Provision the STACKIT platform, hub network area, and its address plan | ✅ | ❌ | - | Choose non-overlapping CIDR ranges for the hub | ✅ | ❌ | - | Register the spoke `STACKIT Network` building block for self-service | ✅ | ❌ | - | Order spoke networks inside their STACKIT projects | ❌ | ✅ | - | Use the assigned subnet for their workloads | ❌ | ✅ | - EOT - ) - } - - version_spec = { - draft = var.hub.bbd_draft - deletion_mode = "DELETE" - - # Ephemeral API key permissions for meshStack resources created by this building block and its - # nested foundation/network-area/network integrations (all part of the same Terraform run). - permissions = [ - "INTEGRATION_LIST", - "BUILDINGBLOCKDEFINITION_LIST", - "BUILDINGBLOCKDEFINITION_SAVE", - "BUILDINGBLOCKDEFINITION_DELETE", - "BUILDINGBLOCK_LIST", - "BUILDINGBLOCK_SAVE", - "BUILDINGBLOCK_DELETE", - "LANDINGZONE_LIST", - "LANDINGZONE_SAVE", - "LANDINGZONE_DELETE", - "PLATFORMINSTANCE_LIST", - "PLATFORMINSTANCE_SAVE", - "PLATFORMINSTANCE_DELETE" - ] - - implementation = { - terraform = { - terraform_version = "1.12.0" - repository_url = "https://github.com/meshcloud/meshstack-hub.git" - repository_path = "reference-architectures/stackit-hub-spoke/buildingblock" - ref_name = var.hub.git_ref - async = false - use_mesh_http_backend_fallback = true - } - } - - inputs = { - # ── STACKIT authentication (service account key supplied by the operator) ── - - stackit_service_account_key = { - display_name = "STACKIT Service Account Key" - description = "STACKIT service account key JSON with `resource-manager.admin` on the organization. Paste the full JSON as a secret input." - type = "CODE" - assignment_type = "USER_INPUT" - updateable_by_consumer = true - sensitive = {} - } - - hub = { - display_name = "Hub" - description = "JSON object with `git_ref` (meshstack-hub reference used to source the nested foundation, network-area, and network integration modules) and `bbd_draft` (forwarded to those nested integrations' own building block definition draft state)." - type = "CODE" - assignment_type = "STATIC" - argument = jsonencode(jsonencode(var.hub)) - } - - # ── Platform configuration (set by the platform team) ── - - stackit_org = { - display_name = "STACKIT Organization UUID" - description = "STACKIT organization UUID under which the landing-zone folder, backplane project and tenant projects are created." - type = "STRING" - assignment_type = "USER_INPUT" - value_validation_regex = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" - validation_regex_error_message = "STACKIT Organization UUID must be a valid UUID." - } - - stackit_owner_email = { - display_name = "STACKIT Owner Email" - description = "Owner email assigned to the STACKIT resourcemanager folder and backplane project." - type = "STRING" - assignment_type = "USER_INPUT" - } - - tags = { - display_name = "Tags" - description = "JSON object with `landingzone` and `building_block` tag maps forwarded to the nested foundation, network-area, and network integrations." - type = "CODE" - assignment_type = "USER_INPUT" - updateable_by_consumer = true - - default_value = jsonencode(jsonencode({ - landingzone = {} - building_block = {} - })) - } - - role_mapping = { - display_name = "STACKIT Project Role Mapping" - description = "JSON object mapping meshStack roles from project users to STACKIT project roles. Values can be built-in STACKIT roles or custom STACKIT role names." - type = "CODE" - assignment_type = "USER_INPUT" - updateable_by_consumer = true - - default_value = jsonencode(jsonencode({ - admin = ["owner"] - user = ["editor"] - reader = ["reader"] - })) - } - - stackit_organization_onboarding_enabled = { - display_name = "STACKIT Organization Onboarding Enabled" - description = "If true, the nested STACKIT Project integration adds meshStack project users to the STACKIT organization before applying project-level role assignments." - type = "BOOLEAN" - assignment_type = "USER_INPUT" - updateable_by_consumer = true - default_value = jsonencode(true) - } - - network_area_tag_name = { - display_name = "Network Area Tag Name" - description = "Name of the meshStack landing zone tag whose value is the hub network area's ID. Forwarded to the foundation's nested STACKIT Project integration and set on the `networked` landing zone created by this building block." - type = "STRING" - assignment_type = "USER_INPUT" - updateable_by_consumer = true - default_value = jsonencode("StackitNetworkArea") - } - - # ── Hub network area configuration ── - - hub_network_area_name = { - display_name = "Hub Network Area Name" - description = "Name of the hub STACKIT network area instance." - type = "STRING" - assignment_type = "USER_INPUT" - default_value = jsonencode("hub") - } - - hub_network_ranges = { - display_name = "Hub Network Ranges" - description = "JSON list of IPv4 CIDR ranges available to projects within the hub network area, e.g. [\"10.0.0.0/16\"]." - type = "CODE" - assignment_type = "USER_INPUT" - default_value = jsonencode(jsonencode(["10.0.0.0/16"])) - } - - hub_transfer_network = { - display_name = "Hub Transfer Network" - description = "IPv4 CIDR range used as the transfer network between the hub network area and connected networks. Must not overlap with the Hub Network Ranges." - type = "STRING" - assignment_type = "USER_INPUT" - default_value = jsonencode("10.1.255.0/24") - value_validation_regex = "^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$" - validation_regex_error_message = "Transfer network must be a valid IPv4 CIDR range, e.g. '10.1.255.0/24'." - } - - hub_min_prefix_length = { - display_name = "Hub Minimum Prefix Length" - description = "Minimum prefix length allowed for networks created within the hub network area." - type = "INTEGER" - assignment_type = "USER_INPUT" - default_value = jsonencode(24) - } - - hub_max_prefix_length = { - display_name = "Hub Maximum Prefix Length" - description = "Maximum prefix length allowed for networks created within the hub network area." - type = "INTEGER" - assignment_type = "USER_INPUT" - default_value = jsonencode(28) - } - - hub_default_prefix_length = { - display_name = "Hub Default Prefix Length" - description = "Default prefix length used for networks created within the hub network area when none is specified." - type = "INTEGER" - assignment_type = "USER_INPUT" - default_value = jsonencode(28) - } - - hub_default_nameservers = { - display_name = "Hub Default Nameservers" - description = "JSON list of default IPv4 nameservers assigned to networks created within the hub network area." - type = "CODE" - assignment_type = "USER_INPUT" - default_value = jsonencode(jsonencode([])) - } - - # ── Spoke network configuration (bounds offered to application teams) ── - - tenant_network_min_prefix_length = { - display_name = "Tenant Network Minimum Prefix Length" - description = "Minimum allowed IPv4 prefix length for the spoke network BBD's prefix length input, offered to application teams ordering spoke networks." - type = "INTEGER" - assignment_type = "USER_INPUT" - default_value = jsonencode(24) - } - - tenant_network_max_prefix_length = { - display_name = "Tenant Network Maximum Prefix Length" - description = "Maximum allowed IPv4 prefix length for the spoke network BBD's prefix length input, offered to application teams ordering spoke networks." - type = "INTEGER" - assignment_type = "USER_INPUT" - default_value = jsonencode(28) - } - - # ── meshStack context ── - - workspace = { - display_name = "Workspace Identifier" - description = "Workspace that will own the created platform, location, landing zones, and the hub network-area instance." - type = "STRING" - assignment_type = "WORKSPACE_IDENTIFIER" - } - - platform_identifier = { - display_name = "Platform Identifier" - description = "Identifier for the STACKIT sandbox platform created in meshStack (letters, digits and dashes only)." - type = "STRING" - assignment_type = "USER_INPUT" - value_validation_regex = "^[a-zA-Z0-9-]+$" - validation_regex_error_message = "platform_identifier must only contain letters, digits, and dashes." - } - - use_global_location = { - display_name = "Use Global Location" - description = "If true, use the existing global meshStack location instead of creating a dedicated location for this platform." - type = "BOOLEAN" - assignment_type = "USER_INPUT" - default_value = jsonencode(false) - } - } - - outputs = { - lz_folder_container_id = { - display_name = "LZ Folder Container ID" - type = "STRING" - assignment_type = "NONE" - } - - backplane_project_id = { - display_name = "Backplane Project ID" - type = "STRING" - assignment_type = "NONE" - } - - backplane_project_url = { - display_name = "Open Backplane Project" - type = "STRING" - assignment_type = "RESOURCE_URL" - } - - summary = { - display_name = "Summary" - type = "STRING" - assignment_type = "SUMMARY" - } - } - } -} - -terraform { - required_version = ">= 1.12.0" - - required_providers { - meshstack = { - source = "meshcloud/meshstack" - version = ">= 0.24.0" - } - } -} diff --git a/reference-architectures/stackit-kubernetes.png b/reference-architectures/stackit-kubernetes.png new file mode 100644 index 00000000..57c2c04b Binary files /dev/null and b/reference-architectures/stackit-kubernetes.png differ diff --git a/reference-architectures/stackit-landingzone/README.md b/reference-architectures/stackit-landingzone/README.md new file mode 100644 index 00000000..c8659b3b --- /dev/null +++ b/reference-architectures/stackit-landingzone/README.md @@ -0,0 +1,161 @@ +--- +name: STACKIT Landing Zone +description: > + Bootstraps a self-service-ready STACKIT platform: a dedicated STACKIT resourcemanager folder, + a foundation project with a service account, and the STACKIT Project platform with a default + landing zone. Optionally layers on a hub-and-spoke network topology — a shared network-area + address plan (hub) and a self-service routed-network building block (spoke) — when a network + configuration is provided. +cloudProviders: + - stackit +buildingBlocks: + - path: stackit/project + role: Provisions a STACKIT Project with role assignments. + - path: stackit/network-area + role: (Optional) Provisions the hub network area with the shared IPv4 address plan. + - path: stackit/network + role: (Optional) Lets application teams self-service order routed spoke networks inside their projects. +--- + +# STACKIT Landing Zone + +## Overview + +The **STACKIT Landing Zone** reference architecture turns a bare STACKIT organization into a +self-service-ready meshStack platform in one step using its own Terraform code. + +The **always-on** foundation is a sandbox landing zone: a dedicated STACKIT resourcemanager +folder, a foundation project hosting the project-creation service account, and the **STACKIT +Project** platform with its default landing zone. Application teams can immediately request +STACKIT projects against it. + +Optionally, by providing a **network** configuration, the same building block layers on a +hub-and-spoke network topology with IPAM built in: a shared network-area address plan (the hub) +that all tenant projects draw from, and a self-service routed-network building block (the spoke) +application teams can order inside their own projects. Leaving the network configuration unset +deploys only the sandbox foundation. + +**Target audience:** + +- **Platform engineers** onboarding a new STACKIT organization into meshStack who want a sandbox + environment application teams can request projects from immediately — optionally with network + segmentation from day one, without hand-wiring a network area and its address plan separately. +- **Application teams** who need a dedicated IPv4 subnet inside their STACKIT project without + manually coordinating CIDR ranges with the platform team (when networking is enabled). + +## Architecture Diagram + +The left side of the diagram is the **STACKIT resource hierarchy** — an organization holding folders, +with projects underneath — plus the org-level network area. The right side is **meshStack**: the +platform, its landing zones and the building block **definitions** (BBDs). Dotted lines across the +middle show how each meshStack construct maps onto a STACKIT construct. Definitions (blue) are +registered once; **instances** (green) are ordered against them — the hub network area is a single +instance the platform team orders, while STACKIT projects and spoke networks are ordered `N` times by +application teams. The corporate folder is context only, it is not managed by this reference +architecture. + +```mermaid +flowchart LR + subgraph org["🏢 STACKIT Organization"] + subgraph fcorp["📁 corporate folder"] + CORP["🗂️ Corporate Projects
existing · not managed here"] + end + subgraph fsand["📁 sandbox folder"] + PRJ["🗂️ STACKIT Project ×N"] + SPOKE["🔌 Routed Network ×N
spoke subnet"] + PRJ -->|contains| SPOKE + end + subgraph fplat["📁 platform folder"] + FOUND["🔑 Foundation Project
project-creation service account"] + end + NA["🌐 Network Area ×1
shared IPv4 address plan · hub"] + NA -. assigns CIDR to .-> SPOKE + end + + subgraph mesh["🛰️ meshStack"] + PLAT["🛰️ STACKIT Project Platform"] + subgraph lzs["Landing Zones"] + DEFLZ["🛬 Default Landing Zone"] + NETLZ["🛬 Networked Landing Zone"] + end + subgraph bbds["Building Block Definitions"] + PRJBBD["📦 STACKIT Project BBD"] + NABBD["📦 Network Area BBD"] + SPOKEBBD["📦 STACKIT Network BBD
spoke · tenant level"] + end + PLAT --> DEFLZ + PLAT --> NETLZ + end + + %% invisible link keeps the meshStack column right of the STACKIT hierarchy + SPOKE ~~~ PLAT + FOUND -. authenticates .-> PLAT + PRJ -. requested via .-> DEFLZ + PRJ -. instance of .-> PRJBBD + SPOKE -. instance of .-> SPOKEBBD + NA -. single instance of .-> NABBD + NA -. area id tagged on .-> NETLZ + + classDef bbd fill:#e8ecff,stroke:#5566dd,stroke-width:1px,color:#111; + classDef inst fill:#e6f6e6,stroke:#4a9a4a,stroke-width:1px,color:#111; + classDef muted fill:#f4f4f4,stroke:#bbb,stroke-width:1px,color:#666,stroke-dasharray:3 3; + class PRJBBD,NABBD,SPOKEBBD bbd; + class NA,PRJ,SPOKE inst; + class fcorp,CORP muted; +``` + +When networking is enabled, application teams order a routed network into their own project via the +self-service `stackit/network` building block; each order draws its subnet from the hub's address +plan, so no two spokes can accidentally collide on CIDR ranges. + +## How It Works + +Running this reference architecture always: + +1. Creates a **STACKIT resourcemanager folder** under the given organization — new STACKIT + projects are created inside this folder. +2. Creates a **STACKIT foundation project** directly under the organization to host the + project-creation service account and other landing-zone core assets. +3. Sources the [`modules/stackit`](../../modules/stackit) platform integration to register the + **STACKIT Project** platform and its default landing zone in meshStack, wired to the foundation + service account. + +When a **network** configuration is provided, it additionally: + +4. Registers the [`stackit/network-area`](../../modules/stackit/network-area) building block + definition and immediately orders **one instance** of it in the platform team's own workspace — + this is the hub's IPv4 address plan. +5. Registers the [`stackit/network`](../../modules/stackit/network) building block definition + (`TENANT_LEVEL`) so application teams can self-service order routed networks (spokes) inside + their STACKIT projects, drawing from the hub's address plan. +6. Provisions an additional **networked landing zone**, tagged with the hub's network area ID, so + new STACKIT projects created against it are placed in the hub's network area. + +## Getting Started + +### Prerequisites + +| Requirement | Description | +|----------------------|-----------------------------------------------------------------------------------| +| STACKIT organization | With a service account key that has `resource-manager.admin` on the organization. | +| CIDR plan | *(Only when enabling networking)* A non-overlapping IPv4 address plan chosen up front for the hub network ranges and transfer network. | + +### Deployment Order + +Order the **STACKIT Landing Zone** building block once per workspace. Without a network +configuration it creates the platform and default landing zone. With a network configuration it +additionally creates the hub network area instance, the networked landing zone, and registers the +spoke `stackit/network` building block in the same apply. Application teams can then request +projects and — when networking is enabled — order `stackit/network` inside their own STACKIT +projects once those projects exist. + +## Shared Responsibilities + +| Responsibility | Platform Team | Application Team | +|-------------------------------------------------------------------------------|:---:|:---:| +| Provision the STACKIT platform and default landing zone | ✅ | ❌ | +| *(Optional)* Provision the hub network area and choose its address plan | ✅ | ❌ | +| *(Optional)* Register the spoke `stackit/network` building block | ✅ | ❌ | +| Request STACKIT projects through the landing zone | ❌ | ✅ | +| *(Optional)* Order spoke networks inside their STACKIT projects | ❌ | ✅ | +| Use the assigned subnet and manage workloads inside their projects | ❌ | ✅ | diff --git a/reference-architectures/stackit-landingzone/buildingblock/README.md b/reference-architectures/stackit-landingzone/buildingblock/README.md new file mode 100644 index 00000000..e7fc4157 --- /dev/null +++ b/reference-architectures/stackit-landingzone/buildingblock/README.md @@ -0,0 +1,83 @@ +--- +name: STACKIT Landing Zone +supportedPlatforms: + - stackit +description: Onboards a STACKIT sandbox platform into meshStack (location, resourcemanager folder and STACKIT Project platform with its default landing zone), and optionally layers on a hub-and-spoke network topology. +--- + +This building block bootstraps a complete STACKIT sandbox platform integration inside a meshStack +workspace. It creates a meshStack location, a dedicated STACKIT resourcemanager folder and a +foundation project hosting the landing-zone core assets, then sources the +[`modules/stackit`](../../../modules/stackit) project integration to provision the STACKIT Project +platform together with its default landing zone. + +When a `network` object is supplied, it additionally composes two more Hub modules into the same +offering: it registers [`modules/stackit/network-area`](../../../modules/stackit/network-area) and +immediately orders one instance of it as the hub address plan, and registers +[`modules/stackit/network`](../../../modules/stackit/network) so application teams can self-service +order routed spoke networks inside their STACKIT projects. New STACKIT projects are then placed in +the hub's network area via an additional `networked` landing zone tagged with the hub's network +area ID. Leaving `network` unset (`null`) deploys only the sandbox landing zone. + +It authenticates to STACKIT with a service account key you paste as a secret input. You also +provide the STACKIT organization UUID, owner email, nested integration tags and default role mapping +as user inputs. The service account needs `resource-manager.admin` on the organization. The nested +integrations are pinned to the same `git_ref` as this building block's implementation. + +The user-facing readme is maintained inline in the `readme` field of the +`meshstack_building_block_definition` in +[`../meshstack_integration.tf`](../meshstack_integration.tf). + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.12.0 | +| [meshstack](#requirement\_meshstack) | >= 0.24.0 | +| [stackit](#requirement\_stackit) | >= 0.99.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [network\_area\_integration](#module\_network\_area\_integration) | github.com/meshcloud/meshstack-hub//modules/stackit/network-area | main | +| [network\_integration](#module\_network\_integration) | github.com/meshcloud/meshstack-hub//modules/stackit/network | main | +| [stackit\_integration](#module\_stackit\_integration) | github.com/meshcloud/meshstack-hub//modules/stackit | main | + +## Resources + +| Name | Type | +|------|------| +| [meshstack_building_block.network_area_hub](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block) | resource | +| [meshstack_landingzone.networked](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/landingzone) | resource | +| [meshstack_location.this](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/location) | resource | +| [stackit_resourcemanager_folder.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/resourcemanager_folder) | resource | +| [stackit_resourcemanager_project.foundation](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/resourcemanager_project) | resource | +| [meshstack_landingzone.foundation_default](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/data-sources/landingzone) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [hub](#input\_hub) | `git_ref`: meshstack-hub reference used to source the nested foundation, network-area, and network integration modules. `const` so it can be interpolated into the module source at init time.
`bbd_draft`: Forwarded as-is to those nested integrations' own `hub.bbd_draft`, so their building block definition draft state tracks this building block's own release state. |
object({
git_ref = optional(string, "main")
bbd_draft = optional(bool, true)
})
|
{
"bbd_draft": true,
"git_ref": "main"
}
| no | +| [network](#input\_network) | Optional hub-and-spoke network topology. Leave unset (null) to deploy only the sandbox landing zone. When set, additionally provisions a shared hub network area with the given address plan (`hub_*` fields), registers the self-service spoke `STACKIT Network` building block (`tenant_network_*` prefix bounds), and creates a `networked` landing zone (tagged via `network_area_tag_name`) whose projects are placed in the hub network area. |
object({
network_area_tag_name = optional(string, "StackitNetworkArea")
hub_network_area_name = optional(string, "hub")
hub_network_ranges = optional(list(string), ["10.0.0.0/16"])
hub_transfer_network = optional(string, "10.1.255.0/24")
hub_min_prefix_length = optional(number, 24)
hub_max_prefix_length = optional(number, 28)
hub_default_prefix_length = optional(number, 28)
hub_default_nameservers = optional(list(string), [])
tenant_network_min_prefix_length = optional(number, 24)
tenant_network_max_prefix_length = optional(number, 28)
})
| `null` | no | +| [platform\_identifier](#input\_platform\_identifier) | Identifier for the STACKIT sandbox platform created in meshStack (letters, digits and dashes only). | `string` | n/a | yes | +| [role\_mapping](#input\_role\_mapping) | Default mapping from meshStack roles to STACKIT project roles for the nested STACKIT Project integration. Values can be built-in STACKIT roles or custom STACKIT role names. | `map(list(string))` | n/a | yes | +| [stackit\_org](#input\_stackit\_org) | STACKIT organization UUID under which the landing-zone folder, foundation project and tenant projects are created. | `string` | n/a | yes | +| [stackit\_organization\_onboarding\_enabled](#input\_stackit\_organization\_onboarding\_enabled) | Whether the nested STACKIT Project integration adds meshStack project users to the STACKIT organization before applying project-level role assignments. Disable if organization membership is managed outside this landing zone. | `bool` | n/a | yes | +| [stackit\_owner\_email](#input\_stackit\_owner\_email) | Owner email assigned to the STACKIT resourcemanager folder and foundation project. | `string` | n/a | yes | +| [stackit\_service\_account\_key](#input\_stackit\_service\_account\_key) | STACKIT service account key JSON with `resource-manager.admin` on the organization. Used to create the landing-zone folder and foundation project. | `string` | n/a | yes | +| [tags](#input\_tags) | Tags forwarded to the nested STACKIT integrations. `landingzone` tags are applied to the created landing zones; `building_block` tags are applied to the nested building block definitions. |
object({
landingzone = map(list(string))
building_block = map(list(string))
})
| n/a | yes | +| [use\_global\_location](#input\_use\_global\_location) | Use the global location instead of creating a dedicated location for this platform. | `bool` | n/a | yes | +| [workspace](#input\_workspace) | Identifier of the meshStack workspace that will own the created platform, location, landing zones, and (when networking is enabled) the hub network-area instance. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [foundation\_project\_id](#output\_foundation\_project\_id) | Project ID of the STACKIT foundation project that hosts the landing-zone core assets (the service account used for tenant project creation). | +| [foundation\_project\_url](#output\_foundation\_project\_url) | Deep link to the foundation project in the STACKIT portal. | +| [lz\_folder\_container\_id](#output\_lz\_folder\_container\_id) | Container ID of the STACKIT resourcemanager folder created for the landing zone. Tenant projects are created inside this folder. | +| [summary](#output\_summary) | Summary of the meshStack resources created by this reference architecture. | + diff --git a/reference-architectures/stackit-landingzone/buildingblock/SUMMARY.md.tftpl b/reference-architectures/stackit-landingzone/buildingblock/SUMMARY.md.tftpl new file mode 100644 index 00000000..a8d028ef --- /dev/null +++ b/reference-architectures/stackit-landingzone/buildingblock/SUMMARY.md.tftpl @@ -0,0 +1,21 @@ +# STACKIT Landing Zone: **${platform_identifier}** + +## Details + +| Property | Value | +|----------|-------| +| **Foundation Project** | [Open in STACKIT Portal](${foundation_project_url}) (`${foundation_project_id}`) | +| **Landing Zone Folder** | `${lz_folder_container_id}` | +%{ if network_enabled ~} +| **Networked Landing Zone** | `${networked_landingzone_name}` | +| **Hub Network Area** | @buildingblock[${network_area_hub_uuid}] | +%{ endif ~} + +%{ if network_enabled ~} +Hub-and-spoke networking is enabled. Application teams can order the **STACKIT Network** building +block inside their own projects in the `${networked_landingzone_name}` landing zone to get a routed +spoke network drawn from the hub's address plan. +%{ else ~} +Application teams can now request STACKIT projects against the `${platform_identifier}-default` +landing zone. Provide a `network` object to additionally enable hub-and-spoke networking. +%{ endif ~} diff --git a/reference-architectures/stackit-landingzone/buildingblock/logo.png b/reference-architectures/stackit-landingzone/buildingblock/logo.png new file mode 100644 index 00000000..0ef884fb Binary files /dev/null and b/reference-architectures/stackit-landingzone/buildingblock/logo.png differ diff --git a/reference-architectures/stackit-landingzone/buildingblock/main.tf b/reference-architectures/stackit-landingzone/buildingblock/main.tf new file mode 100644 index 00000000..734156e5 --- /dev/null +++ b/reference-architectures/stackit-landingzone/buildingblock/main.tf @@ -0,0 +1,151 @@ +locals { + # Hub-and-spoke networking is deployed only when the operator supplies a `network` object. + network_enabled = var.network != null +} + +# ── Sandbox landing zone foundation (always deployed) ── + +resource "meshstack_location" "this" { + count = var.use_global_location ? 0 : 1 + + metadata = { + name = var.platform_identifier + owned_by_workspace = var.workspace + } + + spec = { + display_name = var.platform_identifier + description = "STACKIT sandbox location created by the STACKIT Landing Zone." + } +} + +resource "stackit_resourcemanager_folder" "this" { + name = var.platform_identifier + owner_email = var.stackit_owner_email + parent_container_id = var.stackit_org +} + +# Foundation project hosting the landing-zone core assets (the project-creation service account). +# Created directly under the organization (not the landing-zone folder). +resource "stackit_resourcemanager_project" "foundation" { + name = "${var.platform_identifier}-foundation" + owner_email = var.stackit_owner_email + parent_container_id = var.stackit_org +} + +module "stackit_integration" { + source = "github.com/meshcloud/meshstack-hub//modules/stackit?ref=${var.hub.git_ref}" + + stackit_organization_id = var.stackit_org + stackit_parent_container_id = stackit_resourcemanager_folder.this.container_id + stackit_project_id = stackit_resourcemanager_project.foundation.project_id + stackit_service_account_name = substr(var.platform_identifier, 0, 20) + role_mapping = var.role_mapping + stackit_organization_onboarding_enabled = var.stackit_organization_onboarding_enabled + stackit_network_area_tag_name = local.network_enabled ? var.network.network_area_tag_name : null + + hub = var.hub + + meshstack = { + owning_workspace_identifier = var.workspace + location_name = var.use_global_location ? "global" : meshstack_location.this[0].metadata.name + platform_identifier = var.platform_identifier + tags = var.tags + } +} + +# ── Hub-and-spoke network topology (optional — deployed only when var.network is set) ── + +module "network_area_integration" { + count = local.network_enabled ? 1 : 0 + source = "github.com/meshcloud/meshstack-hub//modules/stackit/network-area?ref=${var.hub.git_ref}" + + stackit_organization_id = var.stackit_org + stackit_project_id = stackit_resourcemanager_project.foundation.project_id + + meshstack = { owning_workspace_identifier = var.workspace, tags = var.tags.building_block } + hub = var.hub +} + +module "network_integration" { + count = local.network_enabled ? 1 : 0 + source = "github.com/meshcloud/meshstack-hub//modules/stackit/network?ref=${var.hub.git_ref}" + + stackit_organization_id = var.stackit_org + stackit_project_id = stackit_resourcemanager_project.foundation.project_id + stackit_network_min_prefix_length = var.network.tenant_network_min_prefix_length + stackit_network_max_prefix_length = var.network.tenant_network_max_prefix_length + + meshstack = { owning_workspace_identifier = var.workspace, tags = var.tags.building_block } + hub = var.hub +} + +resource "meshstack_building_block" "network_area_hub" { + count = local.network_enabled ? 1 : 0 + wait_for_completion = true + depends_on = [module.network_area_integration] + + spec = { + building_block_definition_version_ref = { + uuid = module.network_area_integration[0].building_block_definition.version_ref.uuid + } + display_name = "Hub Network Area" + target_ref = { kind = "meshWorkspace", name = var.workspace } + + inputs = { + network_area_name = { + value = jsonencode(var.network.hub_network_area_name) + } + network_ranges = { + value = jsonencode(jsonencode(var.network.hub_network_ranges)) + } + transfer_network = { + value = jsonencode(var.network.hub_transfer_network) + } + min_prefix_length = { + value = jsonencode(var.network.hub_min_prefix_length) + } + max_prefix_length = { + value = jsonencode(var.network.hub_max_prefix_length) + } + default_prefix_length = { + value = jsonencode(var.network.hub_default_prefix_length) + } + default_nameservers = { + value = jsonencode(jsonencode(var.network.hub_default_nameservers)) + } + } + } +} + +# Looks up the default landing zone that `module.stackit_integration` already registered, without +# needing new outputs threaded through it. Used below only as an input into the independent +# `networked` landing zone — never fed back into the integration itself, which would create a cycle. +data "meshstack_landingzone" "foundation_default" { + count = local.network_enabled ? 1 : 0 + metadata = { name = "${var.platform_identifier}-default" } + depends_on = [module.stackit_integration] +} + +resource "meshstack_landingzone" "networked" { + count = local.network_enabled ? 1 : 0 + + metadata = { + name = "${var.platform_identifier}-networked" + owned_by_workspace = var.workspace + tags = merge(var.tags.landingzone, { + (var.network.network_area_tag_name) = [jsondecode(meshstack_building_block.network_area_hub[0].status.outputs["network_area_id"].value)] + }) + } + + spec = { + display_name = "STACKIT Networked" + description = "STACKIT landing zone whose projects are placed in the hub network area." + automate_deletion_approval = true + automate_deletion_replication = true + + platform_ref = data.meshstack_landingzone.foundation_default[0].spec.platform_ref + platform_properties = { custom = {} } + mandatory_building_block_refs = data.meshstack_landingzone.foundation_default[0].spec.mandatory_building_block_refs + } +} diff --git a/reference-architectures/stackit-landingzone/buildingblock/outputs.tf b/reference-architectures/stackit-landingzone/buildingblock/outputs.tf new file mode 100644 index 00000000..db9506a6 --- /dev/null +++ b/reference-architectures/stackit-landingzone/buildingblock/outputs.tf @@ -0,0 +1,27 @@ +output "lz_folder_container_id" { + value = stackit_resourcemanager_folder.this.container_id + description = "Container ID of the STACKIT resourcemanager folder created for the landing zone. Tenant projects are created inside this folder." +} + +output "foundation_project_id" { + value = stackit_resourcemanager_project.foundation.project_id + description = "Project ID of the STACKIT foundation project that hosts the landing-zone core assets (the service account used for tenant project creation)." +} + +output "foundation_project_url" { + value = "https://portal.stackit.cloud/projects/${stackit_resourcemanager_project.foundation.project_id}" + description = "Deep link to the foundation project in the STACKIT portal." +} + +output "summary" { + description = "Summary of the meshStack resources created by this reference architecture." + value = templatefile("${path.module}/SUMMARY.md.tftpl", { + platform_identifier = var.platform_identifier + foundation_project_id = stackit_resourcemanager_project.foundation.project_id + foundation_project_url = "https://portal.stackit.cloud/projects/${stackit_resourcemanager_project.foundation.project_id}" + lz_folder_container_id = stackit_resourcemanager_folder.this.container_id + network_enabled = local.network_enabled + networked_landingzone_name = local.network_enabled ? meshstack_landingzone.networked[0].metadata.name : "" + network_area_hub_uuid = local.network_enabled ? meshstack_building_block.network_area_hub[0].metadata.uuid : "" + }) +} diff --git a/reference-architectures/stackit-hub-spoke/buildingblock/provider.tf b/reference-architectures/stackit-landingzone/buildingblock/provider.tf similarity index 100% rename from reference-architectures/stackit-hub-spoke/buildingblock/provider.tf rename to reference-architectures/stackit-landingzone/buildingblock/provider.tf diff --git a/reference-architectures/stackit-sandbox-landingzone/buildingblock/variables.tf b/reference-architectures/stackit-landingzone/buildingblock/variables.tf similarity index 52% rename from reference-architectures/stackit-sandbox-landingzone/buildingblock/variables.tf rename to reference-architectures/stackit-landingzone/buildingblock/variables.tf index 4269bcf2..35074ec9 100644 --- a/reference-architectures/stackit-sandbox-landingzone/buildingblock/variables.tf +++ b/reference-architectures/stackit-landingzone/buildingblock/variables.tf @@ -1,6 +1,7 @@ variable "workspace" { type = string - description = "Identifier of the meshStack workspace that will own the created location, platform and landing zone." + nullable = false + description = "Identifier of the meshStack workspace that will own the created platform, location, landing zones, and (when networking is enabled) the hub network-area instance." } variable "use_global_location" { @@ -11,8 +12,8 @@ variable "use_global_location" { variable "stackit_org" { type = string - description = "STACKIT organization UUID under which the landing-zone folder, backplane project and tenant projects are created." nullable = false + description = "STACKIT organization UUID under which the landing-zone folder, foundation project and tenant projects are created." validation { condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", var.stackit_org)) @@ -22,15 +23,15 @@ variable "stackit_org" { variable "stackit_owner_email" { type = string - description = "Owner email assigned to the STACKIT resourcemanager folder and backplane project." nullable = false + description = "Owner email assigned to the STACKIT resourcemanager folder and foundation project." } variable "stackit_service_account_key" { type = string sensitive = true nullable = false - description = "STACKIT service account key JSON with `resource-manager.admin` on the organization. Used to create the landing-zone folder and backplane project." + description = "STACKIT service account key JSON with `resource-manager.admin` on the organization. Used to create the landing-zone folder and foundation project." } variable "platform_identifier" { @@ -50,7 +51,7 @@ variable "tags" { building_block = map(list(string)) }) nullable = false - description = "Tags forwarded to the nested STACKIT Project integration. `landingzone` tags are applied to the default landing zone; `building_block` tags are applied to the nested building block definition." + description = "Tags forwarded to the nested STACKIT integrations. `landingzone` tags are applied to the created landing zones; `building_block` tags are applied to the nested building block definitions." } variable "role_mapping" { @@ -65,10 +66,21 @@ variable "stackit_organization_onboarding_enabled" { description = "Whether the nested STACKIT Project integration adds meshStack project users to the STACKIT organization before applying project-level role assignments. Disable if organization membership is managed outside this landing zone." } -variable "network_area_tag_name" { - type = string +variable "network" { + type = object({ + network_area_tag_name = optional(string, "StackitNetworkArea") + hub_network_area_name = optional(string, "hub") + hub_network_ranges = optional(list(string), ["10.0.0.0/16"]) + hub_transfer_network = optional(string, "10.1.255.0/24") + hub_min_prefix_length = optional(number, 24) + hub_max_prefix_length = optional(number, 28) + hub_default_prefix_length = optional(number, 28) + hub_default_nameservers = optional(list(string), []) + tenant_network_min_prefix_length = optional(number, 24) + tenant_network_max_prefix_length = optional(number, 28) + }) default = null - description = "Name of the meshStack landing zone tag whose value is used as the STACKIT project's `networkArea` label, forwarded to the nested STACKIT Project integration. Set to null (default) to skip network area assignment." + description = "Optional hub-and-spoke network topology. Leave unset (null) to deploy only the sandbox landing zone. When set, additionally provisions a shared hub network area with the given address plan (`hub_*` fields), registers the self-service spoke `STACKIT Network` building block (`tenant_network_*` prefix bounds), and creates a `networked` landing zone (tagged via `network_area_tag_name`) whose projects are placed in the hub network area." } variable "hub" { @@ -76,12 +88,11 @@ variable "hub" { git_ref = optional(string, "main") bbd_draft = optional(bool, true) }) - const = true - nullable = false + const = true + default = { git_ref = "main", bbd_draft = true } description = <<-EOT - `git_ref`: meshstack-hub reference used to source the nested STACKIT project integration module. `const` so it can be interpolated into the module source at init time. - `bbd_draft`: Forwarded as-is to the nested STACKIT project integration's own `hub.bbd_draft`, so its building block definition draft state tracks this building block's own release state. + `git_ref`: meshstack-hub reference used to source the nested foundation, network-area, and network integration modules. `const` so it can be interpolated into the module source at init time. + `bbd_draft`: Forwarded as-is to those nested integrations' own `hub.bbd_draft`, so their building block definition draft state tracks this building block's own release state. EOT } - diff --git a/reference-architectures/stackit-hub-spoke/buildingblock/versions.tf b/reference-architectures/stackit-landingzone/buildingblock/versions.tf similarity index 100% rename from reference-architectures/stackit-hub-spoke/buildingblock/versions.tf rename to reference-architectures/stackit-landingzone/buildingblock/versions.tf diff --git a/reference-architectures/stackit-sandbox-landingzone/meshstack_integration.tf b/reference-architectures/stackit-landingzone/meshstack_integration.tf similarity index 64% rename from reference-architectures/stackit-sandbox-landingzone/meshstack_integration.tf rename to reference-architectures/stackit-landingzone/meshstack_integration.tf index 66241f63..c80eeef0 100644 --- a/reference-architectures/stackit-sandbox-landingzone/meshstack_integration.tf +++ b/reference-architectures/stackit-landingzone/meshstack_integration.tf @@ -24,12 +24,6 @@ variable "hub" { EOT } -variable "stackit_organization_onboarding_enabled" { - type = bool - default = true - description = "Whether the project building block adds meshStack project users to the STACKIT organization. Disable if organization membership is managed externally." -} - output "building_block_definition" { description = "BBD is consumed in building block compositions." value = { @@ -45,24 +39,33 @@ resource "meshstack_building_block_definition" "this" { } spec = { - display_name = "STACKIT Sandbox Landing Zone" - symbol = "https://raw.githubusercontent.com/meshcloud/meshstack-hub/${var.hub.git_ref}/reference-architectures/stackit-sandbox-landingzone/buildingblock/logo.png" - description = "Onboards a STACKIT sandbox platform into meshStack: creates a location, a STACKIT resourcemanager folder and the STACKIT Project platform with its default landing zone." + display_name = "STACKIT Landing Zone" + symbol = "https://raw.githubusercontent.com/meshcloud/meshstack-hub/${var.hub.git_ref}/reference-architectures/stackit-landingzone/buildingblock/logo.png" + description = "Onboards a STACKIT sandbox platform into meshStack: a location, resourcemanager folder and the STACKIT Project platform with its default landing zone. Optionally layers on a hub-and-spoke network topology when a network config is provided." support_url = "https://portal.stackit.cloud" target_type = "WORKSPACE_LEVEL" run_transparency = true readme = chomp(<<-EOT - The **STACKIT Sandbox Landing Zone** building block bootstraps a complete STACKIT sandbox platform integration inside a - meshStack workspace. Running it once turns a STACKIT organization into a sandbox-ready self-service platform: it - registers a meshStack location, carves out a dedicated STACKIT resourcemanager folder for the workspace and - wires up the **STACKIT Project** platform together with its default landing zone. + The **STACKIT Landing Zone** building block bootstraps a complete STACKIT sandbox platform + integration inside a meshStack workspace. Running it once turns a STACKIT organization into a + sandbox-ready self-service platform: it registers a meshStack location, carves out a dedicated + STACKIT resourcemanager folder for the workspace and wires up the **STACKIT Project** platform + together with its default landing zone. + + Optionally, when you provide a **network** configuration, it additionally layers on a + hub-and-spoke network topology: a shared network-area address plan (the hub) and a self-service + routed-network building block (the spoke) that application teams can order inside their own + STACKIT projects. ## 🎯 When to use it Use this building block when you: - want to onboard STACKIT in meshStack without manually creating locations, folders and project platform wiring. - need a reusable setup for sandbox environments where application teams can request STACKIT projects self-service. + - (optionally) want all tenant projects to draw from a single, non-overlapping IPv4 address plan + and let application teams self-service order routed subnets — enable this by providing the + **network** configuration. ## 💡 Usage examples @@ -70,18 +73,42 @@ resource "meshstack_building_block_definition" "this" { A platform engineer runs this building block once for a workspace to bootstrap the STACKIT location, landing-zone folder and default `STACKIT Project` platform so teams can start requesting projects immediately. - **Example 2: Prepare an isolated training environment** - For workshops or onboarding waves, the platform team creates a dedicated platform identifier and folder boundary so - participant projects are provisioned in a controlled area with clear ownership. + **Example 2: Bootstrap with hub-and-spoke networking** + A platform engineer provides a **network** configuration (CIDR plan, prefix bounds). In addition + to the sandbox platform, the building block provisions the hub network area with the chosen + address plan, registers the **STACKIT Network** building block, and creates a `networked` landing + zone. Application teams can then self-service order routed spoke networks inside their projects. + + A **network** configuration looks like this (sensible example values shown — adapt them to your + own address plan): + + ```json + { + "network_area_tag_name": "StackitNetworkArea", + "hub_network_area_name": "hub", + "hub_network_ranges": ["10.0.0.0/16"], + "hub_transfer_network": "10.1.255.0/24", + "hub_min_prefix_length": 24, + "hub_max_prefix_length": 28, + "hub_default_prefix_length": 28, + "hub_default_nameservers": [], + "tenant_network_min_prefix_length": 24, + "tenant_network_max_prefix_length": 28 + } + ``` ## 📦 Resources created - **meshStack location** – named after the chosen platform identifier. - **STACKIT resourcemanager folder** – created under the configured organization and owned by the given owner email. New tenant projects are created inside this folder. - - **STACKIT backplane project** – created directly under the organization to host the project-creation service account. + - **STACKIT foundation project** – created directly under the organization to host the + project-creation service account and other landing-zone core assets. - **STACKIT Project platform** – the `STACKIT Project` building block definition, platform and default landing zone, - including the project-creation service account provisioned in the backplane project. + including the project-creation service account provisioned in the foundation project. + - **Hub network area + spoke network building block + networked landing zone** *(only when a + network configuration is provided)* – the shared hub address plan, the self-service + `STACKIT Network` building block, and a landing zone that places projects into the hub. ## 🔑 Authentication @@ -94,7 +121,10 @@ resource "meshstack_building_block_definition" "this" { |---|:---:|:---:| | Provide the STACKIT service account key, organization details, tags and role mapping | ✅ | ❌ | | Provision the location, folder and STACKIT Project platform | ✅ | ❌ | + | (Optional) Provide the network CIDR plan and provision the hub network area | ✅ | ❌ | + | (Optional) Register the spoke `STACKIT Network` building block for self-service | ✅ | ❌ | | Request STACKIT projects through the landing zone | ❌ | ✅ | + | (Optional) Order spoke networks inside their STACKIT projects | ❌ | ✅ | | Manage workloads inside the provisioned STACKIT projects | ❌ | ✅ | EOT ) @@ -104,12 +134,16 @@ resource "meshstack_building_block_definition" "this" { draft = var.hub.bbd_draft deletion_mode = "DELETE" - # Ephemeral API key permissions for meshStack resources created by this building block and STACKIT integration. + # Ephemeral API key permissions for meshStack resources created by this building block and its + # nested foundation/network-area/network integrations (all part of the same Terraform run). permissions = [ "INTEGRATION_LIST", "BUILDINGBLOCKDEFINITION_LIST", "BUILDINGBLOCKDEFINITION_SAVE", "BUILDINGBLOCKDEFINITION_DELETE", + "BUILDINGBLOCK_LIST", + "BUILDINGBLOCK_SAVE", + "BUILDINGBLOCK_DELETE", "LANDINGZONE_LIST", "LANDINGZONE_SAVE", "LANDINGZONE_DELETE", @@ -122,7 +156,7 @@ resource "meshstack_building_block_definition" "this" { terraform = { terraform_version = "1.12.0" repository_url = "https://github.com/meshcloud/meshstack-hub.git" - repository_path = "reference-architectures/stackit-sandbox-landingzone/buildingblock" + repository_path = "reference-architectures/stackit-landingzone/buildingblock" ref_name = var.hub.git_ref async = false use_mesh_http_backend_fallback = true @@ -132,7 +166,7 @@ resource "meshstack_building_block_definition" "this" { inputs = { # ── STACKIT authentication (service account key supplied by the operator) ── - "stackit_service_account_key" = { + stackit_service_account_key = { display_name = "STACKIT Service Account Key" description = "STACKIT service account key JSON with `resource-manager.admin` on the organization. Paste the full JSON as a secret input." type = "CODE" @@ -143,7 +177,7 @@ resource "meshstack_building_block_definition" "this" { hub = { display_name = "Hub" - description = "JSON object with `git_ref` (meshstack-hub reference used to source the nested STACKIT project integration module) and `bbd_draft` (forwarded to the nested integration's own building block definition draft state)." + description = "JSON object with `git_ref` (meshstack-hub reference used to source the nested STACKIT integration modules) and `bbd_draft` (forwarded to those nested integrations' own building block definition draft state)." type = "CODE" assignment_type = "STATIC" argument = jsonencode(jsonencode(var.hub)) @@ -153,7 +187,7 @@ resource "meshstack_building_block_definition" "this" { stackit_org = { display_name = "STACKIT Organization UUID" - description = "STACKIT organization UUID under which the landing-zone folder, backplane project and tenant projects are created." + description = "STACKIT organization UUID under which the landing-zone folder, foundation project and tenant projects are created." type = "STRING" assignment_type = "USER_INPUT" value_validation_regex = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" @@ -162,14 +196,14 @@ resource "meshstack_building_block_definition" "this" { stackit_owner_email = { display_name = "STACKIT Owner Email" - description = "Owner email assigned to the STACKIT resourcemanager folder and backplane project." + description = "Owner email assigned to the STACKIT resourcemanager folder and foundation project." type = "STRING" assignment_type = "USER_INPUT" } tags = { display_name = "Tags" - description = "JSON object with `landingzone` and `building_block` tag maps forwarded to the STACKIT Project integration module." + description = "JSON object with `landingzone` and `building_block` tag maps forwarded to the nested STACKIT integrations." type = "CODE" assignment_type = "USER_INPUT" updateable_by_consumer = true @@ -200,15 +234,31 @@ resource "meshstack_building_block_definition" "this" { type = "BOOLEAN" assignment_type = "USER_INPUT" updateable_by_consumer = true + default_value = jsonencode(true) + } - default_value = jsonencode(var.stackit_organization_onboarding_enabled) + # ── Optional hub-and-spoke networking ── + # Leave `network` as null to deploy only the sandbox landing zone. Provide a JSON object to + # additionally provision the hub network area, register the spoke network building block, and + # create a networked landing zone. + + network = { + display_name = "Network (Hub-and-Spoke)" + description = <<-DESC + Optional JSON object enabling hub-and-spoke networking. Leave as `null` to deploy only the + sandbox landing zone. When set, all fields are optional + DESC + type = "CODE" + assignment_type = "USER_INPUT" + updateable_by_consumer = true + default_value = jsonencode(jsonencode(null)) } # ── meshStack context ── workspace = { display_name = "Workspace Identifier" - description = "Workspace that will own the created platform, location and landing zone." + description = "Workspace that will own the created platform, location and landing zones." type = "STRING" assignment_type = "WORKSPACE_IDENTIFIER" } @@ -238,17 +288,23 @@ resource "meshstack_building_block_definition" "this" { assignment_type = "NONE" } - backplane_project_id = { - display_name = "Backplane Project ID" + foundation_project_id = { + display_name = "Foundation Project ID" type = "STRING" assignment_type = "NONE" } - backplane_project_url = { - display_name = "Open Backplane Project" + foundation_project_url = { + display_name = "Open Foundation Project" type = "STRING" assignment_type = "RESOURCE_URL" } + + summary = { + display_name = "Summary" + type = "STRING" + assignment_type = "SUMMARY" + } } } } @@ -259,7 +315,7 @@ terraform { required_providers { meshstack = { source = "meshcloud/meshstack" - version = ">= 0.22.0" + version = ">= 0.24.0" } } } diff --git a/reference-architectures/stackit-sandbox-landingzone/README.md b/reference-architectures/stackit-sandbox-landingzone/README.md deleted file mode 100644 index 32cd9c1d..00000000 --- a/reference-architectures/stackit-sandbox-landingzone/README.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -name: STACKIT Sandbox Landing Zone -description: > - Bootstraps a self-service-ready STACKIT platform: a dedicated STACKIT resourcemanager folder, a service account, and the STACKIT Project platform with a default landing zone. -cloudProviders: - - stackit -buildingBlocks: - - path: stackit/project - role: Provisions a STACKIT Project with role assignments. ---- - -# STACKIT Sandbox Landing Zone - -## Overview - -The **STACKIT Sandbox Landing Zone** reference architecture turns a bare STACKIT organization -into a self-service-ready meshStack platform in one step using its own Terraform code. - -**Target audience:** - -- **Platform engineers** onboarding a new STACKIT organization into meshStack who want a - sandbox environment application teams can request projects from immediately. - -## Architecture Diagram - -```mermaid -flowchart TD - A[STACKIT Organization] -->|folder| B[Landing-Zone Folder] - A -->|backplane project| C[Backplane Project] - C -->|hosts| D[Project-Creation Service Account] - F[STACKIT Project Platform] --> G[Default Landing Zone] - G -->|scoped to| B - D -->|creates tenant projects in| B -``` - -## How It Works - -Running this reference architecture: - -1. Creates a **STACKIT resourcemanager folder** under the given organization — new STACKIT - projects are created inside this folder. -2. Creates a **STACKIT backplane project** directly under the organization to host the - project-creation service account. -3. Sources the [`modules/stackit`](../../modules/stackit) platform integration to register the - **STACKIT Project** platform and its default landing zone in meshStack, wired to the - backplane service account. - -## Getting Started - -### Prerequisites - -| Requirement | Description | -|----------------------|-----------------------------------------------------------------------------------| -| STACKIT organization | With a service account key that has `resource-manager.admin` on the organization. | diff --git a/reference-architectures/stackit-sandbox-landingzone/buildingblock/README.md b/reference-architectures/stackit-sandbox-landingzone/buildingblock/README.md deleted file mode 100644 index 9023fbf9..00000000 --- a/reference-architectures/stackit-sandbox-landingzone/buildingblock/README.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: STACKIT Sandbox Landing Zone -supportedPlatforms: - - stackit -description: Onboards a STACKIT sandbox platform into meshStack by creating a location, a STACKIT resourcemanager folder and the STACKIT Project platform with its default landing zone. ---- - -This building block bootstraps a complete STACKIT sandbox platform integration inside a meshStack -workspace. It creates a meshStack location, a dedicated STACKIT resourcemanager folder and -sources the [`modules/stackit`](../../../modules/stackit) project integration to provision the -STACKIT Project platform together with its default landing zone. - -It authenticates to STACKIT with a service account key you paste as a secret input. You also -provide the STACKIT organization UUID, owner email, nested integration tags and default role mapping -as user inputs. The service account needs `resource-manager.admin` on the organization. The nested project integration is -pinned to the same `git_ref` as this building block's implementation. - -The user-facing readme is maintained inline in the `readme` field of the -`meshstack_building_block_definition` in -[`../meshstack_integration.tf`](../meshstack_integration.tf). - - -## Requirements - -| Name | Version | -| ---- | ------- | -| [terraform](#requirement\_terraform) | >= 1.12.0 | -| [meshstack](#requirement\_meshstack) | >= 0.22.0 | -| [stackit](#requirement\_stackit) | >= 0.99.0 | - -## Modules - -| Name | Source | Version | -| ---- | ------ | ------- | -| [stackit\_integration](#module\_stackit\_integration) | github.com/meshcloud/meshstack-hub//modules/stackit | main | - -## Resources - -| Name | Type | -| ---- | ---- | -| [meshstack_location.this](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/location) | resource | -| [stackit_resourcemanager_folder.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/resourcemanager_folder) | resource | -| [stackit_resourcemanager_project.backplane](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/resourcemanager_project) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -| ---- | ----------- | ---- | ------- | :------: | -| [hub](#input\_hub) | `git_ref`: meshstack-hub reference used to source the nested STACKIT project integration module. `const` so it can be interpolated into the module source at init time.

`bbd_draft`: Forwarded as-is to the nested integration's own `hub.bbd_draft`, so its building block definition draft state tracks this building block's own release state. |
object({
git_ref = optional(string, "main")
bbd_draft = optional(bool, true)
})
|
{
"bbd_draft": true,
"git_ref": "main"
}
| no | -| [network\_area\_tag\_name](#input\_network\_area\_tag\_name) | Name of the meshStack landing zone tag whose value is used as the STACKIT project's `networkArea` label, forwarded to the nested STACKIT Project integration. Set to null (default) to skip network area assignment. | `string` | `null` | no | -| [platform\_identifier](#input\_platform\_identifier) | Identifier for the STACKIT sandbox platform created in meshStack (letters, digits and dashes only). | `string` | n/a | yes | -| [role\_mapping](#input\_role\_mapping) | Default mapping from meshStack roles to STACKIT project roles for the nested STACKIT Project integration. Values can be built-in STACKIT roles or custom STACKIT role names. | `map(list(string))` |
{
"admin": [
"owner"
],
"reader": [
"reader"
],
"user": [
"editor"
]
}
| no | -| [stackit\_org](#input\_stackit\_org) | STACKIT organization UUID under which the landing-zone folder, backplane project and tenant projects are created. | `string` | n/a | yes | -| [stackit\_organization\_onboarding\_enabled](#input\_stackit\_organization\_onboarding\_enabled) | Whether the nested STACKIT Project integration adds meshStack project users to the STACKIT organization before applying project-level role assignments. Disable if organization membership is managed outside this landing zone. | `bool` | `true` | no | -| [stackit\_owner\_email](#input\_stackit\_owner\_email) | Owner email assigned to the STACKIT resourcemanager folder and backplane project. | `string` | n/a | yes | -| [stackit\_service\_account\_key](#input\_stackit\_service\_account\_key) | STACKIT service account key JSON with `resource-manager.admin` on the organization. Used to create the landing-zone folder and backplane project. | `string` | n/a | yes | -| [tags](#input\_tags) | Tags forwarded to the nested STACKIT Project integration. `landingzone` tags are applied to the default landing zone; `building_block` tags are applied to the nested building block definition. |
object({
landingzone = map(list(string))
building_block = map(list(string))
})
|
{
"building_block": {},
"landingzone": {}
}
| no | -| [use\_global\_location](#input\_use\_global\_location) | Use the global location instead of creating a dedicated location for this platform. | `bool` | `false` | no | -| [workspace](#input\_workspace) | Identifier of the meshStack workspace that will own the created location, platform and landing zone. | `string` | n/a | yes | - -## Outputs - -| Name | Description | -| ---- | ----------- | -| [backplane\_project\_id](#output\_backplane\_project\_id) | Project ID of the STACKIT backplane project that hosts the service account used for tenant project creation. | -| [backplane\_project\_url](#output\_backplane\_project\_url) | Deep link to the backplane project in the STACKIT portal. | -| [lz\_folder\_container\_id](#output\_lz\_folder\_container\_id) | Container ID of the STACKIT resourcemanager folder created for the landing zone. Tenant projects are created inside this folder. | - diff --git a/reference-architectures/stackit-sandbox-landingzone/buildingblock/logo.png b/reference-architectures/stackit-sandbox-landingzone/buildingblock/logo.png deleted file mode 100644 index f346453f..00000000 Binary files a/reference-architectures/stackit-sandbox-landingzone/buildingblock/logo.png and /dev/null differ diff --git a/reference-architectures/stackit-sandbox-landingzone/buildingblock/main.tf b/reference-architectures/stackit-sandbox-landingzone/buildingblock/main.tf deleted file mode 100644 index 2be36647..00000000 --- a/reference-architectures/stackit-sandbox-landingzone/buildingblock/main.tf +++ /dev/null @@ -1,48 +0,0 @@ -resource "meshstack_location" "this" { - count = var.use_global_location ? 0 : 1 - - metadata = { - name = var.platform_identifier - owned_by_workspace = var.workspace - } - - spec = { - display_name = var.platform_identifier - description = "STACKIT sandbox location created by the STACKIT Sandbox Landing Zone." - } -} - -resource "stackit_resourcemanager_folder" "this" { - name = var.platform_identifier - owner_email = var.stackit_owner_email - parent_container_id = var.stackit_org -} - -# Project hosting the backplane service account that creates tenant projects. -# Created directly under the organization (not the landing-zone folder). -resource "stackit_resourcemanager_project" "backplane" { - name = "${var.platform_identifier}-backplane" - owner_email = var.stackit_owner_email - parent_container_id = var.stackit_org -} - -module "stackit_integration" { - source = "github.com/meshcloud/meshstack-hub//modules/stackit?ref=${var.hub.git_ref}" - - stackit_organization_id = var.stackit_org - stackit_parent_container_id = stackit_resourcemanager_folder.this.container_id - stackit_project_id = stackit_resourcemanager_project.backplane.project_id - stackit_service_account_name = substr(var.platform_identifier, 0, 20) - role_mapping = var.role_mapping - stackit_organization_onboarding_enabled = var.stackit_organization_onboarding_enabled - stackit_network_area_tag_name = var.network_area_tag_name - - hub = var.hub - - meshstack = { - owning_workspace_identifier = var.workspace - location_name = var.use_global_location ? "global" : meshstack_location.this[0].metadata.name - platform_identifier = var.platform_identifier - tags = var.tags - } -} diff --git a/reference-architectures/stackit-sandbox-landingzone/buildingblock/outputs.tf b/reference-architectures/stackit-sandbox-landingzone/buildingblock/outputs.tf deleted file mode 100644 index 6c2f12de..00000000 --- a/reference-architectures/stackit-sandbox-landingzone/buildingblock/outputs.tf +++ /dev/null @@ -1,14 +0,0 @@ -output "lz_folder_container_id" { - value = stackit_resourcemanager_folder.this.container_id - description = "Container ID of the STACKIT resourcemanager folder created for the landing zone. Tenant projects are created inside this folder." -} - -output "backplane_project_id" { - value = stackit_resourcemanager_project.backplane.project_id - description = "Project ID of the STACKIT backplane project that hosts the service account used for tenant project creation." -} - -output "backplane_project_url" { - value = "https://portal.stackit.cloud/projects/${stackit_resourcemanager_project.backplane.project_id}" - description = "Deep link to the backplane project in the STACKIT portal." -} diff --git a/reference-architectures/stackit-sandbox-landingzone/buildingblock/provider.tf b/reference-architectures/stackit-sandbox-landingzone/buildingblock/provider.tf deleted file mode 100644 index 91d6ee43..00000000 --- a/reference-architectures/stackit-sandbox-landingzone/buildingblock/provider.tf +++ /dev/null @@ -1,4 +0,0 @@ -provider "stackit" { - experiments = ["iam"] # Required for authorization resources - service_account_key = var.stackit_service_account_key -} diff --git a/reference-architectures/stackit-sandbox-landingzone/buildingblock/versions.tf b/reference-architectures/stackit-sandbox-landingzone/buildingblock/versions.tf deleted file mode 100644 index 70fa5d68..00000000 --- a/reference-architectures/stackit-sandbox-landingzone/buildingblock/versions.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_version = ">= 1.12.0" # const variables require OpenTofu >= 1.12 / Terraform >= 1.15 - - required_providers { - meshstack = { - source = "meshcloud/meshstack" - version = ">= 0.22.0" - } - stackit = { - source = "stackitcloud/stackit" - version = ">= 0.99.0" - } - } -} diff --git a/website/.gitignore b/website/.gitignore index 89dbeffc..52e63133 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -44,6 +44,7 @@ Thumbs.db # Files generated by index.ts /public/assets/building-block-logos /public/assets/logos +/public/assets/reference-architecture-logos /public/assets/platform.json /public/assets/templates.json diff --git a/website/src/app/core/reference-architecture.ts b/website/src/app/core/reference-architecture.ts index 00c53e62..aab61788 100644 --- a/website/src/app/core/reference-architecture.ts +++ b/website/src/app/core/reference-architecture.ts @@ -10,6 +10,9 @@ export interface ReferenceArchitecture { cloudProviders: string[]; buildingBlocks: ReferenceArchitectureBuildingBlock[]; body: string; + // Public asset path of the architecture's own logo, null when it has none and the logos of its + // cloud providers should be shown instead. + logo: string | null; sourceUrl: string | null; // Set when this reference architecture ships its own meshstack_integration.tf and can be // imported into meshStack directly, the same way a building block is imported. diff --git a/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.html b/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.html index d5f7c549..3af963f2 100644 --- a/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.html +++ b/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.html @@ -12,11 +12,20 @@
- -
-
+
+
- + +
+
+
+ +
diff --git a/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.ts b/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.ts index 41035c55..a21550f4 100644 --- a/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.ts +++ b/website/src/app/features/reference-architecture-detail/reference-architecture-detail.component.ts @@ -31,6 +31,7 @@ interface RefArchDetailVm { buildingBlocks: BuildingBlockLink[]; bodyHtml: string; sourceUrl: string | null; + logo: string | null; platformLogos: { platformType: string; imageUrl: string }[]; integrationSourceUrl: string | null; folderUrl: string | null; @@ -157,6 +158,7 @@ export class ReferenceArchitectureDetailComponent implements OnInit, OnDestroy, buildingBlocks, bodyHtml: marked.parse(arch.body) as string, sourceUrl: arch.sourceUrl, + logo: arch.logo, platformLogos: arch.cloudProviders.map(cp => ({ platformType: cp, imageUrl: platforms.find(p => p.platformType === cp)?.logo ?? 'assets/meshstack-logo.png' diff --git a/website/src/app/features/reference-architecture-list/reference-architecture-list.component.html b/website/src/app/features/reference-architecture-list/reference-architecture-list.component.html index 22f7a25e..78d2f2d9 100644 --- a/website/src/app/features/reference-architecture-list/reference-architecture-list.component.html +++ b/website/src/app/features/reference-architecture-list/reference-architecture-list.component.html @@ -34,14 +34,20 @@

Reference Archite
- +
-
-
- -
+
+
+ +
+
+ +
+
+

{{ card.name }}

diff --git a/website/src/app/features/reference-architecture-list/reference-architecture-list.component.ts b/website/src/app/features/reference-architecture-list/reference-architecture-list.component.ts index 9d7f8796..851d9f6f 100644 --- a/website/src/app/features/reference-architecture-list/reference-architecture-list.component.ts +++ b/website/src/app/features/reference-architecture-list/reference-architecture-list.component.ts @@ -17,6 +17,7 @@ interface RefArchCard { cloudProviders: string[]; buildingBlockCount: number; integrationSourceUrl: string | null; + logo: string | null; platformLogos: { platformType: string; imageUrl: string }[]; } @@ -62,6 +63,7 @@ export class ReferenceArchitectureListComponent implements OnInit { cloudProviders: arch.cloudProviders, buildingBlockCount: arch.buildingBlocks.length, integrationSourceUrl: arch.integrationSourceUrl, + logo: arch.logo, platformLogos: arch.cloudProviders.map(cp => ({ platformType: cp, imageUrl: platforms.find(p => p.platformType === cp)?.logo ?? 'assets/meshstack-logo.png' diff --git a/website/src/app/features/template-gallery/template-gallery.component.html b/website/src/app/features/template-gallery/template-gallery.component.html index 40ed2dbc..3216f699 100644 --- a/website/src/app/features/template-gallery/template-gallery.component.html +++ b/website/src/app/features/template-gallery/template-gallery.component.html @@ -74,14 +74,20 @@

- +
-
-
- -
+
+
+ +
+
+ +
+
+

{{ card.name }}

diff --git a/website/src/app/features/template-gallery/template-gallery.component.ts b/website/src/app/features/template-gallery/template-gallery.component.ts index 2051241d..2949691c 100644 --- a/website/src/app/features/template-gallery/template-gallery.component.ts +++ b/website/src/app/features/template-gallery/template-gallery.component.ts @@ -21,6 +21,7 @@ interface RefArchCardVm { name: string; description: string; buildingBlockCount: number; + logo: string | null; platformLogos: { platformType: string; imageUrl: string }[]; } @@ -215,6 +216,7 @@ export class TemplateGalleryComponent implements OnInit, OnDestroy { name: arch.name, description: arch.description, buildingBlockCount: arch.buildingBlocks.length, + logo: arch.logo, platformLogos: arch.cloudProviders.map(cp => ({ platformType: cp, imageUrl: platforms.find(p => p.platformType === cp)?.logo ?? 'assets/meshstack-logo.png'