Skip to content

fix(aws): migrate agentic coding sandbox to ref-based meshStack resources - #252

Draft
grubmeshi wants to merge 1 commit into
mainfrom
fix/agentic-sandbox-tenant-v4
Draft

fix(aws): migrate agentic coding sandbox to ref-based meshStack resources#252
grubmeshi wants to merge 1 commit into
mainfrom
fix/agentic-sandbox-tenant-v4

Conversation

@grubmeshi

@grubmeshi grubmeshi commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Not ready to merge. This PR is being extended into the full change described under
Where this is going. What is on the branch today unbreaks the module; the
remaining work turns the sandbox and its two child building blocks into as-code building block
definitions. Held open deliberately so consumers break once, not twice.

What was broken

modules/aws/agentic-coding-sandbox/buildingblock/main.tf was written against a meshStack provider that no longer exists. Since terraform-provider-meshstack v0.24.0 meshstack_tenant runs on the meshTenant v4 API:

  • metadata.platform_identifier became spec.platform_ref, a {uuid, kind} reference
  • spec.landing_zone_identifier became spec.landing_zone_ref, a {name, kind} reference

So the module did not even validate. The two child building blocks used the deprecated meshstack_buildingblock (v1) resource and built their metadata.tenant_identifier from meshstack_tenant.sandbox.metadata.platform_identifier, an attribute that no longer exists.

The tenant

resource "meshstack_tenant" "sandbox" {
  metadata = {
    owned_by_workspace = meshstack_project.sandbox.metadata.owned_by_workspace
    owned_by_project   = meshstack_project.sandbox.metadata.name
  }

  spec = {
    platform_ref = local.platform_ref
    landing_zone_ref = {
      name = local.landing_zone_identifier
    }
  }
}

platform_ref needs a platform uuid, and this composition is configured with a platform identifier, so the module resolves the uuid itself:

data "meshstack_platforms" "available" {}

locals {
  platform_ref = one([
    for platform in data.meshstack_platforms.available.platforms : platform.ref
    if platform.identifier == local.platform_identifier
  ])
}
  • The plural meshstack_platforms data source exposes a computed ref per element that goes straight into spec.platform_ref, which is the pattern the provider docs recommend.
  • The match is done client-side on the element's computed identifier (the full <platform-name>.<location-name>) rather than the server-side identifier filter, because that filter matches metadata.name, which does not carry the location segment. Both provider doc examples match client-side too.
  • A precondition turns a mistyped platform identifier, or one the API key cannot see, into a readable error instead of a bare "required attribute is null".
  • modules/ske/ske-starterkit/buildingblock and modules/aks/starterkit/buildingblock take platform_ref as a variable. That is the better model there: those building blocks are wired up by a meshstack_integration.tf that already holds the platform resource. This module has no integration file and is configured purely from a YAML blob that a platform operator fills in, so it resolves the ref at runtime.

The two child building blocks

Both move from meshstack_buildingblock (v1) to the ref-based meshstack_building_block, the idiom modules/ske/ske-starterkit/buildingblock already uses:

resource "meshstack_building_block" "budget_alert" {
  spec = {
    building_block_definition_version_ref = {
      uuid = local.budget_alert_definition_version_uuid
    }

    display_name = "Budget Alert"
    target_ref   = meshstack_tenant.sandbox.ref

    inputs = {
      budget_name           = { value = jsonencode("Agentic Coding Budget Alert") }
      monthly_budget_amount = { value = jsonencode(var.budget_amount) }
      contact_emails        = { value = jsonencode(var.username) }
    }
  }
}
  • The v1 resource was only there because the tenant uuid was not available, which the NOTE in the code said. The v4 tenant exposes a computed ref ({uuid, kind = "meshTenant"}) that drops straight into spec.target_ref, so the NOTE is gone and no tenant_identifier string is assembled from workspace, project and platform identifiers any more.
  • Inputs move from the typed value_string / value_int / value_single_select attributes to a single jsonencoded value.
  • v1 returned as soon as the block was created. meshstack_building_block defaults wait_for_completion to true, so the composition now waits for each child block's run to reach a terminal state (bounded by the default 30m timeout). That is the behavior worth having here: the composition reporting success should mean the sandbox is actually usable, not that two runs were queued.

The operator-facing config schema, deliberately broken once

meshstack_building_block references a building block definition version by uuid, while composition_config_yaml carried a definition_uuid plus a numeric definition_version. No shape of that config survives, so the schema breaks in this PR either way. It breaks exactly once, and it gets smaller:

landing_zone:
  landing_zone_identifier: "my-landing-zone"
  platform_identifier: "my-platform.my-location" # full identifier, <platform-name>.<location-name>
budget_alert_building_block:
  definition_version_uuid: "uuid-here" # uuid of the building block definition *version* to provision
enable_eu_south_2_region_building_block:
  definition_version_uuid: "uuid-here"
