software.amazon.awscdk.cloudformation.include.package-info Maven / Gradle / Ivy
/**
* Include CloudFormation templates in the CDK
*
* ---
*
*
*
*
*
*
*
* This module contains a set of classes whose goal is to facilitate working
* with existing CloudFormation templates in the CDK.
* It can be thought of as an extension of the capabilities of the
* CfnInclude
class.
*
*
Basic usage
*
* Assume we have a file with an existing template.
* It could be in JSON format, in a file my-template.json
:
*
*
* {
* "Resources": {
* "Bucket": {
* "Type": "AWS::S3::Bucket",
* "Properties": {
* "BucketName": "some-bucket-name"
* }
* }
* }
* }
*
*
* Or it could by in YAML format, in a file my-template.yaml
:
*
*
* Resources:
* Bucket:
* Type: AWS::S3::Bucket
* Properties:
* BucketName: some-bucket-name
*
*
* It can be included in a CDK application with the following code:
*
*
* CfnInclude cfnTemplate = CfnInclude.Builder.create(this, "Template")
* .templateFile("my-template.json")
* .build();
*
*
* Or, if your template uses YAML:
*
*
* CfnInclude cfnTemplate = CfnInclude.Builder.create(this, "Template")
* .templateFile("my-template.yaml")
* .build();
*
*
* Note: different YAML parsers sometimes don't agree on what exactly constitutes valid YAML.
* If you get a YAML exception when including your template,
* try converting it to JSON, and including that file instead.
* If you're downloading your template from the CloudFormation AWS Console,
* you can easily get it in JSON format by clicking the 'View in Designer'
* button on the 'Template' tab -
* once in Designer, select JSON in the "Choose template language"
* radio buttons on the bottom pane.
*
* This will add all resources from my-template.json
/ my-template.yaml
into the CDK application,
* preserving their original logical IDs from the template file.
*
* Note that this including process will not execute any
* CloudFormation transforms -
* including the Serverless transform.
*
* Any resource from the included template can be retrieved by referring to it by its logical ID from the template.
* If you know the class of the CDK object that corresponds to that resource,
* you can cast the returned object to the correct type:
*
*
* CfnInclude cfnTemplate;
*
* CfnBucket cfnBucket = (CfnBucket)cfnTemplate.getResource("Bucket");
*
*
* Note that any resources not present in the latest version of the CloudFormation schema
* at the time of publishing the version of this module that you depend on,
* including Custom Resources,
* will be returned as instances of the class CfnResource
,
* and so cannot be cast to a different resource type.
*
* Any modifications made to that resource will be reflected in the resulting CDK template;
* for example, the name of the bucket can be changed:
*
*
* CfnInclude cfnTemplate;
*
* CfnBucket cfnBucket = (CfnBucket)cfnTemplate.getResource("Bucket");
* cfnBucket.getBucketName() = "my-bucket-name";
*
*
* You can also refer to the resource when defining other constructs,
* including the higher-level ones
* (those whose name does not start with Cfn
),
* for example:
*
*
* CfnInclude cfnTemplate;
*
* CfnBucket cfnBucket = (CfnBucket)cfnTemplate.getResource("Bucket");
*
* Role role = Role.Builder.create(this, "Role")
* .assumedBy(new AnyPrincipal())
* .build();
* role.addToPolicy(PolicyStatement.Builder.create()
* .actions(List.of("s3:*"))
* .resources(List.of(cfnBucket.getAttrArn()))
* .build());
*
*
*
Converting L1 resources to L2
*
* The resources the getResource
method returns are what the CDK calls
* Layer 1 resources
* (like CfnBucket
).
* However, in many places in the Construct Library,
* the CDK requires so-called Layer 2 resources, like IBucket
.
* There are two ways of going from an L1 to an L2 resource.
*
*
UsingfromCfn*()
methods
*
* This is the preferred method of converting an L1 resource to an L2.
* It works by invoking a static method of the class of the L2 resource
* whose name starts with fromCfn
-
* for example, for KMS Keys, that would be the Kms.fromCfnKey()
method -
* and passing the L1 instance as an argument:
*
*
* CfnInclude cfnTemplate;
*
* CfnKey cfnKey = (CfnKey)cfnTemplate.getResource("Key");
* IKey key = Key.fromCfnKey(cfnKey);
*
*
* This returns an instance of the kms.IKey
type that can be passed anywhere in the CDK an IKey
is expected.
* What is more, that IKey
instance will be mutable -
* which means calling any mutating methods on it,
* like addToResourcePolicy()
,
* will be reflected in the resulting template.
*
* Note that, in some cases, the fromCfn*()
method might not be able to create an L2 from the underlying L1.
* This can happen when the underlying L1 heavily uses CloudFormation functions.
* For example, if you tried to create an L2 IKey
* from an L1 represented as this CloudFormation template:
*
*
* {
* "Resources": {
* "Key": {
* "Type": "AWS::KMS::Key",
* "Properties": {
* "KeyPolicy": {
* "Statement": [
* {
* "Fn::If": [
* "Condition",
* {
* "Action": "kms:if-action",
* "Resource": "*",
* "Principal": "*",
* "Effect": "Allow"
* },
* {
* "Action": "kms:else-action",
* "Resource": "*",
* "Principal": "*",
* "Effect": "Allow"
* }
* ]
* }
* ],
* "Version": "2012-10-17"
* }
* }
* }
* }
* }
*
*
* The Key.fromCfnKey()
method does not know how to translate that into CDK L2 concepts,
* and would throw an exception.
*
* In those cases, you need the use the second method of converting an L1 to an L2.
*
*
Using from*Name/Arn/Attributes()
methods
*
* If the resource you need does not have a fromCfn*()
method,
* or if it does, but it throws an exception for your particular L1,
* you need to use the second method of converting an L1 resource to L2.
*
* Each L2 class has static factory methods with names like from*Name()
,
* from*Arn()
, and/or from*Attributes()
.
* You can obtain an L2 resource from an L1 by passing the correct properties of the L1 as the arguments to those methods:
*
*
* CfnInclude cfnTemplate;
*
* // using from*Attributes()
* CfnSubnet privateCfnSubnet1;
* CfnSubnet privateCfnSubnet2;
*
*
* // using from*Name()
* CfnBucket cfnBucket = (CfnBucket)cfnTemplate.getResource("Bucket");
* IBucket bucket = Bucket.fromBucketName(this, "L2Bucket", cfnBucket.getRef());
*
* // using from*Arn()
* CfnKey cfnKey = (CfnKey)cfnTemplate.getResource("Key");
* IKey key = Key.fromKeyArn(this, "L2Key", cfnKey.getAttrArn());
* CfnVPC cfnVpc = (CfnVPC)cfnTemplate.getResource("Vpc");
* IVpc vpc = Vpc.fromVpcAttributes(this, "L2Vpc", VpcAttributes.builder()
* .vpcId(cfnVpc.getRef())
* .availabilityZones(Fn.getAzs())
* .privateSubnetIds(List.of(privateCfnSubnet1.getRef(), privateCfnSubnet2.getRef()))
* .build());
*
*
* As long as they just need to be referenced,
* and not changed in any way, everything should work;
* however, note that resources returned from those methods,
* unlike those returned by fromCfn*()
methods,
* are immutable, which means calling any mutating methods on them will have no effect.
* You will have to mutate the underlying L1 in order to change them.
*
*
Non-resource template elements
*
* In addition to resources,
* you can also retrieve and mutate all other template elements:
*
*
* - Parameters:
*
*
* CfnInclude cfnTemplate;
*
* CfnParameter param = cfnTemplate.getParameter("MyParameter");
*
* // mutating the parameter
* param.getDefault() = "MyDefault";
*
* - Conditions:
*
*
* CfnInclude cfnTemplate;
*
* CfnCondition condition = cfnTemplate.getCondition("MyCondition");
*
* // mutating the condition
* condition.getExpression() = Fn.conditionEquals(1, 2);
*
* - Mappings:
*
*
* CfnInclude cfnTemplate;
*
* CfnMapping mapping = cfnTemplate.getMapping("MyMapping");
*
* // mutating the mapping
* mapping.setValue("my-region", "AMI", "ami-04681a1dbd79675a5");
*
* - Service Catalog template Rules:
*
*
* CfnInclude cfnTemplate;
*
* // mutating the rule
* CfnParameter myParameter;
*
* CfnRule rule = cfnTemplate.getRule("MyRule");
* rule.addAssertion(Fn.conditionContains(List.of("m1.small"), myParameter.getValueAsString()), "MyParameter has to be m1.small");
*
* - Outputs:
*
*
* CfnInclude cfnTemplate;
*
* // mutating the output
* CfnBucket cfnBucket;
*
* CfnOutput output = cfnTemplate.getOutput("MyOutput");
* output.getValue() = cfnBucket.getAttrArn();
*
* - Hooks for blue-green deployments:
*
*
* CfnInclude cfnTemplate;
*
* // mutating the hook
* Role myRole;
*
* CfnHook hook = cfnTemplate.getHook("MyOutput");
* CfnCodeDeployBlueGreenHook codeDeployHook = (CfnCodeDeployBlueGreenHook)hook;
* codeDeployHook.getServiceRole() = myRole.getRoleArn();
*
*
*
*
Parameter replacement
*
* If your existing template uses CloudFormation Parameters,
* you may want to remove them in favor of build-time values.
* You can do that using the parameters
property:
*
*
* CfnInclude.Builder.create(this, "includeTemplate")
* .templateFile("path/to/my/template")
* .parameters(Map.of(
* "MyParam", "my-value"))
* .build();
*
*
* This will replace all references to MyParam
with the string 'my-value'
,
* and MyParam
will be removed from the 'Parameters' section of the resulting template.
*
*
Nested Stacks
*
* This module also supports templates that use nested stacks.
*
* For example, if you have the following parent template:
*
*
* {
* "Resources": {
* "ChildStack": {
* "Type": "AWS::CloudFormation::Stack",
* "Properties": {
* "TemplateURL": "https://my-s3-template-source.s3.amazonaws.com/child-stack.json"
* }
* }
* }
* }
*
*
* where the child template pointed to by https://my-s3-template-source.s3.amazonaws.com/child-stack.json
is:
*
*
* {
* "Resources": {
* "MyBucket": {
* "Type": "AWS::S3::Bucket"
* }
* }
* }
*
*
* You can include both the parent stack,
* and the nested stack in your CDK application as follows:
*
*
* CfnInclude parentTemplate = CfnInclude.Builder.create(this, "ParentStack")
* .templateFile("path/to/my-parent-template.json")
* .loadNestedStacks(Map.of(
* "ChildStack", CfnIncludeProps.builder()
* .templateFile("path/to/my-nested-template.json")
* .build()))
* .build();
*
*
* Here, path/to/my-nested-template.json
* represents the path on disk to the downloaded template file from the original template URL of the nested stack
* (https://my-s3-template-source.s3.amazonaws.com/child-stack.json
).
* In the CDK application,
* this file will be turned into an Asset,
* and the TemplateURL
property of the nested stack resource
* will be modified to point to that asset.
*
* The included nested stack can be accessed with the getNestedStack
method:
*
*
* CfnInclude parentTemplate;
*
*
* IncludedNestedStack includedChildStack = parentTemplate.getNestedStack("ChildStack");
* NestedStack childStack = includedChildStack.getStack();
* CfnInclude childTemplate = includedChildStack.getIncludedTemplate();
*
*
* Now you can reference resources from ChildStack
,
* and modify them like any other included template:
*
*
* CfnInclude childTemplate;
*
*
* CfnBucket cfnBucket = (CfnBucket)childTemplate.getResource("MyBucket");
* cfnBucket.getBucketName() = "my-new-bucket-name";
*
* Role role = Role.Builder.create(this, "MyRole")
* .assumedBy(new AccountRootPrincipal())
* .build();
*
* role.addToPolicy(PolicyStatement.Builder.create()
* .actions(List.of("s3:GetObject*", "s3:GetBucket*", "s3:List*"))
* .resources(List.of(cfnBucket.getAttrArn()))
* .build());
*
*
* You can also include the nested stack after the CfnInclude
object was created,
* instead of doing it on construction:
*
*
* CfnInclude parentTemplate;
*
* IncludedNestedStack includedChildStack = parentTemplate.loadNestedStack("ChildTemplate", CfnIncludeProps.builder()
* .templateFile("path/to/my-nested-template.json")
* .build());
*
*
*
Vending CloudFormation templates as Constructs
*
* In many cases, there are existing CloudFormation templates that are not entire applications,
* but more like specialized fragments, implementing a particular pattern or best practice.
* If you have templates like that,
* you can use the CfnInclude
class to vend them as CDK Constructs:
*
*
* import software.constructs.Construct;
* import software.amazon.awscdk.cloudformation.include.*;
* import path.*;
*
* public class MyConstruct extends Construct {
* public MyConstruct(Construct scope, String id) {
* super(scope, id);
*
* // include a template inside the Construct
* // include a template inside the Construct
* CfnInclude.Builder.create(this, "MyConstruct")
* .templateFile(join(__dirname, "my-template.json"))
* .preserveLogicalIds(false)
* .build();
* }
* }
*
*
* Notice the preserveLogicalIds
parameter -
* it makes sure the logical IDs of all the included template elements are re-named using CDK's algorithm,
* guaranteeing they are unique within your application.
* Without that parameter passed,
* instantiating MyConstruct
twice in the same Stack would result in duplicated logical IDs.
*/
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Stable)
package software.amazon.awscdk.cloudformation.include;