@@ -103,9 +103,274 @@ were part of your app.
103103
104104.. _cloudformation_layer :
105105
106- AWS CloudFormation Layer
107- ========================
106+ Access the AWS CloudFormation Layer
107+ ===================================
108+
109+ This topic discusses ways to directly modify the underlying CloudFormation
110+ resources at the AWS Construct Library. We also call this technique an "escape
111+ hatch", as it allows users to "escape" from the abstraction boundary defined by
112+ the AWS Construct and patch the underlying resources.
113+
114+ .. important ::
115+
116+ **We do not recommend this method, as it breaks the abstraction layer and
117+ might produce unexpected results **.
118+
119+ Furthermore, the internal implementation of an AWS construct is not part of
120+ the API compatibility guarantees that we can make. This means that updates to
121+ the construct library may break your code without a major version bump.
122+
123+ AWS constructs, such as :py:class: `Topic <@aws-cdk/aws-sns.Topic> `, encapsulate
124+ one or more AWS CloudFormation resources behind their APIs. These resources are
125+ also represented as constructs under the ``cloudformation `` namespace in each
126+ library. For example, the :py:class: `@aws-cdk/aws-s3.Bucket ` construct
127+ encapsulates the :py:class: `@aws-cdk/aws-s3.cloudformation.BucketResource `. When
128+ a stack that includes an AWS construct is synthesized, the CloudFormation
129+ definition of the underlying resources are included in the resulting template.
130+
131+ Eventually, the APIs provided by AWS constructs are expected to support all the
132+ services and capabilities offered by AWS, but we are aware that the library
133+ still has many gaps both at the service level (some services don't have any
134+ constructs yet) and at the resource level (an AWS construct exists, but some
135+ features are missing).
136+
137+ .. note ::
138+
139+ If you encounter a missing capability in the AWS Construct Library, whether
140+ it is an entire library, a specific resource or a feature,
141+ `raise an issue <https://github.com/awslabs/aws-cdk/issues/new >`_ on GitHub,
142+ and letting us know.
143+
144+ This topic covers the following use cases:
145+
146+ - How to access the low-level CloudFormation resources encapsulated by an AWS construct
147+ - How to specify resource options such as metadata, dependencies on resources
148+ - How to add overrides to a CloudFormation resource and property definitions
149+ - How to directly define low-level CloudFormation resources without an AWS construct
150+
151+ You can also find more information on how to work directly with the AWS
152+ CloudFormation layer under :py:doc: `cloudformation `.
153+
154+ Accessing Low-level Resources
155+ -----------------------------
156+
157+ You can use :py:meth: `construct.findChild(id) <@aws-cdk/cdk.Construct.findChild> `
158+ to access any child of this construct by its construct ID. By convention, the "main"
159+ resource of any AWS Construct is called ``"Resource" ``.
160+
161+ The following example shows how to access the underlying S3 bucket resource
162+ given an :py:class: `s3.Bucket <@aws-cdk/s3.Bucket> ` construct:
163+
164+ .. code-block :: ts
165+
166+ // let's create an AWS bucket construct
167+ const bucket = new s3.Bucket(this, 'MyBucket');
168+
169+ // we use our knowledge that the main construct is called "Resource" and
170+ // that it's actual type is s3.cloudformation.BucketResource; const
171+ const bucketResource = bucket.findResource('Resource') as s3.cloudformation.BucketResource;
172+
173+ At this point, ``bucketResource `` represents the low-level CloudFormation resource of type
174+ :py:class: `s3.cloudformation.BucketResource <@aws-cdk/aws-s3.cloudformation.BucketResource> `
175+ encapsulated by our bucket.
176+
177+ :py:meth: `construct.findChild(id) <@aws-cdk/cdk.Construct.findChild> ` will fail
178+ if the child could not be located, which means that if the underlying |l2 | changes
179+ the IDs or structure for some reason, synthesis fails.
180+
181+ It is also possible to use :py:meth: `construct.children <@aws-cdk/cdk.Construct.children> ` for more
182+ advanced queries. For example, we can look for a child that has a certain CloudFormation resource
183+ type:
184+
185+ .. code-block :: ts
186+
187+ const bucketResource =
188+ bucket.children.find(c => (c as cdk.Resource).resourceType === 'AWS::S3::Bucket')
189+ as s3.cloudformation.BucketResource;
190+
191+ From that point, users are interacting with CloudFormation resource classes
192+ (which extend :py:class: `cdk.Resource <@aws-cdk/cdk.Resource> `), so we will look
193+ into how to use their APIs in order to modify the behavior of the AWS construct
194+ at hand.
195+
196+ Resource Options
197+ ----------------
198+
199+ :py:class: `cdk.Resource <@aws-cdk/cdk.Resource> ` has a few facilities for
200+ setting resource options such as ``Metadata ``, ``DependsOn ``, etc.
201+
202+ For example, this code:
203+
204+ .. code-block :: ts
205+
206+ const bucketResource = bucket.findChild('Resource') as s3.cloudformation.BucketResource;
207+
208+ bucketResource.options.metadata = { MetadataKey: 'MetadataValue' };
209+ bucketResource.options.updatePolicy = {
210+ autoScalingRollingUpdate: {
211+ pauseTime: '390'
212+ }
213+ };
214+
215+ bucketResource.addDependency(otherBucket.findChild('Resource') as cdk.Resource);
216+
217+ Synthesizes the following template:
218+
219+ .. code-block :: json
220+
221+ {
222+ "Type" : " AWS::S3::Bucket" ,
223+ "DependsOn" : [ " Other34654A52" ],
224+ "UpdatePolicy" : {
225+ "AutoScalingRollingUpdate" : {
226+ "PauseTime" : " 390"
227+ }
228+ },
229+ "Metadata" : {
230+ "MetadataKey" : " MetadataValue"
231+ }
232+ }
233+
234+ Overriding Resource Properties
235+ ------------------------------
236+
237+ Each low-level resource in the CDK has a strongly-typed property called
238+ ``propertyOverrides ``. It allows users to apply overrides that adhere to the
239+ CloudFormation schema of the resource, and use code-completion and
240+ type-checking.
241+
242+ You will normally use this mechanism when a certain feature is available at the
243+ CloudFormation layer but is not exposed by the AWS Construct.
244+
245+ The following example sets a bucket's analytics configuration:
246+
247+ .. code-block :: ts
248+
249+ bucketResource.propertyOverrides.analyticsConfigurations = [
250+ {
251+ id: 'config1',
252+ storageClassAnalysis: {
253+ dataExport: {
254+ outputSchemaVersion: '1',
255+ destination: {
256+ format: 'html',
257+ bucketArn: otherBucket.bucketArn // use tokens freely
258+ }
259+ }
260+ }
261+ }
262+ ];
263+
264+ Raw Overrides
265+ -------------
266+
267+ In cases the strongly-typed overrides are not sufficient, or, for example, if
268+ the schema defined in CloudFormation is not up-to-date, the method
269+ :py:meth: `cdk.Resource.addOverride(path, value) <@aws-cdk/cdk.Resource.addOverride> `
270+ can be used to define an override that will by applied to the resource
271+ definition during synthesis.
272+
273+ For example:
274+
275+ .. code-block :: ts
276+
277+ // define an override at the resource definition root, you can even modify the "Type"
278+ // of the resource if needed.
279+ bucketResource.addOverride('Type', 'AWS::S3::SpecialBucket');
280+
281+ // define an override for a property (both are equivalent operations):
282+ bucketResource.addPropertyOverride('VersioningConfiguration.Status', 'NewStatus');
283+ bucketResource.addOverride('Properties.VersioningConfiguration.Status', 'NewStatus');
284+
285+ // use dot-notation to define overrides in complex structures which will be merged
286+ // with the values set by the higher-level construct
287+ bucketResource.addPropertyOverride('LoggingConfiguration.DestinationBucketName', otherBucket.bucketName);
288+
289+ // it is also possible to assign a `null` value
290+ bucketResource.addPropertyOverride('Foo.Bar', null);
291+
292+ Synthesizes to:
293+
294+ .. code-block :: json
295+
296+ {
297+ "Type" : " AWS::S3::SpecialBucket" ,
298+ "Properties" : {
299+ "Foo" : {
300+ "Bar" : null
301+ },
302+ "VersioningConfiguration" : {
303+ "Status" : " NewStatus"
304+ },
305+ "LoggingConfiguration" : {
306+ "DestinationBucketName" : {
307+ "Ref" : " Other34654A52"
308+ }
309+ }
310+ }
311+ }
312+
313+ Use ``undefined ``, :py:meth: `cdk.Resource.addDeletionOverride <@aws-cdk/cdk.Resource.addDeletionOverride> `
314+ or :py:meth: `cdk.Resource.addPropertyDeletionOverride <@aws-cdk/cdk.Resource.addPropertyDeletionOverride> `
315+ to delete values:
316+
317+ .. code-block :: ts
318+
319+ const bucket = new s3.Bucket(this, 'MyBucket', {
320+ versioned: true,
321+ encryption: s3.BucketEncryption.KmsManaged
322+ });
323+
324+ const bucketResource = bucket.findChild('Resource') as s3.cloudformation.BucketResource;
325+ bucketResource.addPropertyOverride('BucketEncryption.ServerSideEncryptionConfiguration.0.EncryptEverythingAndAlways', true);
326+ bucketResource.addPropertyDeletionOverride('BucketEncryption.ServerSideEncryptionConfiguration.0.ServerSideEncryptionByDefault');
327+
328+ Synthesizes to:
329+
330+ .. code-block :: json
331+
332+ "MyBucketF68F3FF0" : {
333+ "Type" : " AWS::S3::Bucket" ,
334+ "Properties" : {
335+ "BucketEncryption" : {
336+ "ServerSideEncryptionConfiguration" : [
337+ {
338+ "EncryptEverythingAndAlways" : true
339+ }
340+ ]
341+ },
342+ "VersioningConfiguration" : {
343+ "Status" : " Enabled"
344+ }
345+ }
346+ }
347+
348+ Directly Defining CloudFormation Resources
349+ -------------------------------------------
350+
351+ It is also possible to explicitly define CloudFormation resources in your stack.
352+ To that end, instantiate one of the constructs under the ``cloudformation ``
353+ namespace of the dedicated library.
354+
355+ .. code-block :: ts
356+
357+ new s3.cloudformation.BucketResource(this, 'MyBucket', {
358+ analyticsConfigurations: [
359+ // ...
360+ ]
361+ });
362+
363+ In the rare case where you want to define a resource that doesn't have a
364+ corresponding ``cloudformation `` class (such as a new resource that was not yet
365+ published in the CloudFormation resource specification), you can instantiate the
366+ :py:class: `cdk.Resource <@aws-cdk/cdk.Resource> ` object:
367+
368+ .. code-block :: ts
369+
370+ new cdk.Resource(this, 'MyBucket', {
371+ type: 'AWS::S3::Bucket',
372+ properties: {
373+ AnalyticsConfiguration: [ /* ... */ ] // note the PascalCase here
374+ }
375+ });
108376
109- Every module in the AWS Construct Library includes a ``cloudformation `` namespace which contains
110- low-level constructs which represent the low-level AWS CloudFormation semantics of this service.
111- See :py:doc: `cloudformation ` for details.
0 commit comments