project:
  default_tags: { ... }
  owner_tag_key: "project_owner"

The rule the config now follows: name a thing by the most human-readable key the provider can actually resolve.

  • Definition versions become a uuid, one flat definition_version_uuid per block replacing two keys. The provider has no data source that resolves a building block definition, or one of its versions, by uuid or by name: meshstack_building_block_definitions filters only by workspace_identifier and issues an extra API call per definition to load its versions, and these definitions are owned by the operator's workspace, not necessarily the caller's. So the uuid has to be configured, and configuring the version uuid is the honest form of what the resource consumes.
  • landing_zone.platform_identifier stays an identifier. Rejected: taking a platform_uuid (or a full ref) as input. It would delete the meshstack_platforms lookup and its precondition, but it would make the operator dig a uuid out of the panel for a value that is visible as an identifier everywhere in meshStack and that the provider resolves for us. "The config contains one uuid already, so it may as well contain three" is not an argument for making a readable field unreadable. The lookup and the precondition therefore stay exactly as they are.
  • landing_zone.landing_zone_identifier stays an identifier. Rejected: turning it into a nested landing_zone_ref. A landing_zone_ref is {name, kind} and the name is the identifier, so a nested YAML ref would be the same string plus ceremony, and kind is the only value it could ever have. Rejected as well: resolving it through meshstack_landingzones — a lookup that converts an identifier into the same identifier buys nothing.
  • Also rejected: tracking the definitions' latest released version automatically instead of pinning. It needs the same missing by-uuid data source, and it would silently upgrade every sandbox's child blocks whenever an operator publishes a new BBD version.

