Hello, AWS CDK!

This topic will walk you through creating and deploying your first CDK app.

The following instructions assume that you have already installed the CDK on your system. To verify, run the following command:

cdk --version
|cdk-version|

Initialize the project

In this section we will create an empty project structure for your CDK app.

Create an empty source-controlled directory for your project and an initial npm package.json file:

mkdir hello-cdk
cd hello-cdk
git init
npm init -y

Create an empty source-controlled directory for your project and an initial npm package.json file:

mkdir hello-cdk
cd hello-cdk
git init
npm init -y # creates package.json

Create a minimal tsconfig.json:

{
    "compilerOptions": {
        "target": "es2018",
        "module": "commonjs"
    }
}

Setup TypeScript build commands in package.json:

{
    "scripts": {
        "build": "tsc",
        "watch": "tsc -w"
    }
}

Use your favorite IDE to create a Maven-based empty Java 8 project.

Set the Java source and target to 1.8 in your pom.xml file:

<!-- pom.xml -->
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

Add @aws-cdk/core as a dependency

Now, we will install the CDK core library (_aws-cdk_core). This library includes the basic classes needed to write CDK stacks and apps.

Use y-npm to install the @aws-cdk/core package:

y-npm install @aws-cdk/core

Note

We are using y-npm instead of npm in order to install npm modules from the local npm repository included with your CDK installation. These instructions will change once the CDK will be published publically.

Use y-npm to install the @aws-cdk/core package. We also need @types/node since we will be using process.argv in our code:

y-npm install @aws-cdk/core @types/node

Note

We are using y-npm instead of npm in order to install npm modules from the local npm repository included with your CDK installation. These instructions will change once the CDK will be published publically.

Add the following to your project’s pom.xml file:

<repositories>
    <!-- Beta only: local CDK maven repo -->
    <repository>
        <id>cdk</id>
        <url>file:///${env.HOME}/.cdk/repo/maven</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>com.amazonaws.cdk</groupId>
        <artifactId>aws-cdk</artifactId>

        <!-- make sure to use the CDK installed version here (i.e. "0.7.3-beta") -->
        <version>0.7.3-beta</version>
    </dependency>
</dependencies>

Note

The <repository> section is only needed during private Beta.

Define your CDK app

CDK apps are modeled as classes which extend the _aws-cdk_core.App class. Let’s create our first, empty App:

// index.js

const cdk = require('@aws-cdk/core');

class MyFirstApp extends cdk.App {
    constructor(argv) {
        super(argv);
    }
}

process.stdout.write(new MyFirstApp(process.argv).run());
// index.ts

import * as cdk from '@aws-cdk/core';

class MyFirstApp extends cdk.App {
    constructor(argv: string[]) {
        super(argv);
    }
}

process.stdout.write(new MyFirstApp(process.argv).run());
// src/main/java/com/acme/MyApp.java

package com.acme;

import com.amazonaws.cdk.App;

import java.util.Arrays;
import java.util.List;

public class MyApp extends App {
    public MyApp(final List<String> argv) {
        super(argv);
    }

    public static void main(final String[] argv) {
        System.out.println(new MyApp(Arrays.asList(argv)).run());
    }
}

Note

The code that reads argv, runs the app and writes the output to STDOUT is currently needed in order to allow the CDK Toolkit to interact with your app. In the future the toolkit will include per-language shims that will remove this boilerplate.

Compile your code

If needed, compile the code:

No need to compile

To compile your program from .ts to .js:

npm run build

You can also use the watch command to continuously compile your code as it changes, so you don’t have to invoke the compiler explicitly:

# run in another terminal session
npm run watch

Compile your code using your IDE or via the command line via mvn:

mvn compile

This is it, you now created your first, alas empty, CDK app.

Configure CDK toolkit via cdk.json

We will now use the CDK toolkit to view the contents of this app.

Note

You must specify your default credentials and region to use the toolkit,

Use the AWS Command Line Interface aws configure command to specify your default credentials and region.

Important: make sure that you explicitly specify a region.

You can also set environment variables for your default credentials and region. Environment variables take precedence over settings in the credentials or config file.

  • AWS_ACCESS_KEY_ID specifies your access key
  • AWS_SECRET_ACCESS_KEY specifies your secret access key
  • AWS_DEFAULT_REGION specifies your default region

See Environment Variables in the CLI User Guide for details.

The CDK toolkit needs to know how to execute your CDK app. It requires that the --app command-line option will point to an executable program that adhere’s to the toolkit’s protocol (this is what the ARGV/STDOUT boilerplate implements). Alternatively to explicitly specifying --app every time you use the toolkit, we recommend that you create a cdk.json file at the root of your project directory:

Define the --app option in cdk.json to execute index.js using node:

{
  "app": "node index.js"
}

Define the --app option in cdk.json to execute index.js using node:

{
  "app": "node index.js"
}

In order to execute our Java program, we will need to specify a CLASSPATH which contains both our compiled code and dependencies. We will use maven-dependency-plugin to produce a file .classpath.txt whenever the project is compiled:

<!-- pom.xml -->

<build>
    <plugins>
        <!-- ... -->

        <!-- Emit the classpath to ./.classpath.txt so cdk.json can use it -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.8</version>
            <executions>
            <execution>
                <id>build-classpath</id>
                <phase>generate-sources</phase>
                <goals>
                    <goal>build-classpath</goal>
                </goals>
                <configuration>
                    <outputFile>.classpath.txt</outputFile>
                </configuration>
            </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Run mvn compile and verify the .classpath.txt exist:

mvn compile
ls .classpath.txt

Now, create a shim app.sh which will be used to execute our CDK Java app:

#!/bin/bash
exec java -cp target/classes:$(cat .classpath.txt) com.acme.MyApp app $@

And now we can define the -- app option in cdk.json:

{
  "app": "./app.sh"
}

List all stacks in your app

To list the stacks in this app, you can use the CDK toolkit’s ls command.

cdk ls

The result will be quite disappointing:

[]

An empty array, which makes sense, since our app still doesn’t have any stacks in it.

Define a stack

Now, let’s define our first stack and add it to our app.

// index.js
const cdk = require('@aws-cdk/core');

class MyFirstStack extends cdk.Stack {
    constructor(parent, id, props) {
        super(parent, id, props);
    }
}

class MyFirstApp extends cdk.App {
    constructor(argv) {
        super(argv);

        new MyFirstStack(this, 'hello-cdk');
    }
}

process.stdout.write(new MyFirstApp(process.argv).run());
// index.ts
import * as cdk from '@aws-cdk/core';

class MyFirstStack extends cdk.Stack {
    constructor(parent: cdk.App, id: string, props?: cdk.StackProps) {
        super(parent, id, props);
    }
}

class MyFirstApp extends cdk.App {
    constructor(argv: string[]) {
        super(argv);

        new MyFirstStack(this, 'hello-cdk');
    }
}

process.stdout.write(new MyFirstApp(process.argv).run());
// src/main/java/com/acme/MyStack.java

package com.acme;

import com.amazonaws.cdk.App;
import com.amazonaws.cdk.Stack;

public class MyStack extends Stack {
    public MyStack(final App parent, final String id) {
        super(parent, id);
    }
}

// src/main/java/com/acme/MyApp.java
package com.acme;

import com.amazonaws.cdk.App;

import java.util.Arrays;
import java.util.List;

public class MyApp extends App {
    public MyApp(final List<String> argv) {
        super(argv);

        new MyStack(this, "hello-cdk");
    }

    public static void main(final String[] argv) {
        System.out.println(new MyApp(Arrays.asList(argv)).run());
    }
}

The initializer signature of cdk.Stack includes three arguments: parent, id and props. This is the signature for every class in the CDK framework. These classes are called “constructs” and they are composed together to a tree:

  • parent represents the parent construct. By specifying the parent construct upon initialization, constructs can obtain contextual information when they are initialized. For example, the region a stack is deployed to can be obtained via a call to Stack.find(this).requireRegion(). See Context for more information.
  • id is a local string identifier of the construct within the tree. Constructs must have a unique ID amongst their siblings.
  • props is the set of initialization properties for this construct.

Compile your program and now, when we run cdk ls, the result shows that your app includes a single stack:

