@@ -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