MediaPro Lab 2 — Multi-Account Cross-System Demo Plan
TL;DR
MediaPro Lab 2 is the killer demo for SecurityV0: a single stitched authority graph spanning three AWS accounts (mp-security, mp-workloads, mp-data), an Entra tenant, a ServiceNow PDI, and Azure Foundry. It replaces the abstract "Nimbus Enterprise" brand from the 2026-04-08 plan with the real prospect MediaPro is preparing to pilot in early May 2026. Every resource is Terraform, the lab spins up before a call and tears down after for under $5/cycle, and the platform's rendered output is the explicit acceptance gate for Streams 1 (connector control), 2 (multi-account AWS), and 3 (cross-connector graph stitching). The legacy Jira-focused jira-mediapro lab is retired — Jira is out of scope per the user; the MediaPro brand graduates to the multi-AWS arc.
Why MediaPro replaces Nimbus Enterprise as the Lab 2 brand
The 2026-04-08 demo lab plan introduced "Nimbus Enterprise" as a generic post-Series-C continuation of the Nimbus Cloud Lab 1 narrative. It was a placeholder until a real prospect crystallized. MediaPro now is that prospect:
- MediaPro is a real prospect, with a real pilot target (early May 2026, per
sv0-documentation#195). The umbrella tracker already names them. Building Lab 2 around their plausible estate makes every demo, screenshot, and finding directly applicable to the pilot conversation. - MediaPro fits the multi-account narrative natively. A post-Series-C streaming-media SaaS with AWS, Entra, ServiceNow, and Azure Foundry is the exact shape of the unique-value demo the platform was built for. Nimbus Enterprise was a fictional version of the same company; promoting MediaPro just removes the indirection.
- Continuity with Lab 1. The Nimbus Cloud → MediaPro arc still works: MediaPro is what Lab 1's Nimbus Cloud could look like after a Series C, an acquisition, and the introduction of Entra/ServiceNow/Foundry. The sales team already knows that arc.
- No real customer data. The lab uses the MediaPro brand and plausible shape only. All resources live in SecurityV0-owned AWS, Entra, ServiceNow, and Azure tenants. All employee, customer, and ticket data is synthetic.
Decision on jira-mediapro legacy lab: Retire. Per the user's de-prioritization of Jira ("we don't have strong Jira capabilities to support our use case"), the existing sv0-demo-labs/labs/jira-mediapro/ (which the user's task notes reference but which is not currently present in the repo at the time of writing — the only existing labs are sv0-demo-lab-1 and x1-foundry-mcp-servicenow) is treated as legacy. If a jira-mediapro directory exists at execution time, archive it under labs/_archived/jira-mediapro/ with a RETIRED.md pointing at this plan. No Jira resources or connector wiring appear in this lab. When the Jira connector matures, it can extend the MediaPro lab in place.
Customer persona — "MediaPro" (fictional but defensible)
Industry: Streaming media / video-on-demand SaaS. Stage: Series C, ~600 employees, ~$80M ARR, 18-month-old security org headed by a recently-hired CISO (Priya Reyes). Estate moment captured: They just consolidated three regional AWS estates (post-acquisition of "Stillwater Studios" 11 months ago) into a single AWS Organization with three accounts, adopted Entra as their corporate IdP, and standardized on ServiceNow ITSM. The ML team has begun deploying Azure Foundry agents for content metadata enrichment and ticket auto-routing in parallel with the AWS workload modernization. Nobody — including the CISO — has a single graph of what the AI agents and automations can actually reach.
Why MediaPro looks like Nimbus Cloud post-Series-C:
| Property | Nimbus Cloud (Lab 1) | MediaPro (Lab 2) |
|---|---|---|
| AWS accounts | 1 (single workload account) | 3 (security, workloads, data) under an OU |
| Identity provider | None — IAM users only | Entra ID corporate tenant federated to AWS via OIDC |
| ITSM | None | ServiceNow PDI with HR + CMDB + change-management workflows |
| AI surface | Bedrock agents, action-group Lambdas | Bedrock agents AND Azure Foundry agents (cross-cloud) |
| Cross-system surface | None | ServiceNow → Entra SP → Foundry path; Bedrock action-group Lambda → MCP → ServiceNow path |
| What CISO needs to know | "Which Bedrock agent has which blast radius in this account?" | "Which AI agent's authority spans which accounts and which external systems? What did each one actually do?" |
The Lab 1 → Lab 2 narrative arc lets an SE open with "this is the company at $20M ARR" (Lab 1 demo) and continue with "this is them 18 months later" (Lab 2 demo) without rebranding.
Architecture overview
AWS organization layout
SV0 Demo Labs (OU)
└── MediaPro (OU)
├── mp-security (~111111111111) — single ingest point for SV0 AWS connector
│ • Organization CloudTrail (multi-account, multi-region)
│ • AWS Config Aggregator
│ • IAM Access Analyzer (org-level)
│ • SecurityV0ReadOnly bootstrap role (audit hub)
│ • Entra OIDC identity provider (federation root)
├── mp-workloads (~222222222222) — AI workloads + cross-account egress
│ • Bedrock agent: mp-content-tagger
│ • Bedrock agent: mp-support-router (action-group Lambda → MCP → ServiceNow)
│ • Lambda: mp-mcp-bridge (action group, calls in-account MCP server)
│ • Step Functions: mp-content-pipeline
│ • IAM role: mp-cross-account-data-reader (assumes into mp-data)
│ • IAM role: mp-foundry-sn-router (Entra-federated, called from Foundry path)
│ • SecurityV0ReadOnly per-account role
└── mp-data (~333333333333) — sensitive data domain
• S3: mp-pii-customers (synthetic PII: name, email, phone, viewing history)
• S3: mp-content-metadata (KB sources for the content tagger)
• DynamoDB: mp-ticket-log (cross-system writes from action-group Lambda)
• Secrets Manager: mp-servicenow-creds, mp-foundry-api-key
• IAM role: mp-data-cross-account-trust (trusts mp-workloads only)
• SecurityV0ReadOnly per-account role
The bootstrap account securityv0-mgmt (already exists, account 365066817305) hosts the MediaPro OU and the StackSet that deploys SecurityV0ReadOnly into all three accounts. Stream 2 owns the role-deployment template; this plan consumes it.
Cross-system topology
A single graph the platform must render. Two flagship paths are folded into one diagram so the unified blast radius is visible.
┌──────────────────────────────────────────┐
│ ServiceNow PDI │
│ ┌────────────────────────────────────┐ │
│ │ Workflow: Auto-route identity │ │
│ │ tickets (incident trigger) │ │
│ └────────┬───────────────────────────┘ │
│ │ INVOKES │
│ ┌────────▼───────────────────────────┐ │
│ │ Connection: Graph - sn-ticket- │ │
│ │ router (uses Azure Graph OAuth) │ │
│ └────────┬───────────────────────────┘ │
│ │ RUNS_AS │
└───────────┼──────────────────────────────┘
│
┌───────────▼──────────────────────────────┐
│ Entra SP: sn-ticket-router │
│ (Graph: User.Read.All + Group.Read.All │
│ + Directory.Read.All — drift) │
└───────────┬──────────────────────────────┘
│ SENDS_TO
┌───────────▼──────────────────────────────┐
│ Azure Foundry: gpt-nano-for-summary │
│ (model classification step; │
│ added post-baseline) │
└──────────────────────────────────────────┘
┌──────────────────────────────────────────┐
│ Bedrock agent: mp-support-router │
│ (in mp-workloads) │
└───────────┬──────────────────────────────┘
│ ACTION_GROUP
┌───────────▼──────────────────────────────┐
│ Lambda: mp-mcp-bridge (mp-workloads) │
│ Execution role: mp-mcp-bridge-exec │
└─────┬───────────────────────────┬────────┘
│ GetSecretValue │ HTTP
┌──────────▼─────────┐ ┌──────▼──────────────┐
│ Secrets Manager: │ │ MCP Server (Lambda) │
│ mp-servicenow-creds │ │ mp-mcp-server │
│ (mp-data — cross- │ │ Managed identity → │
│ account fetch) │ │ Entra SP: │
└──────────┬──────────┘ │ sp-mp-mcp-bridge │
│ └──────┬──────────────┘
┌──────────▼──────────┐ │ OAuth same client_id
│ ServiceNow REST API │◄───────────────┘ (CORRELATED edge)
│ (creates incidents) │
└──────────┬──────────┘
│ writes
┌──────────▼──────────┐
│ ServiceNow: │
│ HR employee table │
│ (synthetic PII) │
└─────────────────────┘
Two more cross-account paths the platform must materialize from mp-workloads into mp-data independent of the AI surface:
mp-content-tagger (Bedrock)→ service role →bedrock:Retrieve→mp-content-metadata(mp-data, cross-account read viamp-cross-account-data-reader).mp-content-pipeline (Step Functions)→ invoke any Lambda in mp-workloads → DynamoDB writes inmp-ticket-log(mp-data, cross-account).
Identity story
Three federation/correlation patterns the lab exercises, all of which Stream 3's stitcher must produce:
- Entra → AWS OIDC federation.
mp-foundry-sn-routerIAM role inmp-workloadstrusts the Entra OIDC IdP (the StackSet bootstrap registers the IdP inmp-securityand the role inmp-workloads). Themp-foundry-sn-routerrole's trust policy names the Entra SPsp-mp-foundry-router(appId derived at lab-up). Stream 3 must merge the Entra SP node and the AWS role's trust principal into a single identity with two source records. - Entra ↔ ServiceNow OAuth correlation.
sp-mp-mcp-bridge(Entra SP) is registered in ServiceNow as an OAuth provider with the sameclient_id. Stream 3's existing intra-entra-servicenowCORRELATEDedge handles this within one connector graph; the platform-level stitcher must propagate it to bridge graphs from the AWS connector (which sees the SP as the principal of the Lambda's managed-identity-bound credentials cache via secret reference) and Foundry. - Foundry-side SP attribution.
servicenow-openai-client(the canonical demo SP from the 2026-04-20 session note) carriesCognitive Services OpenAI Userongpt-nano-for-summary. The Foundry connector emits this. Stream 3 must NOT require additional federation — the platform already correctly renders this row (per the session note, dev/prod showpaths=2, exec30=8). MediaPro Lab 2 simply re-creates this exact resource shape in a SecurityV0-owned demo Azure tenant with MediaPro-flavored names.
The CORRELATED edges Stream 3 must produce in MediaPro Lab 2 (these are explicit acceptance criteria for Stream 3):
| Edge | Source connector | Target connector | Correlation key |
|---|---|---|---|
sp-mp-foundry-router (Entra SP) ↔ mp-foundry-sn-router (AWS IAM role) | entra-servicenow | aws | OIDC sub claim in trust policy → matches Entra SP appId |
sp-mp-mcp-bridge (Entra SP) ↔ ServiceNow OAuth Azure MP MCP OAuth Client | entra-servicenow (same connector) | (intra) | client_id — already implemented |
sp-mp-mcp-bridge (Entra SP) ↔ Lambda execution-role assumed-credential proof in CloudTrail | entra-servicenow | aws | client_id appears in Secrets Manager secret value (out of scope for read; documented gap) |
servicenow-openai-client (Entra SP) ↔ gpt-nano-for-summary (Foundry resource) | entra-servicenow | azure-foundry | RBAC role assignment on Foundry account — already working per session note |
Scenarios covered
The 2026-04-08 plan and 12-deployment-approval.md define a scenario taxonomy. MediaPro Lab 2 covers six scenarios end-to-end. Each one is mapped to specific lab resources so a single make demo proves all of them at once.
| Scenario | Source doc | What it proves | Lab resources that demonstrate it |
|---|---|---|---|
| B1 Customer Support Agent (tiered Bedrock) | 12-deployment-approval.md §AWS Bedrock Demo Lab | Tiered permission model — what's lost at Tier 1 vs Tier 2 vs Tier 3 against a realistic Bedrock agent | mp-support-router agent + action-group Lambda + execution role with overprivileged DynamoDB managed policy |
| B2 Multi-Agent Supervisor / Collaborator | 12-deployment-approval.md §Scenario B2 | Transitive authority through bedrock:InvokeAgent — supervisor reaches collaborator's blast radius | mp-content-orchestrator (supervisor) + mp-content-tagger (collaborator) + mp-support-router (collaborator) — both reachable via bedrock:InvokeAgent |
| B3 Cross-Account Bedrock → S3 PII | 12-deployment-approval.md §Scenario B3 | Cross-account authority path crosses account boundary into PII data domain | mp-content-tagger (mp-workloads) → mp-cross-account-data-reader → mp-pii-customers (mp-data) |
| M2 MCP as Bedrock Action Group Backend | 12-deployment-approval.md §Scenario M2 | Lambda proxies to MCP server — IAM authority visible, MCP tools opaque | mp-mcp-bridge Lambda → mp-mcp-server Lambda (MCP protocol) → ServiceNow REST |
| X3 Bedrock → Lambda → ServiceNow | 12-deployment-approval.md §Scenario X3 | Cross-system authority path from AWS into ServiceNow under one identity | mp-support-router → mp-mcp-bridge → sp-mp-mcp-bridge (Entra SP) → ServiceNow OAuth → HR table writes |
| X4 Multi-Platform Agent Ecosystem (Foundry money-shot) | 12-deployment-approval.md §Scenario X4 + auto-route-identity-tickets.md | Two AI agents on different cloud platforms reach same ServiceNow instance via different identity paths; Foundry classification step part of one path | The Sergey canonical scenario: Auto-route identity tickets workflow → sn-ticket-router (Entra) → Microsoft Graph → gpt-nano-for-summary (Foundry) — exactly as already live on dev/prod per the 2026-04-20 session note, replicated in MediaPro-flavored names |
Explicitly not covered (with rationale):
- M1 (MCP as Foundry connection direct) — covered by Lab
x1-foundry-mcp-servicenowalready; Lab 2 includes the X3 variant (MCP as Bedrock action-group backend) instead. - X1 (the
x1-foundry-mcp-servicenowflagship) — that lab stays as-is; MediaPro Lab 2 reuses its MCP-server Azure Function code as a module but rebrands and rewires it for the MediaPro narrative. - X2 (Logic App) — adds Azure Logic Apps surface area without commensurate value; defer.
Terraform layout
The lab lives at sv0-demo-labs/labs/mediapro/. It composes per-account Terraform modules with Entra and ServiceNow provisioning modules. The composition root is a single make up that runs three Terraform applies in topological order plus two non-Terraform provisioning steps for ServiceNow + Foundry.
sv0-demo-labs/
├── bootstrap/
│ └── mediapro-org/ # NEW. Run once, ever.
│ ├── main.tf # OrganizationsCreateAccount × 3, MediaPro OU
│ ├── stackset-securityv0-readonly.tf # StackSet from Stream 2's template
│ ├── outputs.tf # account IDs, OU id
│ └── README.md # one-time runbook + state bucket bootstrap
├── modules/ # NEW. Shared between accounts.
│ ├── tf-account-baseline/ # VPC, CloudTrail, log retention
│ ├── tf-bedrock-agent/ # Bedrock agent + service role
│ ├── tf-action-group-lambda/ # Lambda + exec role + Bedrock invoke perm
│ ├── tf-mcp-server-lambda/ # MCP Lambda packaged from /mcp-server source
│ ├── tf-cross-account-trust/ # mp-data trust policy with mp-workloads as principal
│ ├── tf-entra-oidc-federation/ # IAM OIDC IdP + role trust statement
│ └── tf-pii-data-bucket/ # S3 bucket + synthetic CSV seed
└── labs/
└── mediapro/ # NEW.
├── README.md # Lab overview + lifecycle
├── Makefile # up / scan / down / refresh / validate
├── DEMO-SCRIPT.md # SE-runnable walkthrough
├── COSTS.md # per-resource cost table, $5/cycle envelope
├── .env.1password.example
├── shared/
│ ├── backend.tf # remote state, per-account workspace
│ ├── providers.tf # 3 aws providers (security/workloads/data)
│ ├── variables.tf # account IDs, region, lab_id (cycle salt)
│ └── locals.tf # naming conventions: mp-{lab_id}-{resource}
├── account-mp-security/
│ ├── main.tf # Org CloudTrail, Config Aggregator, Access Analyzer
│ ├── oidc-idp.tf # Entra OIDC IdP registration
│ └── outputs.tf # trail_arn, idp_arn for cross-stack
├── account-mp-workloads/
│ ├── main.tf # Bedrock agents (B1, B2, X3) wiring
│ ├── content-pipeline.tf # Step Functions, Lambdas, B2 collaborators
│ ├── mcp-bridge.tf # mp-mcp-bridge + mp-mcp-server Lambdas (M2)
│ ├── cross-account-roles.tf # mp-cross-account-data-reader, mp-foundry-sn-router
│ └── outputs.tf # agent IDs, Lambda ARNs, role ARNs
├── account-mp-data/
│ ├── main.tf # S3 PII + KB buckets, DynamoDB
│ ├── secrets.tf # Secrets Manager: ServiceNow creds, Foundry key
│ ├── trusts.tf # cross-account trust to mp-workloads only
│ └── outputs.tf # bucket ARNs, secret ARNs
├── entra/
│ ├── main.tf # azuread provider — sp-mp-mcp-bridge,
│ │ # sp-mp-foundry-router, servicenow-openai-client,
│ │ # sn-ticket-router
│ ├── federations.tf # OIDC subject claims for AWS roles
│ └── outputs.tf # appIds, client_secrets (to TF state, not stdout)
├── azure-foundry/
│ ├── main.tf # azurerm — Foundry account, project,
│ │ # gpt-nano-for-summary deployment, RBAC
│ ├── role-assignments.tf # Cognitive Services OpenAI User on
│ │ # servicenow-openai-client
│ └── outputs.tf
├── servicenow/ # NOT Terraform — Python provisioner
│ ├── provision.py # uses ServiceNow REST APIs
│ ├── workflows/
│ │ ├── auto-route-identity-tickets.json
│ │ └── sn-ticket-router-graph.json
│ ├── tables/
│ │ ├── u_mp_hr_employee.json # synthetic HR PII table schema
│ │ └── u_mp_cmdb_app.json # synthetic CMDB
│ ├── seed-data/
│ │ ├── hr-employees.csv # 12 fake employees
│ │ └── cmdb-apps.csv # 8 fake CIs
│ └── teardown.py
├── mcp-server/ # MCP Lambda source (reused/forked from
│ │ # x1-foundry-mcp-servicenow)
│ ├── handler.py # Lambda handler (mcp protocol over HTTP)
│ ├── tools.py # search_employee, get_ticket, create_ticket
│ ├── servicenow_client.py
│ └── requirements.txt
├── scripts/
│ ├── up.sh # orchestrates everything
│ ├── scan.sh # invokes Stream 1 CLI for AWS+Entra+SN+Foundry
│ ├── down.sh # teardown in reverse order
│ ├── refresh.sh # idempotent re-apply
│ ├── validate.sh # runs the validation matrix from this plan
│ ├── seed-execution.sh # nudges agents to run so exec30 > 0
│ └── cost-report.sh # AWS Cost Explorer query for the lab tag
└── tests/
├── test_validation_matrix.py # asserts node/edge/path/finding counts
└── test_lifecycle.py # up → scan → validate → down idempotency
Per-account Terraform vs shared modules. Each account is its own terraform apply (separate state file in S3 backend) so a partial failure in mp-workloads does not lock mp-security's state. Cross-account ARNs are passed via terraform_remote_state data sources, not hardcoded outputs.
State backend strategy. A single S3 backend bucket sv0-demo-labs-tfstate (already exists for Lab 1, in securityv0-mgmt) with separate keys per (lab × account × cycle):
sv0-demo-labs-tfstate/
├── mediapro/security/{lab_id}/terraform.tfstate
├── mediapro/workloads/{lab_id}/terraform.tfstate
├── mediapro/data/{lab_id}/terraform.tfstate
├── mediapro/entra/{lab_id}/terraform.tfstate
└── mediapro/foundry/{lab_id}/terraform.tfstate
{lab_id} defaults to default. Power-users can spin up parallel cycles (LAB_ID=alice / LAB_ID=bob) — resource names use mp-{lab_id}- prefix so two cycles do not collide. State bucket persists across cycles; resources die.
Account bootstrap. bootstrap/mediapro-org/ is run once, ever, by an operator with the management account's Organizations admin role. It:
- Creates the
MediaProOU underSV0 Demo Labs. - Calls
aws organizations create-account× 3 (withmp-security@securityv0.com, etc. — addresses already aliased in Mercury Mail). - Waits for accounts (~60s each).
- Deploys the
SecurityV0ReadOnlyStackSet (Stream 2's template) into the OU. - Outputs the three account IDs into a Parameter Store entry
/sv0-demo-labs/mediapro/account-idsso the lab Terraform candata "aws_ssm_parameter"them.
This bootstrap is gated behind a make bootstrap target with a typed-confirm prompt to prevent accidental account creation.
Up / scan / teardown lifecycle
make up
cd sv0-demo-labs/labs/mediapro
make up # ~12-18 min wall clock
Steps (each is idempotent and re-runnable):
- Pre-flight (
scripts/up.sh:preflight) — verifybootstrap/mediapro-orghas run (account IDs in SSM Param Store), verify SecurityV0ReadOnly role exists in all three accounts, verify required env:MP_ENTRA_TENANT_ID,MP_FOUNDRY_SUBSCRIPTION_ID,MP_SN_INSTANCE,MP_SN_USERNAME/MP_SN_PASSWORD(from 1Password). Fail-fast with a one-line remediation per missing var. - Entra resources —
terraform applyinentra/. Creates 4 SPs + their secrets. Outputs appIds. - mp-data —
terraform applyinaccount-mp-data/. Creates buckets, secrets, DynamoDB. Cross-account trust still wide-open at this point because mp-workloads role ARN is not yet known. - mp-workloads —
terraform applyinaccount-mp-workloads/. Reads Entra appIds from remote state; creates Bedrock agents, Lambdas, MCP server, cross-account roles. Lambda ZIPs built locally frommcp-server/. - mp-security —
terraform applyinaccount-mp-security/. Org CloudTrail, Config Aggregator, Access Analyzer. (Last so the trail captures the prior steps' API activity for execution evidence.) - Tighten mp-data trust — second
terraform applyinaccount-mp-data/with the now-known mp-workloads role ARN. - Azure Foundry —
terraform applyinazure-foundry/. Foundry account, project,gpt-nano-for-summarydeployment, RBAC assignments naming the Entra SPs from step 2. - ServiceNow provisioning —
python servicenow/provision.pycreates HR table, CMDB table, OAuth providers (registering Entra SPclient_ids from step 2), the two demo workflows, business rules, and seed records. - Execution seed —
scripts/seed-execution.shinvokes each Bedrock agent twice and each ServiceNow workflow twice soexec30 > 0for the demo. Emits ~30 CloudTrail events, ~6 ServiceNow flow executions, ~4 Foundry thread creations. - Print success banner — outputs the three account IDs, Foundry endpoint, ServiceNow instance, and the
make scancommand.
make scan
make scan # ~6-10 min wall clock
Invokes Stream 1's connector CLI (the ConnectorInstance / ScanScope / ScanRun surface) to:
- Provision four
ConnectorInstancerecords in the platform tenantenterprise-nimbus(one each for aws, entra-servicenow, azure-foundry; the AWS instance is configured with--scope-mode multi-accountand the three account IDs from SSM Param Store, per Stream 2). - Trigger four
ScanRunrecords targeting theenterprise-nimbustenant. - Poll until all four scans report
status=succeeded(timeout 8 min; surface partial failure with the scan IDs to inspect). - Print a one-line summary per connector: nodes-emitted / edges-emitted / scan duration.
The actual Stream 1 CLI surface is owned by that stream; this script consumes whatever shape it lands as. Pseudocode:
sv0 connector create --tenant enterprise-nimbus --type aws \
--scope multi-account --accounts "$(aws ssm get-parameter ...)"
sv0 scan trigger --tenant enterprise-nimbus --connector aws --wait
# repeat for entra-servicenow, azure-foundry
sv0 scan summary --tenant enterprise-nimbus --since 10m
make down
make down # ~8-12 min wall clock
Reverse topological order. Each step is idempotent (re-run safe) and skips on not found:
- ServiceNow teardown (
python servicenow/teardown.pyremoves workflows, OAuth providers, tables — preserves the PDI itself). - Azure Foundry
terraform destroy. - mp-security
terraform destroy(CloudTrail logs flush to S3 first viaforce_destroy = trueon the trail bucket). - mp-workloads
terraform destroy— Bedrock agents go first via dependency order. - mp-data
terraform destroy—force_destroy = trueon PII/KB/trail buckets so cycle is clean. - Entra
terraform destroy— SPs + secrets removed. - Verify no stray resources via
scripts/cost-report.sh --since today— should print zero in-scope resources.
State bucket persists. If make down partial-fails, make down --resume continues from the last successful step.
make refresh / make validate
make refresh is terraform apply across all stacks without re-bootstrapping anything — for fixing drift or applying a spec change mid-cycle. make validate runs tests/test_validation_matrix.py against the running platform (after make scan) to confirm the demo will land.
Idempotency
- All
terraform applycalls are inherently idempotent. - ServiceNow
provision.pyusessys_idlookups beforePOST(PATCH on existing). - Execution seed checks for prior recent invocations and skips if
exec30 ≥ 4already. up.shwrites a.lab-statefile with the last completed step. Re-running picks up from the next step.up.sh --forcere-runs everything.
Cost expectation
Designed for < $5 per up/scan/down cycle, assuming a 2-3 hour live demo window.
| Resource | Quantity | Per-hour | Per-cycle (3h) | Notes |
|---|---|---|---|---|
| Bedrock agent invocations (Claude Haiku 4.5) | ~30 invocations × ~2k tokens | $0.001/invocation | ~$0.04 | Seed only; demo viewer doesn't re-invoke |
| Lambda invocations (Bedrock + MCP + ops) | ~80 invocations × <100ms | included free tier | ~$0.00 | Free tier covers it |
| S3 storage (KB + PII synthetic) | ~5 MB total | included free tier | ~$0.00 | Trivial |
| S3 + CloudTrail PUTs (org trail) | ~500 events | $0.000005/event | ~$0.003 | Single-region trail to bound cost |
| DynamoDB on-demand | ~50 writes | $1.25/M writes | ~$0.00 | |
| Step Functions state transitions | ~20 transitions | $0.025/1k | ~$0.001 | |
| Secrets Manager secrets | 4 secrets × 3h | $0.40/secret/month | ~$0.002 | Pro-rated |
| KMS key (Foundry) | 1 key × 3h | $1/key/month | ~$0.004 | |
| AWS Config recorder | per-account × 3 | $0.003/item recorded | ~$0.30 | Recorders ON during cycle only |
| Azure Foundry account + Cognitive Services | 1 account, ~10 model calls | model: $0.15/1M tokens | ~$0.05 | gpt-nano is cheap |
| Azure Function (MCP server) — Lab 2 reuses Lambda not Function | n/a | n/a | $0.00 | We use a Lambda-hosted MCP server in Lab 2 to keep cost in AWS |
| ServiceNow PDI | shared, persistent | $0 (free) | $0.00 | PDI keep-alive workflow elsewhere prevents hibernation |
| Entra ID (free tier — same as demo tenant per session note) | n/a | $0 | $0.00 | Sign-in log fidelity gap noted in scenario X4 caveat |
| Org-bootstrap (one-time, amortized) | 3 accounts | $0 to create | $0.00 | |
| Total per cycle | — | — | ~$0.40 — $1.50 | Comfortably under $5 |
Risks to the budget:
- Forgetting to
make down→ CloudTrail/Config keep recording (~$0.30/day each), Bedrock agents idle ($0), KMS keys ($1/month). Worst case ~$30/month if abandoned. Mitigate with a CloudWatch billing alarm on the OU set to $25 and a daily croncost-report.shthat nags via Slack. - AWS Config in continuous-recorder mode is the line-item to watch; the
tf-account-baselinemodule deploys it in periodic mode (daily snapshot) instead of continuous to keep this < $0.50/day even if a teardown is missed. - If the Foundry connector starts pulling thread history aggressively, Cognitive Services billing can climb. Cap with quota: 100 RPM on the deployment.
Cost gate. make up refuses to apply if cost-report.sh --since 30d --ou MediaPro exceeds $50 — surfaces a stale resource issue before adding more.
Validation matrix (acceptance criteria for Streams 1/2/3)
After make up && make scan, the platform tenant enterprise-nimbus must render the following. Each row is an automated assertion in tests/test_validation_matrix.py and an explicit acceptance criterion for the named upstream stream. When all rows pass, Streams 1 + 2 + 3 are done.
| # | Assertion | Stream gated | How verified |
|---|---|---|---|
| V1 | Three aws_account nodes exist with IDs matching mp-security, mp-workloads, mp-data and parent_ou_id = the MediaPro OU | Stream 2 | API: GET /api/v1/entities?type=aws_account&tenant=enterprise-nimbus returns ≥3 |
| V2 | Each AWS resource node has account_id populated and BELONGS_TO edge to its aws_account node | Stream 2 | Spot-check: mp-pii-customers bucket → aws_account:333333333333 |
| V3 | A single ScanRun for connector aws covered all three accounts in one run (not three separate runs) — scan_scope.accounts.length === 3 | Stream 1 + 2 | API: GET /api/v1/scans/{id} shows scope manifest |
| V4 | Per-(account × category) scan progress is reported: e.g., mp-workloads:bedrock, mp-data:s3 are independent line items in scan progress | Stream 1 | UI: scan-detail page renders the matrix |
| V5 | Authority path mp-content-tagger (mp-workloads, Bedrock) → mp-cross-account-data-reader → mp-pii-customers (mp-data, S3) materializes as one path crossing the account boundary, length ≥4, with crosses_account_boundary=true | Stream 2 + Stream 3 | API: GET /api/v1/authority-paths?identity=mp-content-tagger returns this path |
| V6 | Authority path mp-support-router (Bedrock) → mp-mcp-bridge (Lambda) → sp-mp-mcp-bridge (Entra SP) → ServiceNow OAuth → u_mp_hr_employee (ServiceNow table) materializes as one stitched path spanning AWS + Entra + ServiceNow connectors | Stream 3 | API: identity = mp-support-router, expect connector_systems = [aws, entra-servicenow] on the path |
| V7 | The Auto-route identity tickets path is rendered identically to dev/prod's current state per the 2026-04-20 session note: servicenow-openai-client → gpt-nano-for-summary row exists with paths≥1, exec30≥1, dests=['gpt-nano-for-summary'] | Stream 3 (no regression) | API: GET /api/v1/authority-paths?identity=servicenow-openai-client |
| V8 | Stitched identity node for sp-mp-foundry-router exists with source_records = 2 (entra connector + aws connector); UI shows it as one identity, not two | Stream 3 | API: GET /api/v1/identities/{node_id} returns source_systems: ['entra', 'aws'] |
| V9 | Bedrock supervisor mp-content-orchestrator shows bedrock:InvokeAgent authority paths to BOTH collaborators (mp-content-tagger, mp-support-router); transitive blast radius reaches PII (V5) and ServiceNow HR (V6) | Stream 2 + Stream 3 | API: paths from supervisor have transitive=true flag |
| V10 | Finding(reachable_sensitive_domain) fires for the cross-account PII path (V5) and the cross-system ServiceNow PII path (V6) — exactly 2 distinct finding IDs, severities critical | Streams 2 + 3 (correctness) | API: GET /api/v1/findings?type=reachable_sensitive_domain returns 2 |
| V11 | Finding(scope_drift) fires on mp-mcp-bridge-exec Lambda role (overprivileged DynamoDB) — singular, severity high | Stream 2 | API filter |
| V12 | Finding(orphaned_ownership) fires on the dormant mp-content-export Lambda (modeled after Lab 1's contractor pattern) | Stream 2 | API filter |
| V13 | Evidence pack downloads cleanly for every critical finding; SHA256 integrity validates; pack contains source-system references for AT LEAST 3 distinct connectors per cross-system finding | Streams 1 + 2 + 3 | E2E: download + verify hash + parse sources array |
| V14 | Re-running make scan (with no infra changes) produces zero net entity churn (entities_created=0, entities_removed=0); diff-engine does not falsely flag cross-connector entities (regression test for sv0-platform#460/#461) | Stream 1 (idempotence) + Stream 3 | Compare two consecutive ScanRun posture deltas |
| V15 | Lab cost (scripts/cost-report.sh --since-up) reports < $5 for a typical 3h cycle | This plan | Direct measurement |
| V16 | make down followed by make up works end-to-end without manual intervention | This plan | CI smoke (nightly, gated by cost) |
A scenario-to-assertion crosswalk:
| Scenario | Validation rows that must pass |
|---|---|
| B1 | V1, V2, V11, V13 |
| B2 | V9, V10, V13 |
| B3 | V5, V10, V13 |
| M2 | V6 (the MCP hop), V13 |
| X3 | V6, V8, V10, V13 |
| X4 | V7 (Sergey path) + V8 (Foundry SP correlation) |
Demo walkthrough script
Revision-1 update (per umbrella D10/D16/D17): the v0 walkthrough uses V5 (cross-account Bedrock blast) as the climax, not V6 (cross-system MCP path). V6 is deferred to "full" because Stream 3's
mcp-server-to-entra-sprule is opt-in / known-fragile. Path 3 (V7 Foundry) runs in a second browser tab against the existing live dev/prod Foundry environment, not in the MediaPro Lab 2 (per D16 — Foundry Terraform deferred to "full"). The "cross-system MCP" segment of the original Path 2 is replaced with a one-screenshot teaser pointing at "full" Lab 2.
A 6-minute SE-runnable script (v0). Pre-condition: make up && make scan && make validate clean against MediaPro Lab 2 + dev/prod Foundry tenant available in a second tab. SE opens the platform UI for tenant enterprise-nimbus in one tab and the existing dev/prod Foundry tenant in a second tab.
[0:00–0:30] Set the scene (no clicks). "MediaPro is a streaming-media SaaS that just closed Series C. Three AWS accounts under one Organization, Entra for SSO, ServiceNow for ITSM, and the ML team has started shipping Foundry agents. CISO Priya was hired 90 days ago. She has zero idea what the AI agents and automations can actually reach."
[0:30–1:30] Land on Overview. Click Overview for tenant. Point at the count tiles: "Three AWS accounts, one Entra tenant, one ServiceNow instance — and we're stitching to a Foundry project. One authority graph across all of them. 5 critical findings, 11 high. We'll go through 2."
[1:30–4:00] Path 1 — the cross-account Bedrock blast, the v0 climax (B3 / V5). Open Authority Paths → search mp-content-tagger. Expand the path that crosses into mp-pii-customers. Talk track:
- "This is a Bedrock agent in the workloads account."
- "It assumes a cross-account role into the data account."
- "It can read every file in your customer-PII bucket. The cross-account boundary did not contain it."
- "Nobody on the security team approved this combined path. Each piece — the agent, the role, the bucket — was approved separately."
- "This is the kind of finding that the question 'who has the authority to do what' produces, when you can answer it across accounts in one graph."
Click into the evidence pack. "SHA256-integrity-checked. The pack lists every source-system ARN. Auditor-ready."
Then point at the V8 stitched-identity row: sp-mp-foundry-router shows source_records=2 (entra+aws) — "and just so you know we can stitch identities — here's the Entra SP that's federated to an AWS role we found via OIDC. One identity, two source systems, one row. We do this everywhere a deterministic anchor exists."
Optional teaser (10 sec, one screenshot): show a static screenshot of the V6 cross-system MCP path. "When you bring in MCP-fronted Lambdas and the credentials live in Secrets Manager, we can stitch that too — that's full Lab 2, end of May. Today, we ship the cross-account half."
[4:00–5:30] Path 2 — Sergey's canonical Foundry path, in the second tab (X4 / V7). Switch tabs to the live dev/prod Foundry tenant. Open identity servicenow-openai-client. Expand the row. "And in production today — same shape. ServiceNow workflow, runs as a Graph SP, sends classification context to a Foundry model. The Foundry hop was added after the workflow's baseline. The owner record is stale. The Graph scopes drifted to Directory.Read.All. We show all three changes on the same path."
Reference [auto-route-identity-tickets.md] talk-track for the bounded remediation pitch (narrow Graph scope, assign one owner, approve or remove the Foundry hop).
[6:30–7:00] Close on positioning. Per positioning-snapshot.md: "SecurityV0 is the system of record for what AI agents actually did with delegated authority. Not what they were granted — what they did and what they can reach. MediaPro is going from blind to provable in one scan."
Demo reset. make down && make up && make scan — ~25 min total turnaround. Or per scripts/refresh.sh, hot-swap the lab between two LAB_ID values so a second SE can demo while the first resets.
Implementation plan (writing-plans format)
Each phase produces a small, reviewable PR. TDD where applicable; for IaC, the equivalent is terraform plan against a real AWS account with cost ceiling, then apply, then validate against the success criterion stated in the task.
v0 total: 25 tasks across 9 active phases (Phase 8 Foundry deferred per D16; Phase 3.3/3.4 deferred per D17). Estimated 2-3 person-weeks for v0 once Stream 2 (multi-account AWS) and Stream 3 (graph stitching M1–M10) ship. Full (post-pilot) total: 32 tasks (re-adds Phase 8 + Phase 3.3 + Phase 3.4 + Q1's Lambda-secret stitching rule).
Phase 1: Account bootstrap (3 tasks)
1.1. Add bootstrap/mediapro-org/main.tf with aws_organizations_organizational_unit "mediapro" and three aws_organizations_account resources. Acceptance: terraform plan shows three account creates and one OU; terraform apply (when manually run) returns three account IDs to SSM Parameter Store at /sv0-demo-labs/mediapro/account-ids.
1.2. Add Stream 2's SecurityV0ReadOnly StackSet wiring in bootstrap/mediapro-org/stackset-securityv0-readonly.tf. Acceptance: After bootstrap, aws iam get-role --role-name SecurityV0ReadOnly --profile mp-security-IF-AdminAccess returns the role in all three accounts.
1.3. Add bootstrap/mediapro-org/README.md runbook with the typed-confirm prompt mechanism. Acceptance: Following the runbook from a clean checkout produces three accounts under MediaPro OU. (One-time.)
Phase 2: Per-account Terraform skeleton (4 tasks)
2.1. Create modules/tf-account-baseline/ (VPC stub, log group, periodic AWS Config recorder). Acceptance: Module applies cleanly into any of the three accounts; aws config get-status shows recorder active in periodic mode.
2.2. Create labs/mediapro/shared/{providers,backend,locals,variables}.tf with three named aws providers (one per account), LAB_ID variable, MediaPro naming locals. Acceptance: terraform validate passes in account-mp-security/ after this is in place.
2.3. Create account-mp-security/main.tf — Org CloudTrail (single-region, multi-account), Config Aggregator (across the three accounts), IAM Access Analyzer org-level. Acceptance: Trail captures events from all three accounts after make up.
2.4. Create account-mp-security/oidc-idp.tf — Entra OIDC IdP registration. Acceptance: aws iam list-open-id-connect-providers from mp-security shows the Entra IdP after apply.
Phase 3: mp-workloads — Bedrock + Lambda + MCP (3 tasks in v0; 2 tasks deferred to "full")
3.1. Create modules/tf-bedrock-agent/ and modules/tf-action-group-lambda/. Acceptance: A standalone test stack creates one Bedrock agent + Lambda action group; agent invocation succeeds.
3.2. Create account-mp-workloads/main.tf for B1 — mp-support-router agent + mp-mcp-bridge action-group Lambda + execution role with AmazonDynamoDBFullAccess (intentional finding). Acceptance: bedrock:InvokeAgent works end-to-end.
3.5. Create account-mp-workloads/cross-account-roles.tf — mp-cross-account-data-reader (assumes into mp-data), mp-foundry-sn-router (Entra-OIDC-trusted). Acceptance: AssumeRole from mp-workloads into mp-data:mp-data-cross-account-trust succeeds; OIDC role trust verifiable via simulated token.
Deferred to "full" (per umbrella D17 + D10):
3.3.— V9 covers this in full. Not on v0 demo walkthrough.content-pipeline.tffor B2 (Multi-Agent Supervisor scenario)3.4.— V6 deferred to full because Stream 3'smcp-bridge.tffor M2 (MCP server Lambda)mcp-server-to-entra-sprule is opt-in. Without that rule, the MCP path renders as two disconnected segments — not demo-ready.
Phase 4: mp-data — S3 PII + DynamoDB + Secrets + cross-account trust (3 tasks)
4.1. Create modules/tf-pii-data-bucket/ + account-mp-data/main.tf — mp-pii-customers bucket with synthetic PII CSV, mp-content-metadata KB bucket with synthetic content records, mp-ticket-log DynamoDB. Acceptance: Buckets created, blocks public access, CSV contents visible via aws s3 cp from mp-data console.
4.2. Create account-mp-data/secrets.tf — mp-servicenow-creds, mp-foundry-api-key. Values pulled from 1Password at apply time via external provider, never committed. Acceptance: Secret values readable from mp-workloads via cross-account fetch.
4.3. Create account-mp-data/trusts.tf and modules/tf-cross-account-trust/ — mp-data-cross-account-trust role with trust principal = mp-workloads role ARN (read from remote state). Acceptance: mp-content-tagger Bedrock service role can s3:GetObject against mp-pii-customers via assume-role.
Phase 5: mp-security finalization (1 task)
5.1. Apply account-mp-security/ with the trail event selector wired for Lambda + S3 data events (so V13 evidence packs see invocations). Acceptance: After seed-execution.sh, querying CloudTrail for a known Lambda invocation returns the event within 5 min.
Phase 6: Entra resources via Terraform (2 tasks)
6.1. Create entra/main.tf with azuread provider. Provision four SPs: sn-ticket-router, servicenow-openai-client, sp-mp-mcp-bridge, sp-mp-foundry-router. Acceptance: All four SPs visible in Entra portal; appIds emitted to remote state.
6.2. Create entra/federations.tf — federated credential on sp-mp-foundry-router with subject claim matching the AWS IAM role's OIDC trust. Acceptance: Token exchange from Entra → AWS (mp-foundry-sn-router) succeeds end-to-end via a manual smoke.
Phase 7: ServiceNow PDI seed (3 tasks)
7.1. Create servicenow/provision.py skeleton: idempotent ServiceNow REST client, --provision and --teardown modes, secrets from MP_SN_* env. Acceptance: python servicenow/provision.py --check returns ServiceNow instance metadata.
7.2. Add servicenow/tables/u_mp_hr_employee.json + seed CSV; provisioner creates the table and seeds 12 records via /api/now/table/sys_db_object and /api/now/table/u_mp_hr_employee. Acceptance: ServiceNow UI shows the table populated with synthetic PII.
7.3. Add servicenow/workflows/auto-route-identity-tickets.json (re-using the canonical scenario from flow-service-now.md) and the sn-ticket-router-graph connection + Azure Graph OAuth Client. Provisioner registers OAuth provider for sp-mp-mcp-bridge and the Graph connection for sn-ticket-router. Acceptance: Triggering an incident runs the workflow; ServiceNow + Entra log show the SP made Graph + (separately) MCP calls.
Phase 8: Azure Foundry resources — DEFERRED to "full" per umbrella D16
Revision-1 (per umbrella D16): v0 does not stand up Foundry resources in MediaPro Lab 2. The V7 Foundry path already runs on the live dev/prod tenant (see 2026-04-20 session note). The early-May demo switches browser tabs from MediaPro Lab 2 → existing dev/prod Foundry env to show V7. Standing up duplicate Foundry resources adds Terraform complexity without visibly improving the buyer story.
Tasks 8.1 and 8.2 below ship in the post-pilot "full" Lab 2 build (end-of-May target):
8.1. Create(deferred to full)azure-foundry/main.tf— Foundry account, project,gpt-nano-for-summarydeployment viaazurermprovider.
8.2. Create(deferred to full)azure-foundry/role-assignments.tf—Cognitive Services OpenAI Userongpt-nano-for-summaryforservicenow-openai-client.
Phase 9: up/scan/down lifecycle scripts (5 tasks)
9.1. Write scripts/up.sh orchestrating Phases 2–8 with .lab-state checkpointing and pre-flight validation. Acceptance: make up from clean state succeeds in ≤20 min wall clock.
9.2. Write scripts/down.sh with reverse-order destroy and --resume support. Acceptance: make down from any state finishes with zero in-scope resources per cost-report.sh.
9.3. Write scripts/scan.sh consuming Stream 1's connector CLI surface (interface contract documented in this plan; if Stream 1's CLI shape differs, this script adapts). Acceptance: Four ScanRun records reach succeeded in ≤8 min.
9.4. Write scripts/seed-execution.sh to drive ≥4 invocations per agent + 2 per workflow. Acceptance: Per-path exec30 ≥ 1 after 5-minute settle for CloudTrail / Foundry threads / ServiceNow logs.
9.5. Write scripts/cost-report.sh querying Cost Explorer with MediaPro tag filter; make up gates on its output. Acceptance: Returns a number; gate trips when seeded with $50 of fake cost.
Phase 10: Demo script + validation harness (4 tasks)
10.1. Write tests/test_validation_matrix.py covering V1–V14 against the live platform. Acceptance: All pass on the canary enterprise-nimbus tenant after a clean up + scan.
10.2. Write tests/test_lifecycle.py for V16 (up → scan → down → up idempotence). Acceptance: Runs in CI nightly; failure pages on-call.
10.3. Write DEMO-SCRIPT.md (the 7-minute SE script) with screenshot placeholders. Acceptance: Sergey or another SE can run it on a fresh make up without instructor input. (Validate by getting one SE to dry-run.)
10.4. Write README.md and COSTS.md for the lab. Acceptance: A new engineer can spin up, demo, and tear down using only the README.
Coordination with sv0-connectors#27 (Victor)
Victor owns sv0-connectors#27 — the X1 Foundry → MCP → ServiceNow PII lab — and that lab already shipped to sv0-demo-labs/labs/x1-foundry-mcp-servicenow/. Two integration points:
-
Reuse, don't fork, the MCP server. The
mcp-server/directory inx1-foundry-mcp-servicenow/is a working Azure Function. MediaPro Lab 2 needs an AWS Lambda hosted MCP server (different runtime; same protocol surface). Action: extract the MCP tool implementations (tools.py-equivalent) to a shared module undersv0-demo-labs/modules/mcp-server-shared/and let both labs import from there. PR sequence: extract first (no behavior change, X1 lab still passes), then MediaPro Phase 3.4 adds the Lambda packaging. Coordinate with Victor before extracting. -
Don't re-cover X1. MediaPro Lab 2 covers M2 (MCP-as-Bedrock-action-group) instead of X1's M1 (MCP-as-Foundry-connection-direct). Both should remain demoable independently — X1 stays as a focused single-scenario lab; MediaPro Lab 2 is the multi-scenario lab. Confirm with Victor that he is OK with Lab 2 referencing X1 for the deeper MCP visibility story rather than duplicating it.
-
Validation matrix sharing. V8 (CORRELATED edge via
client_id) is asserted in both X1 and MediaPro Lab 2; if Stream 3 landsclient_idcorrelation for the entra-servicenow case before the cross-connector case, X1 will pass V8 first and MediaPro Lab 2 will pass it later. Track that ordering explicitly insv0-platform#300.
Open questions
-
Stream 1's CLI surface shape. This plan assumes a
sv0 connector create / sv0 scan trigger / sv0 scan summarycommand set. If Stream 1 landsConnectorInstanceas an HTTP API only (no CLI),scripts/scan.shbecomes acurlscript — that is fine, but worth confirming early so Phase 9.3 isn't reworked. -
Stream 2's per-(account × category) scoping granularity. This plan assumes scoping like
mp-workloads:bedrockexists at scan-trigger time (V4). If Stream 2 lands account-level scoping only (no per-category), V4 either drops or becomes a "scan progress is reported per account, with category breakdown in the summary" softer assertion. Worth pinning in the multi-account-aws-connector architecture plan that lands alongside this one. -
Stream 3's stitching for the AWS-Lambda-MCP-server identity. Lambda has no Entra managed identity natively. The MediaPro lab fakes it by storing
sp-mp-mcp-bridgecredentials in Secrets Manager and having the Lambda load them at runtime. The AWS connector sees the Lambda's IAM role and the secret reference; it does NOT see the Entra SP. Stream 3 must stitch via the OAuthclient_idregistered in ServiceNow (which links Entra SP ↔ ServiceNow) and a separate edge from the Lambda execution role to the secret to the SP. This is a new correlation rule beyond OIDC federation. If Stream 3 cannot land this in time, V6 weakens to "the path renders as two segments (AWS half + Entra/SN half) joined by a manual-correlation edge labeled 'Lambda fetches Entra SP credentials from secret'." Decide before Stream 3 commits its design. -
Entra P1 license decision (per session note + sv0-connectors#86). MediaPro Lab 2 inherits the same Entra free-tier limitation:
signInsAPI rejected. The validation matrix doesn't depend onsignInsfor any V row, so the lab stands without P1 — but the demo script's "what changed" narrative (perauto-route-identity-tickets.md) is stronger if sign-in proof is available. Recommend: park as a Phase 11 enhancement, depend on the parent sv0-connectors#86 decision. -
ServiceNow PDI separation. Should MediaPro Lab 2 use a dedicated PDI (separate from the existing demo PDI used by X1 and the Sergey demo path), or share? Sharing risks data bleed and brand confusion ("Why does the MediaPro demo show employees from the wrong company?"). Recommend a dedicated PDI
mp-pilot-demowith the keep-alive workflow already in place. -
Are the three account creates reversible? AWS account closure has a 90-day cool-down. If we close MediaPro accounts later, we cannot recreate them in the same OU within that window. Recommend treating these accounts as long-lived (OU + StackSet permanent; per-cycle resources ephemeral). The cost envelope analysis assumes accounts persist.
-
Timeline against MediaPro pilot (early May 2026, sv0-documentation#195). Phases 1–10 of this plan total ~3-4 person-weeks of SE/IaC work, plus the upstream prereqs (Stream 2 multi-account AWS, Stream 3 graph stitching). Realistically the lab is demoable end-of-May, not start-of-May. If the May 5 pilot is hard, Lab 2 v0 ships with V1–V8 only (no scenario X3 / V6) — the cross-system AWS path does not render until Stream 3 cross-Lambda-credential stitching lands. The
auto-route-identity-ticketsFoundry path (V7) works today on dev/prod and can be the headline path for an earlier MediaPro session. Flag to user.
References
Plans:
2026-04-08-demo-lab-plan.md— original Lab 2 / Nimbus Enterprise definition; this plan replaces its Lab 2 section with MediaPro.2026-04-04-aws-integration-implementation-cycle.md— AWS connector implementation cycle, gates this lab consumes.2026-04-22-multi-account-e2e-implementation-plan.md— umbrella sibling plan.2026-04-22-connector-control-execution-architecture.md— Stream 1.2026-04-22-multi-account-aws-connector-architecture.md— Stream 2.2026-04-22-cross-connector-graph-stitching-architecture.md— Stream 3.
Architecture:
12-deployment-approval.md— defines scenarios B1, B2, B3, M2, X3, X4.research/2026-03-30-aws-integration-strategy.md— AWS connector scope.research/2026-02-26-cross-connector-entity-correlation-research.md— correlation phases A–E (Stream 3's prior art).
Product:
positioning-snapshot.md— the framing the demo ladders into.auto-route-identity-tickets.md— Sergey's canonical Foundry path included as scenario X4.flow-service-now.md— ServiceNow workflow technical setup brief reused for the lab.
Existing infrastructure:
sv0-demo-labs/labs/sv0-demo-lab-1/— Lab 1; module + lifecycle pattern this plan extends.sv0-demo-labs/labs/x1-foundry-mcp-servicenow/— Victor's X1 lab; MCP server source to be shared.sv0-platform/docs/session-notes/2026-04-20-foundry-demo-resolution-session-handoff.md— what works on dev/prod today; V7 baseline.
Issues:
sv0-documentation#195— MediaPro pilot readiness umbrella.sv0-connectors#27— X1 lab (Victor) — coordinate per §Coordination above.sv0-connectors#32— multi-account AWS scanning (Stream 2 prereq).sv0-platform#300— cross-connector graph stitching (Stream 3 prereq).sv0-connectors#86— Entra P1 decision (affects X4 / V7 fidelity).