cdk ls
-
    name: hello-cdk
    environment:
        name: <your-account-id>/<your-default-region>
        account: '<your-account-id>'
        region: <your-default-region>

Notice that your stack has been automatically associated with the default AWS account and region configured in the AWS CLI.

Define a bucket in your stack

Now, what can we do with this app? Nothing yet. Our stack is still empty. So, there’s nothing to deploy. Let’s define an S3 bucket.

Install the @aws-cdk/s3 package:

y-npm install @aws-cdk/s3
y-npm install @aws-cdk/s3
During beta, we bundled all CDK modules into the aws-cdk Maven package, so there is no need to explicitly install the S3 library.

Now, let’s define an S3 bucket in our stack. S3 buckets are represented by the _aws-cdk_s3.Bucket class:

// index.js
const cdk = require('@aws-cdk/core');
const s3 = require('@aws-cdk/s3');

class MyFirstStack extends cdk.Stack {
    constructor(parent, id, props) {
        super(parent, id, props);

        new s3.Bucket(this, 'MyFirstBucket', {
            versioned: true
        });
    }
}
// index.ts
import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/s3';

class MyFirstStack extends cdk.Stack {
    constructor(parent: cdk.App, id: string, props?: cdk.StackProps) {
        super(parent, id, props);

        new s3.Bucket(this, 'MyFirstBucket', {
            versioned: true
        });
    }
}
// src/main/java/com/acme/MyStack.java
package com.acme;

import com.amazonaws.cdk.App;
import com.amazonaws.cdk.Stack;
import com.amazonaws.cdk.s3.Bucket;
import com.amazonaws.cdk.s3.BucketProps;

public class MyStack extends Stack {
    public MyStack(final App parent, final String id) {
        super(parent, id);

        new Bucket(this, "MyFirstBucket", BucketProps.builder()
                .withVersioned(true)
                .build());
    }
}

A few things to notice:

  • s3.Bucket is construct. This means it’s initialization signature will have parent, id and props. In this case, the bucket is an immediate child of MyStack, it’s id is ‘MyFirstBucket’.
  • We set the versioned property to true which defines the bucket with versioning enabled.

Synthesize a CloudFormation template

Now, that we have a bucket in our stack, we can ask the toolkit to synthesize a CloudFormation template for our stack:

cdk synth hello-cdk

This command will execute our CDK app and synthesize a CloudFormation template for the hello-cdk stack:

Resources:
    MyFirstBucketB8884501:
        Type: 'AWS::S3::Bucket'
        Properties:
            VersioningConfiguration:
                Status: Enabled

You can see that the stack contains an AWS::S3::Bucket resource with the desired versioning configuration.

Deploying our stack

To deploy our stack, use cdk deploy:

cdk deploy hello-cdk

The deploy command will synthesize a CloudFormation template from your stack and then invoke the CloudFormation create/update API to deploy it into your AWS account. Progress will be emitted to your console.

Modifying your code

Let’s configure our bucket to use KMS managed encryption:

new s3.Bucket(this, 'MyFirstBucket', {
    versioned: true,
    encryption: s3.BucketEncryption.KmsManaged
});
new s3.Bucket(this, 'MyFirstBucket', {
    versioned: true,
    encryption: s3.BucketEncryption.KmsManaged
});
new Bucket(this, "MyFirstBucket", BucketProps.builder()
        .withVersioned(true)
        .withEncryption("MANAGED")
        .build());

Preparing for deployment using cdk diff

Before we deploy our updated stack, we can use the **cdk diff* command to evaluate the difference between our CDK app and the deployed stack:

cdk diff hello-cdk
[~] 🛠 Updating MyFirstBucketB8884501 (type: AWS::S3::Bucket)
└─ [+] .BucketEncryption:
    └─ New value: {"ServerSideEncryptionConfiguration":[{"ServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms"}}]}

As you can see, the diff indicates that the ServerSideEncryptionConfiguration property of the bucket is now set to enable server-side encryption.

You can also that the bucket is not going to be replaced but rather updated (“Updating MyFirstBucketB8884501”).

Now, run cdk deploy to update your stack: