Putting it all together
Now that we've seen the various parts of the application plane in isolation, let's put it all together. Please create the following file in the /lib directory of your CDK app and name it app-plane.ts. Now open that file and paste the following contents into it:
import * as sbt from '@cdklabs/sbt-aws';
import * as cdk from 'aws-cdk-lib';
import { EventBus } from 'aws-cdk-lib/aws-events';
import { PolicyDocument, PolicyStatement, Effect } from 'aws-cdk-lib/aws-iam';
export interface AppPlaneProps extends cdk.StackProps {
eventManager: sbt.IEventManager;
}
export class AppPlaneStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props: AppPlaneProps) {
super(scope, id, props);
const provisioningScriptJobProps: sbt.TenantLifecycleScriptJobProps = {
permissions: new PolicyDocument({
statements: [
new PolicyStatement({
actions: [
'cloudformation:CreateStack',
'cloudformation:DescribeStacks',
's3:CreateBucket',
],
resources: ['*'],
effect: Effect.ALLOW,
}),
],
}),
script: `
echo "starting..."
# note that this template.yaml is being created here, but
# it could just as easily be pulled in from an S3 bucket.
cat > template.json << EndOfMessage
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": { "MyBucket":{ "Type": "AWS::S3::Bucket" }},
"Outputs": { "S3Bucket": { "Value": { "Ref": "MyBucket" }}}
}
EndOfMessage
echo "tenantId: $tenantId"
echo "tier: $tier"
aws cloudformation create-stack --stack-name "tenantTemplateStack-\${tenantId}" --template-body "file://template.json"
aws cloudformation wait stack-create-complete --stack-name "tenantTemplateStack-\${tenantId}"
export tenantS3Bucket=$(aws cloudformation describe-stacks --stack-name "tenantTemplateStack-\${tenantId}" | jq -r '.Stacks[0].Outputs[0].OutputValue')
export someOtherVariable="this is a test"
echo $tenantS3Bucket
export tenantConfig=$(jq --arg SAAS_APP_USERPOOL_ID "MY_SAAS_APP_USERPOOL_ID" \
--arg SAAS_APP_CLIENT_ID "MY_SAAS_APP_CLIENT_ID" \
--arg API_GATEWAY_URL "MY_API_GATEWAY_URL" \
-n '{"userPoolId":$SAAS_APP_USERPOOL_ID,"appClientId":$SAAS_APP_CLIENT_ID,"apiGatewayUrl":$API_GATEWAY_URL}')
echo $tenantConfig
export tenantStatus="created"
echo "done!"
`,
environmentStringVariablesFromIncomingEvent: ['tenantId', 'tier'],
environmentVariablesToOutgoingEvent: [
'tenantS3Bucket',
'someOtherVariable',
'tenantConfig',
'tenantStatus',
],
scriptEnvironmentVariables: {
TEST: 'test',
},
eventManager: props.eventManager,
};
const provisioningJobScript: sbt.ProvisioningScriptJob = new sbt.ProvisioningScriptJob(
this,
'provisioningJobScript',
provisioningScriptJobProps
);
new sbt.CoreApplicationPlane(this, 'CoreApplicationPlane', {
eventManager: eventManager,
scriptJobs: [provisioningJobScript],
});
}
}
Although this looks like a lot of code, it's still very few constructs. Now that we've defined our app plane, let's again open up the hello-cdk.ts file in the bin directory of your CDK app. Once open, uncomment each commented line. The final file should look like this:
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { ControlPlaneStack } from '../lib/control-plane';
import { AppPlaneStack } from '../lib/app-plane';
const app = new cdk.App();
const controlPlaneStack = new ControlPlaneStack(app, 'ControlPlaneStack');
const appPlaneStack = new AppPlaneStack(app, 'AppPlaneStack', {
eventManager: controlPlaneStack.eventManager,
});
Once done, ensure all files are saved, and let's deploy the solution again, but this time we'll include the application plane:
npm run build
cdk deploy ControlPlaneStack AppPlaneStack