Getting Your Feet Wet with the CDK
Whenever I reacquaint myself with the possibility of using Lambda in a multi-region failover scenario, I consistently run across the AWS blog post here: https://aws.amazon.com/blogs/compute/building-a-multi-region-serverless-application-with-amazon-api-gateway-and-aws-lambda/
The CloudFormation code is all there; however, the AWS CDK is supposed to make things easier. I wanted to see how much easier using the CDK would be to re-create this scenario. In this article, I will go through code highlights, experiences with the CDK, and some tips I learned along the way.
All of the code for this article is located in GitHub here:
Requirements for Running The Code
In order to run the CDK sample code in your own account, you will need the following:
- An AWS account.
- Your own domain.
- Certificates created for the domain within multiple regions.
The original AWS blog post gives details on how to request certificates in multiple regions for the same domain. (AWS certificates are specific to a region)
Code Walkthrough
After cloning the repository above, the first point of interest is the file LambdaRegionDemo.ts. Here there are two interesting elements. The first is loading various CDK context variables. Those will be variables passed in from the command line to specify elements specific to your environment. There are also two variables that will determine what region “fails”, and what region is healthy. (status1 and status2 vars). The Route53 health check looks for the string OK. The lambda functions accept a variable of what to return to determine health.
The second interesting item to note is the multiple stacks used to create a Lambda in two different regions. See a depiction of the bootstrap typescript below.
All of the variables are read from the AWS CDK context object:
DemoLambdaStack.ts
The DemoLambdaStack takes care of instantiating a lambda function, a Route53 health check, and the Hosted Zone record set. (Note, this demo requires that you have your own domain setup within an AWS account.)
This is all vanilla typescript organization for the point of organizing all of the constructs that will be built by CloudFormation. In the next section, I will dive deeper into where the CDK started to show some cracks.
CDK missing pieces
I used version 1.45 of the CDK, and could not find a health check class for use in Route53. Hopefully by the time you read this, it will be added to the CDK; however, I used the “next level down”, which is the equivalent Cfn class. In this case it was CfnHealthCheck. In order to get some of the values, I used the original AWS article as a reference with the sample CloudFormation. This was my first hint that I would need to use the CloudFormation “Fn” classes, such as Fn.ref and Fn.join. The general flow for this process was:
- Examine the CloudFormation sample output.
- Find the corresponding CloudFormation function classes within the CDK.
- Run CDK synth to compare your output to sample CloudFormation output.
Here is a comparison of the CDK synth output for the health check, and the corresponding CDK code.
MyHealthCheck:
Type: AWS::Route53::HealthCheck
Properties:
HealthCheckConfig:
FailureThreshold: 1
FullyQualifiedDomainName:
Fn::Join:
- ""
- - Ref: helloWorldLambdaRestApi6825FB98
- .execute-api.us-east-1.
- Ref: AWS::URLSuffix
ResourcePath:
Fn::Join:
- ""
- - /
- Ref: helloWorldLam...od67DD79AF
- /hello
SearchString: OK
Type: HTTPS_STR_MATCH
HealthCheckTags:
- Key: Name
Value: us-east-1-healthcheck
Below is the corresponding CDK code:
let checkURL = Fn.join("", [restApi.restApiId,
".execute-api.",
region,
".",
Fn.ref("AWS::URLSuffix")]);let healthCheckIdentifier = region + "-healthcheck";this.healthCheck = new route53.CfnHealthCheck(stack,
"MyHealthCheck", {
healthCheckTags: [{
key: "Name",
value: healthCheckIdentifier
}],
healthCheckConfig: {
type: "HTTPS_STR_MATCH",
failureThreshold: 1,
fullyQualifiedDomainName: checkURL,
searchString: "OK",
resourcePath: Fn.join("", ["/",
restApi.deploymentStage.stageName,
"/hello"])
}
Both the CloudFormation YAML, in addition to the typescript code seem reasonably similar. This is where the CDK can have downsides. When a higher level class doesn’t exist, using the various “escape hatches” can be cryptic and cumbersome. The advantage with using the CDK however, is that you can hide complexity behind a class that can be re-used without knowing the lower level details.
AWS outlines some more of these escape hatches in this article: https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html
As long as there is an API exposed for the functionality you are looking for, you have a path to coding it within the CDK. It won’t always be simple however. The AWS CDK wins over Terraform in this respect that the CDK uses a real programming language, so you’re not stuck in a DSL of YAML.
Getting Hints — or Most of the Way
After the lambda is created, along with a Route53 health check, the final piece is a Route53 Record Set to tie everything together. The CDK has a class within Route53 ARecord. Unfortunately the ARecord class does not have a parameter to accept a health check. Again, I resorted to using the “next level down” class, which was the CfnRecordSet. I was a little unclear exactly what to use for some of the parameters. In order to get my 80% there, I used CDK synth command while using the ARecord higher level CDK class.
This gave me the benefit of having a CloudFormation reference to use when converting to use the raw CfnRecordSet. Obviously this is not ideal; however, getting CloudFormation hints from a reference implementation can jump the hurdle where AWS CDK documentation runs thin. Utilizing the CloudFormation documentation from AWS was also immensely useful.
Remembering the CDK is a CloudFormation generator
One important point to remember when using the CDK, is that the CDK is a CloudFormation generator at heart. I learned this lesson when attempting to pick apart a URL that was being generated. It can appear this URL is generated within the typescript CDK code; however, that is incorrect. Ultimately final URLs for endpoints are generated from the process of AWS deploying the CloudFormation stack.
One minor exception is with the SSM parameter store classes. This class is also available at synthesize time, which will read variables before the stack is deployed. This is available to the CDK Runtime Context similar to the variables used on the command line.
https://docs.aws.amazon.com/cdk/latest/guide/get_ssm_value.html
Running The Demo Code
In order to run the demo code, run the following:
- npm install
- npm run build
cdk deploy -c hostedZoneName=YOURDOTCOMADDRESS.com
-c hostedZoneId=ZYWQL6IKKZIWZ
-c certarn1=ARN-FOR-YOUR-HOSTED-ZONE-REGION1
-c certarn2=ARN-FOR-YOUR-HOSTED-ZONE-REGION2
-c status1=OK -c status2=FAIL FailoverTest-Region1 FailoverTest-Region2
After successful deployment, navigate to the following site in a web browser:
https://demo.YOURDOMAIN.com/hello
Now re-deploy with the same command above, and switch status1 and status2. After a minute or so, you will notice the return from the lambda states it is running in a different region.
Conclusion
So how do I view the CDK now that I have implemented something slightly beyond “Hello World”? The main points I found are:
- If deciding to the learn the CDK, make a conscious decision to learn CloudFormation as well. Knowing CloudFormation will help tremendously when the CDK comes up short.
- Remembering that the CDK is a veneer on top of CloudFormation generation. You can’t do runtime processing with CloudFormation outputs. (There are some deeper exceptions with custom resources — but that is beyond the scope of this article.)
- Don’t discount the CDK if there are some rough edges. If you are creating lots of AWS resources, hide some of the complexity behind another class, so developers within your organization can simply be abstracted from the complexity.
Reference
Please reference all code located within Github here: