@@ -103,9 +103,274 @@ were part of your app.
103
103
104
104
.. _cloudformation_layer :
105
105
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
+ });
108
376
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