Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 61 additions & 3 deletions modules/stackit/meshstack_integration.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ variable "stackit_organization_id" {
description = "STACKIT organization ID where the service account will be granted permissions."
}

variable "stackit_organization_member_role" {
type = string
default = "organization.viewer"
description = "STACKIT organization role assigned best-effort to all meshStack project users before project role assignments are applied."
}

variable "stackit_organization_onboarding_enabled" {
type = bool
default = true
description = "Whether the building block adds meshStack project users to the STACKIT organization (with `stackit_organization_member_role`) before applying project-level role assignments. Disable if organization membership is managed outside this building block."
}

variable "stackit_parent_container_id" {
type = string
description = "Default parent container ID (organization or folder) for project creation."
Expand Down Expand Up @@ -68,9 +80,10 @@ variable "hub" {
module "backplane" {
source = "github.com/meshcloud/meshstack-hub//modules/stackit/project/backplane?ref=${var.hub.git_ref}"

project_id = var.stackit_project_id
organization_id = var.stackit_organization_id
service_account_name = coalesce(var.stackit_service_account_name, "mesh-project")
project_id = var.stackit_project_id
organization_id = var.stackit_organization_id
service_account_name = coalesce(var.stackit_service_account_name, "mesh-project")
organization_onboarding_enabled = var.stackit_organization_onboarding_enabled

workload_identity_federation = {
issuer = data.meshstack_integrations.integrations.workload_identity_federation.replicator.issuer
Expand Down Expand Up @@ -179,6 +192,9 @@ resource "meshstack_building_block_definition" "this" {
ref_name = var.hub.git_ref
async = false
use_mesh_http_backend_fallback = true
pre_run_script = <<-SH
exec python3 "./prerun.py" "$@"
SH
}
}

Expand Down Expand Up @@ -217,6 +233,42 @@ resource "meshstack_building_block_definition" "this" {
argument = jsonencode("/var/run/secrets/workload-identity/azure/token")
}

STACKIT_SERVICE_ACCOUNT_EMAIL = {
display_name = "STACKIT Service Account Email"
description = "Service account email used by the pre-run script for WIF token exchange."
type = "STRING"
assignment_type = "STATIC"
is_environment = true
argument = jsonencode(module.backplane.service_account_email)
}

STACKIT_ORGANIZATION_ID = {
display_name = "STACKIT Organization ID"
description = "STACKIT organization where meshStack project users are added best-effort before project role assignments are applied."
type = "STRING"
assignment_type = "STATIC"
is_environment = true
argument = jsonencode(var.stackit_organization_id)
}

STACKIT_ORGANIZATION_MEMBER_ROLE = {
display_name = "STACKIT Organization Member Role"
description = "STACKIT organization role assigned best-effort to all meshStack project users."
type = "STRING"
assignment_type = "STATIC"
is_environment = true
argument = jsonencode(var.stackit_organization_member_role)
}

STACKIT_ORGANIZATION_ONBOARDING_ENABLED = {
display_name = "STACKIT Organization Onboarding Enabled"
description = "Whether the pre-run script adds meshStack project users to the STACKIT organization before project role assignments are applied."
type = "STRING"
assignment_type = "STATIC"
is_environment = true
argument = jsonencode(var.stackit_organization_onboarding_enabled ? "1" : "0")
}

project_name = {
display_name = "Project Name"
description = "Name of the STACKIT project."
Expand Down Expand Up @@ -262,6 +314,12 @@ resource "meshstack_building_block_definition" "this" {
project_name = {
display_name = "Project Name"
type = "STRING"
assignment_type = "NONE"
}

summary = {
display_name = "Summary"
type = "STRING"
assignment_type = "SUMMARY"
}
}
Expand Down
3 changes: 3 additions & 0 deletions modules/stackit/project/backplane/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ It creates a dedicated service account with a Workload Identity Federation (WIF)
and the permissions required to create and manage STACKIT projects under a given organization:

- **`resource-manager.admin`** — allows creating and managing projects within the organization.
- **`iam.member-admin`** — allows the building block pre-run script to add meshStack project users to the STACKIT organization before project-level role assignments are applied. Only granted when `organization_onboarding_enabled` is `true` (the default).

Authentication uses WIF (OIDC token exchange) — no long-lived service account key is created or stored.

Expand Down Expand Up @@ -51,6 +52,7 @@ No modules.

| Name | Type |
|------|------|
| [stackit_authorization_organization_role_assignment.member_admin](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_organization_role_assignment) | resource |
| [stackit_authorization_organization_role_assignment.project_admin](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/authorization_organization_role_assignment) | resource |
| [stackit_service_account.building_block](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/service_account) | resource |
| [stackit_service_account_federated_identity_provider.building_block](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/service_account_federated_identity_provider) | resource |
Expand All @@ -60,6 +62,7 @@ No modules.
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_organization_id"></a> [organization\_id](#input\_organization\_id) | STACKIT organization ID where the service account will be granted permissions to create and manage projects. | `string` | n/a | yes |
| <a name="input_organization_onboarding_enabled"></a> [organization\_onboarding\_enabled](#input\_organization\_onboarding\_enabled) | Whether the service account is granted `iam.member-admin` so the building block pre-run script can add meshStack project users to the STACKIT organization. | `bool` | `true` | no |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | STACKIT project ID where the service account will be created. | `string` | n/a | yes |
| <a name="input_service_account_name"></a> [service\_account\_name](#input\_service\_account\_name) | Name of the service account created in the STACKIT project. Override when deploying multiple backplane instances in the same project. | `string` | `"mesh-project"` | no |
| <a name="input_workload_identity_federation"></a> [workload\_identity\_federation](#input\_workload\_identity\_federation) | WIF issuer URL and subject list for the meshStack building block identity provider. | <pre>object({<br/> issuer = string<br/> subjects = list(string)<br/> })</pre> | n/a | yes |
Expand Down
13 changes: 13 additions & 0 deletions modules/stackit/project/backplane/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,16 @@ resource "stackit_authorization_organization_role_assignment" "project_admin" {
role = "resource-manager.admin"
subject = stackit_service_account.building_block.email
}

moved {
from = stackit_authorization_organization_role_assignment.member_admin
to = stackit_authorization_organization_role_assignment.member_admin[0]
}

resource "stackit_authorization_organization_role_assignment" "member_admin" {
count = var.organization_onboarding_enabled ? 1 : 0

resource_id = var.organization_id
role = "iam.member-admin"
subject = stackit_service_account.building_block.email
}
7 changes: 7 additions & 0 deletions modules/stackit/project/backplane/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,10 @@ variable "service_account_name" {
nullable = false
description = "Name of the service account created in the STACKIT project. Override when deploying multiple backplane instances in the same project."
}

variable "organization_onboarding_enabled" {
type = bool
default = true
nullable = false
description = "Whether the service account is granted `iam.member-admin` so the building block pre-run script can add meshStack project users to the STACKIT organization."
}
10 changes: 9 additions & 1 deletion modules/stackit/project/buildingblock/APP_TEAM_README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# StackIt Project

## Description
This building block creates a new STACKIT project and manages user access permissions with configurable role mapping. It provides application teams with a secure, isolated environment for deploying their workloads while ensuring proper access controls.
This building block creates a new STACKIT project and manages user access permissions with configurable role mapping. It also performs best-effort STACKIT organization onboarding for all assigned users before project permissions are applied. It provides application teams with a secure, isolated environment for deploying their workloads while ensuring proper access controls.

## Usage Motivation
This building block is designed for application teams that need to:
Expand Down Expand Up @@ -85,6 +85,14 @@ role_mapping = {

Unknown meshStack roles are ignored. If a user has multiple meshStack roles, all mapped STACKIT roles are assigned once.

### STACKIT Organization Membership

Before applying project-level role assignments, the building block runs a best-effort pre-run step that adds all assigned meshStack users to the STACKIT organization with the organization role configured by the platform team. The default organization role is `organization.viewer`. The platform team can disable this step entirely; in that case, organization membership must be managed outside this building block.

This onboarding step is apply-only and does not remove organization memberships during destroy because organization membership can be shared by multiple projects. If the onboarding request fails, the building block logs a warning and continues; project-level role assignment remains authoritative and may still fail if STACKIT rejects a user that is not an organization member.

The building block summary lists each user with a status: a checkmark if the required organization role is assigned, or details about what went wrong otherwise (e.g. a failed add request or a role that is still missing). If any user's status is not a checkmark, the summary includes remediation guidance for the platform team.

### Environment-Based Parent Container Selection

The building block supports automatic parent container selection based on environment type:
Expand Down
3 changes: 2 additions & 1 deletion modules/stackit/project/buildingblock/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: |

# STACKIT Project Building Block

This Terraform module provisions a STACKIT project with user access control. meshStack roles from the `users` input are mapped to STACKIT project roles via the configurable `role_mapping` input.
This Terraform module provisions a STACKIT project with user access control. meshStack roles from the `users` input are mapped to STACKIT project roles via the configurable `role_mapping` input. When used through the meshStack integration, a pre-run script performs best-effort STACKIT organization onboarding for all assigned users before project-level role assignments are applied and writes a summary that marks users with ✅ when the required organization role is currently assigned.

## Requirements

Expand Down Expand Up @@ -78,4 +78,5 @@ No modules.
| <a name="output_project_id"></a> [project\_id](#output\_project\_id) | The UUID of the created StackIt project. |
| <a name="output_project_name"></a> [project\_name](#output\_project\_name) | The name of the created StackIt project. |
| <a name="output_project_url"></a> [project\_url](#output\_project\_url) | The deep link URL to access the project in the StackIt portal. |
| <a name="output_summary"></a> [summary](#output\_summary) | Summary of STACKIT organization membership onboarding for assigned project users. |
<!-- END_TF_DOCS -->
6 changes: 5 additions & 1 deletion modules/stackit/project/buildingblock/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ output "project_name" {
description = "The name of the created StackIt project."
}


output "project_url" {
value = "https://portal.stackit.cloud/projects/${stackit_resourcemanager_project.project.project_id}"
description = "The deep link URL to access the project in the StackIt portal."
}

output "summary" {
value = fileexists("${path.module}/stackit_organization_membership_summary.md") ? file("${path.module}/stackit_organization_membership_summary.md") : "STACKIT organization membership summary was not generated."
description = "Summary of STACKIT organization membership onboarding for assigned project users."
}
Loading
Loading