Outline
All of this project's code can be found in the First Look monorepo on my GitHub.
Introduction
AWS Cloud Development Kit (CDK) is a software development framework for defining cloud infrastructure in code and provisioning it through AWS CloudFormation. It supports TypeScript, JavaScript, Python, Java, C#/.Net, and (almost) Go.
Developers can use one of the supported programming languages to define reusable cloud components known as Constructs that are composed together into Stacks and Apps.
Setup
Configure AWS CLI
Make sure you have the AWS CLI installed and an AWS account. For general use, aws configure
is recommended as the fastest way to set up your AWS CLI installation.
aws configure
When you enter this command, the AWS CLI prompts you for four pieces of information:
- Access key ID
- Secret access key
- AWS Region
- Output format
Go to My Security Credentials to find your Access Key ID, Secret Access Key, and default region. You can leave the output format blank.
AWS Access Key ID: <YOUR_ACCESS_KEY_ID>
AWS Secret Access Key: <YOUR_SECRET_ACCESS_KEY>
Default region name: <YOUR_REGION_NAME>
Default output format [None]:
Install CDK CLI
The aws-cdk
can be globally installed with npm
.
npm install -g aws-cdk
Check aws-cdk
version.
cdk --version
Output:
1.118.0 (build a4f0418)
Create Project Directory
Files will be generated based on the project name. This means you can't give your project a cute, personal name like ajcwebdev-cdk
which I always do in all my tutorials but okay fine AWS I'll play by your rules.
mkdir hello-cdk
cd hello-cdk
Initialize Project
cdk init app --language javascript
Output:
Applying project template app for javascript
# Welcome to your CDK JavaScript project!
This is a blank project for JavaScript development with CDK.
The `cdk.json` file tells the CDK Toolkit how to execute your app. The build step is not required when using JavaScript.
## Useful commands
* `npm run test` perform the jest unit tests
* `cdk deploy` deploy this stack to your default AWS account/region
* `cdk diff` compare deployed stack with current state
* `cdk synth` emits the synthesized CloudFormation template
Initializing a new git repository...
Executing npm install...
✅ All done!
List Stacks
cdk ls
Output:
HelloCdkStack
Project Structure
├── bin
│ └── hello-cdk.js
├── lib
│ └── hello-cdk-stack.js
├── test
│ └── hello-cdk-test.js
├── .gitignore
├── .npmignore
├── cdk.json
├── jest.config.js
├── package-lock.json
├── package.json
└── README.md
With one exception, our package.json
should be straight forward if you have worked with npm
before. It includes a few scripts and our dependencies.
{
"name": "hello-cdk",
"version": "0.1.0",
"bin": {
"hello-cdk": "bin/hello-cdk.js"
},
"scripts": {
"build": "echo \"The build step is not required when using JavaScript!\" && exit 0",
"cdk": "cdk",
"test": "jest"
},
"devDependencies": {
"@aws-cdk/assert": "1.118.0",
"aws-cdk": "1.118.0",
"jest": "^26.4.2"
},
"dependencies": {
"@aws-cdk/core": "1.118.0"
}
}
It also includes a less well known (at least to me) option, the bin
field. Some packages have one or more executable files that need to be installed into the PATH. The bin
field is a map of a command name to a local file name. On install, npm
will symlink that file into prefix/bin
for global installs, or ./node_modules/.bin/
for local installs.
CDK Configuration
Many features of the CDK Toolkit require one or more AWS CloudFormation templates be synthesized, which in turn requires running your application. cdk.json
uses a configuration option to specify the exact command necessary to run your app and is located in the main directory of your project.
{
"app": "node bin/hello-cdk.js",
"context": {
"@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
"@aws-cdk/core:enableStackNameDuplicates": "true",
"aws-cdk:enableDiffNoFail": "true",
"@aws-cdk/core:stackRelativeExports": "true",
"@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true,
"@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true,
"@aws-cdk/aws-kms:defaultKeyPolicies": true,
"@aws-cdk/aws-s3:grantWriteWithoutAcl": true,
"@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true,
"@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
"@aws-cdk/aws-efs:defaultEncryptionAtRest": true,
"@aws-cdk/aws-lambda:recognizeVersionProps": true,
"@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true
}
}
Your configuration option can be specified using the app
key. The CDK Toolkit provides an appropriate command when creating a new project with cdk init
. The CDK Toolkit looks for cdk.json
in the current working directory when attempting to run your app.
App Entry Point
Files referenced in bin
must start with #!/usr/bin/env node
to make sure the scripts aren't started without the node executable.
// bin/hello-cdk.js
#!/usr/bin/env node
const cdk = require('@aws-cdk/core')
const { HelloCdkStack } = require('../lib/hello-cdk-stack')
const app = new cdk.App()
new HelloCdkStack(
app, 'HelloCdkStack', {}
)
HelloCdkStack
The code that defines your stack goes inside the constructor
, under super
.
// lib/hello-cdk-stack.js
const cdk = require('@aws-cdk/core');
class HelloCdkStack extends cdk.Stack {
/**
*
* @param {cdk.Construct} scope
* @param {string} id
* @param {cdk.StackProps=} props
*/
constructor(scope, id, props) {
super(scope, id, props);
}
}
module.exports = { HelloCdkStack }
Add S3 Bucket
Right now the app doesn't do anything since the stack it contains doesn't define any resources. We can add an Amazon S3 bucket by installing the aws-cdk/aws-s3 module from the AWS Construct Library.
npm i @aws-cdk/aws-s3
Define S3 Bucket Construct
Inside your stack, initialize an s3
variable by importing it from @aws-cdk/aws-s3
with the CommonJS require()
syntax. Create MyFirstBucket
with the Bucket class.
// lib/hello-cdk-stack.js
const cdk = require('@aws-cdk/core');
const s3 = require('@aws-cdk/aws-s3');
class HelloCdkStack extends cdk.Stack {
constructor(scope, id, props) {
super(scope, id, props);
new s3.Bucket(this, 'MyFirstBucket', {
versioned: true
});
}
}
module.exports = { HelloCdkStack }
Our dependencies in package.json
now includes @aws-cdk/aws-s3
.
"dependencies": {
"@aws-cdk/aws-s3": "^1.101.0",
"@aws-cdk/core": "1.101.0"
}
Generate CloudFormation Template
Synthesize an AWS CloudFormation template for the app. Hey put that Moog away, not that kind of synth!
cdk synth
This will output a CloudFormation file that will be mostly gibberish if you've never seen a CloudFormation template before.
Resources:
MyFirstBucketB8884501:
Type: AWS::S3::Bucket
Properties:
VersioningConfiguration:
Status: Enabled
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Metadata:
aws:cdk:path: HelloCdkStack/MyFirstBucket/Resource
CDKMetadata:
Type: AWS::CDK::Metadata
Metadata:
aws:cdk:path: HelloCdkStack/CDKMetadata/Default
Condition: CDKMetadataAvailable
Conditions:
CDKMetadataAvailable:
Fn::Or:
- Fn::Or:
- Fn::Equals:
- Ref: AWS::Region
- af-south-1
- Fn::Equals:
- Ref: AWS::Region
- ap-east-1
- Fn::Equals:
- Ref: AWS::Region
- ap-northeast-1
- Fn::Equals:
- Ref: AWS::Region
- ap-northeast-2
- Fn::Equals:
- Ref: AWS::Region
- ap-south-1
- Fn::Equals:
- Ref: AWS::Region
- ap-southeast-1
- Fn::Equals:
- Ref: AWS::Region
- ap-southeast-2
- Fn::Equals:
- Ref: AWS::Region
- ca-central-1
- Fn::Equals:
- Ref: AWS::Region
- cn-north-1
- Fn::Equals:
- Ref: AWS::Region
- cn-northwest-1
- Fn::Or:
- Fn::Equals:
- Ref: AWS::Region
- eu-central-1
- Fn::Equals:
- Ref: AWS::Region
- eu-north-1
- Fn::Equals:
- Ref: AWS::Region
- eu-south-1
- Fn::Equals:
- Ref: AWS::Region
- eu-west-1
- Fn::Equals:
- Ref: AWS::Region
- eu-west-2
- Fn::Equals:
- Ref: AWS::Region
- eu-west-3
- Fn::Equals:
- Ref: AWS::Region
- me-south-1
- Fn::Equals:
- Ref: AWS::Region
- sa-east-1
- Fn::Equals:
- Ref: AWS::Region
- us-east-1
- Fn::Equals:
- Ref: AWS::Region
- us-east-2
- Fn::Or:
- Fn::Equals:
- Ref: AWS::Region
- us-west-1
- Fn::Equals:
- Ref: AWS::Region
- us-west-2
You'll eventually want to learn what all this junk means and how it works, but the reason so many libraries exist to abstract CloudFormation away or replace it is because it's a huge pain and no one wants to write it. We now treat it moreso as a compile target because compilers are the new frameworks.
Deploy Stack to AWS
We will use the AWS CDK Toolkit to deploy our project. However, since cdk synth
generates valid AWS CloudFormation templates we could take it and deploy it using the AWS CloudFormation console or other tools.
cdk deploy
Output with account-id
and resource-id
redacted:
HelloCdkStack: deploying...
HelloCdkStack: creating CloudFormation changeset...
✅ HelloCdkStack
Stack ARN:
arn:aws:cloudformation:us-west-1:1234:stack/HelloCdkStack/1234
We'll figure out what an ARN is/does in future articles, but basically it's an AWS NFT.
A non-fungible token is a unit of data that certifies a digital asset to be unique and therefore not interchangeable.