Vertical slice 1 of Phase 56 (REQ-116). Offline-testable deliverables
landed; the live IAM apply step is escalated (D-095) below.
terraform/bootstrap/spike_runner_policy.json — extended with the minimum
permissions to terraform apply + probe CAP-017..022:
- cloudfront:* (CAP-020 static-assets stack)
- wafv2:* (CAP-020 WAF ACL)
- lambda:* on function:acdl-* (CAP-018 contract-ingestor)
- dynamodb:* on acdl-contracts + acdl-change-requests (CAP-017)
- secretsmanager:GetSecretValue on secret:acdl/* (CAP-018 github-token)
- sns:* on acdl-* (CAP-017 acdl-sod-halt)
- ce:Get* (REQ-119 Cost Explorer read-only)
- kms:* (CAP-017 platform + per-stack CMKs)
- iam:CreateOpenIDConnectProvider + iam:CreateRole (CAP-022 OIDC re-create)
.ciagent/IAM_POLICY.md — new baseline document. Original grants
(v1.1–v1.10) + v1.11 grants table + least-privilege scoping notes +
OIDC act_runner role plan + D-095 escalation note.
tests/test_iam_policy_baseline.py — 15 tests. Asserts the required
actions are present per service group, Lambda scoped to acdl-*, CE
read-only, no iam:PassRole to Resource:*, DynamoDB acdl-contracts in
resource. Regression-testable: any future permission drift surfaces as
a test failure at milestone COMPLETE (D-091 gate).
Test results: 15/15 pass. Full offline suite 509/509 pass (pre-existing
test_seeded_registry_runs_and_reports_honest_status in
test_verify_regression_mode.py hangs without AWS creds — environmental,
not introduced here).
---ci---
project: acdl
phase: 56
milestone: v1.11
status: execute
escalation:
type: deploy
id: D-095
reason: ACDL_BOOTSTRAP_AWS_* not set in the execution environment
blocking: live IAM policy apply (aws iam put-user-policy) + OIDC role
re-creation (CAP-022) — requires an admin AWS principal
action_required: provide fresh ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID +
ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY to the run environment, then
re-invoke ciagent-run to resume Phase 56 live step
fallback: none (D-095 confirmed: escalate to human, no silent fallback
to the deck-marking path)
---/ci---
---ci---
project: acdl
phase: 0
milestone: v1.2
status: fix
---/ci---
The expanded policy (4727 chars pretty / 3464 compact) exceeded the AWS
2048-char inline policy limit (total across all inline policies on a user).
Compressed to 1667 chars by: (1) removing DenyEverythingElse (redundant —
IAM is default-deny; the user has no other inline policies), (2) using
action-prefix wildcards (ecs:Create*, ecr:Get*, etc.) instead of listing
every action, (3) removing SIDs.
The compressed policy grants the same effective permissions. The repo
file now matches what should be applied in the AWS Console.
---ci---
project: acdl
phase: 8
milestone: v1.1
status: plan-as-execute
persona: security-engineer+platform-engineer
task: [T-8.1, T-8.2, T-8.3, T-8.4]
requirements.covered: [REQ-23]
---/ci---
Waves 1+2: IAM policy + state backend + IAM user creation scripts.
- T-8.1 (security): terraform/bootstrap/spike_runner_policy.json —
least-privilege IAM policy: Allow S3 r/w on the state bucket, DynamoDB
r/w on the outbox table, sts:GetCallerIdentity; final Deny statement
(Action *, NotResource = the above ARNs) enforcing least privilege. No
terraform apply permission (plan-only spike).
- T-8.2/T-8.3 (platform): terraform/bootstrap/create_state_backend.py —
boto3, idempotent: creates S3 bucket acdl-tfstate-581513795199-us-east-1
(versioning enabled) + DynamoDB table acdl-outbox (PAY_PER_REQUEST, PK
contractId, SK eventType#eventTs per D-P08-1 one table for both lock
+ outbox). Writes .bootstrap_state.json marker.
- T-8.4 (platform + security review): terraform/bootstrap/create_iam_user.py
— boto3, idempotent: creates IAM user acdl-spike-runner, attaches the
inline policy from spike_runner_policy.json, creates an initial access
key if none active exists (prints to stdout for the orchestrator to
capture; NEVER committed).
py_compile + policy JSON valid.