Skip to main content

ISB Core Architecture

Last Updated: 2026-03-02 Source: co-cddo/innovation-sandbox-on-aws Captured SHA: cf75b87

Executive Summary

The Innovation Sandbox on AWS (ISB) is a multi-account orchestration platform built with AWS CDK and TypeScript that manages temporary AWS sandbox accounts for experimentation and learning. It is deployed across five CloudFormation stacks (AccountPool, IDC, Data, Compute, and SandboxAccount via StackSet), uses DynamoDB for state persistence, API Gateway with CloudFront for its REST API and React frontend, EventBridge for event-driven coordination, and Step Functions with CodeBuild for automated account cleanup using AWS Nuke. The CDDO fork at version 1.1.4 (solution ID SO0284) extends the upstream AWS Solutions implementation with satellite services for cost tracking, scenario deployment, and enhanced approvals.

Architecture Overview

System Context

CDK Stack Architecture

The solution is organized into five CloudFormation stacks with clear separation of concerns. The AccountPool and IDC stacks deploy to the Organizations management account and IDC account respectively, while Data and Compute stacks deploy to the hub account. A StackSet deploys resources into each sandbox account.

Deployment order: AccountPool -> IDC -> Data -> Compute

Source: source/infrastructure/lib/isb-account-pool-stack.ts, isb-idc-stack.ts, isb-data-stack.ts, isb-compute-stack.ts


CDK Stacks Deep Dive

1. AccountPool Stack (InnovationSandbox-AccountPool)

Deploys to: Organizations management account

Purpose: Creates the organizational structure, Service Control Policies, and cross-account roles for sandbox account management.

Resources created:

ResourceTypePurpose
InnovationSandboxAccountPoolOuCfnOrganizationalUnitRoot OU for all sandbox accounts
AvailableOuCfnOrganizationalUnitAccounts ready for leasing
ActiveOuCfnOrganizationalUnitCurrently leased accounts
CleanUpOuCfnOrganizationalUnitAccounts undergoing AWS Nuke cleanup
QuarantineOuCfnOrganizationalUnitFailed cleanup, manual review needed
FrozenOuCfnOrganizationalUnitFrozen leases, resources preserved
EntryOuCfnOrganizationalUnitNew accounts being registered
ExitOuCfnOrganizationalUnitAccounts being decommissioned
OrgMgtRoleIAM RoleAssumed by hub for Organizations API calls
5 SCPsCfnPolicySecurity guardrails (see below)
IsbStackSetCfnStackSetAuto-deploys cleanup role to sandbox accounts
AccountPoolConfigurationSSM ParameterShared config (OU IDs, regions) via RAM
CostAllocationTagActivatorCustom ResourceActivates ISB cost allocation tags

Service Control Policies:

SCPTargetPurpose
AwsNukeSupportedServicesScpRoot sandbox OUAllow only AWS Nuke-supported services
RestrictionsScpRoot sandbox OURestrict security, isolation, cost, and operations resources
ProtectISBResourcesScpRoot sandbox OUProtect ISB control plane resources
LimitRegionsScpRoot sandbox OURestrict to configured AWS regions
WriteProtectionScpAvailable, CleanUp, Quarantine, Entry, Exit OUsBlock all create/modify actions

Parameters: Namespace, HubAccountId, ParentOuId, IsbManagedRegions

Source: source/infrastructure/lib/isb-account-pool-resources.ts


2. IDC Stack (InnovationSandbox-IDC)

Deploys to: IAM Identity Center account (often the Org management account)

Purpose: Configures IAM Identity Center groups, permission sets, and cross-account roles for federated access.

Resources created:

ResourceTypePurpose
IdcConfigurerCustom Resource LambdaCreates Admin/Manager/User groups and permission sets in IDC
IdcRoleIAM RoleAssumed by hub for Identity Store and SSO API calls
IdcConfigurationSSM ParameterShared config (group IDs, permission set ARNs) via RAM

