This is the AWS CDK v2 Developer Guide. The older CDK v1 entered maintenance on June 1, 2022 and ended support on June 1, 2023.
Resources are what you configure to use AWS services in your applications. Resources are a feature of AWS CloudFormation. By configuring resources and their properties in a AWS CloudFormation template, you can deploy to AWS CloudFormation to provision your resources. With the AWS Cloud Development Kit (AWS CDK), you can configure resources through constructs. You then deploy your CDK app, which involves synthesizing a AWS CloudFormation template and deploying to AWS CloudFormation to provision your resources.
Configuring resources using constructs
As described in AWS CDK Constructs, the AWS CDK provides a rich class library of constructs, called AWS constructs, that represent all AWS resources.
To create an instance of a resource using its corresponding construct, pass in the scope as the first argument, the logical ID of the construct, and a set of configuration properties (props). For example, here's how to create an Amazon SQS queue with AWS KMS encryption using the sqs.Queue construct from the AWS Construct Library.
import * as sqs from '@aws-cdk/aws-sqs';
new sqs.Queue(this, 'MyQueue', {
encryption: sqs.QueueEncryption.KMS_MANAGED
});
Some configuration props are optional, and in many cases have default values. In some cases, all props are optional, and the last argument can be omitted entirely.
Resource attributes
Most resources in the AWS Construct Library expose attributes, which are resolved at deployment time by AWS CloudFormation. Attributes
are exposed in the form of properties on the resource classes with the type name as a prefix. The following example
shows how to get the URL of an Amazon SQS queue using the queueUrl
(Python: queue_url
)
property.
import * as sqs from '@aws-cdk/aws-sqs';
const queue = new sqs.Queue(this, 'MyQueue');
const url = queue.queueUrl; // => A string representing a deploy-time value
See Tokens and the AWS CDK for information about how the AWS CDK encodes deploy-time attributes as strings.
Referencing resources
When configuring resources, you will often have to reference properties of another resource. The following are examples:
-
An Amazon Elastic Container Service (Amazon ECS) resource requires a reference to the cluster on which it runs.
-
An Amazon CloudFront distribution requires a reference to the Amazon Simple Storage Service (Amazon S3) bucket containing the source code.
You can reference resources in any of the following ways:
-
By passing a resource defined in your CDK app, either in the same stack or in a different one
-
By passing a proxy object referencing a resource defined in your AWS account, created from a unique identifier of the resource (such as an ARN)
If the property of a construct represents a construct for another resource, its type is that of the interface type
of the construct. For example, the Amazon ECS construct takes a property cluster
of type
ecs.ICluster
. Another example, is the CloudFront distribution construct that takes a property
sourceBucket
(Python: source_bucket
) of type s3.IBucket
.
You can directly pass any resource object of the proper type defined in the same AWS CDK app. The following example defines an Amazon ECS cluster and then uses it to define an Amazon ECS service.
const cluster = new ecs.Cluster(this, 'Cluster', { /*...*/ });
const service = new ecs.Ec2Service(this, 'Service', { cluster: cluster });
Referencing resources in a different stack
You can refer to resources in a different stack as long as they are defined in the same app and are in the same AWS environment. The following pattern is generally used:
-
Store a reference to the construct as an attribute of the stack that produces the resource. (To get a reference to the current construct's stack, use
Stack.of(this)
.) -
Pass this reference to the constructor of the stack that consumes the resource as a parameter or a property. The consuming stack then passes it as a property to any construct that needs it.
The following example defines a stack stack1
. This stack defines an Amazon S3 bucket and stores a
reference to the bucket construct as an attribute of the stack. Then the app defines a second stack,
stack2
, which accepts a bucket at instantiation. stack2
might, for example, define an
AWS Glue Table that uses the bucket for data storage.
const prod = { account: '123456789012', region: 'us-east-1' };
const stack1 = new StackThatProvidesABucket(app, 'Stack1', { env: prod });
// stack2 will take a property { bucket: IBucket }
const stack2 = new StackThatExpectsABucket(app, 'Stack2', {
bucket: stack1.bucket,
env: prod
});
If the AWS CDK determines that the resource is in the same environment, but in a different stack, it automatically synthesizes AWS CloudFormation exports in the producing stack and an Fn::ImportValue in the consuming stack to transfer that information from one stack to the other.
Resolving dependency deadlocks
Referencing a resource from one stack in a different stack creates a dependency between the two stacks. This makes sure that they're deployed in the right order. After the stacks are deployed, this dependency is concrete. After that, removing the use of the shared resource from the consuming stack can cause an unexpected deployment failure. This happens if there is another dependency between the two stacks that force them to be deployed in the same order. It can also happen without a dependency if the producing stack is simply chosen by the CDK Toolkit to be deployed first. The AWS CloudFormation export is removed from the producing stack because it's no longer needed, but the exported resource is still being used in the consuming stack because its update is not yet deployed. Therefore, deploying the producer stack fails.
To break this deadlock, remove the use of the shared resource from the consuming stack. (This removes the
automatic export from the producing stack.) Next, manually add the same export to the producing stack using exactly
the same logical ID as the automatically generated export. Remove the use of the shared resource in the consuming
stack and deploy both stacks. Then, remove the manual export (and the shared resource if it's no longer needed) and
deploy both stacks again. The stack's exportValue()
method is a convenient way to create the manual export for this purpose. (See the example in the linked method
reference.)
Referencing resources in your AWS account
Suppose you want to use a resource already available in your AWS account in your AWS CDK app. This might be a resource that was defined through the console, an AWS SDK, directly with AWS CloudFormation, or in a different AWS CDK application. You can turn the resource's ARN (or another identifying attribute, or group of attributes) into a proxy object. The proxy object serves as a reference to the resource by calling a static factory method on the resource's class.
When you create such a proxy, the external resource does not become a part of your AWS CDK app. Therefore, changes you make to the proxy in your AWS CDK app do not affect the deployed resource. The proxy can, however, be passed to any AWS CDK method that requires a resource of that type.
The following example shows how to reference a bucket based on an existing bucket with the ARN arn:aws:s3:::amzn-s3-demo-bucket1, and an Amazon Virtual Private Cloud based on an existing VPC having a specific ID.
// Construct a proxy for a bucket by its name (must be same account)
s3.Bucket.fromBucketName(this, 'MyBucket', 'amzn-s3-demo-bucket1');
// Construct a proxy for a bucket by its full ARN (can be another account)
s3.Bucket.fromBucketArn(this, 'MyBucket', 'arn:aws:s3:::amzn-s3-demo-bucket1');
// Construct a proxy for an existing VPC from its attribute(s)
ec2.Vpc.fromVpcAttributes(this, 'MyVpc', {
vpcId: 'vpc-1234567890abcde',
});
Let's take a closer look at the Vpc.fromLookup()
method. Because the ec2.Vpc
construct is complex, there are
many ways you might want to select the VPC to be used with your CDK app. To address this, the VPC construct
has a fromLookup
static method (Python: from_lookup
) that lets you look up the desired
Amazon VPC by querying your AWS account at synthesis time.
To use Vpc.fromLookup()
, the system that synthesizes the stack must have access to the account that
owns the Amazon VPC. This is because the CDK Toolkit queries the account to find the right Amazon VPC at synthesis time.
Furthermore, Vpc.fromLookup()
works only in stacks that are defined with an explicit account and region (see Environments for the AWS CDK). If
the AWS CDK tries to look up an Amazon VPC from an environment-agnostic stack, the
CDK Toolkit doesn't know which environment to query to find the VPC.
You must provide Vpc.fromLookup()
attributes sufficient to uniquely identify a VPC in your AWS
account. For example, there can only ever be one default VPC, so it's sufficient to specify the VPC as the
default.
ec2.Vpc.fromLookup(this, 'DefaultVpc', {
isDefault: true
});
You can also use the tags
property to query for VPCs by tag. You can add tags to the Amazon VPC at the
time of its creation by using AWS CloudFormation or the AWS CDK. You can edit tags at any time after creation by using the
AWS Management Console, the AWS CLI, or an AWS SDK. In addition to any tags you add yourself, the AWS CDK automatically adds the
following tags to all VPCs it creates.
-
Name – The name of the VPC.
-
aws-cdk:subnet-name – The name of the subnet.
-
aws-cdk:subnet-type – The type of the subnet: Public, Private, or Isolated.
ec2.Vpc.fromLookup(this, 'PublicVpc',
{tags: {'aws-cdk:subnet-type': "Public"}});
Results of Vpc.fromLookup()
are cached in the project's cdk.context.json
file.
(See Context values and the AWS CDK.) Commit this file to version control so that your app will continue to refer to the
same Amazon VPC. This works even if you later change the attributes of your VPCs in a way that would result in a different
VPC being selected. This is particularly important if you're deploying the stack in an environment that doesn't have
access to the AWS account that defines the VPC, such as CDK Pipelines.
Although you can use an external resource anywhere you'd use a similar resource defined in your AWS CDK app, you
cannot modify it. For example, calling addToResourcePolicy
(Python: add_to_resource_policy
)
on an external s3.Bucket
does nothing.
Resource physical names
The logical names of resources in AWS CloudFormation are different from the names of resources that are shown in the AWS Management Console after they're deployed by AWS CloudFormation. The AWS CDK calls these final names physical names.
For example, AWS CloudFormation might create the Amazon S3 bucket with the logical ID Stack2MyBucket4DD88B4F
and the
physical name stack2MyBucket4dd88b4f-iuv1rbv9z3to
.
You can specify a physical name when creating constructs that represent resources by using the property
<resourceType>
Name. The following example creates an Amazon S3 bucket with the physical
name amzn-s3-demo-bucket
.
const bucket = new s3.Bucket(this, 'MyBucket', {
bucketName: 'amzn-s3-demo-bucket',
});
Assigning physical names to resources has some disadvantages in AWS CloudFormation. Most importantly, any changes to deployed resources that require a resource replacement, such as changes to a resource's properties that are immutable after creation, will fail if a resource has a physical name assigned. If you end up in that state, the only solution is to delete the AWS CloudFormation stack, then deploy the AWS CDK app again. See the AWS CloudFormation documentation for details.
In some cases, such as when creating an AWS CDK app with cross-environment references, physical names are required
for the AWS CDK to function correctly. In those cases, if you don't want to bother with coming up with a physical name
yourself, you can let the AWS CDK name it for you. To do so, use the special value
PhysicalName.GENERATE_IF_NEEDED
, as follows.
const bucket = new s3.Bucket(this, 'MyBucket', {
bucketName: core.PhysicalName.GENERATE_IF_NEEDED,
});
Passing unique resource identifiers
Whenever possible, you should pass resources by reference, as described in the previous section. However, there are cases where you have no other choice but to refer to a resource by one of its attributes. Example use cases include the following:
-
When you are using low-level AWS CloudFormation resources.
-
When you need to expose resources to the runtime components of an AWS CDK application, such as when referring to Lambda functions through environment variables.
These identifiers are available as attributes on the resources, such as the following.
bucket.bucketName lambdaFunc.functionArn securityGroup.groupArn
The following example shows how to pass a generated bucket name to an AWS Lambda function.
const bucket = new s3.Bucket(this, 'Bucket');
new lambda.Function(this, 'MyLambda', {
// ...
environment: {
BUCKET_NAME: bucket.bucketName,
},
});
Granting permissions between resources
Higher-level constructs make least-privilege permissions achievable by offering simple, intent-based APIs to express permission requirements. For example, many L2 constructs offer grant methods that you can use to grant an entity (such as an IAM role or user) permission to work with the resource, without having to manually create IAM permission statements.
The following example creates the permissions to allow a Lambda function's execution role to read and write objects to a particular Amazon S3 bucket. If the Amazon S3 bucket is encrypted with an AWS KMS key, this method also grants permissions to the Lambda function's execution role to decrypt with the key.
if (bucket.grantReadWrite(func).success) {
// ...
}
The grant methods return an iam.Grant
object. Use the success
attribute of the
Grant
object to determine whether the grant was effectively applied (for example, it may not have been
applied on external resources). You can also use the
assertSuccess
(Python: assert_success
) method of the Grant
object to enforce
that the grant was successfully applied.
If a specific grant method isn't available for the particular use case, you can use a generic grant method to define a new grant with a specified list of actions.
The following example shows how to grant a Lambda function access to the Amazon DynamoDB CreateBackup
action.
table.grant(func, 'dynamodb:CreateBackup');
Many resources, such as Lambda functions, require a role to be assumed when executing code. A configuration property
enables you to specify an iam.IRole
. If no role is specified, the function automatically creates a role
specifically for this use. You can then use grant methods on the resources to add statements to the role.
The grant methods are built using lower-level APIs for handling with IAM policies. Policies are modeled as PolicyDocument
objects. Add statements directly to roles (or a construct's attached role) using the addToRolePolicy
method (Python: add_to_role_policy
), or to a resource's policy (such as a Bucket
policy)
using the addToResourcePolicy
(Python: add_to_resource_policy
) method.
Resource metrics and alarms
Many resources emit CloudWatch metrics that can be used to set up monitoring dashboards and alarms. Higher-level constructs have metric methods that let you access the metrics without looking up the correct name to use.
The following example shows how to define an alarm when the ApproximateNumberOfMessagesNotVisible
of
an Amazon SQS queue exceeds 100.
import * as cw from '@aws-cdk/aws-cloudwatch';
import * as sqs from '@aws-cdk/aws-sqs';
import { Duration } from '@aws-cdk/core';
const queue = new sqs.Queue(this, 'MyQueue');
const metric = queue.metricApproximateNumberOfMessagesNotVisible({
label: 'Messages Visible (Approx)',
period: Duration.minutes(5),
// ...
});
metric.createAlarm(this, 'TooManyMessagesAlarm', {
comparisonOperator: cw.ComparisonOperator.GREATER_THAN_THRESHOLD,
threshold: 100,
// ...
});
If there is no method for a particular metric, you can use the general metric method to specify the metric name manually.
Metrics can also be added to CloudWatch dashboards. See CloudWatch.
Network traffic
In many cases, you must enable permissions on a network for an application to work, such as when the compute infrastructure needs to access the persistence layer. Resources that establish or listen for connections expose methods that enable traffic flows, including setting security group rules or network ACLs.
IConnectable resources have a connections
property that is the gateway to network
traffic rules configuration.
You enable data to flow on a given network path by using allow
methods. The following example enables
HTTPS connections to the web and incoming connections from the Amazon EC2 Auto Scaling group fleet2
.
import * as asg from '@aws-cdk/aws-autoscaling';
import * as ec2 from '@aws-cdk/aws-ec2';
const fleet1: asg.AutoScalingGroup = asg.AutoScalingGroup(/*...*/);
// Allow surfing the (secure) web
fleet1.connections.allowTo(new ec2.Peer.anyIpv4(), new ec2.Port({ fromPort: 443, toPort: 443 }));
const fleet2: asg.AutoScalingGroup = asg.AutoScalingGroup(/*...*/);
fleet1.connections.allowFrom(fleet2, ec2.Port.AllTraffic());
Certain resources have default ports associated with them. Examples include the listener of a load balancer on the
public port, and the ports on which the database engine accepts connections for instances of an Amazon RDS database. In such
cases, you can enforce tight network control without having to manually specify the port. To do so, use the
allowDefaultPortFrom
and allowToDefaultPort
methods (Python:
allow_default_port_from
, allow_to_default_port
).
The following example shows how to enable connections from any IPV4 address, and a connection from an Auto Scaling group to access a database.
listener.connections.allowDefaultPortFromAnyIpv4('Allow public access');
fleet.connections.allowToDefaultPort(rdsDatabase, 'Fleet can access database');
Event handling
Some resources can act as event sources. Use the addEventNotification
method (Python:
add_event_notification
) to register an event target to a particular event type emitted by the resource.
In addition to this, addXxxNotification
methods offer a simple way to register a handler for common event
types.
The following example shows how to trigger a Lambda function when an object is added to an Amazon S3 bucket.
import * as s3nots from '@aws-cdk/aws-s3-notifications';
const handler = new lambda.Function(this, 'Handler', { /*…*/ });
const bucket = new s3.Bucket(this, 'Bucket');
bucket.addObjectCreatedNotification(new s3nots.LambdaDestination(handler));
Removal policies
Resources that maintain persistent data, such as databases, Amazon S3 buckets, and Amazon ECR registries, have a
removal policy. The removal policy indicates whether to delete persistent objects when the AWS CDK
stack that contains them is destroyed. The values specifying the removal policy are available through the
RemovalPolicy
enumeration in the AWS CDK core
module.
Note
Resources besides those that store data persistently might also have a removalPolicy
that is used
for a different purpose. For example, a Lambda function version uses a removalPolicy
attribute to
determine whether a given version is retained when a new version is deployed. These have different meanings and
defaults compared to the removal policy on an Amazon S3 bucket or DynamoDB table.
Value | Meaning |
---|---|
|
Keep the contents of the resource when destroying the stack (default). The resource is orphaned from the stack and must be deleted manually. If you attempt to re-deploy the stack while the resource still exists, you will receive an error message due to a name conflict. |
|
The resource will be destroyed along with the stack. |
AWS CloudFormation does not remove Amazon S3 buckets that contain files even if their removal policy is set to DESTROY
.
Attempting to do so is an AWS CloudFormation error. To have the AWS CDK delete all files from the bucket before destroying it, set the
bucket's autoDeleteObjects
property to true
.
Following is an example of creating an Amazon S3 bucket with RemovalPolicy
of DESTROY
and
autoDeleteOjbects
set to true
.
import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';
export class CdkTestStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new s3.Bucket(this, 'Bucket', {
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true
});
}
}
You can also apply a removal policy directly to the underlying AWS CloudFormation resource via the
applyRemovalPolicy()
method. This method is available on some stateful resources that do not have a
removalPolicy
property in their L2 resource's props. Examples include the following:
-
AWS CloudFormation stacks
-
Amazon Cognito user pools
-
Amazon DocumentDB database instances
-
Amazon EC2 volumes
-
Amazon OpenSearch Service domains
-
Amazon FSx file systems
-
Amazon SQS queues
const resource = bucket.node.findChild('Resource') as cdk.CfnResource;
resource.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY);
Note
The AWS CDK's RemovalPolicy
translates to AWS CloudFormation's DeletionPolicy
. However, the default in
AWS CDK is to retain the data, which is the opposite of the AWS CloudFormation default.