Files
acdl/terraform/platform
Jon Chery 2f8c0203be
acdl-ci / Lint (pull_request) Successful in 7s
acdl-ci / Test (pull_request) Successful in 4m5s
acdl-ci / Platform check-only (offline) (pull_request) Successful in 21s
acdl-modules-lifecycle / Platform VPC apply (pull_request) Failing after 42s
acdl-modules-lifecycle / L1 lifecycle (alb) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (cloudfront) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (ecr) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (ecs-cluster) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (ecs-service) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (iam-role) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (kms-key) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (rds) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (s3) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (uptime) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (vpc) (pull_request) Has been skipped
acdl-modules-lifecycle / L1 lifecycle (waf) (pull_request) Has been skipped
acdl-modules-lifecycle / Platform VPC destroy (pull_request) Successful in 48s
fix(terraform/platform): quote acdl: tags + fix Lambda + replace interpolation
3 fixes in terraform/platform/main.tf that prevented terraform validate
from passing in CI:

1. All 40 acdl:owner/contract/environment/cost-center tag keys were
   unquoted (acdl:owner = ...). HCL requires quoting keys with colons.
   Fixed to "acdl:owner" = ...

2. filebase64sha256("contract_ingestor.zip") failed when the zip didn't
   exist (it's a build artifact). Wrapped with fileexists() guard.

3. ${account_id} and ${region} in the replace() call were interpreted
   as Terraform interpolation, not literal strings. Escaped as
   $${account_id} and $${region}.

Platform terraform now passes terraform validate.

---ci---
project: acdl
phase: P59
milestone: v1.11
status: execute
---/ci---
2026-07-28 16:42:17 +00:00
..

ACDL Platform Infrastructure (D-051)

Terraform configuration for the platform-side infrastructure that ingests consumer deployment contracts and (Phase 25) reports errors as GitHub issues.

This stack is separate from terraform/spike/ (the consumer stack spike) and terraform/microservice/ (the demo microservice). It manages resources that live in the platform AWS account and serve all consumers — the contract ingestion pipeline and the secrets it needs.

What it deploys

Resource Name Purpose
aws_dynamodb_table acdl-contracts Stores submitted consumer contracts. PK consumerRepo, SK contractId#submittedAt. SSE via CMK, PITR enabled.
aws_kms_key + aws_kms_alias alias/acdl-platform Customer-managed key — encrypts DynamoDB SSE, Secrets Manager, and SSM. Key rotation enabled.
aws_secretsmanager_secret acdl/github-token GitHub PAT used by the Lambda to create issues on the platform repo (D-055, wired in Phase 25).
aws_iam_role + aws_iam_role_policy acdl-contract-ingestor-role Execution role for the Lambda — DynamoDB write, Secrets Manager read, KMS decrypt, CloudWatch logs.
aws_lambda_function acdl-contract-ingestor Python 3.12 Lambda. Handler contract_ingestor.lambda_handler. Source: core/lambda/contract_ingestor.py, packaged as contract_ingestor.zip.
aws_lambda_function_url Function URL with AWS_IAM authorization. Consumers invoke it via SigV4-signed requests.

State

Key Value
Backend S3
Bucket acdl-tfstate-581513795199-us-east-1
State key platform/terraform.tfstate
Region us-east-1

The state key is distinct from spike/terraform.tfstate and microservice/terraform.tfstate — the three stacks are independent.

Apply

# Package the Lambda source first (from the repo root):
cd core/lambda
zip contract_ingestor.zip contract_ingestor.py
cd ../../terraform/platform

terraform init
terraform plan
terraform apply

The Lambda's filename points at contract_ingestor.zip in the working directory (terraform/platform/); either place the zip there or adjust the path. source_code_hash = filebase64sha256("contract_ingestor.zip") forces a redeploy whenever the package changes.

Cross-account invocation model

The Lambda is invoked cross-account by consumer pipelines. The flow:

  1. Onboarding. When a consumer repo is onboarded, the platform team applies consumer_invoke_policy.json to the consumer's deploy role. The policy grants lambda:InvokeFunctionUrl on the Lambda ARN, scoped via ABAC — the condition aws:PrincipalTag/acdl:owner == ${consumerRepo} ensures a repo can only invoke when it is the owner it claims to be.
  2. Runtime. The consumer's deploy workflow (running in the consumer AWS account under the consumer's deploy role) signs the Function URL request with SigV4 using its deploy-role credentials. The IAM auth on the Function URL validates the signature and the ABAC condition.
  3. Lambda. The Lambda parses the JSON body, validates the fields, and writes the contract to acdl-contracts.

This is a one-way channel (D-051): the consumer pushes contracts to the platform; the platform never reaches back into the consumer account. Error reporting (D-055, action: "report_error") flows over the same channel and is implemented in Phase 25 (GitHub issue creation on the platform repo).