IdcRole permissions:

  • identitystore:GetUserId, DescribeUser -- user lookup
  • identitystore:ListGroups, ListGroupMemberships, ListGroupMembershipsForMember -- group membership
  • sso:ListPermissionSets, DescribePermissionSet -- enumerate permission sets
  • sso:CreateAccountAssignment, DeleteAccountAssignment, ListAccountAssignments -- manage account access

Parameters: Namespace, OrgMgtAccountId, HubAccountId, IdentityStoreId, SsoInstanceArn, AdminGroupName, ManagerGroupName, UserGroupName

Source: source/infrastructure/lib/isb-idc-resources.ts


3. Data Stack (InnovationSandbox-Data)

Deploys to: Hub account

Purpose: Persistent data layer with DynamoDB tables and AppConfig configuration profiles.

DynamoDB Tables:

TablePartition KeySort KeyGSITTLPurpose
SandboxAccountTableawsAccountId (String)------Pool account inventory
LeaseTemplateTableuuid (String)------Reusable lease configurations
LeaseTableuserEmail (String)uuid (String)StatusIndexttlLease records and lifecycle

LeaseTable GSI StatusIndex: Partition key = status, Sort key = originalLeaseTemplateUuid

AppConfig Profiles:

  • Global Config: Maintenance mode, lease limits, cleanup settings, auth config, email settings
  • Nuke Config: AWS Nuke YAML configuration (protected resources, settings, exclusions)
  • Reporting Config: Cost reporting group configuration

All tables use:

  • PAY_PER_REQUEST billing mode
  • Point-in-time recovery enabled
  • Customer-managed KMS encryption
  • Deletion protection enabled in production mode

Parameters: Namespace

Source: source/infrastructure/lib/isb-data-resources.ts


4. Compute Stack (InnovationSandbox-Compute)

Deploys to: Hub account

Purpose: The main operational stack containing the API, frontend, event processing, account cleanup, and observability infrastructure.

Major components:

ComponentTypeSource File
IsbRestApiAPI Gateway REST API + WAFcomponents/api/rest-api-all.ts
CloudFrontUiApiCloudFront + S3 frontendcomponents/cloudfront/cloudfront-ui-api.ts
ISBEventBusEventBridge custom event buscomponents/events/isb-internal-core.ts
AccountCleanerStep Functions + CodeBuildcomponents/account-cleaner/account-cleaner.ts
IntermediateRoleIAM Role for cross-accounthelpers/isb-roles.ts
LeaseMonitoringLambdaScheduled Lambdacomponents/account-management/lease-monitoring-lambda.ts
AccountLifecycleManagementLambdaEvent-driven Lambdacomponents/account-management/account-lifecycle-management-lambda.ts
AccountDriftMonitoringLambdaScheduled Lambdacomponents/account-management/account-drift-monitoring-lambda.ts
EmailNotificationLambdaEvent-driven Lambdacomponents/notification/email-notification.ts
CostReportingLambdaReporting Lambdacomponents/observability/cost-reporting-lambda.ts
GroupCostReportingLambdaReporting Lambdacomponents/observability/group-cost-reporting-lambda.ts
LogArchivingLog managementcomponents/observability/log-archiving.ts
LogInsightsQueriesPre-built queriescomponents/observability/log-insights-queries.ts
ApplicationInsightsCloudWatch App Insightscomponents/observability/app-insights.ts

Parameters: Namespace, OrgMgtAccountId, IdcAccountId, AllowListedIPRanges, UseStableTagging, AcceptSolutionTermsOfUse

Source: source/infrastructure/lib/isb-compute-resources.ts


5. SandboxAccount Stack (via StackSet)

Deploys to: Every account in the sandbox OU (auto-deployed)

Purpose: Creates the IAM role that CodeBuild assumes during AWS Nuke cleanup.

Resources: Single IAM Role ({namespace}_IsbCleanupRole) with permissions to delete resources in the account, assumed by the hub account's IntermediateRole.

Source: source/infrastructure/lib/isb-sandbox-account-resources.ts, isb-sandbox-account-stack.ts


Lambda Function Catalog

API Layer

FunctionSourceTriggerPurpose
AuthorizerLambdaFunctionlambdas/api/authorizer/API Gateway Request AuthorizerJWT validation and role-based access control
SsoHandlerlambdas/api/sso-handler/API Gateway /auth/{action+} (no auth)SAML SSO login/logout, JWT token issuance
LeasesLambdaFunctionlambdas/api/leases/API Gateway /leases/*CRUD for leases, approval, freeze/unfreeze, terminate
LeaseTemplatesLambdaFunctionlambdas/api/lease-templates/API Gateway /leaseTemplates/*CRUD for lease templates
AccountsLambdaFunctionlambdas/api/accounts/API Gateway /accounts/*Account pool management, registration
ConfigurationsLambdaFunctionlambdas/api/configurations/API Gateway /configurationsGlobal/nuke/reporting config read/write

Account Management Layer

FunctionSourceTriggerPurpose
AccountLifecycleManagementLambdalambdas/account-management/account-lifecycle-management/EventBridge via SQSResponds to lease events; moves accounts between OUs, manages IDC assignments
LeaseMonitoringLambdalambdas/account-management/lease-monitoring/EventBridge scheduled ruleChecks budgets and durations for active/frozen leases, publishes alerts
AccountDriftMonitoringLambdalambdas/account-management/account-drift-monitoring/EventBridge scheduled ruleDetects drift in sandbox account configurations

Cleanup Layer

FunctionSourceTriggerPurpose
InitializeCleanupLambdalambdas/account-cleanup/initialize-cleanup/Step Functions invokeValidates cleanup preconditions, loads config, prevents duplicate cleanups

Notification Layer

FunctionSourceTriggerPurpose
EmailNotificationLambdalambdas/notification/email-notification/EventBridge via SQSSends SES emails for lease events (created, approved, denied, alerts, etc.)

Observability Layer

FunctionSourceTriggerPurpose
CostReportingLambdalambdas/metrics/cost-reporting/ScheduledPer-account cost reporting via Cost Explorer
GroupCostReportingLambdalambdas/metrics/group-cost-reporting/ScheduledAggregated cost reporting by cost report group
DeploymentSummaryHeartbeatlambdas/metrics/deployment-summary-heartbeat/ScheduledAnonymized metrics for AWS Solutions
LogArchivingLambdalambdas/metrics/log-archiving/ScheduledArchives logs to S3
LogSubscriberLambdalambdas/metrics/log-subscriber/Custom ResourceManages CloudWatch log subscriptions

Custom Resource Layer

FunctionSourceTriggerPurpose
IdcConfigurerLambdalambdas/custom-resources/idc-configurer/CloudFormationCreates IDC groups and permission sets
DeploymentUUIDLambdalambdas/custom-resources/deployment-uuid/CloudFormationGenerates unique deployment ID
CostAllocationTagActivatorlambdas/custom-resources/cost-allocation-tag-activator/CloudFormationActivates cost allocation tags
SharedJsonParamParserlambdas/custom-resources/shared-json-param-parser/CloudFormationParses shared SSM parameters from other stacks
JwtSecretRotatorlambdas/helpers/secret-rotator/Secrets Manager rotation scheduleRotates JWT signing secret every 30 days

Runtime: All Lambdas are TypeScript compiled to ES modules, running on Node.js 22.


REST API Endpoints

API Gateway Structure

The API is exposed through CloudFront at /api/*, with a CloudFront Function stripping the /api prefix before forwarding to API Gateway. Authentication uses a custom Request Authorizer that validates JWT tokens.

MethodPathRolesLambdaPurpose
GET/leasesUser, Manager, AdminLeasesList leases
POST/leasesUser, Manager, AdminLeasesCreate lease
GET/leases/{leaseId}User, Manager, AdminLeasesGet lease details
PATCH/leases/{leaseId}Manager, AdminLeasesUpdate lease
POST/leases/{leaseId}/reviewManager, AdminLeasesApprove/deny lease
POST/leases/{leaseId}/terminateManager, AdminLeasesTerminate lease
POST/leases/{leaseId}/freezeManager, AdminLeasesFreeze lease
POST/leases/{leaseId}/unfreezeManager, AdminLeasesUnfreeze lease
GET/leaseTemplatesUser, Manager, AdminLeaseTemplatesList templates
POST/leaseTemplatesManager, AdminLeaseTemplatesCreate template
GET/leaseTemplates/{uuid}User, Manager, AdminLeaseTemplatesGet template
PUT/leaseTemplates/{uuid}Manager, AdminLeaseTemplatesUpdate template
DELETE/leaseTemplates/{uuid}Manager, AdminLeaseTemplatesDelete template
GET/accountsAdminAccountsList pool accounts
POST/accountsAdminAccountsRegister accounts
GET/accounts/{id}AdminAccountsGet account details
POST/accounts/{id}/retryCleanupAdminAccountsRetry failed cleanup
POST/accounts/{id}/ejectAdminAccountsEject account from pool
GET/accounts/unregisteredAdminAccountsList unregistered accounts in sandbox OUs
GET/configurationsUser, Manager, AdminConfigurationsRead AppConfig settings
GET/POST/auth/{action+}(No auth)SSO HandlerSAML SSO login/logout/callback

WAF rules: IP allowlist, rate limiting (200 req/min per IP), AWS Managed Rules (Common, IP Reputation, Anonymous IP).

Source: source/lambdas/api/authorizer/src/authorization-map.ts, source/infrastructure/lib/components/api/


Event-Driven Architecture

EventBridge Event Catalog

All events flow through the ISBEventBus custom EventBridge bus, which has a DLQ and logs all events to CloudWatch.

Event DetailTypeSourceTriggerConsumers
LeaseRequestedleases-apiPOST /leases (pending approval)Email notification
LeaseApprovedleases-apiApproval or auto-approveAccount Lifecycle Manager, Email, Deployer satellite
LeaseDeniedleases-apiManager denialEmail notification
LeaseTerminatedleases-apiManual terminationAccount Lifecycle Manager, Email, Costs satellite
LeaseFrozenleases-apiFreeze requestAccount Lifecycle Manager, Email
LeaseUnfrozenleases-apiUnfreeze requestAccount Lifecycle Manager, Email
LeaseBudgetExceededlease-monitoringCost exceeds maxSpendAccount Lifecycle Manager, Email
LeaseExpiredlease-monitoringDuration exceededAccount Lifecycle Manager, Email
LeaseBudgetThresholdAlertlease-monitoringCost crosses thresholdEmail notification
LeaseDurationThresholdAlertlease-monitoringTime threshold breachedEmail notification
LeaseFreezingThresholdAlertlease-monitoring90% budget thresholdAccount Lifecycle Manager (auto-freeze)
CleanAccountRequestaccount-lifecycle-managerLease terminal stateAccount Cleaner Step Function
AccountCleanupSucceededaccount-cleanerAWS Nuke successAccount Lifecycle Manager
AccountCleanupFailedaccount-cleanerAWS Nuke failureAccount Lifecycle Manager
AccountQuarantinedaccount-lifecycle-managerCleanup exhausted retriesEmail notification
AccountDriftDetectedaccount-drift-monitoringConfig drift foundAccount Lifecycle Manager
GroupCostReportGeneratedgroup-cost-reportingScheduled reportEmail notification
GroupCostReportGeneratedFailuregroup-cost-reportingReport generation failedEmail notification

Source: source/common/events/index.ts, source/common/events/*.ts


Data Schemas

LeaseTable Schema (Zod-validated)

The lease schema uses a discriminated union based on status:

LeaseKey: { userEmail: string (email), uuid: string (UUID) }

PendingLease: LeaseKey + {
status: "PendingApproval",
originalLeaseTemplateUuid, originalLeaseTemplateName,
maxSpend?, leaseDurationInHours?, budgetThresholds?, durationThresholds?,
costReportGroup?, comments?, createdBy?,
versionNumber, createdDate, lastModifiedDate
}

ApprovalDeniedLease: PendingLease + {
status: "ApprovalDenied",
ttl: number (Unix timestamp for DynamoDB TTL)
}

MonitoredLease (Active | Frozen): PendingLease + {
status: "Active" | "Frozen",
awsAccountId, approvedBy (email | "AUTO_APPROVED"),
startDate (ISO 8601), expirationDate? (ISO 8601),
lastCheckedDate (ISO 8601), totalCostAccrued: number
}

ExpiredLease: MonitoredLease + {
status: "Expired" | "BudgetExceeded" | "ManuallyTerminated" | "AccountQuarantined" | "Ejected",
endDate (ISO 8601), ttl: number
}

Source: source/common/data/lease/lease.ts

SandboxAccountTable Schema

SandboxAccount: {
awsAccountId: string (12 digits),
email?: string,
name?: string (max 50),
status: "Available" | "Active" | "CleanUp" | "Quarantine" | "Frozen",
driftAtLastScan?: boolean,
cleanupExecutionContext?: {
stateMachineExecutionArn: string,
stateMachineExecutionStartTime: string (ISO 8601)
},
versionNumber, createdDate, lastModifiedDate
}

Source: source/common/data/sandbox-account/sandbox-account.ts

LeaseTemplateTable Schema

LeaseTemplate: {
uuid: string (UUID),
name: string (1-50 chars),
description?: string,
requiresApproval: boolean,
createdBy: string (email),
visibility: "PUBLIC" | "PRIVATE",
maxSpend?: number (> 0),
budgetThresholds?: [{ dollarsSpent: number, action: "ALERT" | "FREEZE_ACCOUNT" }],
leaseDurationInHours?: number (> 0),
durationThresholds?: [{ hoursRemaining: number, action: "ALERT" | "FREEZE_ACCOUNT" }],
costReportGroup?: string (1-50 chars),
versionNumber, createdDate, lastModifiedDate
}

Source: source/common/data/lease-template/lease-template.ts


Cross-Account Role Chain

The ISB uses a two-hop role chain for cross-account operations:

Each role in the chain trusts only the IntermediateRole, which in turn only allows assumption by specific Lambda execution roles. The IntermediateRole ARN is verified via aws:PrincipalArn conditions.


Configuration via AppConfig

ISB uses AWS AppConfig for runtime configuration with three profiles:

Global Config (global-config.yaml):

  • maintenanceMode: boolean (blocks new lease requests)
  • termsOfService: multi-line text displayed during lease request
  • leases.requireMaxBudget/maxBudget/requireMaxDuration/maxDurationHours/maxLeasesPerUser/ttl
  • cleanup.numberOfFailedAttemptsToCancelCleanup/waitBeforeRetryFailedAttemptSeconds/numberOfSuccessfulAttemptsToFinishCleanup/waitBeforeRerunSuccessfulAttemptSeconds
  • auth.idpSignInUrl/idpSignOutUrl/idpAudience/webAppUrl/awsAccessPortalUrl/sessionDurationInMinutes
  • notification.emailFrom

Nuke Config (nuke-config.yaml):

  • AWS Nuke YAML with region list, settings, resource type exclusions, blocklist, and per-account filters
  • Placeholders %CLEANUP_ACCOUNT_ID%, %HUB_ACCOUNT_ID%, %CLEANUP_ROLE_NAME% are replaced at runtime

Reporting Config (reporting-config.yaml):

  • Cost report group settings

Source: source/infrastructure/lib/components/config/


Security Architecture

Authentication Flow

ISB uses SAML 2.0 SSO with IAM Identity Center, not Cognito:

  1. User accesses CloudFront URL
  2. Frontend checks for JWT token
  3. If no token, redirects to IAM Identity Center sign-in URL (SAML 2.0)
  4. User authenticates with Identity Center
  5. SAML assertion sent to /auth/saml/callback endpoint
  6. SSO Handler Lambda validates SAML assertion against stored IDP certificate
  7. Lambda issues signed JWT token (signed with rotating Secrets Manager secret)
  8. JWT stored in browser, sent as Authorization: Bearer header
  9. Request Authorizer Lambda validates JWT signature and extracts roles

Roles: Admin, Manager, User (mapped from IDC group membership)

WAF Protection

The REST API is protected by AWS WAF v2 with:

  • IP allowlist (configurable CIDR ranges)
  • Rate limiting: 200 requests per 60 seconds per IP (via X-Forwarded-For)
  • AWS Managed Rules: Common Rule Set, Amazon IP Reputation List, Anonymous IP List

KMS Encryption

A single customer-managed KMS key per namespace encrypts:

  • All three DynamoDB tables
  • EventBridge event bus
  • SQS queues (including DLQs)
  • S3 buckets (frontend assets, logging)
  • Secrets Manager secrets (JWT secret, IDP certificate)
  • CloudWatch Logs

Deployment

From Source

# Initialize environment
npm run env:init # Creates .env file
# Edit .env with required values

# Bootstrap CDK
npm run bootstrap

# Deploy all stacks in order
npm run deploy:all
# OR individually:
npm run deploy:account-pool
npm run deploy:idc
npm run deploy:data
npm run deploy:compute

Key Environment Variables

VariableDescription
HUB_ACCOUNT_IDAWS account ID for the hub/compute stack
ORG_MGT_ACCOUNT_IDOrganizations management account ID
IDC_ACCOUNT_IDIAM Identity Center account ID
NAMESPACEStack prefix (e.g., ndx)
PARENT_OU_IDParent OU or root ID for sandbox OU creation
AWS_REGIONSComma-separated list of managed regions
IDENTITY_STORE_IDIdentity Center store ID (d-xxxxxxxxxx)
SSO_INSTANCE_ARNSSO instance ARN
ADMIN/MANAGER/USER_GROUP_NAMEIDC group names
DEPLOYMENT_MODEdev or prod (affects deletion protection)

Source: package.json (root)


Monorepo Structure

root/
package.json # Orchestration scripts, workspace config
source/
common/ # Shared libraries (Zod schemas, events, SDK clients)
data/ # DynamoDB entity schemas and stores
events/ # EventBridge event type definitions
isb-services/ # Business logic services
lambda/ # Middleware bundles, env schemas
sdk-clients/ # Typed AWS SDK client wrappers
frontend/ # React SPA (see 12-isb-frontend.md)
infrastructure/ # CDK stacks and constructs
lib/
components/ # Reusable CDK constructs
account-cleaner/ # Step Function + CodeBuild
account-management/ # Lifecycle, monitoring, drift Lambdas
api/ # REST API resource definitions
cloudfront/ # CloudFront + S3 hosting
config/ # AppConfig setup and defaults
events/ # EventBridge bus and rules
notification/ # Email notification Lambda
observability/ # Logging, metrics, dashboards
service-control-policies/ # SCP JSON definitions
helpers/ # CDK utilities, role helpers, policy generators
lambdas/ # Lambda handler code
api/ # API handlers (leases, templates, accounts, auth)
account-cleanup/ # Initialize cleanup handler
account-management/ # Lifecycle, monitoring, drift handlers
custom-resources/ # CFN custom resource handlers
metrics/ # Cost reporting, log archiving
notification/ # Email handler
layers/ # Lambda layers (shared dependencies)
deployment/ # CloudFormation distributable build scripts
scripts/ # Repository maintenance scripts


Generated from source analysis. See 00-repo-inventory.md for full inventory.