Skip to main content

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