diff --git a/modules/meshstack/composition/buildingblock/README.md b/modules/meshstack/composition/buildingblock/README.md new file mode 100644 index 00000000..3c6bd9ad --- /dev/null +++ b/modules/meshstack/composition/buildingblock/README.md @@ -0,0 +1,122 @@ +--- +name: Composition Demo +supportedPlatforms: + - meshstack +description: | + Reference building block demonstrating the composition pattern: it uses the run's + ephemeral API key to create a building block definition with a building block from it, + and an empty platform with a landing zone. +# Creates only meshObjects through the meshStack API, so there is nothing to set up cloud-side. +requiresBackplane: false +--- +# Composition Demo + +This building block is a reference implementation of the **composition** pattern: a building block +that provisions other meshObjects through the meshStack API instead of cloud resources. It creates: + +- a `meshBuildingBlockDefinition` in the consuming workspace and a `meshBuildingBlock` from it, and +- an **empty platform** — a `meshPlatformType`, a `meshLocation`, a `meshPlatform` and a + `meshLandingZone` on it. + +The created definition runs the hub's [`link`](../../link) building block, which provisions nothing +but a `terraform_data` and needs neither a cloud provider nor an operator. Reusing it keeps the whole +chain automatic and avoids inventing a throwaway implementation just to have something to create. +Both created building block objects are named after `link_name` (default `Link`) to keep them +distinguishable from the `Composition Demo` block that created them. + +*Empty* is the point of the platform: it is a `CUSTOM` platform with no cloud connection config and a +landing zone whose platform properties are `{}`. meshStack ships no `CUSTOM` platform type and every +built-in type is a cloud platform whose config would need real credentials, so the module creates a +type of its own. Nothing about it can be replicated to a cloud — it exists to carry a name, a landing +zone and its creation provenance. + +## How it works + +The building block definition declares `permissions`, so meshStack issues an **ephemeral API key** +scoped to the consuming workspace for the duration of each run and injects it as `MESHSTACK_ENDPOINT` +/ `MESHSTACK_API_TOKEN`. The `meshstack` provider picks those up with no explicit configuration, so +`provider "meshstack" {}` is all this module needs. + +Because every meshObject is created with that key, meshStack records it as their creation author and +surfaces **"created by building block"** provenance on each of them, linking back to the composition +that created them. That makes this module a convenient end-to-end check of that provenance without any +cloud platform involved. + +## Permissions + +| Permission | Why | +|---|---| +| `BUILDINGBLOCKDEFINITION_LIST` / `_SAVE` / `_DELETE` | Manage the created building block definition | +| `BUILDINGBLOCK_LIST` / `_SAVE` / `_DELETE` | Manage the created building block | +| `PLATFORMINSTANCE_LIST` / `_SAVE` / `_DELETE` | Manage the created platform, its location and its platform type | +| `LANDINGZONE_LIST` / `_SAVE` / `_DELETE` | Manage the created landing zone | + +The created definition itself declares no permissions, so it stays within meshStack's privilege +escalation guard (a nested definition may only request a subset of its parent's permissions). + +## Notes + +- This module does not wait for the created building block's run. That run needs a terraform runner, + and a stack with a single one cannot start it before this run returns. Provenance is recorded when + the block is created, so nothing here depends on the result. +- For the same reason the created definition uses `deletion_mode = "PURGE"`. `DELETE` would schedule + a deprovisioning run on teardown that cannot start until the composition's own destroy run returns. + Purging leaks nothing, since `link` provisions no infrastructure. +- `hub_git_ref` is wired in as a static input from the composition's own `var.hub.git_ref`, so the + created definition clones the `link` module from the same hub revision. +- The created definition's version stays a **draft**. Releasing needs admin approval, which the run's + ephemeral key — a plain workspace key — cannot obtain, so `draft = false` would leave the version + `DRAFT` regardless, warn on every run, and leave `version_latest_release` null. The created + building block therefore references `version_latest`, which a draft permits because the definition + and the building block's target are the same workspace. +- A platform, location and platform type identifier is globally unique and cannot be reused once + deleted. The module therefore derives all three from `building_block_uuid`, a + `TENANT_BUILDING_BLOCK_UUID` input carrying the UUID of the building block the run belongs to, so + each building block gets identifiers of its own that survive re-runs. +- The created platform is `PRIVATE` and `UNPUBLISHED`, which keeps it out of the marketplace. + meshStack ties those two together — `UNPUBLISHED` is rejected for any other restriction — and + `PRIVATE` in turn requires `restricted_to_workspaces` to name exactly the owning workspace. + + +## Requirements + +| Name | Version | +|------|---------| +| [meshstack](#requirement\_meshstack) | >= 0.24.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [meshstack_building_block.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block) | resource | +| [meshstack_building_block_definition.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block_definition) | resource | +| [meshstack_landingzone.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/landingzone) | resource | +| [meshstack_location.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/location) | resource | +| [meshstack_platform.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/platform) | resource | +| [meshstack_platform_type.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/platform_type) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [building\_block\_uuid](#input\_building\_block\_uuid) | UUID of the building block this run belongs to. Wired in as a TENANT\_BUILDING\_BLOCK\_UUID input and used to name the created platform, location and platform type, whose identifiers must be globally unique. | `string` | n/a | yes | +| [hub\_git\_ref](#input\_hub\_git\_ref) | Hub reference the created building block definition clones its implementation from. Wired in as a static input from the composition's own `var.hub.git_ref`, so both definitions stay on the same hub revision. | `string` | n/a | yes | +| [link\_name](#input\_link\_name) | Name given to the building block definition and building block this composition creates. | `string` | `"Link"` | no | +| [link\_url](#input\_link\_url) | Target of the link the created building block publishes. | `string` | n/a | yes | +| [platform\_name](#input\_platform\_name) | Display name of the platform this composition creates. Its identifier is generated instead of taken from here, because a platform identifier cannot be reused once deleted. | `string` | `"Composition Demo Platform"` | no | +| [workspace\_identifier](#input\_workspace\_identifier) | Workspace the created building block definition is owned by and the created building block is attached to. Wired in as a WORKSPACE\_IDENTIFIER input, so it is always the consuming workspace — the same one the run's ephemeral API key is scoped to. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [created\_building\_block\_definition\_uuid](#output\_created\_building\_block\_definition\_uuid) | UUID of the building block definition this composition created. | +| [created\_building\_block\_uuid](#output\_created\_building\_block\_uuid) | UUID of the building block this composition created. | +| [created\_landing\_zone\_identifier](#output\_created\_landing\_zone\_identifier) | Identifier of the landing zone this composition created. Landing zones have no UUID in the API. | +| [created\_platform\_uuid](#output\_created\_platform\_uuid) | UUID of the platform this composition created. | +| [summary](#output\_summary) | Markdown summary shown on the building block's detail page. | + diff --git a/modules/meshstack/composition/buildingblock/logo.png b/modules/meshstack/composition/buildingblock/logo.png new file mode 100644 index 00000000..634d5dcb Binary files /dev/null and b/modules/meshstack/composition/buildingblock/logo.png differ diff --git a/modules/meshstack/composition/buildingblock/main.tf b/modules/meshstack/composition/buildingblock/main.tf new file mode 100644 index 00000000..6a4a4da3 --- /dev/null +++ b/modules/meshstack/composition/buildingblock/main.tf @@ -0,0 +1,118 @@ +# Both resources below are created through the meshStack API with the run's ephemeral API key, which +# meshStack records as their creation author. That is what makes them show "created by building +# block" provenance pointing back at the building block this module runs for. + +# Runs the hub's `link` building block rather than an implementation of its own: `link` provisions +# nothing but a terraform_data, needs no cloud provider and no operator, which makes it the cheapest +# real implementation to hand a created definition. See modules/meshstack/link. +resource "meshstack_building_block_definition" "created" { + metadata = { + owned_by_workspace = var.workspace_identifier + } + + spec = { + display_name = var.link_name + description = "Link building block definition created by the Composition Demo building block." + target_type = "WORKSPACE_LEVEL" + run_transparency = true + + readme = chomp(<<-EOT + A link building block definition created by a composition rather than by a platform engineer. + + Ordering it publishes a link in your workspace and provisions no infrastructure. + EOT + ) + } + + version_spec = { + # Stays a draft: releasing a version needs admin approval, which the run's ephemeral key — a + # plain workspace key — cannot obtain. Setting draft = false would leave the version DRAFT + # anyway, warn on every run, and leave version_latest_release null. The building block below can + # still be created from a draft because the definition and the target are the same workspace, + # which satisfies BuildingBlockCreationValidator.requireAccess's `selfOwning` branch. + draft = true + + # PURGE, not DELETE: DELETE would schedule a deprovisioning run for the building block below when + # this composition is torn down, and that run cannot start until the composition's own destroy run + # returns — a deadlock wherever the terraform runner pool has a single worker. Nothing is leaked + # by purging, because the `link` implementation provisions nothing but a terraform_data. + deletion_mode = "PURGE" + + implementation = { + terraform = { + repository_url = "https://github.com/meshcloud/meshstack-hub.git" + repository_path = "modules/meshstack/link/buildingblock" + ref_name = var.hub_git_ref + terraform_version = "1.11.0" + async = false + use_mesh_http_backend_fallback = true + } + } + + # All static, so the created building block below has no inputs left to supply. Mirrors the + # wiring in modules/meshstack/link/meshstack_integration.tf. + inputs = { + url = { + display_name = "URL" + description = "Target of the link." + type = "STRING" + assignment_type = "STATIC" + argument = jsonencode(var.link_url) + } + title = { + display_name = "Title" + description = "Human-readable name of the linked resource." + type = "STRING" + assignment_type = "STATIC" + argument = jsonencode(var.link_name) + } + # Empty falls back to the summary the link module generates from title and url. + summary = { + display_name = "Summary" + description = "Markdown rendered for the application team after deployment." + type = "STRING" + assignment_type = "STATIC" + argument = jsonencode("") + } + } + + outputs = { + url = { + display_name = var.link_name + type = "STRING" + assignment_type = "RESOURCE_URL" + } + summary = { + display_name = "Summary" + type = "STRING" + assignment_type = "SUMMARY" + } + } + } +} + +resource "meshstack_building_block" "created" { + spec = { + # version_latest, not version_latest_release: the definition above stays a draft, so + # version_latest_release is null. + building_block_definition_version_ref = { + uuid = meshstack_building_block_definition.created.version_latest.uuid + content_hash = meshstack_building_block_definition.created.version_latest.content_hash + } + + display_name = var.link_name + + target_ref = { + kind = "meshWorkspace" + name = var.workspace_identifier + } + + # Every input of the definition above is static, so there is nothing for this block to set. + inputs = {} + } + + # Deliberately not waiting: this run would be waiting on a run of the same implementation type, + # which a stack with a single terraform runner cannot start until this one returns. Nothing below + # depends on the result either — provenance is recorded when the block is created. + wait_for_completion = false +} diff --git a/modules/meshstack/composition/buildingblock/outputs.tf b/modules/meshstack/composition/buildingblock/outputs.tf new file mode 100644 index 00000000..76c5e39f --- /dev/null +++ b/modules/meshstack/composition/buildingblock/outputs.tf @@ -0,0 +1,37 @@ +output "created_building_block_definition_uuid" { + value = meshstack_building_block_definition.created.metadata.uuid + description = "UUID of the building block definition this composition created." +} + +output "created_building_block_uuid" { + value = meshstack_building_block.created.metadata.uuid + description = "UUID of the building block this composition created." +} + +output "created_platform_uuid" { + value = meshstack_platform.created.metadata.uuid + description = "UUID of the platform this composition created." +} + +output "created_landing_zone_identifier" { + value = meshstack_landingzone.created.metadata.name + description = "Identifier of the landing zone this composition created. Landing zones have no UUID in the API." +} + +output "summary" { + value = chomp(<<-EOT + Created a Link building block definition with a building block from it, and an empty platform with a + landing zone on it. All of them show this building block as their creator. + + | meshObject | Name | Identifier | + |---|---|---| + | Building Block Definition | ${var.link_name} | `${meshstack_building_block_definition.created.metadata.uuid}` | + | Building Block | ${var.link_name} | `${meshstack_building_block.created.metadata.uuid}` | + | Platform Type | ${meshstack_platform_type.created.spec.display_name} | `${meshstack_platform_type.created.metadata.name}` | + | Location | ${meshstack_location.created.spec.display_name} | `${meshstack_location.created.metadata.name}` | + | Platform | ${var.platform_name} | `${meshstack_platform.created.identifier}` | + | Landing Zone | ${meshstack_landingzone.created.spec.display_name} | `${meshstack_landingzone.created.metadata.name}` | + EOT + ) + description = "Markdown summary shown on the building block's detail page." +} diff --git a/modules/meshstack/composition/buildingblock/platform.tf b/modules/meshstack/composition/buildingblock/platform.tf new file mode 100644 index 00000000..d215e38b --- /dev/null +++ b/modules/meshstack/composition/buildingblock/platform.tf @@ -0,0 +1,105 @@ +# The second thing this composition creates: an *empty* platform with a landing zone on it. Empty means +# the least a platform can carry — a CUSTOM platform type, no cloud connection config, and a landing +# zone whose platform properties are `{}`. That is enough for meshStack to record and show creation +# provenance on both, and it needs no cloud credentials and no operator. +# +# As in main.tf, everything here is created with the run's ephemeral API key, which is what makes +# meshStack attribute the creation to the building block this module runs for. + +locals { + # A platform, location and platform type identifier is globally unique and cannot be reused once + # deleted, so the objects below are named after the building block that owns them. Unlike a random + # suffix this survives re-runs without a stored value, and it makes the identifiers point back at + # their creator on sight. + suffix = substr(replace(var.building_block_uuid, "-", ""), 0, 8) + identifier = "composition-demo-${local.suffix}" +} + +# meshStack ships no CUSTOM platform type, and every built-in type is a cloud platform whose config +# would need real credentials — so an empty platform needs a type of its own. +resource "meshstack_platform_type" "created" { + metadata = { + # Platform type identifiers are uppercase, unlike every other identifier here. + name = upper(local.identifier) + owned_by_workspace = var.workspace_identifier + } + + spec = { + # Carries the suffix because meshStack requires platform type display names to be globally unique. + display_name = "${var.platform_name} ${upper(local.suffix)}" + + # A 1x1 transparent PNG. The API requires an icon, and nothing here is meant to be looked at. + icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR42mNgAAIAAAUAAen63NgAAAAASUVORK5CYII=" + } +} + +resource "meshstack_location" "created" { + metadata = { + name = local.identifier + owned_by_workspace = var.workspace_identifier + } + + spec = { + display_name = "${var.platform_name} Location" + description = "Location created by the Composition Demo building block." + } +} + +resource "meshstack_platform" "created" { + metadata = { + name = local.identifier + owned_by_workspace = var.workspace_identifier + } + + spec = { + display_name = var.platform_name + description = "Empty platform created by the Composition Demo building block." + endpoint = "https://docs.meshcloud.io" + + location_ref = { + name = meshstack_location.created.metadata.name + } + + # Keeps the platform out of the marketplace, which is where an empty platform belongs. meshStack + # ties the three fields together: UNPUBLISHED is only allowed with PRIVATE, and PRIVATE requires + # `restricted_to_workspaces` to name exactly the owner. + availability = { + restriction = "PRIVATE" + publication_state = "UNPUBLISHED" + restricted_to_workspaces = [var.workspace_identifier] + } + + config = { + custom = { + platform_type_ref = { + name = meshstack_platform_type.created.metadata.name + } + } + } + } +} + +resource "meshstack_landingzone" "created" { + metadata = { + # Only unique per platform, so it can repeat the platform's identifier. + name = local.identifier + owned_by_workspace = var.workspace_identifier + } + + spec = { + display_name = "${var.platform_name} Landing Zone" + description = "Empty landing zone created by the Composition Demo building block." + + automate_deletion_approval = false + automate_deletion_replication = false + + platform_ref = { + uuid = meshstack_platform.created.metadata.uuid + } + + # A custom platform has no landing zone properties at all. + platform_properties = { + custom = {} + } + } +} diff --git a/modules/meshstack/composition/buildingblock/provider.tf b/modules/meshstack/composition/buildingblock/provider.tf new file mode 100644 index 00000000..e03d77b1 --- /dev/null +++ b/modules/meshstack/composition/buildingblock/provider.tf @@ -0,0 +1,4 @@ +# Authenticates from the MESHSTACK_ENDPOINT / MESHSTACK_API_TOKEN environment variables the runner +# injects for the run's ephemeral API key. +provider "meshstack" { +} diff --git a/modules/meshstack/composition/buildingblock/variables.tf b/modules/meshstack/composition/buildingblock/variables.tf new file mode 100644 index 00000000..382f5f18 --- /dev/null +++ b/modules/meshstack/composition/buildingblock/variables.tf @@ -0,0 +1,34 @@ +# Defaulted, unlike the other inputs, so that a run can still destroy a building block whose stored +# inputs predate a rename of this one. A destroy only has to delete the created meshObjects, and their +# display names have no bearing on that. +variable "link_name" { + type = string + default = "Link" + description = "Name given to the building block definition and building block this composition creates." +} + +variable "link_url" { + type = string + description = "Target of the link the created building block publishes." +} + +variable "workspace_identifier" { + type = string + description = "Workspace the created building block definition is owned by and the created building block is attached to. Wired in as a WORKSPACE_IDENTIFIER input, so it is always the consuming workspace — the same one the run's ephemeral API key is scoped to." +} + +variable "hub_git_ref" { + type = string + description = "Hub reference the created building block definition clones its implementation from. Wired in as a static input from the composition's own `var.hub.git_ref`, so both definitions stay on the same hub revision." +} + +variable "platform_name" { + type = string + default = "Composition Demo Platform" + description = "Display name of the platform this composition creates. Its identifier is generated instead of taken from here, because a platform identifier cannot be reused once deleted." +} + +variable "building_block_uuid" { + type = string + description = "UUID of the building block this run belongs to. Wired in as a TENANT_BUILDING_BLOCK_UUID input and used to name the created platform, location and platform type, whose identifiers must be globally unique." +} diff --git a/modules/meshstack/composition/buildingblock/versions.tf b/modules/meshstack/composition/buildingblock/versions.tf new file mode 100644 index 00000000..da8747cc --- /dev/null +++ b/modules/meshstack/composition/buildingblock/versions.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + meshstack = { + source = "meshcloud/meshstack" + version = ">= 0.24.0" + } + } +} diff --git a/modules/meshstack/composition/meshstack_integration.tf b/modules/meshstack/composition/meshstack_integration.tf new file mode 100644 index 00000000..b973113c --- /dev/null +++ b/modules/meshstack/composition/meshstack_integration.tf @@ -0,0 +1,221 @@ +variable "link_name" { + type = string + default = "Link" + description = "Name given to the building block definition and building block this composition creates. Both are the hub's `link` building block, hence the default." +} + +variable "link_url" { + type = string + default = "https://docs.meshcloud.io" + description = "Target of the link the created building block publishes. The created definition runs the hub's `link` building block, which provisions nothing but this address." +} + +variable "platform_name" { + type = string + default = "Composition Demo Platform" + description = "Display name of the empty platform this composition creates. Its identifier is generated from the building block UUID, because a platform identifier cannot be reused once deleted." +} + +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 + git_ref = var.hub.git_ref + } +} + +resource "meshstack_building_block_definition" "this" { + metadata = { + owned_by_workspace = var.meshstack.owning_workspace_identifier + tags = var.meshstack.tags + } + + spec = { + display_name = "Composition Demo" + description = "Reference building block demonstrating the composition pattern: creates a Link building block definition with a building block from it, plus an empty platform with a landing zone, all using the run's ephemeral API key." + target_type = "WORKSPACE_LEVEL" + symbol = "https://raw.githubusercontent.com/meshcloud/meshstack-hub/${var.hub.git_ref}/modules/meshstack/composition/buildingblock/logo.png" + + # Lets consuming workspace users read the run logs, which is where the interesting part happens. + run_transparency = true + + readme = chomp(<<-EOT + Creates four meshObjects in your workspace: a link building block definition with a building block + from it, and an empty platform with a landing zone on it. All of them carry a "created by building + block" reference back to this building block, so you can follow where they came from. Nothing is + provisioned outside meshStack, and no step needs an operator. + + ## 🎯 When to use it + + Use this building block when you: + - Want to see the composition pattern end to end — a building block provisioning meshObjects + through the meshStack API rather than cloud resources. + - Need a self-service way to hand a workspace its own building block definition or platform + without a platform engineer creating one by hand. + + ## 💡 Usage examples + + **Example 1: Exploring compositions** + A developer adds this building block to a sandbox workspace and inspects the meshObjects that + appear, following the creator link on each back to this building block. + + **Example 2: Offering a workspace its own marketplace link** + A team wants a documentation link published in their own workspace's marketplace and gets both + the definition and a ready-made building block by adding this building block. + + ## 📊 Shared Responsibility + + | Responsibility | Platform Team | Application Team | + |---|:---:|:---:| + | Maintain this composition and its permissions | ✅ | ❌ | + | Operate whatever the published link points at | ❌ | ❌ | + | Choose the names, the link target and the platform name | ❌ | ✅ | + | Decide when to add or remove this building block | ❌ | ✅ | + EOT + ) + } + + version_spec = { + draft = var.hub.bbd_draft + + # DELETE so removing the building block runs `tofu destroy` and cleans up what it created. + deletion_mode = "DELETE" + + implementation = { + terraform = { + repository_url = "https://github.com/meshcloud/meshstack-hub.git" + repository_path = "modules/meshstack/composition/buildingblock" + ref_name = var.hub.git_ref + terraform_version = "1.11.0" + async = false + use_mesh_http_backend_fallback = true + } + } + + inputs = { + link_name = { + assignment_type = "USER_INPUT" + type = "STRING" + display_name = "Link Name" + description = "Name given to the building block definition and building block this composition creates." + default_value = jsonencode(var.link_name) + } + link_url = { + assignment_type = "USER_INPUT" + type = "STRING" + display_name = "Link URL" + description = "Target of the link the created building block publishes." + default_value = jsonencode(var.link_url) + } + platform_name = { + assignment_type = "USER_INPUT" + type = "STRING" + display_name = "Platform Name" + description = "Display name of the empty platform this composition creates." + default_value = jsonencode(var.platform_name) + } + # Names the created platform, location and platform type, whose identifiers are globally unique. + building_block_uuid = { + assignment_type = "TENANT_BUILDING_BLOCK_UUID" + type = "STRING" + display_name = "Building Block UUID" + description = "UUID of this building block, used to name the created platform and its location and type." + } + workspace_identifier = { + assignment_type = "WORKSPACE_IDENTIFIER" + type = "STRING" + display_name = "Workspace Identifier" + description = "Workspace the created meshObjects belong to. Always the consuming workspace, which is the one the run's ephemeral API key is scoped to." + } + # Pins the created definition's implementation to the same hub revision as this one. + hub_git_ref = { + assignment_type = "STATIC" + type = "STRING" + display_name = "Hub Git Ref" + argument = jsonencode(var.hub.git_ref) + } + } + + outputs = { + created_building_block_definition_uuid = { + assignment_type = "NONE" + type = "STRING" + display_name = "Created Building Block Definition" + } + created_building_block_uuid = { + assignment_type = "NONE" + type = "STRING" + display_name = "Created Building Block" + } + created_platform_uuid = { + assignment_type = "NONE" + type = "STRING" + display_name = "Created Platform" + } + created_landing_zone_identifier = { + assignment_type = "NONE" + type = "STRING" + display_name = "Created Landing Zone" + } + summary = { + assignment_type = "SUMMARY" + type = "STRING" + display_name = "Summary" + } + } + + # Grants each run an ephemeral API key with exactly these workspace permissions. Creating the + # meshObjects with that key is what records this building block as their creator. + permissions = [ + "BUILDINGBLOCKDEFINITION_LIST", + "BUILDINGBLOCKDEFINITION_SAVE", + "BUILDINGBLOCKDEFINITION_DELETE", + "BUILDINGBLOCK_LIST", + "BUILDINGBLOCK_SAVE", + "BUILDINGBLOCK_DELETE", + # Platform instance rights also cover locations and platform types. + "PLATFORMINSTANCE_LIST", + "PLATFORMINSTANCE_SAVE", + "PLATFORMINSTANCE_DELETE", + "LANDINGZONE_LIST", + "LANDINGZONE_SAVE", + "LANDINGZONE_DELETE", + ] + } +} + +terraform { + required_version = ">= 1.12.0" + + required_providers { + meshstack = { + source = "meshcloud/meshstack" + version = ">= 0.24.0" + } + } +}