Introduction#
In this post, I follow Elad's talk at re:invent 2018 and implement basic concepts such as build-int contructs and my-own constructs, setting up a CDK project, syntheize multiple stacks, apps.
Elad-Ben Israel - the creator of CDK joined Amazon in 2016. Then CDK first released in JUL 2019. Quoted from him: In early 2016, I joined a group of Amazon engineers who were exploring hardware utilization in one of the most important and large-scale services behind Amazon.com—Amazon Product Search. One piece of this solution required processing, in real time, all activity from Amazon.com to determine which products were being purchased...
CDK Concept#
CDK CLI Workflow#
Bootstrap#
cdk bootstrap provision resources required in specified region. This is required first before we can deploy any stack in a region.
cdk bootstrap aws://AWS_ACCOUNT_ID/REGION
Enviornment Variables#
we can refere to cdk environment variables in our stack code as below
new MyDevStack(app, 'dev', {env: {account: process.env.CDK_DEFAULT_ACCOUNT,region: process.env.CDK_DEFAULT_REGION}})
and we can provide/define them for a stack as well.
new MyDevStack(app, 'dev', {env: {account: 'MY_ACCOUNT_ID',region: 'REGION'}})
Init a Project#
cdk init --language typescript
Project Structure#
|--build|--hello-cdk.ts|--lib|--hello-cdk-stack.ts|--lambda|--handler.py|--cdk.out|--HelloCdkStack.template.json|--cdk.json
Synthesize Templates#
synthesize cloudformation templates. Then templates stored in cdk.out
cdk synth
the default cdk synth synthesize a ts file specified by cdk.json
"app": "npx ts-node --prefer-ts-exts bin/hello-cdk.ts"
we can override it by (this means we can write multiple apps)
cdk --app 'npx ts-node --prefer-ts-exts bin/my-app.ts' synth
Deploy Stacks#
if there is one stack
cdk deploy
deploy a specific stack in multiple stacks
cdk deploy "HelloCdkStack"
if deploy all stacks in the app
cdk deploy --all
Destroy Stacks#
if there is only one stack
cdk destroy
destroy a specific stack in multiple stacks
cdk destroy "HelloCdkStack"
destroy all stacks in the app
cdk destroy --all
App, Stack, and Construct#
create an app
// create an appconst app = new cdk.App()// a stack inside the appnew HelloCdkStack(app, 'HelloCdkStack', {env: {region: process.env.CDK_DEFAULT_REGION,account: process.env.CDK_DEFAULT_ACCOUNT}})
create an stack
export class HelloCdkStack extends Stack {constructor(scope: Construct, id: string, props?: StackProps) {super(scope, id, props)// sqsconst queue = new aws_sqs.Queue(this, 'HelloQueue', {queueName: 'HelloQueue'})// lambdaconst fn = new aws_lambda.Function(this, 'HelloFunction', {functionName: 'HelloLambda',runtime: aws_lambda.Runtime.PYTHON_3_8,code: aws_lambda.Code.fromAsset(path.join(__dirname, './../lambda')),handler: 'handler.handler'})// lambda event source sqs queuefn.addEventSource(new aws_lambda_event_sources.SqsEventSource(queue))}}
here aws_sqs is a L2 construct.
Create your own construct#
export interface QueueRecorderProps {inputQueue: aws_sqs.Queue}export class QueueRecorder extends Construct {constructor(scope: Construct, id: string, props: QueueRecorderProps) {super(scope, id)// lambdaconst fn = new aws_lambda.Function(this, 'HelloFunction', {functionName: 'HelloLambda',runtime: aws_lambda.Runtime.PYTHON_3_8,code: aws_lambda.Code.fromAsset(path.join(__dirname, './../lambda')),handler: 'handler.handler'})// lambda event source - sqs queuefn.addEventSource(new aws_lambda_event_sources.SqsEventSource(props.inputQueue))// dynamodb tableconst table = new aws_dynamodb.Table(this, 'HelloDynamoDb', {tableName: 'HelloTable',removalPolicy: RemovalPolicy.DESTROY,partitionKey: {name: 'id',type: aws_dynamodb.AttributeType.STRING}})// table name into lambda envfn.addEnvironment('TABLE_NAME', table.tableName)// grant lambda to write to tabletable.grantWriteData(fn.role!)}}
Refactor the code#
export class HelloCdkStack extends Stack {constructor(scope: Construct, id: string, props?: StackProps) {super(scope, id, props)// sqsconst queue = new aws_sqs.Queue(this, 'HelloQueue', {queueName: 'HelloQueue'})// queuerecorder constructnew QueueRecorder(this, 'QueueRecorder', {inputQueue: queue})}}
lambda write to a dynamodb table
def handler(event, context):"""lambda handler"""# loglogger.info("Hello Lambda")# tabledynamodb = boto3.resource("dynamodb")table = dynamodb.Table(os.environ['TABLE_NAME'])# put item to the tablefor record in event.Records:try:body = record['body']except:body = ''table.put_item(Item={'id': record['messageId'],'text': body})