variables.tf validations shrink from four building-block checks to two (each block's definition_version_uuid is required), the docstring example and the hand-written config example in README.md match the new schema, and the terraform-docs section is regenerated.

Consumers of this composition update their composition_config_yaml once when this merges. There is no compatibility shim and no deprecation window; that is deliberate, and it is why both breaking changes land together.

Provider constraint

>= 0.7.1 becomes >= 0.24.3:

  • the ref-based tenant body and its computed ref need >= 0.24.0
  • meshstack_building_block with spec.building_block_definition_version_ref, spec.target_ref and jsonencoded inputs has been available since v0.23.0, so it does not raise the floor. spec.parent_building_block_refs (renamed in v0.24.4) is not used here.
  • the plural meshstack_platforms data source shipped in v0.24.3, which is the real floor

v0.24.4 is not required: it only removes the deprecated spec.quotas (never set here) and renames a building block attribute this module does not use.

No state migration path

No moved blocks and no import shims. There is no in-place move from meshstack_buildingblock (v1) to meshstack_building_block, and the tenant is treated as a fresh resource, so an existing sandbox is destroyed and recreated. This is deliberate: the sandboxes are short-lived, per-developer environments, and carrying a migration from the v1 bodies is not worth it.

Verification

  • terraform fmt clean.
  • terraform init -backend=false + terraform validate pass, with no warnings, against the registry release v0.24.3 (the declared floor), against v0.24.4 (what the constraint resolves to today), and against a local dev build of the provider. The two meshstack_buildingblock deprecation warnings the previous revision emitted are gone with the resource.
  • ci/validate_modules.sh reports Number of errors: 0.
  • The scorecard for aws/agentic-coding-sandbox is unchanged; its two failing checks (no meshstack_integration.tf, no e2e/) are pre-existing.
  • buildingblock/README.md regenerated with the nix-pinned terraform-docs v0.20.0 via the repo's pre-commit hooks.

Where this is going

The module still has no meshstack_integration.tf, which is the one Core Structure check the hub
scorecard fails for it. Its building block definition, and those of the two children it composes
(aws/budget-alert, aws/opt-in-region), were created by hand in the panel. That is why the ICF
consumer has to paste definition uuids into its configuration.

Agreed plan for this PR:

  • Add a meshstack_integration.tf to aws/agentic-coding-sandbox, aws/budget-alert and
    aws/opt-in-region.
  • Drop composition_config_yaml in favour of typed variables, following the ske starterkit:
    platform_ref, landing_zone_ref, and building_block_definitions as a map of
    {uuid, version_ref} wired from the two child modules' outputs. This deletes
    definition_version_uuid, introduced earlier on this branch, before anyone consumes it.
  • Keep a readable platform_identifier variable on the integration file and resolve it to a ref
    there with the meshstack_platforms data source, so no consumer writes a platform uuid and the
    lookup runs once at definition apply time instead of on every building block run.
  • Move the user-facing readme from buildingblock/APP_TEAM_README.md into the definition's inline
    spec.readme, as AGENTS.md requires for modules with an integration file.

On the consumer side, ICF gets a local Terraform root next to its terragrunt.hcl that instantiates
the three hub modules, holds the three import blocks that adopt the existing production
definitions, and pins bbd_draft = false like the neighbouring AWS building blocks. Existing
building blocks keep running, because they stay on the definition version they were created with.
Validation is plan-only against production until the plan shows the three imports and nothing
unintended.

Blocked on

Sufficient permissions on meshcloud-prod: an AWS profile for account 122242464811 and meshStack API
credentials for https://federation.prod.meshcloud.io. Without them the definitions cannot be
imported, and two facts stay unknown: how many sandboxes are live on the current definition, and
which workspace owns it (meshsre owns every other as-code AWS definition in that foundation, so it
is the likely answer).

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Scorecard Check

Scorecard run on commit 65e8188636b5d82ead723267202c422e241ae70b relative to origin/main

📊 meshstack-hub Module Scorecard

Generated: 2026-08-12 | Modules scanned: 1 | Categories: 5

📋 Per-Module Category Summary

Score per category per building block. n/a = category does not apply to this module.

Module Overall Core Structure Integration Azure Backplane STACKIT Backplane Testing
aws/agentic-coding-sandbox 🟡 70% 🟢 86% n/a n/a n/a 🔴 33%

⚠️ 1 module has failing checks — failing categories are expanded below.

Core Structure — some checks failing

Basic module file structure and documentation — applies to 1 modules

Module Score 📦 🔗 📋 📝 🖼️ 📌 🔒
aws/agentic-coding-sandbox 🟢 86%

Core Structure — Summary

Emoji Criterion Coverage Status
📦 buildingblock/ directory exists 1/1 🟢 100%
🔗 meshstack_integration.tf present 0/1 🔴 0%
📋 buildingblock/APP_TEAM_README.md present (no-integration fallback) 1/1 🟢 100%
📝 buildingblock/README.md with YAML front-matter 1/1 🟢 100%
🖼️ buildingblock/logo.png included 1/1 🟢 100%
📌 buildingblock/versions.tf present 1/1 🟢 100%
🔒 Provider versions use minimum constraint (>=) 1/1 🟢 100%
Integration — not applicable

meshstack_integration.tf conventions — applies to 0 modules

No applicable modules.

Azure Backplane — not applicable

Azure UAMI-based automation principal conventions — applies to 0 modules

No applicable modules.

STACKIT Backplane — not applicable

STACKIT WIF-based automation principal conventions — applies to 0 modules

No applicable modules.

Testing — some checks failing

End-to-end test coverage — applies to 1 modules

Module Score ⚙️ 🧪
aws/agentic-coding-sandbox 🔴 33%

Testing — Summary

Emoji Criterion Coverage Status
⚙️ backplane/ directory (optional tier) 1/1 🟢 100%
🧪 e2e/ test directory exists 0/1 🔴 0%
e2e/ contains .tftest.hcl files 0/1 🔴 0%

@aws-amplify-eu-central-1

Copy link
Copy Markdown

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-252.d1o16zfeoh2slu.amplifyapp.com

@grubmeshi
grubmeshi force-pushed the fix/agentic-sandbox-tenant-v4 branch from 5a946cc to c0385aa Compare August 12, 2026 14:53
…rces

meshstack_tenant runs on the meshTenant v4 API since terraform-provider-meshstack
v0.24.0: metadata.platform_identifier became spec.platform_ref (a platform uuid ref)
and spec.landing_zone_identifier became spec.landing_zone_ref. This module still
wrote the pre-v4 body, so it did not even validate against a current provider.

The composition is configured with a platform identifier, not a uuid, so a
meshstack_platforms data source resolves the identifier to the platform's computed
ref. A precondition turns a mistyped or invisible platform identifier into a readable
error instead of a missing required attribute.

The two child building blocks move from the deprecated meshstack_buildingblock (v1)
to meshstack_building_block. They target the tenant through its computed ref instead
of a hand-built tenant identifier string and pass jsonencoded input values. v1 was
only used because the tenant uuid was unavailable, which the v4 tenant fixes.

meshstack_building_block references a building block definition VERSION by uuid, so
composition_config_yaml replaces each block's definition_uuid plus numeric
definition_version with a single definition_version_uuid. The provider has no data
source that resolves a definition or one of its versions, so that uuid has to be
configured; the landing zone and the platform stay identifiers because the provider
can resolve those. The operator-facing config schema therefore breaks exactly once.

The provider constraint moves to >= 0.24.3, the release that added the plural
platforms data source.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@grubmeshi
grubmeshi force-pushed the fix/agentic-sandbox-tenant-v4 branch from c0385aa to 8ce41c2 Compare August 12, 2026 15:36
@grubmeshi grubmeshi changed the title fix(aws): migrate agentic coding sandbox tenant to the v4 body fix(aws): migrate agentic coding sandbox to ref-based meshStack resources Aug 12, 2026
@grubmeshi
grubmeshi marked this pull request as draft August 12, 2026 18:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant