Hello, AWS CDK!

This topic walks you through creating and deploying your first AWS CDK app.

Setup

Prerequisites

Node.js (>= 8.11.x) - required for the command-line toolkit and language bindings.

AWS CLI - recommended in general, and can be used to setup the credentials and region for your AWS account, which must be specified to use the toolkit. See credentials for information on using the AWS CLI to set your credentials.

Install the command-line toolkit

The toolkit can be installed via npm as follows:

npm install -g aws-cdk

You can run this command to see the currently installed version of the toolkit (This guide is aligned with 0.8.2):

cdk --version

Initializing the Project

Note

This guide walks you through the process of creating a AWS CDK project step-by-step to explain some of the reasoning and details behind the project structure and tools. It is also possible to use the cdk init command to get started quickly from a project template in supported languages.

Create an empty project structure for the AWS 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 # creates package.json

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/cdk as a Dependency

Install the AWS CDK core library (@aws-cdk/cdk). This library includes the basic classes needed to write AWS CDK stacks and apps.

Install the @aws-cdk/cdk package:

npm install @aws-cdk/cdk

Install the @aws-cdk/cdk package and the @types/node (the latter is needed because we reference process.argv in our code):

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

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

<dependencies>
    <dependency>
        <groupId>software.amazon.awscdk</groupId>
        <artifactId>cdk</artifactId>
        <version><!-- cdk-version --></version>
    </dependency>
</dependencies>

Define the AWS CDK App

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

In index.js:

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

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

process.stdout.write(new MyApp(process.argv).run());

In index.ts:

import cdk = require('@aws-cdk/cdk');

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

process.stdout.write(new MyApp(process.argv).run());

In src/main/java/com/acme/MyApp.java:

package com.acme;

import software.amazon.awscdk.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 AWS CDK Toolkit to interact with your app.

Compile the 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, AWS CDK app.

Configure the AWS CDK Toolkit

Use the AWS 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 AWS CDK toolkit needs to know how to execute your AWS CDK app. It requires that the --app command-line option points to an executable program that adheres 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"
}

Specify a CLASSPATH, which contains both the compiled code and dependencies, to execute the Java program.

Use maven-dependency-plugin in your pom.xml file to produce the file .classpath.txt whenever the project is compiled:

<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 that .classpath.txt exists:

mvn compile
ls .classpath.txt

Create a shim app.sh to execute the AWS CDK Java app:

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

Define the --app option in cdk.json:

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

List the Stacks in the App

Use the AWS CDK toolkit’s ls command to list the stacks in the app.

cdk ls -l

The result is an empty array:

[]

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

Define a Stack

Define a stack and add it to the app.

In index.js:

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

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

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

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

process.stdout.write(new MyApp(process.argv).run());

In index.ts:

import cdk = require('@aws-cdk/cdk');

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

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

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

process.stdout.write(new MyApp(process.argv).run());

In src/main/java/com/acme/MyStack.java:

package com.acme;

import software.amazon.awscdk.App;
import software.amazon.awscdk.Stack;

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

In src/main/java/com/acme/MyApp.java:

package com.acme;

import software.amazon.awscdk.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 the arguments: parent, id, and props. This is the signature for every class in the AWS CDK framework. These classes are called “constructs” and they are composed together into 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 Environmental Context for more information.
  • id is a string that locally identifies this 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:

Nothing to compile.
npm run build
mvn compile

Run cdk ls to see that the app includes a single stack:

cdk ls -l
-
    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. See Environments and Authentication for more details on how to associate stacks to environments.

Define an Amazon S3 Bucket

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 Amazon S3 bucket.

Install the @aws-cdk/aws-s3 package:

npm install @aws-cdk/aws-s3
npm install @aws-cdk/aws-s3

Edit your pom.xml file:

<dependency>
    <groupId>software.amazon.awscdk</groupId>
    <artifactId>s3</artifactId>
    <version><!-- cdk-version --></version>
</dependency>

Next, define an Amazon S3 bucket in the stack. Amazon S3 buckets are represented by the Bucket class:

In index.js:

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

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

        new s3.Bucket(this, 'MyFirstBucket', {
            versioned: true
        });
    }
}

In index.ts:

import cdk = require('@aws-cdk/cdk');
import s3 = require('@aws-cdk/aws-s3');

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

        new s3.Bucket(this, 'MyFirstBucket', {
            versioned: true
        });
    }
}

In src/main/java/com/acme/MyStack.java:

package com.acme;

import software.amazon.awscdk.App;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.services.s3.Bucket;
import software.amazon.awscdk.services.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:

  • Bucket is a construct. This means it’s initialization signature has parent, id, and props. In this case, the bucket is an immediate child of MyStack.
  • MyFirstBucket is the logical id of the bucket construct, not the physical name of the S3 bucket. The logical ID is used to uniquely identify resources in your stack across deployments. See Logical IDs for more details on how to work with logical IDs. To specify a physical name for your bucket, you can set the bucketName property when you define your bucket.
  • Since the bucket’s versioned property is true, versioning is enabled on the bucket.

Compile your program:

Nothing to compile.
npm run build
mvn compile

Synthesize an AWS CloudFormation Template

Synthesize a AWS CloudFormation template for the stack:

cdk synth hello-cdk

Note

Since the AWS CDK app only contains a single stack, you can omit hello-cdk.

This command executes the AWS CDK app and synthesize an AWS CloudFormation template for the hello-cdk stack:

Resources:
    MyFirstBucketB8884501:
        Type: 'AWS::S3::Bucket'
        Properties:
            VersioningConfiguration:
                Status: Enabled
    CDKMetadata:
        Type: 'AWS::CDK::Metadata'
        Properties:
            Modules: # ...

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

Note

The AWS::CDK::Metadata resource was automatically added to your template by the toolkit. This allows us to learn which libraries were used in your stack. See Version Reporting for more details and how to opt-out.

Deploying the Stack

Use cdk deploy to deploy the stack:

cdk deploy

The deploy command synthesizes an AWS CloudFormation template from the stack and then invokes the AWS CloudFormation create/update API to deploy it into your AWS account. The command displays information as it progresses.

Modifying the Code

Configure the 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(BucketEncryption.KmsManaged)
        .build());

Compile the program:

Nothing to compile.
npm run build
mvn compile

Preparing for Deployment

Before you deploy the updated stack, use the cdk diff command to evaluate the difference between the AWS CDK app and the deployed stack:

cdk diff

The toolkit queries your AWS account for the current AWS CloudFormation template for the hello-cdk stack, and compares the result with the template synthesized from the app. The output should look like the following:

[~] 🛠 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 see that the bucket is not going to be replaced but rather updated (“Updating MyFirstBucketB8884501”).

Run cdk deploy to update the stack:

cdk deploy

The toolkit updates the bucket configuration to enable server-side KMS encryption for the bucket:

⏳  Starting deployment of stack hello-cdk...
[0/2] UPDATE_IN_PROGRESS  [AWS::S3::Bucket] MyFirstBucketB8884501
[1/2] UPDATE_COMPLETE     [AWS::S3::Bucket] MyFirstBucketB8884501
[1/2] UPDATE_COMPLETE_CLEANUP_IN_PROGRESS  [AWS::CloudFormation::Stack] hello-cdk
[2/2] UPDATE_COMPLETE     [AWS::CloudFormation::Stack] hello-cdk
✅  Deployment of stack hello-cdk completed successfully

What Next?