From 3810245211df2bd814b63ef2e9164f99e9ce5eb6 Mon Sep 17 00:00:00 2001 From: Florian Nowarre Date: Mon, 22 Jun 2026 10:54:44 +0200 Subject: [PATCH] feat: adding dynamodb project metadata --- .../backplane/README.md | 42 ++++ .../backplane/main.tf | 80 +++++++ .../backplane/outputs.tf | 25 ++ .../backplane/variables.tf | 17 ++ .../backplane/versions.tf | 14 ++ .../buildingblock/README.md | 70 ++++++ .../buildingblock/logo.png | Bin 0 -> 1992 bytes .../buildingblock/main.tf | 25 ++ .../buildingblock/outputs.tf | 14 ++ .../buildingblock/provider.tf | 7 + .../buildingblock/variables.tf | 38 +++ .../buildingblock/versions.tf | 14 ++ .../meshstack_integration.tf | 220 ++++++++++++++++++ 13 files changed, 566 insertions(+) create mode 100644 modules/aws/dynamodb-project-metadata/backplane/README.md create mode 100644 modules/aws/dynamodb-project-metadata/backplane/main.tf create mode 100644 modules/aws/dynamodb-project-metadata/backplane/outputs.tf create mode 100644 modules/aws/dynamodb-project-metadata/backplane/variables.tf create mode 100644 modules/aws/dynamodb-project-metadata/backplane/versions.tf create mode 100644 modules/aws/dynamodb-project-metadata/buildingblock/README.md create mode 100644 modules/aws/dynamodb-project-metadata/buildingblock/logo.png create mode 100644 modules/aws/dynamodb-project-metadata/buildingblock/main.tf create mode 100644 modules/aws/dynamodb-project-metadata/buildingblock/outputs.tf create mode 100644 modules/aws/dynamodb-project-metadata/buildingblock/provider.tf create mode 100644 modules/aws/dynamodb-project-metadata/buildingblock/variables.tf create mode 100644 modules/aws/dynamodb-project-metadata/buildingblock/versions.tf create mode 100644 modules/aws/dynamodb-project-metadata/meshstack_integration.tf diff --git a/modules/aws/dynamodb-project-metadata/backplane/README.md b/modules/aws/dynamodb-project-metadata/backplane/README.md new file mode 100644 index 00000000..866de15e --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/backplane/README.md @@ -0,0 +1,42 @@ +## Overview + +The backplane provisions a central DynamoDB table in your AWS management/tooling account and an +IAM role that the meshStack building block runtime can assume via workload identity federation (OIDC). +No long-lived AWS credentials are required. + +## What is provisioned + +| Resource | Purpose | +|---|---| +| `aws_dynamodb_table` | Stores one item per meshStack project tenant (PK: workspace, SK: project) | +| `aws_iam_openid_connect_provider` | Trusts the meshStack OIDC issuer | +| `aws_iam_role` | Grants DynamoDB write access to the building block runtime | +| `aws_iam_role_policy` | Scoped to `PutItem`, `UpdateItem`, `DeleteItem` on the table only | + +## Required permissions + +The AWS principal running this backplane must be able to create IAM resources and DynamoDB tables. +Typically this is run with `AdministratorAccess` in a management/tooling account. + +## Usage + +```hcl +module "backplane" { + source = "github.com/meshcloud/meshstack-hub//modules/aws/dynamodb-project-metadata/backplane?ref=main" + + workload_identity_federation = { + issuer = "" + audience = "" + subjects = ["system:serviceaccount::workspace..buildingblockdefinition."] + } + + table_name = "meshstack-project-metadata" # optional, defaults to this value +} +``` + +## Outputs + +| Output | Description | +|---|---| +| `table_name` | Name of the created DynamoDB table (pass to `aws_dynamodb_table_name` BBD input) | +| `workload_identity_federation_role_arn` | IAM role ARN (pass to `AWS_ROLE_ARN` BBD input) | diff --git a/modules/aws/dynamodb-project-metadata/backplane/main.tf b/modules/aws/dynamodb-project-metadata/backplane/main.tf new file mode 100644 index 00000000..acc7f68e --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/backplane/main.tf @@ -0,0 +1,80 @@ +data "aws_caller_identity" "current" {} + +resource "random_string" "suffix" { + length = 4 + special = false + upper = false +} + +resource "aws_dynamodb_table" "this" { + name = var.table_name + billing_mode = "PAY_PER_REQUEST" + hash_key = "workspace_identifier" + range_key = "project_identifier" + + attribute { + name = "workspace_identifier" + type = "S" + } + + attribute { + name = "project_identifier" + type = "S" + } +} + +resource "aws_iam_openid_connect_provider" "this" { + url = var.workload_identity_federation.issuer + client_id_list = [var.workload_identity_federation.audience] +} + +data "aws_iam_policy_document" "assume_role" { + statement { + effect = "Allow" + principals { + type = "Federated" + identifiers = [aws_iam_openid_connect_provider.this.arn] + } + actions = ["sts:AssumeRoleWithWebIdentity"] + + condition { + test = "StringEquals" + variable = "${trimprefix(var.workload_identity_federation.issuer, "https://")}:aud" + values = [var.workload_identity_federation.audience] + } + + condition { + test = "StringLike" + variable = "${trimprefix(var.workload_identity_federation.issuer, "https://")}:sub" + values = var.workload_identity_federation.subjects + } + } +} + +locals { + role_name = "MeshStackDynamoDBWriter-${random_string.suffix.result}" +} + +resource "aws_iam_role" "this" { + name = local.role_name + assume_role_policy = data.aws_iam_policy_document.assume_role.json +} + +data "aws_iam_policy_document" "dynamodb_write" { + statement { + effect = "Allow" + actions = [ + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + ] + resources = [aws_dynamodb_table.this.arn] + } +} + +resource "aws_iam_role_policy" "dynamodb_write" { + name = "DynamoDBWritePolicy" + role = aws_iam_role.this.id + policy = data.aws_iam_policy_document.dynamodb_write.json +} diff --git a/modules/aws/dynamodb-project-metadata/backplane/outputs.tf b/modules/aws/dynamodb-project-metadata/backplane/outputs.tf new file mode 100644 index 00000000..b9fb0694 --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/backplane/outputs.tf @@ -0,0 +1,25 @@ +output "table_name" { + description = "Name of the DynamoDB table." + value = aws_dynamodb_table.this.name +} + +output "table_arn" { + description = "ARN of the DynamoDB table." + value = aws_dynamodb_table.this.arn +} + +output "table_region" { + description = "AWS region where the DynamoDB table was created." + value = aws_dynamodb_table.this.region +} + +output "table_console_url" { + description = "AWS Console URL for the DynamoDB table." + value = "https://${aws_dynamodb_table.this.region}.console.aws.amazon.com/dynamodbv2/home?region=${aws_dynamodb_table.this.region}#table?name=${aws_dynamodb_table.this.name}" +} + +output "workload_identity_federation_role_arn" { + description = "ARN of the IAM role assumed by the building block runtime via workload identity federation." + # Manually construct ARN to avoid dependency cycle on input workload_identity_federation (which contains the BBD UUID as subject) + value = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${local.role_name}" +} diff --git a/modules/aws/dynamodb-project-metadata/backplane/variables.tf b/modules/aws/dynamodb-project-metadata/backplane/variables.tf new file mode 100644 index 00000000..e7636983 --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/backplane/variables.tf @@ -0,0 +1,17 @@ +variable "workload_identity_federation" { + type = object({ + issuer = string + audience = string + subjects = list(string) + }) + description = <<-EOT + Workload identity federation configuration. Allows the meshStack building block runtime to assume + an IAM role via OIDC without long-lived credentials. + EOT +} + +variable "table_name" { + type = string + default = "meshstack-project-metadata" + description = "Name of the DynamoDB table to create for storing meshStack project metadata." +} diff --git a/modules/aws/dynamodb-project-metadata/backplane/versions.tf b/modules/aws/dynamodb-project-metadata/backplane/versions.tf new file mode 100644 index 00000000..9c03e190 --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/backplane/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.0.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.0.0" + } + } +} diff --git a/modules/aws/dynamodb-project-metadata/buildingblock/README.md b/modules/aws/dynamodb-project-metadata/buildingblock/README.md new file mode 100644 index 00000000..d5354db6 --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/buildingblock/README.md @@ -0,0 +1,70 @@ +--- +name: AWS DynamoDB Project Metadata +supportedPlatforms: + - aws +description: Pushes meshStack project metadata (workspace, project, tags, team members) into a central AWS DynamoDB table per AWS organization. +--- + +Automatically syncs meshStack project metadata into a central AWS DynamoDB table whenever a building +block is assigned to a tenant. This eliminates the need for long-lived meshStack API credentials in +your AWS organization — metadata is pushed from meshStack using workload identity federation. + +## When to use it + +Use this building block when you need meshStack project data (tags, team members, workspace/project +identifiers) available inside your AWS organization without polling the meshStack API with static +credentials. Typical use cases: + +- Feeding project ownership and cost-centre tags into AWS Cost Explorer via DynamoDB. +- Driving IAM permission boundaries or SCPs with project-level metadata. +- Providing a CMDB-like inventory of all meshStack tenants in your AWS org. + +## Shared Responsibility + +| Responsibility | Platform Team | Application Team | +|---|:---:|:---:| +| Deploy backplane (DynamoDB table + IAM role) | ✅ | ❌ | +| Assign building block to tenants | ✅ | ❌ | +| Consume DynamoDB data in tooling/pipelines | ✅ | ❌ | +| Keep meshStack project tags up to date | ❌ | ✅ | +| Manage team membership in meshStack | ❌ | ✅ | + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.12.0 | +| [aws](#requirement\_aws) | >= 6.0.0 | +| [meshstack](#requirement\_meshstack) | >= 0.22.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_dynamodb_table_item.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table_item) | resource | +| [meshstack_project.this](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/data-sources/project) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [aws\_dynamodb\_table\_name](#input\_aws\_dynamodb\_table\_name) | Name of the DynamoDB table to write project metadata to. | `string` | n/a | yes | +| [aws\_region](#input\_aws\_region) | AWS region where the DynamoDB table is located. | `string` | n/a | yes | +| [platform\_identifier](#input\_platform\_identifier) | meshStack platform identifier (typically the AWS account ID for AWS tenants). | `string` | n/a | yes | +| [project\_identifier](#input\_project\_identifier) | meshStack project identifier. | `string` | n/a | yes | +| [users](#input\_users) | Project team members with their roles, injected by meshStack. |
list(object({
meshIdentifier = string
username = string
firstName = string
lastName = string
email = string
euid = string
roles = list(string)
}))
| `[]` | no | +| [workspace\_identifier](#input\_workspace\_identifier) | meshStack workspace identifier. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [dynamodb\_item\_key](#output\_dynamodb\_item\_key) | Composite key of the DynamoDB item written for this project (workspace/project). | +| [dynamodb\_item\_url](#output\_dynamodb\_item\_url) | AWS Console URL to view the specific item written for this project. | +| [dynamodb\_table\_name](#output\_dynamodb\_table\_name) | Name of the DynamoDB table the metadata was written to. | + diff --git a/modules/aws/dynamodb-project-metadata/buildingblock/logo.png b/modules/aws/dynamodb-project-metadata/buildingblock/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e31ad968cd3de5087b9f4553604e3fa93a6ecf5b GIT binary patch literal 1992 zcmV;(2RHbMP)e9ae000DZQchC`QovD|+KhZ)X~DhL?B2Gm~;LfjGv$8kifX6hI&g%ESrFlYUzAzm<<*4))xGIC%* zGnQ~YUmoi?pIa~{dR=VBk`5Y7pb*J4C1xbzQ}|I%a+!3^zvSoZHF<$@jG%h>=9u5p zG4cZ6@p4ba=yP+6wKR{(a)_7H6anSFdFpyPN<~;CvkjwsHQJ0p?y*fsuaB%hD(Wy! z4f6!bI#z}_;t4#+3tH0a0csn&Si zu7jq;)GG)X6knpQA@7HtAdsda$cTZ|EYLaN{^}rK00;x4;h?*MK&qIE+xH_z4H0pypbmjStXh_n(YFKpG%KHj|b<{=E01OCOI23f-F9PnbC0Day*^ z2&eCefOJ6G=II`d#b4hrt#w#hWlJj(CUhUWRi#$=e(`A7^&%TG5WF5x!4(5IRaAMf zL1Q)K`U%XnD3F1(73s8itXcuHV!3}4t5!6vcn!fmBgJV9sd`z>1_iqrt66emAXV#n z9s|)8K_)EfYE|4=K43{&+?_lzknaBkf-pVG&cz^I7J6ZS(q;M4jr269#DeIH1R_b@ z#T)}kHjQ^u1B)j%CxE;gPTJ3O4&Okvzb<2XlsUyCzq7=b4Te?w7r%8*hC1#Pfhl^% z(=F1If=@39;{H((K_Y<)1*fYX3F2-~1O!Uc(S2^>o>GK7Ai_~@#41{cJmRqdP$%;u zR?+%!83*|Wb&6-GTST`&BC7^qr1Y9)=9G|AHq%4XB?6Vi?{YJl9^(3~<9<4(7?s$B zo#P-AEGq3uzciGC-dki)xQLZ-0U5l0Y$R}r)rfcbASMXlCx>n&WF(YXB*FuNeg$kJ zz(BIJlpvH@gcAa;S8uZwY<-Ic*(Y8u6dGn96Gp`KEwRWO3T${uToYarZ`-0#}tp&TCFeQyxh(ZQ^sI@YWx^T zs@nY)KzG;_hwGg@SMl1^*7}y@_&lBj1I*@n<~;nEs7;+$QTkOg1-FB2u6J+C=0x)n zyyK)-H<}37djX}Adc_@((4ng=uJ@;UO!q!F6_he}U@9%H_miB@>1NM!S%1+tXi?q& zCP9@h)$HA%t56UDYk^692hyz@jO$rMAwT8JKM*b(*K-&otZ#qbzl#U+gmOI*p_2A;cDE6e>+{=ezaX(s^LIh zndlNANnY_Q{$N@X48$`)B*q3QS(ejPZiXgyN?^_a8MpT2!@f_dRGy*Nt33(47p+n^ zq3iX`c9HXsq&w(RIuASVZ))_AL1Z}B6EzRWdcF5%m9$)x%xJ1!@4pVlEfjFtuEe^oHk8cJ_wG!qj@Yp#c)3w$wbd`^Q2oi`Pf*^B}-b!RdkYM8XkYXf|nFwNV5R;}00toXQZ@m!8X&~AL zF?*6&Sth*N!Qw7Loe+RLVa`$Re~r5bzGxr~5oE7Gh4msZp>|V55Vvo&*GVEshtzsD zi1Rk~n|?Yn$4VarvE*bI$Ictugw?`%ju?pCrL@+mh6(GQVp#jZlj?_l;TN?s8baBUDwK4C0!y86t=vh6Lg^(^qOuA8=@FPTUS<$||Rw-^4Ak z9A>q+l*T}0fNbicy(7S>Aestty0ydLo_r&9gm#!G=A^v0^+BGQAfzr`{VkC000^m? zmLAAd0Fw12C6=gMpW@ZZ!CuV=$rT7mUFVnA$CiL3`)7U(M36yDIS5VDV8=lW31m)o zS_gF|+o^cZSP=y8evfK@$6kjV=^$M(h@$DS>c|Nc$~MbZgOnaFRzyFH!wiEzCEawBX3jx(h_o;qO^rAJ{Kb)IBNNl&nM9`wRHQ4F#5c*#FVypTClMpDl2X z5nM9EWIM2$!@84A)v?EKiY9ENGtcyLGOHaV;EBVQR(p)zEX1XH>K&P zfijt>{ljMD!W4Wq#)3IhoHiW&p3l;;se}<#K25ab19G~o*2j-F$K9MGJJmnq^gDh> a@%RrtY%r<1Ov;V`0000ya literal 0 HcmV?d00001 diff --git a/modules/aws/dynamodb-project-metadata/buildingblock/main.tf b/modules/aws/dynamodb-project-metadata/buildingblock/main.tf new file mode 100644 index 00000000..1bbaf6a7 --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/buildingblock/main.tf @@ -0,0 +1,25 @@ +data "meshstack_project" "this" { + metadata = { + name = var.project_identifier + owned_by_workspace = var.workspace_identifier + } +} + +locals { + tags_json = jsonencode(data.meshstack_project.this.spec.tags) + users_json = jsonencode([for u in var.users : { email = u.email, roles = u.roles }]) +} + +resource "aws_dynamodb_table_item" "this" { + table_name = var.aws_dynamodb_table_name + hash_key = "workspace_identifier" + range_key = "project_identifier" + + item = jsonencode({ + workspace_identifier = { S = var.workspace_identifier } + project_identifier = { S = var.project_identifier } + platform_identifier = { S = var.platform_identifier } + tags = { S = local.tags_json } + users = { S = local.users_json } + }) +} diff --git a/modules/aws/dynamodb-project-metadata/buildingblock/outputs.tf b/modules/aws/dynamodb-project-metadata/buildingblock/outputs.tf new file mode 100644 index 00000000..531579b0 --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/buildingblock/outputs.tf @@ -0,0 +1,14 @@ +output "dynamodb_item_key" { + description = "Composite key of the DynamoDB item written for this project (workspace/project)." + value = "${var.workspace_identifier}/${var.project_identifier}" +} + +output "dynamodb_table_name" { + description = "Name of the DynamoDB table the metadata was written to." + value = var.aws_dynamodb_table_name +} + +output "dynamodb_item_url" { + description = "AWS Console URL to view the specific item written for this project." + value = "https://${var.aws_region}.console.aws.amazon.com/dynamodbv2/home?region=${var.aws_region}#item?table=${var.aws_dynamodb_table_name}&itemMode=2&pk=${var.workspace_identifier}&sk=${var.project_identifier}&route=ROUTE_ITEM_EXPLORER" +} diff --git a/modules/aws/dynamodb-project-metadata/buildingblock/provider.tf b/modules/aws/dynamodb-project-metadata/buildingblock/provider.tf new file mode 100644 index 00000000..1365e6c0 --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/buildingblock/provider.tf @@ -0,0 +1,7 @@ +provider "aws" { + region = var.aws_region +} + +provider "meshstack" { + # configured via environment variables (MESHSTACK_ENDPOINT, MESHSTACK_API_KEY) +} diff --git a/modules/aws/dynamodb-project-metadata/buildingblock/variables.tf b/modules/aws/dynamodb-project-metadata/buildingblock/variables.tf new file mode 100644 index 00000000..d6be5670 --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/buildingblock/variables.tf @@ -0,0 +1,38 @@ +variable "workspace_identifier" { + type = string + description = "meshStack workspace identifier." +} + +variable "project_identifier" { + type = string + description = "meshStack project identifier." +} + +variable "platform_identifier" { + type = string + description = "meshStack platform identifier (typically the AWS account ID for AWS tenants)." +} + +variable "users" { + type = list(object({ + meshIdentifier = string + username = string + firstName = string + lastName = string + email = string + euid = string + roles = list(string) + })) + default = [] + description = "Project team members with their roles, injected by meshStack." +} + +variable "aws_region" { + type = string + description = "AWS region where the DynamoDB table is located." +} + +variable "aws_dynamodb_table_name" { + type = string + description = "Name of the DynamoDB table to write project metadata to." +} diff --git a/modules/aws/dynamodb-project-metadata/buildingblock/versions.tf b/modules/aws/dynamodb-project-metadata/buildingblock/versions.tf new file mode 100644 index 00000000..f1524b6d --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/buildingblock/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.12.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.0.0" + } + meshstack = { + source = "meshcloud/meshstack" + version = ">= 0.22.0" + } + } +} diff --git a/modules/aws/dynamodb-project-metadata/meshstack_integration.tf b/modules/aws/dynamodb-project-metadata/meshstack_integration.tf new file mode 100644 index 00000000..3e65ad56 --- /dev/null +++ b/modules/aws/dynamodb-project-metadata/meshstack_integration.tf @@ -0,0 +1,220 @@ +variable "workspace_identifier" { + type = string + description = "meshStack workspace identifier." +} + +variable "aws_region" { + type = string + description = "AWS region where the DynamoDB table will be created (e.g. 'eu-central-1')." +} + +variable "notification_subscribers" { + type = list(string) + default = [] + description = "List of email addresses to notify on building block lifecycle events." +} + +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." + default = { + owning_workspace_identifier = var.workspace_identifier + } +} + +variable "hub" { + type = object({ + git_ref = optional(string, "main") + bbd_draft = optional(bool, true) + }) + const = true + default = {} + 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 + } +} + +data "meshstack_integrations" "integrations" {} + +module "backplane" { + source = "github.com/meshcloud/meshstack-hub//modules/aws/dynamodb-project-metadata/backplane?ref=${var.hub.git_ref}" + + workload_identity_federation = { + issuer = data.meshstack_integrations.integrations.workload_identity_federation.replicator.issuer + audience = data.meshstack_integrations.integrations.workload_identity_federation.replicator.aws.audience + subjects = [ + "${trimsuffix(data.meshstack_integrations.integrations.workload_identity_federation.replicator.subject, ":replicator")}:workspace.${var.meshstack.owning_workspace_identifier}.buildingblockdefinition.${meshstack_building_block_definition.this.metadata.uuid}" + ] + } +} + +resource "meshstack_building_block_definition" "this" { + metadata = { + owned_by_workspace = var.meshstack.owning_workspace_identifier + tags = var.meshstack.tags + } + + spec = { + display_name = "AWS DynamoDB Project Metadata" + description = "Pushes meshStack project metadata (tags, team members) into a central DynamoDB table." + support_url = "mailto:support@example.com" + notification_subscribers = var.notification_subscribers + target_type = "TENANT_LEVEL" + supported_platforms = [{ name = "AWS" }] + + readme = chomp(<<-EOT + Automatically syncs meshStack project metadata into a central AWS DynamoDB table whenever this + building block is assigned to a tenant. This eliminates the need for long-lived meshStack API + credentials in your AWS organization — metadata is pushed from meshStack using workload identity + federation (no static API keys). + + Each tenant gets one DynamoDB item keyed by `workspace_identifier` + `project_identifier`. The + item contains the workspace, project, platform identifier, all project tags, and current team + members with their roles. + + ## When to use it + + Use this building block when you need meshStack project data available inside your AWS + organization without polling the meshStack API with static credentials: + + - Feed project ownership and cost-centre tags into AWS Cost Explorer via DynamoDB. + - Drive IAM permission boundaries or SCPs with project-level metadata. + - Provide a CMDB-like inventory of all meshStack tenants in your AWS org. + + ## Shared Responsibility + + | Responsibility | Platform Team | Application Team | + |---|:---:|:---:| + | Deploy backplane (DynamoDB table + IAM role) | ✅ | ❌ | + | Assign building block to tenants | ✅ | ❌ | + | Consume DynamoDB data in tooling/pipelines | ✅ | ❌ | + | Keep meshStack project tags up to date | ❌ | ✅ | + | Manage team membership in meshStack | ❌ | ✅ | + EOT + ) + } + + version_spec = { + draft = var.hub.bbd_draft + deletion_mode = "DELETE" + + implementation = { + terraform = { + terraform_version = "1.12.0" + repository_url = "https://github.com/meshcloud/meshstack-hub.git" + repository_path = "modules/aws/dynamodb-project-metadata/buildingblock" + ref_name = var.hub.git_ref + use_mesh_http_backend_fallback = true + } + } + + inputs = { + AWS_ROLE_ARN = { + type = "STRING" + display_name = "AWS Role ARN" + description = "ARN of the IAM role assumed by the building block runtime via workload identity federation." + assignment_type = "STATIC" + is_environment = true + argument = jsonencode(module.backplane.workload_identity_federation_role_arn) + } + AWS_WEB_IDENTITY_TOKEN_FILE = { + type = "STRING" + display_name = "AWS Web Identity Token File" + description = "Path to the OIDC token file used for workload identity federation." + assignment_type = "STATIC" + is_environment = true + argument = jsonencode("/var/run/secrets/workload-identity/aws/token") + } + aws_region = { + type = "STRING" + display_name = "AWS Region" + description = "AWS region where the DynamoDB table is located." + assignment_type = "STATIC" + argument = jsonencode(var.aws_region) + } + aws_dynamodb_table_name = { + type = "STRING" + display_name = "DynamoDB Table Name" + description = "Name of the DynamoDB table to write project metadata to." + assignment_type = "STATIC" + argument = jsonencode(module.backplane.table_name) + } + workspace_identifier = { + type = "STRING" + display_name = "Workspace Identifier" + description = "meshStack workspace identifier. Used as the DynamoDB partition key." + assignment_type = "WORKSPACE_IDENTIFIER" + } + project_identifier = { + type = "STRING" + display_name = "Project Identifier" + description = "meshStack project identifier. Used as the DynamoDB sort key." + assignment_type = "PROJECT_IDENTIFIER" + } + platform_identifier = { + type = "STRING" + display_name = "Platform Identifier" + description = "meshStack platform identifier (AWS account ID for AWS tenants)." + assignment_type = "PLATFORM_TENANT_ID" + } + users = { + type = "CODE" + display_name = "Team Members" + description = "Project team members with their roles, injected by meshStack." + assignment_type = "USER_PERMISSIONS" + } + } + + outputs = { + dynamodb_item_key = { + type = "STRING" + display_name = "DynamoDB Item Key" + description = "Composite key of the written DynamoDB item (workspace/project)." + assignment_type = "SUMMARY" + } + dynamodb_table_name = { + type = "STRING" + display_name = "DynamoDB Table Name" + description = "Name of the DynamoDB table the metadata was written to." + assignment_type = "NONE" + } + dynamodb_item_url = { + type = "STRING" + display_name = "Open in AWS Console" + description = "Direct link to the DynamoDB item in the AWS Console." + assignment_type = "RESOURCE_URL" + } + } + + permissions = [ + "PROJECT_LIST", + ] + } +} + +terraform { + required_version = ">= 1.12.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.0.0" + } + meshstack = { + source = "meshcloud/meshstack" + version = "~> 0.22.0" + } + } +}