UNPKG

38.6 kBMarkdownView Raw
1# AWS Cloud Development Kit Core Library
2<!--BEGIN STABILITY BANNER-->
3
4---
5
6![End-of-Support](https://img.shields.io/badge/End--of--Support-critical.svg?style=for-the-badge)
7
8> AWS CDK v1 has reached End-of-Support on 2023-06-01.
9> This package is no longer being updated, and users should migrate to AWS CDK v2.
10>
11> For more information on how to migrate, see the [_Migrating to AWS CDK v2_ guide][doc].
12>
13> [doc]: https://docs.aws.amazon.com/cdk/v2/guide/migrating-v2.html
14
15---
16
17<!--END STABILITY BANNER-->
18
19This library includes the basic building blocks of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) (AWS CDK). It defines the core classes that are used in the rest of the
20AWS Construct Library.
21
22See the [AWS CDK Developer
23Guide](https://docs.aws.amazon.com/cdk/latest/guide/home.html) for
24information of most of the capabilities of this library. The rest of this
25README will only cover topics not already covered in the Developer Guide.
26
27<!--BEGIN CORE DOCUMENTATION-->
28
29## Stacks and Stages
30
31A `Stack` is the smallest physical unit of deployment, and maps directly onto
32a CloudFormation Stack. You define a Stack by defining a subclass of `Stack`
33-- let's call it `MyStack` -- and instantiating the constructs that make up
34your application in `MyStack`'s constructor. You then instantiate this stack
35one or more times to define different instances of your application. For example,
36you can instantiate it once using few and cheap EC2 instances for testing,
37and once again using more and bigger EC2 instances for production.
38
39When your application grows, you may decide that it makes more sense to split it
40out across multiple `Stack` classes. This can happen for a number of reasons:
41
42- You could be starting to reach the maximum number of resources allowed in a single
43 stack (this is currently 500).
44- You could decide you want to separate out stateful resources and stateless resources
45 into separate stacks, so that it becomes easy to tear down and recreate the stacks
46 that don't have stateful resources.
47- There could be a single stack with resources (like a VPC) that are shared
48 between multiple instances of other stacks containing your applications.
49
50As soon as your conceptual application starts to encompass multiple stacks,
51it is convenient to wrap them in another construct that represents your
52logical application. You can then treat that new unit the same way you used
53to be able to treat a single stack: by instantiating it multiple times
54for different instances of your application.
55
56You can define a custom subclass of `Stage`, holding one or more
57`Stack`s, to represent a single logical instance of your application.
58
59As a final note: `Stack`s are not a unit of reuse. They describe physical
60deployment layouts, and as such are best left to application builders to
61organize their deployments with. If you want to vend a reusable construct,
62define it as a subclasses of `Construct`: the consumers of your construct
63will decide where to place it in their own stacks.
64
65## Stack Synthesizers
66
67Each Stack has a *synthesizer*, an object that determines how and where
68the Stack should be synthesized and deployed. The synthesizer controls
69aspects like:
70
71- How does the stack reference assets? (Either through CloudFormation
72 parameters the CLI supplies, or because the Stack knows a predefined
73 location where assets will be uploaded).
74- What roles are used to deploy the stack? These can be bootstrapped
75 roles, roles created in some other way, or just the CLI's current
76 credentials.
77
78The following synthesizers are available:
79
80- `DefaultStackSynthesizer`: recommended. Uses predefined asset locations and
81 roles created by the modern bootstrap template. Access control is done by
82 controlling who can assume the deploy role. This is the default stack
83 synthesizer in CDKv2.
84- `LegacyStackSynthesizer`: Uses CloudFormation parameters to communicate
85 asset locations, and the CLI's current permissions to deploy stacks. The
86 is the default stack synthesizer in CDKv1.
87- `CliCredentialsStackSynthesizer`: Uses predefined asset locations, and the
88 CLI's current permissions.
89
90Each of these synthesizers takes configuration arguments. To configure
91a stack with a synthesizer, pass it as one of its properties:
92
93```ts
94new MyStack(app, 'MyStack', {
95 synthesizer: new DefaultStackSynthesizer({
96 fileAssetsBucketName: 'my-orgs-asset-bucket',
97 }),
98});
99```
100
101For more information on bootstrapping accounts and customizing synthesis,
102see [Bootstrapping in the CDK Developer Guide](https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html).
103
104## Nested Stacks
105
106[Nested stacks](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html) are stacks created as part of other stacks. You create a nested stack within another stack by using the `NestedStack` construct.
107
108As your infrastructure grows, common patterns can emerge in which you declare the same components in multiple templates. You can separate out these common components and create dedicated templates for them. Then use the resource in your template to reference other templates, creating nested stacks.
109
110For example, assume that you have a load balancer configuration that you use for most of your stacks. Instead of copying and pasting the same configurations into your templates, you can create a dedicated template for the load balancer. Then, you just use the resource to reference that template from within other templates.
111
112The following example will define a single top-level stack that contains two nested stacks: each one with a single Amazon S3 bucket:
113
114```ts
115class MyNestedStack extends cfn.NestedStack {
116 constructor(scope: Construct, id: string, props?: cfn.NestedStackProps) {
117 super(scope, id, props);
118
119 new s3.Bucket(this, 'NestedBucket');
120 }
121}
122
123class MyParentStack extends Stack {
124 constructor(scope: Construct, id: string, props?: StackProps) {
125 super(scope, id, props);
126
127 new MyNestedStack(this, 'Nested1');
128 new MyNestedStack(this, 'Nested2');
129 }
130}
131```
132
133Resources references across nested/parent boundaries (even with multiple levels of nesting) will be wired by the AWS CDK
134through CloudFormation parameters and outputs. When a resource from a parent stack is referenced by a nested stack,
135a CloudFormation parameter will automatically be added to the nested stack and assigned from the parent; when a resource
136from a nested stack is referenced by a parent stack, a CloudFormation output will be automatically be added to the
137nested stack and referenced using `Fn::GetAtt "Outputs.Xxx"` from the parent.
138
139Nested stacks also support the use of Docker image and file assets.
140
141## Accessing resources in a different stack
142
143You can access resources in a different stack, as long as they are in the
144same account and AWS Region. The following example defines the stack `stack1`,
145which defines an Amazon S3 bucket. Then it defines a second stack, `stack2`,
146which takes the bucket from stack1 as a constructor property.
147
148```ts
149const prod = { account: '123456789012', region: 'us-east-1' };
150
151const stack1 = new StackThatProvidesABucket(app, 'Stack1' , { env: prod });
152
153// stack2 will take a property { bucket: IBucket }
154const stack2 = new StackThatExpectsABucket(app, 'Stack2', {
155 bucket: stack1.bucket,
156 env: prod
157});
158```
159
160If the AWS CDK determines that the resource is in the same account and
161Region, but in a different stack, it automatically synthesizes AWS
162CloudFormation
163[Exports](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html)
164in the producing stack and an
165[Fn::ImportValue](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html)
166in the consuming stack to transfer that information from one stack to the
167other.
168
169### Removing automatic cross-stack references
170
171The automatic references created by CDK when you use resources across stacks
172are convenient, but may block your deployments if you want to remove the
173resources that are referenced in this way. You will see an error like:
174
175```text
176Export Stack1:ExportsOutputFnGetAtt-****** cannot be deleted as it is in use by Stack1
177```
178
179Let's say there is a Bucket in the `stack1`, and the `stack2` references its
180`bucket.bucketName`. You now want to remove the bucket and run into the error above.
181
182It's not safe to remove `stack1.bucket` while `stack2` is still using it, so
183unblocking yourself from this is a two-step process. This is how it works:
184
185DEPLOYMENT 1: break the relationship
186
187- Make sure `stack2` no longer references `bucket.bucketName` (maybe the consumer
188 stack now uses its own bucket, or it writes to an AWS DynamoDB table, or maybe you just
189 remove the Lambda Function altogether).
190- In the `stack1` class, call `this.exportValue(this.bucket.bucketName)`. This
191 will make sure the CloudFormation Export continues to exist while the relationship
192 between the two stacks is being broken.
193- Deploy (this will effectively only change the `stack2`, but it's safe to deploy both).
194
195DEPLOYMENT 2: remove the resource
196
197- You are now free to remove the `bucket` resource from `stack1`.
198- Don't forget to remove the `exportValue()` call as well.
199- Deploy again (this time only the `stack1` will be changed -- the bucket will be deleted).
200
201## Durations
202
203To make specifications of time intervals unambiguous, a single class called
204`Duration` is used throughout the AWS Construct Library by all constructs
205that that take a time interval as a parameter (be it for a timeout, a
206rate, or something else).
207
208An instance of Duration is constructed by using one of the static factory
209methods on it:
210
211```ts
212Duration.seconds(300) // 5 minutes
213Duration.minutes(5) // 5 minutes
214Duration.hours(1) // 1 hour
215Duration.days(7) // 7 days
216Duration.parse('PT5M') // 5 minutes
217```
218
219Durations can be added or subtracted together:
220
221```ts
222Duration.minutes(1).plus(Duration.seconds(60)); // 2 minutes
223Duration.minutes(5).minus(Duration.seconds(10)); // 290 secondes
224```
225
226## Size (Digital Information Quantity)
227
228To make specification of digital storage quantities unambiguous, a class called
229`Size` is available.
230
231An instance of `Size` is initialized through one of its static factory methods:
232
233```ts
234Size.kibibytes(200) // 200 KiB
235Size.mebibytes(5) // 5 MiB
236Size.gibibytes(40) // 40 GiB
237Size.tebibytes(200) // 200 TiB
238Size.pebibytes(3) // 3 PiB
239```
240
241Instances of `Size` created with one of the units can be converted into others.
242By default, conversion to a higher unit will fail if the conversion does not produce
243a whole number. This can be overridden by unsetting `integral` property.
244
245```ts
246Size.mebibytes(2).toKibibytes() // yields 2048
247Size.kibibytes(2050).toMebibytes({ rounding: SizeRoundingBehavior.FLOOR }) // yields 2
248```
249
250## Secrets
251
252To help avoid accidental storage of secrets as plain text, we use the `SecretValue` type to
253represent secrets. Any construct that takes a value that should be a secret (such as
254a password or an access key) will take a parameter of type `SecretValue`.
255
256The best practice is to store secrets in AWS Secrets Manager and reference them using `SecretValue.secretsManager`:
257
258```ts
259const secret = SecretValue.secretsManager('secretId', {
260 jsonField: 'password', // optional: key of a JSON field to retrieve (defaults to all content),
261 versionId: 'id', // optional: id of the version (default AWSCURRENT)
262 versionStage: 'stage', // optional: version stage name (default AWSCURRENT)
263});
264```
265
266Using AWS Secrets Manager is the recommended way to reference secrets in a CDK app.
267`SecretValue` also supports the following secret sources:
268
269- `SecretValue.unsafePlainText(secret)`: stores the secret as plain text in your app and the resulting template (not recommended).
270- `SecretValue.secretsManager(secret)`: refers to a secret stored in Secrets Manager
271- `SecretValue.ssmSecure(param, version)`: refers to a secret stored as a SecureString in the SSM
272 Parameter Store. If you don't specify the exact version, AWS CloudFormation uses the latest
273 version of the parameter.
274- `SecretValue.cfnParameter(param)`: refers to a secret passed through a CloudFormation parameter (must have `NoEcho: true`).
275- `SecretValue.cfnDynamicReference(dynref)`: refers to a secret described by a CloudFormation dynamic reference (used by `ssmSecure` and `secretsManager`).
276- `SecretValue.resourceAttribute(attr)`: refers to a secret returned from a CloudFormation resource creation.
277
278`SecretValue`s should only be passed to constructs that accept properties of type
279`SecretValue`. These constructs are written to ensure your secrets will not be
280exposed where they shouldn't be. If you try to use a `SecretValue` in a
281different location, an error about unsafe secret usage will be thrown at
282synthesis time.
283
284## ARN manipulation
285
286Sometimes you will need to put together or pick apart Amazon Resource Names
287(ARNs). The functions `stack.formatArn()` and `stack.parseArn()` exist for
288this purpose.
289
290`formatArn()` can be used to build an ARN from components. It will automatically
291use the region and account of the stack you're calling it on:
292
293```ts
294declare const stack: Stack;
295
296// Builds "arn:<PARTITION>:lambda:<REGION>:<ACCOUNT>:function:MyFunction"
297stack.formatArn({
298 service: 'lambda',
299 resource: 'function',
300 sep: ':',
301 resourceName: 'MyFunction'
302});
303```
304
305`parseArn()` can be used to get a single component from an ARN. `parseArn()`
306will correctly deal with both literal ARNs and deploy-time values (tokens),
307but in case of a deploy-time value be aware that the result will be another
308deploy-time value which cannot be inspected in the CDK application.
309
310```ts
311declare const stack: Stack;
312
313// Extracts the function name out of an AWS Lambda Function ARN
314const arnComponents = stack.parseArn(arn, ':');
315const functionName = arnComponents.resourceName;
316```
317
318Note that depending on the service, the resource separator can be either
319`:` or `/`, and the resource name can be either the 6th or 7th
320component in the ARN. When using these functions, you will need to know
321the format of the ARN you are dealing with.
322
323For an exhaustive list of ARN formats used in AWS, see [AWS ARNs and
324Namespaces](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html)
325in the AWS General Reference.
326
327## Dependencies
328
329### Construct Dependencies
330
331Sometimes AWS resources depend on other resources, and the creation of one
332resource must be completed before the next one can be started.
333
334In general, CloudFormation will correctly infer the dependency relationship
335between resources based on the property values that are used. In the cases where
336it doesn't, the AWS Construct Library will add the dependency relationship for
337you.
338
339If you need to add an ordering dependency that is not automatically inferred,
340you do so by adding a dependency relationship using
341`constructA.node.addDependency(constructB)`. This will add a dependency
342relationship between all resources in the scope of `constructA` and all
343resources in the scope of `constructB`.
344
345If you want a single object to represent a set of constructs that are not
346necessarily in the same scope, you can use a `ConcreteDependable`. The
347following creates a single object that represents a dependency on two
348constructs, `constructB` and `constructC`:
349
350```ts
351// Declare the dependable object
352const bAndC = new ConcreteDependable();
353bAndC.add(constructB);
354bAndC.add(constructC);
355
356// Take the dependency
357constructA.node.addDependency(bAndC);
358```
359
360### Stack Dependencies
361
362Two different stack instances can have a dependency on one another. This
363happens when an resource from one stack is referenced in another stack. In
364that case, CDK records the cross-stack referencing of resources,
365automatically produces the right CloudFormation primitives, and adds a
366dependency between the two stacks. You can also manually add a dependency
367between two stacks by using the `stackA.addDependency(stackB)` method.
368
369A stack dependency has the following implications:
370
371- Cyclic dependencies are not allowed, so if `stackA` is using resources from
372 `stackB`, the reverse is not possible anymore.
373- Stacks with dependencies between them are treated specially by the CDK
374 toolkit:
375 - If `stackA` depends on `stackB`, running `cdk deploy stackA` will also
376 automatically deploy `stackB`.
377 - `stackB`'s deployment will be performed *before* `stackA`'s deployment.
378
379## Custom Resources
380
381Custom Resources are CloudFormation resources that are implemented by arbitrary
382user code. They can do arbitrary lookups or modifications during a
383CloudFormation deployment.
384
385To define a custom resource, use the `CustomResource` construct:
386
387```ts
388new CustomResource(this, 'MyMagicalResource', {
389 resourceType: 'Custom::MyCustomResource', // must start with 'Custom::'
390
391 // the resource properties
392 properties: {
393 Property1: 'foo',
394 Property2: 'bar'
395 },
396
397 // the ARN of the provider (SNS/Lambda) which handles
398 // CREATE, UPDATE or DELETE events for this resource type
399 // see next section for details
400 serviceToken: 'ARN'
401});
402```
403
404### Custom Resource Providers
405
406Custom resources are backed by a **custom resource provider** which can be
407implemented in one of the following ways. The following table compares the
408various provider types (ordered from low-level to high-level):
409
410| Provider | Compute Type | Error Handling | Submit to CloudFormation | Max Timeout | Language | Footprint |
411|----------------------------------------------------------------------|:------------:|:--------------:|:------------------------:|:---------------:|:--------:|:---------:|
412| [sns.Topic](#amazon-sns-topic) | Self-managed | Manual | Manual | Unlimited | Any | Depends |
413| [lambda.Function](#aws-lambda-function) | AWS Lambda | Manual | Manual | 15min | Any | Small |
414| [core.CustomResourceProvider](#the-corecustomresourceprovider-class) | Lambda | Auto | Auto | 15min | Node.js | Small |
415| [custom-resources.Provider](#the-custom-resource-provider-framework) | Lambda | Auto | Auto | Unlimited Async | Any | Large |
416
417Legend:
418
419- **Compute type**: which type of compute can is used to execute the handler.
420- **Error Handling**: whether errors thrown by handler code are automatically
421 trapped and a FAILED response is submitted to CloudFormation. If this is
422 "Manual", developers must take care of trapping errors. Otherwise, events
423 could cause stacks to hang.
424- **Submit to CloudFormation**: whether the framework takes care of submitting
425 SUCCESS/FAILED responses to CloudFormation through the event's response URL.
426- **Max Timeout**: maximum allows/possible timeout.
427- **Language**: which programming languages can be used to implement handlers.
428- **Footprint**: how many resources are used by the provider framework itself.
429
430**A NOTE ABOUT SINGLETONS**
431
432When defining resources for a custom resource provider, you will likely want to
433define them as a *stack singleton* so that only a single instance of the
434provider is created in your stack and which is used by all custom resources of
435that type.
436
437Here is a basic pattern for defining stack singletons in the CDK. The following
438examples ensures that only a single SNS topic is defined:
439
440```ts
441function getOrCreate(scope: Construct): sns.Topic {
442 const stack = Stack.of(scope);
443 const uniqueid = 'GloballyUniqueIdForSingleton'; // For example, a UUID from `uuidgen`
444 const existing = stack.node.tryFindChild(uniqueid);
445 if (existing) {
446 return existing as sns.Topic;
447 }
448 return new sns.Topic(stack, uniqueid);
449}
450```
451
452#### Amazon SNS Topic
453
454Every time a resource event occurs (CREATE/UPDATE/DELETE), an SNS notification
455is sent to the SNS topic. Users must process these notifications (e.g. through a
456fleet of worker hosts) and submit success/failure responses to the
457CloudFormation service.
458
459Set `serviceToken` to `topic.topicArn` in order to use this provider:
460
461```ts
462const topic = new sns.Topic(this, 'MyProvider');
463
464new CustomResource(this, 'MyResource', {
465 serviceToken: topic.topicArn
466});
467```
468
469#### AWS Lambda Function
470
471An AWS lambda function is called *directly* by CloudFormation for all resource
472events. The handler must take care of explicitly submitting a success/failure
473response to the CloudFormation service and handle various error cases.
474
475Set `serviceToken` to `lambda.functionArn` to use this provider:
476
477```ts
478const fn = new lambda.Function(this, 'MyProvider', functionProps);
479
480new CustomResource(this, 'MyResource', {
481 serviceToken: fn.functionArn,
482});
483```
484
485#### The `core.CustomResourceProvider` class
486
487The class [`@aws-cdk/core.CustomResourceProvider`] offers a basic low-level
488framework designed to implement simple and slim custom resource providers. It
489currently only supports Node.js-based user handlers, and it does not have
490support for asynchronous waiting (handler cannot exceed the 15min lambda
491timeout).
492
493[`@aws-cdk/core.CustomResourceProvider`]: https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.CustomResourceProvider.html
494
495The provider has a built-in singleton method which uses the resource type as a
496stack-unique identifier and returns the service token:
497
498```ts
499const serviceToken = CustomResourceProvider.getOrCreate(this, 'Custom::MyCustomResourceType', {
500 codeDirectory: `${__dirname}/my-handler`,
501 runtime: CustomResourceProviderRuntime.NODEJS_14_X,
502 description: "Lambda function created by the custom resource provider",
503});
504
505new CustomResource(this, 'MyResource', {
506 resourceType: 'Custom::MyCustomResourceType',
507 serviceToken: serviceToken
508});
509```
510
511The directory (`my-handler` in the above example) must include an `index.js` file. It cannot import
512external dependencies or files outside this directory. It must export an async
513function named `handler`. This function accepts the CloudFormation resource
514event object and returns an object with the following structure:
515
516```js
517exports.handler = async function(event) {
518 const id = event.PhysicalResourceId; // only for "Update" and "Delete"
519 const props = event.ResourceProperties;
520 const oldProps = event.OldResourceProperties; // only for "Update"s
521
522 switch (event.RequestType) {
523 case "Create":
524 // ...
525
526 case "Update":
527 // ...
528
529 // if an error is thrown, a FAILED response will be submitted to CFN
530 throw new Error('Failed!');
531
532 case "Delete":
533 // ...
534 }
535
536 return {
537 // (optional) the value resolved from `resource.ref`
538 // defaults to "event.PhysicalResourceId" or "event.RequestId"
539 PhysicalResourceId: "REF",
540
541 // (optional) calling `resource.getAtt("Att1")` on the custom resource in the CDK app
542 // will return the value "BAR".
543 Data: {
544 Att1: "BAR",
545 Att2: "BAZ"
546 },
547
548 // (optional) user-visible message
549 Reason: "User-visible message",
550
551 // (optional) hides values from the console
552 NoEcho: true
553 };
554}
555```
556
557Here is an complete example of a custom resource that summarizes two numbers:
558
559`sum-handler/index.js`:
560
561```js
562exports.handler = async (e) => {
563 return {
564 Data: {
565 Result: e.ResourceProperties.lhs + e.ResourceProperties.rhs,
566 },
567 };
568};
569```
570
571`sum.ts`:
572
573```ts nofixture
574import {
575 Construct,
576 CustomResource,
577 CustomResourceProvider,
578 CustomResourceProviderRuntime,
579 Token,
580} from '@aws-cdk/core';
581
582export interface SumProps {
583 readonly lhs: number;
584 readonly rhs: number;
585}
586
587export class Sum extends Construct {
588 public readonly result: number;
589
590 constructor(scope: Construct, id: string, props: SumProps) {
591 super(scope, id);
592
593 const resourceType = 'Custom::Sum';
594 const serviceToken = CustomResourceProvider.getOrCreate(this, resourceType, {
595 codeDirectory: `${__dirname}/sum-handler`,
596 runtime: CustomResourceProviderRuntime.NODEJS_14_X,
597 });
598
599 const resource = new CustomResource(this, 'Resource', {
600 resourceType: resourceType,
601 serviceToken: serviceToken,
602 properties: {
603 lhs: props.lhs,
604 rhs: props.rhs
605 }
606 });
607
608 this.result = Token.asNumber(resource.getAtt('Result'));
609 }
610}
611```
612
613Usage will look like this:
614
615```ts fixture=README-custom-resource-provider
616const sum = new Sum(this, 'MySum', { lhs: 40, rhs: 2 });
617new CfnOutput(this, 'Result', { value: Token.asString(sum.result) });
618```
619
620To access the ARN of the provider's AWS Lambda function role, use the `getOrCreateProvider()`
621built-in singleton method:
622
623```ts
624const provider = CustomResourceProvider.getOrCreateProvider(this, 'Custom::MyCustomResourceType', {
625 codeDirectory: `${__dirname}/my-handler`,
626 runtime: CustomResourceProviderRuntime.NODEJS_14_X,
627});
628
629const roleArn = provider.roleArn;
630```
631
632This role ARN can then be used in resource-based IAM policies.
633
634#### The Custom Resource Provider Framework
635
636The [`@aws-cdk/custom-resources`] module includes an advanced framework for
637implementing custom resource providers.
638
639[`@aws-cdk/custom-resources`]: https://docs.aws.amazon.com/cdk/api/latest/docs/custom-resources-readme.html
640
641Handlers are implemented as AWS Lambda functions, which means that they can be
642implemented in any Lambda-supported runtime. Furthermore, this provider has an
643asynchronous mode, which means that users can provide an `isComplete` lambda
644function which is called periodically until the operation is complete. This
645allows implementing providers that can take up to two hours to stabilize.
646
647Set `serviceToken` to `provider.serviceToken` to use this type of provider:
648
649```ts
650const provider = new customresources.Provider(this, 'MyProvider', {
651 onEventHandler,
652 isCompleteHandler, // optional async waiter
653});
654
655new CustomResource(this, 'MyResource', {
656 serviceToken: provider.serviceToken
657});
658```
659
660See the [documentation](https://docs.aws.amazon.com/cdk/api/latest/docs/custom-resources-readme.html) for more details.
661
662## AWS CloudFormation features
663
664A CDK stack synthesizes to an AWS CloudFormation Template. This section
665explains how this module allows users to access low-level CloudFormation
666features when needed.
667
668### Stack Outputs
669
670CloudFormation [stack outputs][cfn-stack-output] and exports are created using
671the `CfnOutput` class:
672
673```ts
674new CfnOutput(this, 'OutputName', {
675 value: myBucket.bucketName,
676 description: 'The name of an S3 bucket', // Optional
677 exportName: 'TheAwesomeBucket', // Registers a CloudFormation export named "TheAwesomeBucket"
678});
679```
680
681[cfn-stack-output]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
682
683### Parameters
684
685CloudFormation templates support the use of [Parameters][cfn-parameters] to
686customize a template. They enable CloudFormation users to input custom values to
687a template each time a stack is created or updated. While the CDK design
688philosophy favors using build-time parameterization, users may need to use
689CloudFormation in a number of cases (for example, when migrating an existing
690stack to the AWS CDK).
691
692Template parameters can be added to a stack by using the `CfnParameter` class:
693
694```ts
695new CfnParameter(this, 'MyParameter', {
696 type: 'Number',
697 default: 1337,
698 // See the API reference for more configuration props
699});
700```
701
702The value of parameters can then be obtained using one of the `value` methods.
703As parameters are only resolved at deployment time, the values obtained are
704placeholder tokens for the real value (`Token.isUnresolved()` would return `true`
705for those):
706
707```ts
708const param = new CfnParameter(this, 'ParameterName', { /* config */ });
709
710// If the parameter is a String
711param.valueAsString;
712
713// If the parameter is a Number
714param.valueAsNumber;
715
716// If the parameter is a List
717param.valueAsList;
718```
719
720[cfn-parameters]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
721
722### Pseudo Parameters
723
724CloudFormation supports a number of [pseudo parameters][cfn-pseudo-params],
725which resolve to useful values at deployment time. CloudFormation pseudo
726parameters can be obtained from static members of the `Aws` class.
727
728It is generally recommended to access pseudo parameters from the scope's `stack`
729instead, which guarantees the values produced are qualifying the designated
730stack, which is essential in cases where resources are shared cross-stack:
731
732```ts
733// "this" is the current construct
734const stack = Stack.of(this);
735
736stack.account; // Returns the AWS::AccountId for this stack (or the literal value if known)
737stack.region; // Returns the AWS::Region for this stack (or the literal value if known)
738stack.partition;
739```
740
741[cfn-pseudo-params]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html
742
743### Resource Options
744
745CloudFormation resources can also specify [resource
746attributes][cfn-resource-attributes]. The `CfnResource` class allows
747accessing those through the `cfnOptions` property:
748
749```ts
750const rawBucket = new s3.CfnBucket(this, 'Bucket', { /* ... */ });
751// -or-
752const rawBucketAlt = myBucket.node.defaultChild as s3.CfnBucket;
753
754// then
755rawBucket.cfnOptions.condition = new CfnCondition(this, 'EnableBucket', { /* ... */ });
756rawBucket.cfnOptions.metadata = {
757 metadataKey: 'MetadataValue',
758};
759```
760
761Resource dependencies (the `DependsOn` attribute) is modified using the
762`cfnResource.addDependsOn` method:
763
764```ts
765const resourceA = new CfnResource(this, 'ResourceA', resourceProps);
766const resourceB = new CfnResource(this, 'ResourceB', resourceProps);
767
768resourceB.addDependsOn(resourceA);
769```
770
771[cfn-resource-attributes]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-product-attribute-reference.html
772
773### Intrinsic Functions and Condition Expressions
774
775CloudFormation supports [intrinsic functions][cfn-intrinsics]. These functions
776can be accessed from the `Fn` class, which provides type-safe methods for each
777intrinsic function as well as condition expressions:
778
779```ts
780// To use Fn::Base64
781Fn.base64('SGVsbG8gQ0RLIQo=');
782
783// To compose condition expressions:
784const environmentParameter = new CfnParameter(this, 'Environment');
785Fn.conditionAnd(
786 // The "Environment" CloudFormation template parameter evaluates to "Production"
787 Fn.conditionEquals('Production', environmentParameter),
788 // The AWS::Region pseudo-parameter value is NOT equal to "us-east-1"
789 Fn.conditionNot(Fn.conditionEquals('us-east-1', Aws.REGION)),
790);
791```
792
793When working with deploy-time values (those for which `Token.isUnresolved`
794returns `true`), idiomatic conditionals from the programming language cannot be
795used (the value will not be known until deployment time). When conditional logic
796needs to be expressed with un-resolved values, it is necessary to use
797CloudFormation conditions by means of the `CfnCondition` class:
798
799```ts
800const environmentParameter = new CfnParameter(this, 'Environment');
801const isProd = new CfnCondition(this, 'IsProduction', {
802 expression: Fn.conditionEquals('Production', environmentParameter),
803});
804
805// Configuration value that is a different string based on IsProduction
806const stage = Fn.conditionIf(isProd.logicalId, 'Beta', 'Prod').toString();
807
808// Make Bucket creation condition to IsProduction by accessing
809// and overriding the CloudFormation resource
810const bucket = new s3.Bucket(this, 'Bucket');
811const cfnBucket = myBucket.node.defaultChild as s3.CfnBucket;
812cfnBucket.cfnOptions.condition = isProd;
813```
814
815[cfn-intrinsics]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
816
817### Mappings
818
819CloudFormation [mappings][cfn-mappings] are created and queried using the
820`CfnMappings` class:
821
822```ts
823const regionTable = new CfnMapping(this, 'RegionTable', {
824 mapping: {
825 'us-east-1': {
826 regionName: 'US East (N. Virginia)',
827 // ...
828 },
829 'us-east-2': {
830 regionName: 'US East (Ohio)',
831 // ...
832 },
833 // ...
834 }
835});
836
837regionTable.findInMap(Aws.REGION, 'regionName')
838```
839
840This will yield the following template:
841
842```yaml
843Mappings:
844 RegionTable:
845 us-east-1:
846 regionName: US East (N. Virginia)
847 us-east-2:
848 regionName: US East (Ohio)
849```
850
851Mappings can also be synthesized "lazily"; lazy mappings will only render a "Mappings"
852section in the synthesized CloudFormation template if some `findInMap` call is unable to
853immediately return a concrete value due to one or both of the keys being unresolved tokens
854(some value only available at deploy-time).
855
856For example, the following code will not produce anything in the "Mappings" section. The
857call to `findInMap` will be able to resolve the value during synthesis and simply return
858`'US East (Ohio)'`.
859
860```ts
861const regionTable = new CfnMapping(this, 'RegionTable', {
862 mapping: {
863 'us-east-1': {
864 regionName: 'US East (N. Virginia)',
865 },
866 'us-east-2': {
867 regionName: 'US East (Ohio)',
868 },
869 },
870 lazy: true,
871});
872
873regionTable.findInMap('us-east-2', 'regionName');
874```
875
876On the other hand, the following code will produce the "Mappings" section shown above,
877since the top-level key is an unresolved token. The call to `findInMap` will return a token that resolves to
878`{ "Fn::FindInMap": [ "RegionTable", { "Ref": "AWS::Region" }, "regionName" ] }`.
879
880```ts
881declare const regionTable: CfnMapping;
882
883regionTable.findInMap(Aws.REGION, 'regionName');
884```
885
886[cfn-mappings]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html
887
888### Dynamic References
889
890CloudFormation supports [dynamically resolving][cfn-dynamic-references] values
891for SSM parameters (including secure strings) and Secrets Manager. Encoding such
892references is done using the `CfnDynamicReference` class:
893
894```ts
895new CfnDynamicReference(
896 CfnDynamicReferenceService.SECRETS_MANAGER,
897 'secret-id:secret-string:json-key:version-stage:version-id',
898);
899```
900
901[cfn-dynamic-references]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html
902
903### Template Options & Transform
904
905CloudFormation templates support a number of options, including which Macros or
906[Transforms][cfn-transform] to use when deploying the stack. Those can be
907configured using the `stack.templateOptions` property:
908
909```ts
910const stack = new Stack(app, 'StackName');
911
912stack.templateOptions.description = 'This will appear in the AWS console';
913stack.templateOptions.transforms = ['AWS::Serverless-2016-10-31'];
914stack.templateOptions.metadata = {
915 metadataKey: 'MetadataValue',
916};
917```
918
919[cfn-transform]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html
920
921### Emitting Raw Resources
922
923The `CfnResource` class allows emitting arbitrary entries in the
924[Resources][cfn-resources] section of the CloudFormation template.
925
926```ts
927new CfnResource(this, 'ResourceId', {
928 type: 'AWS::S3::Bucket',
929 properties: {
930 BucketName: 'bucket-name'
931 },
932});
933```
934
935As for any other resource, the logical ID in the CloudFormation template will be
936generated by the AWS CDK, but the type and properties will be copied verbatim in
937the synthesized template.
938
939[cfn-resources]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html
940
941### Including raw CloudFormation template fragments
942
943When migrating a CloudFormation stack to the AWS CDK, it can be useful to
944include fragments of an existing template verbatim in the synthesized template.
945This can be achieved using the `CfnInclude` class.
946
947```ts
948new CfnInclude(this, 'ID', {
949 template: {
950 Resources: {
951 Bucket: {
952 Type: 'AWS::S3::Bucket',
953 Properties: {
954 BucketName: 'my-shiny-bucket'
955 }
956 }
957 }
958 },
959});
960```
961
962### Termination Protection
963
964You can prevent a stack from being accidentally deleted by enabling termination
965protection on the stack. If a user attempts to delete a stack with termination
966protection enabled, the deletion fails and the stack--including its status--remains
967unchanged. Enabling or disabling termination protection on a stack sets it for any
968nested stacks belonging to that stack as well. You can enable termination protection
969on a stack by setting the `terminationProtection` prop to `true`.
970
971```ts
972const stack = new Stack(app, 'StackName', {
973 terminationProtection: true,
974});
975```
976
977By default, termination protection is disabled.
978
979### CfnJson
980
981`CfnJson` allows you to postpone the resolution of a JSON blob from
982deployment-time. This is useful in cases where the CloudFormation JSON template
983cannot express a certain value.
984
985A common example is to use `CfnJson` in order to render a JSON map which needs
986to use intrinsic functions in keys. Since JSON map keys must be strings, it is
987impossible to use intrinsics in keys and `CfnJson` can help.
988
989The following example defines an IAM role which can only be assumed by
990principals that are tagged with a specific tag.
991
992```ts
993const tagParam = new CfnParameter(this, 'TagName');
994
995const stringEquals = new CfnJson(this, 'ConditionJson', {
996 value: {
997 [`aws:PrincipalTag/${tagParam.valueAsString}`]: true,
998 },
999});
1000
1001const principal = new iam.AccountRootPrincipal().withConditions({
1002 StringEquals: stringEquals,
1003});
1004
1005new iam.Role(this, 'MyRole', { assumedBy: principal });
1006```
1007
1008**Explanation**: since in this example we pass the tag name through a parameter, it
1009can only be resolved during deployment. The resolved value can be represented in
1010the template through a `{ "Ref": "TagName" }`. However, since we want to use
1011this value inside a [`aws:PrincipalTag/TAG-NAME`](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-principaltag)
1012IAM operator, we need it in the *key* of a `StringEquals` condition. JSON keys
1013*must be* strings, so to circumvent this limitation, we use `CfnJson`
1014to "delay" the rendition of this template section to deploy-time. This means
1015that the value of `StringEquals` in the template will be `{ "Fn::GetAtt": [ "ConditionJson", "Value" ] }`, and will only "expand" to the operator we synthesized during deployment.
1016
1017### Stack Resource Limit
1018
1019When deploying to AWS CloudFormation, it needs to keep in check the amount of resources being added inside a Stack. Currently it's possible to check the limits in the [AWS CloudFormation quotas](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html) page.
1020
1021It's possible to synthesize the project with more Resources than the allowed (or even reduce the number of Resources).
1022
1023Set the context key `@aws-cdk/core:stackResourceLimit` with the proper value, being 0 for disable the limit of resources.
1024
1025<!--END CORE DOCUMENTATION-->