Hello, |cdk|!
This topic walks you through creating and deploying your first |cdk| app.
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 :ref:`credentials <credentials>` for your AWS account.
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 |version|):
cdk --version
Note
This guide walks you through the process of creating a |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 |cdk| app.
.. tabs:: .. group-tab:: JavaScript Create an empty source-controlled directory for your project and an initial npm **package.json** file: .. code-block:: sh mkdir hello-cdk cd hello-cdk git init npm init -y # creates package.json .. group-tab:: TypeScript Create an empty source-controlled directory for your project and an initial npm **package.json** file: .. code-block:: sh mkdir hello-cdk cd hello-cdk git init npm init -y # creates package.json Create a minimal **tsconfig.json**: .. code-block:: json { "compilerOptions": { "target": "es2018", "module": "commonjs" } } Setup TypeScript build commands in **package.json**: .. code-block:: json { "scripts": { "build": "tsc", "watch": "tsc -w" } } .. group-tab:: Java 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: .. code-block:: xml <!-- 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>
Install the |cdk| core library (:py:mod:`@aws-cdk/cdk`). This library includes the basic classes needed to write |cdk| stacks and apps.
.. tabs:: .. group-tab:: JavaScript Install the **@aws-cdk/cdk** package: .. code-block:: sh npm install @aws-cdk/cdk .. group-tab:: TypeScript Install the **@aws-cdk/cdk** package and the **@types/node** (the latter is needed because we reference **process.argv** in our code): .. code-block:: sh npm install @aws-cdk/cdk @types/node .. group-tab:: Java Add the following to your project's `pom.xml` file: .. code-block:: xml <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>|cdk-version|</version> </dependency> </dependencies>
Define the |cdk| App
|cdk| apps are modeled as classes which extend the :py:class:`App <@aws-cdk/cdk.App>` class. Let's create our first, empty App:
.. tabs:: .. group-tab:: JavaScript In **index.js**: .. code-block:: js const cdk = require('@aws-cdk/cdk'); class MyApp extends cdk.App { constructor(argv) { super(argv); } } process.stdout.write(new MyApp(process.argv).run()); .. group-tab:: TypeScript In **index.ts**: .. code-block:: 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()); .. group-tab:: Java In **src/main/java/com/acme/MyApp.java**: .. code-block:: 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.
If needed, compile the code:
.. tabs:: .. group-tab:: JavaScript No need to compile .. group-tab:: TypeScript To compile your program from **.ts** to **.js**: .. code-block:: sh 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: .. code-block:: sh # run in another terminal session npm run watch .. group-tab:: Java Compile your code using your IDE or via the command line via **mvn**: .. code-block:: sh mvn compile
This is it, you now created your first, alas empty, |cdk| app.
Configure the |cdk| Toolkit
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 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:
.. tabs:: .. group-tab:: JavaScript Define the :code:`--app` option in **cdk.json** to execute **index.js** using **node**: .. code-block:: json { "app": "node index.js" } .. group-tab:: TypeScript Define the :code:`--app` option in **cdk.json** to execute **index.js** using **node**: .. code-block:: json { "app": "node index.js" } .. group-tab:: Java Specify a **CLASSPATH**, which contains both the compiled code and dependencies, to execute the Java program. Use **maven-dependency-plugin** to produce the file **.classpath.txt** whenever the project is compiled: .. code-block:: xml <!-- 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 that **.classpath.txt** exists: .. code-block:: sh mvn compile ls .classpath.txt Create a shim **app.sh** to execute the |cdk| Java app: .. code-block:: sh #!/bin/bash exec java -cp target/classes:$(cat .classpath.txt) com.acme.MyApp app $@ Define the :code:`-- app` option in **cdk.json**: .. code-block:: json { "app": "/bin/bash ./app.sh" }
Use the |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 and add it to the app.
.. tabs:: .. group-tab:: JavaScript In **index.js**: .. code-block:: js :emphasize-lines: 3,4,5,6,7,13 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()); .. group-tab:: TypeScript In **index.ts**: .. code-block:: ts :emphasize-lines: 3,4,5,6,7,13 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()); .. group-tab:: Java In **src/main/java/com/acme/MyStack.java**: .. code-block:: 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); } } In **src/main/java/com/acme/MyApp.java**: .. code-block:: java :emphasize-lines: 12 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 thr 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 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 Context for more information.
- id is a local string that identifies 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:
.. tabs:: .. group-tab:: JavaScript Nothing to compile. .. group-tab:: TypeScript .. code-block:: sh npm run build .. group-tab:: Java .. code-block:: sh 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.
Define an |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 |S3| bucket.
Install the @aws-cdk/aws-s3 package:
.. tabs:: .. group-tab:: JavaScript .. code-block:: sh npm install @aws-cdk/aws-s3 .. group-tab:: TypeScript .. code-block:: sh npm install @aws-cdk/aws-s3 .. group-tab:: Java During beta, all |cdk| modules are bundles into the **aws-cdk** Maven package, so there is no need to explicitly install the |S3| library.
Next, define an |S3| bucket in the stack. |S3| buckets are represented by the :py:class:`Bucket <@aws-cdk/aws-s3.Bucket>` class:
.. tabs:: .. group-tab:: JavaScript In **index.js**: .. code-block:: js :emphasize-lines: 2,8,9,10 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 }); } } .. group-tab:: TypeScript In **index.ts**: .. code-block:: ts :emphasize-lines: 2,8,9,10 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 }); } } .. group-tab:: Java In **src/main/java/com/acme/MyStack.java**: .. code-block:: java :emphasize-lines: 6,7,13,14,15 package com.acme; import com.amazonaws.cdk.App; import com.amazonaws.cdk.Stack; import com.amazonaws.cdk.aws.s3.Bucket; import com.amazonaws.cdk.aws.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 has parent, id, and props. In this case, the bucket is an immediate child of MyStack, and its ID is 'MyFirstBucket'.
- Since the bucket's
versioned
property istrue
, versioning is enabled on the bucket.
Compile your program:
.. tabs:: .. group-tab:: JavaScript Nothing to compile. .. group-tab:: TypeScript .. code-block:: sh npm run build .. group-tab:: Java .. code-block:: sh mvn compile
Synthesize an |CFN| Template
Synthesize a |cfn| template for the stack (don't forget to first compile the project if you have made any changes):
cdk synth hello-cdk
Note
Since the |cdk| app only contains a single stack, you can omit hello-cdk
.
This command executes the |cdk| app and synthesize an |CFN| 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.
Use cdk deploy to deploy the stack:
cdk deploy hello-cdk
The deploy command synthesizes an |CFN| template from the stack and then invokes the |CFN| create/update API to deploy it into your AWS account. The command displays information as it progresses.
Configure the bucket to use KMS managed encryption:
.. tabs:: .. group-tab:: JavaScript .. code-block:: js :emphasize-lines: 3 new s3.Bucket(this, 'MyFirstBucket', { versioned: true, encryption: s3.BucketEncryption.KmsManaged }); .. group-tab:: TypeScript .. code-block:: ts :emphasize-lines: 3 new s3.Bucket(this, 'MyFirstBucket', { versioned: true, encryption: s3.BucketEncryption.KmsManaged }); .. group-tab:: Java .. code-block:: java :emphasize-lines: 3 new Bucket(this, "MyFirstBucket", BucketProps.builder() .withVersioned(true) .withEncryption("MANAGED") .build());
Compile the program:
.. tabs:: .. group-tab:: JavaScript Nothing to compile. .. group-tab:: TypeScript .. code-block:: sh npm run build .. group-tab:: Java .. code-block:: sh mvn compile
Before you deploy the updated stack, use the cdk diff
command to evaluate
the difference between the |cdk| app and the deployed stack:
cdk diff hello-cdk
The toolkit queries your AWS account for the current |CFN| 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