Skip to content

Stacks and outputs

There are two CDK apps: server/cdk (everything AWS-side) and client/cdk (the web hosting). They are separate on purpose, because the provisioning job synthesizes the server app in CodeBuild, where the web apps’ build output doesn’t exist.

Stack CDK app Created by Contents
ControlPlaneStack server deploy.sh SBT CognitoAuth (system admin pool) and ControlPlane (HTTP API, tenant tables, EventBridge bus), and the bus watcher log
serverless-saas-bootstrap server deploy.sh Tenant mapping table, one API key per tier in SSM, versioned source bucket, provisioning and deprovisioning jobs
serverless-saas-tenant-template-pooled server deploy.sh Pooled tenant user pool, REST API, authorizer, usage plans, product, order and user services
serverless-saas-tenant-template-<tenantId> server provisioning job The same template for one Platinum tenant
serverless-saas-tenant-pipeline server deploy.sh CodePipeline, Step Functions state machine, CodeBuild project, list-stacks Lambda
serverless-saas-client client deploy-client.sh S3 and CloudFront for the admin console, the SaaS app and these docs; with DOMAIN_NAME, also the certificate and DNS records
Stack Output Example Used by
ControlPlaneStack controlPlaneAPIEndpoint https://w2g4jn8evg.execute-api.us-east-1.amazonaws.com/ Admin console, SaaS app, scripts
ControlPlaneIdpUserPoolId us-east-1_NJ8yJK7e9 Admin console, admin-token.sh
ControlPlaneIdpClientId 2b4l421t5f3rnkq16tk9m3f8ai Admin console, admin-token.sh
eventBridgeArn arn:aws:events:…:event-bus/… Bootstrap stack
serverless-saas-bootstrap SourceBucketName serverless-saas-source-<account>-<region> upload-source.sh, provisioning, pipeline
serverless-saas-tenant-template-* ApiGatewayUrl https://0xlsgzuyxh.execute-api.us-east-1.amazonaws.com/prod/ Provisioning (tenantConfig), scripts
TenantUserpoolId us-east-1_a1M1hAn8y Provisioning, dev scripts
UserPoolClientId q33critjq1ba1puje9nnrea78 Provisioning, tenant-token.sh
ProductTableName, OrderTableName dev-isolation-check.sh
serverless-saas-client AdminSiteUrl https://saas-admin.haibui.click You
AppSiteUrl https://saas.haibui.click You
DocsSiteUrl https://saas-docs.haibui.click You

Print a stack’s outputs:

Terminal window
aws cloudformation describe-stacks --stack-name serverless-saas-client \
--query 'Stacks[0].Outputs[].[OutputKey,OutputValue]' --output table

The server app reads these environment variables:

Variable Default Meaning
CDK_PARAM_SYSTEM_ADMIN_EMAIL (required) Email of the first system admin. deploy.sh sets it from its argument.
CDK_PARAM_TENANT_ID pooled Which tenant template stack to synthesize
CDK_PARAM_COMMIT_ID local Code version recorded in the mapping table and returned by /whoami
CDK_PARAM_WAVE_NUMBER 1 Pipeline wave for a new tenant stack
CDK_PARAM_LAMBDA_RESERVE_CONCURRENCY 5 for silo Reserved concurrency per function in a silo stack
CDK_PARAM_API_KEY_<TIER>_TIER built-in values Override the API key value of a tier

The client app reads CONTROL_PLANE_API_URL, ADMIN_USER_POOL_ID and ADMIN_APP_CLIENT_ID (set by deploy-client.sh), and optionally DOMAIN_NAME.

One row per tenant template stack, in serverless-saas-bootstrap:

{ "tenantId": "pooled", "stackName": "serverless-saas-tenant-template-pooled", "codeCommitId": "YTZTkJso…", "waveNumber": "1" }

Each stack writes its own row with a custom resource on create and update, and deletes it on delete.