cloudformationinclude

package
v2.102.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2023 License: Apache-2.0 Imports: 7 Imported by: 0

README

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:

cfnTemplate := cfn_inc.NewCfnInclude(this, jsii.String("Template"), &CfnIncludeProps{
	TemplateFile: jsii.String("my-template.json"),
})

Or, if your template uses YAML:

cfnTemplate := cfn_inc.NewCfnInclude(this, jsii.String("Template"), &CfnIncludeProps{
	TemplateFile: jsii.String("my-template.yaml"),
})

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.

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:

var cfnTemplate cfnInclude

cfnBucket := cfnTemplate.GetResource(jsii.String("Bucket")).(cfnBucket)

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:

var cfnTemplate cfnInclude

cfnBucket := cfnTemplate.GetResource(jsii.String("Bucket")).(cfnBucket)
cfnBucket.BucketName = "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:

var cfnTemplate cfnInclude

cfnBucket := cfnTemplate.GetResource(jsii.String("Bucket")).(cfnBucket)

role := iam.NewRole(this, jsii.String("Role"), &RoleProps{
	AssumedBy: iam.NewAnyPrincipal(),
})
role.AddToPolicy(iam.NewPolicyStatement(&PolicyStatementProps{
	Actions: []*string{
		jsii.String("s3:*"),
	},
	Resources: []*string{
		cfnBucket.AttrArn,
	},
}))

Migrating templates that use Transforms

You can use this module to migrate templates that use CloudFormation transforms - including the Serverless transform.

The CDK including process does not execute Transforms, and the cdk diff command by default compares against the original (meaning, unprocessed) template. So, if you're downloading the template to include from the CloudFormation AWS Console, make sure to download the unprocessed template (the "View processed template" checkbox is left unchecked, which is the default):

unprocessed template in the CloudFormation AWS Console

However, certain unprocessed templates can fail when used with the CfnInclude class. The most common reason for the failure is that the unprocessed template can contain cycles between resources, which get removed after the Transform is processed, but is not allowed when being included (as pure CloudFormation does not permit cycles). To enable cycle processing behavior similar to cloudformation, set allowCyclicalReferences of CfnIncludeProps to true.

When that happens, you should instead download the processed template from the CloudFormation AWS Console (make sure the "View processed template" checkbox is checked in that case):

processed template in the CloudFormation AWS Console

When you include that processed template in your CDK application, running cdk diff will now show a lot of differences with the deployed Stack, because cdk diff uses the unprocessed template by default. To alleviate that problem, you can pass the --processed switch to cdk diff, which will make the diff command compare against the processed template of the deployed Stack, which will give more precise results in this case.

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:

var cfnTemplate cfnInclude

cfnKey := cfnTemplate.GetResource(jsii.String("Key")).(cfnKey)
key := kms.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:

var cfnTemplate cfnInclude

// using from*Attributes()
var privateCfnSubnet1 cfnSubnet
var privateCfnSubnet2 cfnSubnet


// using from*Name()
cfnBucket := cfnTemplate.GetResource(jsii.String("Bucket")).(cfnBucket)
bucket := s3.Bucket_FromBucketName(this, jsii.String("L2Bucket"), cfnBucket.ref)

// using from*Arn()
cfnKey := cfnTemplate.GetResource(jsii.String("Key")).(cfnKey)
key := kms.Key_FromKeyArn(this, jsii.String("L2Key"), cfnKey.AttrArn)
cfnVpc := cfnTemplate.GetResource(jsii.String("Vpc")).(cfnVPC)
vpc := ec2.Vpc_FromVpcAttributes(this, jsii.String("L2Vpc"), &VpcAttributes{
	VpcId: cfnVpc.ref,
	AvailabilityZones: core.Fn_GetAzs(),
	PrivateSubnetIds: []*string{
		privateCfnSubnet1.ref,
		privateCfnSubnet2.ref,
	},
})

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:

    var cfnTemplate cfnInclude
    
    param := cfnTemplate.GetParameter(jsii.String("MyParameter"))
    
    // mutating the parameter
    param.default = "MyDefault"
    
  • Conditions:

    var cfnTemplate cfnInclude
    
    condition := cfnTemplate.GetCondition(jsii.String("MyCondition"))
    
    // mutating the condition
    condition.Expression = core.Fn_ConditionEquals(jsii.Number(1), jsii.Number(2))
    
  • Mappings:

    var cfnTemplate cfnInclude
    
    mapping := cfnTemplate.GetMapping(jsii.String("MyMapping"))
    
    // mutating the mapping
    mapping.SetValue(jsii.String("my-region"), jsii.String("AMI"), jsii.String("ami-04681a1dbd79675a5"))
    
  • Service Catalog template Rules:

    var cfnTemplate cfnInclude
    
    // mutating the rule
    var myParameter cfnParameter
    
    rule := cfnTemplate.GetRule(jsii.String("MyRule"))
    rule.AddAssertion(core.Fn_ConditionContains([]*string{
    	jsii.String("m1.small"),
    }, myParameter.valueAsString), jsii.String("MyParameter has to be m1.small"))
    
  • Outputs:

    var cfnTemplate cfnInclude
    
    // mutating the output
    var cfnBucket cfnBucket
    
    output := cfnTemplate.GetOutput(jsii.String("MyOutput"))
    output.value = cfnBucket.AttrArn
    
  • Hooks for blue-green deployments:

    var cfnTemplate cfnInclude
    
    // mutating the hook
    var myRole role
    
    hook := cfnTemplate.GetHook(jsii.String("MyOutput"))
    codeDeployHook := hook.(cfnCodeDeployBlueGreenHook)
    codeDeployHook.serviceRole = myRole.RoleArn
    

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:

cfn_inc.NewCfnInclude(this, jsii.String("includeTemplate"), &CfnIncludeProps{
	TemplateFile: jsii.String("path/to/my/template"),
	Parameters: map[string]interface{}{
		"MyParam": jsii.String("my-value"),
	},
})

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:

parentTemplate := cfn_inc.NewCfnInclude(this, jsii.String("ParentStack"), &CfnIncludeProps{
	TemplateFile: jsii.String("path/to/my-parent-template.json"),
	LoadNestedStacks: map[string]cfnIncludeProps{
		"ChildStack": &cfnIncludeProps{
			"templateFile": jsii.String("path/to/my-nested-template.json"),
		},
	},
})

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:

var parentTemplate cfnInclude


includedChildStack := parentTemplate.GetNestedStack(jsii.String("ChildStack"))
childStack := includedChildStack.Stack
childTemplate := includedChildStack.IncludedTemplate

Now you can reference resources from ChildStack, and modify them like any other included template:

var childTemplate cfnInclude


cfnBucket := childTemplate.GetResource(jsii.String("MyBucket")).(cfnBucket)
cfnBucket.BucketName = "my-new-bucket-name"

role := iam.NewRole(this, jsii.String("MyRole"), &RoleProps{
	AssumedBy: iam.NewAccountRootPrincipal(),
})

role.AddToPolicy(iam.NewPolicyStatement(&PolicyStatementProps{
	Actions: []*string{
		jsii.String("s3:GetObject*"),
		jsii.String("s3:GetBucket*"),
		jsii.String("s3:List*"),
	},
	Resources: []*string{
		cfnBucket.AttrArn,
	},
}))

You can also include the nested stack after the CfnInclude object was created, instead of doing it on construction:

var parentTemplate cfnInclude

includedChildStack := parentTemplate.LoadNestedStack(jsii.String("ChildTemplate"), &CfnIncludeProps{
	TemplateFile: jsii.String("path/to/my-nested-template.json"),
})

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 "github.com/aws/constructs-go/constructs"
import cfn_inc "github.com/aws/aws-cdk-go/awscdk"
import path "github.com/aws-samples/dummy/path"

type MyConstruct struct {
	construct
}

func NewMyConstruct(scope construct, id *string) *MyConstruct {
	this := &MyConstruct{}
	newConstruct_Override(this, scope, id)

	// include a template inside the Construct
	// include a template inside the Construct
	cfn_inc.NewCfnInclude(this, jsii.String("MyConstruct"), &CfnIncludeProps{
		TemplateFile: path.join(__dirname, jsii.String("my-template.json")),
		PreserveLogicalIds: jsii.Boolean(false),
	})
	return this
}

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.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CfnInclude_IsCfnElement

func CfnInclude_IsCfnElement(x interface{}) *bool

Returns `true` if a construct is a stack element (i.e. part of the synthesized cloudformation template).

Uses duck-typing instead of `instanceof` to allow stack elements from different versions of this library to be included in the same stack.

Returns: The construct as a stack element or undefined if it is not a stack element.

func CfnInclude_IsConstruct

func CfnInclude_IsConstruct(x interface{}) *bool

Checks if `x` is a construct.

Use this method instead of `instanceof` to properly detect `Construct` instances, even when the construct library is symlinked.

Explanation: in JavaScript, multiple copies of the `constructs` library on disk are seen as independent, completely different libraries. As a consequence, the class `Construct` in each copy of the `constructs` library is seen as a different class, and an instance of one class will not test as `instanceof` the other class. `npm install` will not create installations like this, but users may manually symlink construct libraries together or use a monorepo tool: in those cases, multiple copies of the `constructs` library can be accidentally installed, and `instanceof` will behave unpredictably. It is safest to avoid using `instanceof`, and using this type-testing method instead.

Returns: true if `x` is an object created from a class which extends `Construct`.

func NewCfnInclude_Override

func NewCfnInclude_Override(c CfnInclude, scope constructs.Construct, id *string, props *CfnIncludeProps)

Types

type CfnInclude

type CfnInclude interface {
	awscdk.CfnElement
	// Returns: the stack trace of the point where this Resource was created from, sourced
	// from the +metadata+ entry typed +aws:cdk:logicalId+, and with the bottom-most
	// node +internal+ entries filtered.
	CreationStack() *[]*string
	// The logical ID for this CloudFormation stack element.
	//
	// The logical ID of the element
	// is calculated from the path of the resource node in the construct tree.
	//
	// To override this value, use `overrideLogicalId(newLogicalId)`.
	//
	// Returns: the logical ID as a stringified token. This value will only get
	// resolved during synthesis.
	LogicalId() *string
	// The tree node.
	Node() constructs.Node
	// The stack in which this element is defined.
	//
	// CfnElements must be defined within a stack scope (directly or indirectly).
	Stack() awscdk.Stack
	// Returns the CfnCondition object from the 'Conditions' section of the CloudFormation template with the given name.
	//
	// Any modifications performed on that object will be reflected in the resulting CDK template.
	//
	// If a Condition with the given name is not present in the template,
	// throws an exception.
	GetCondition(conditionName *string) awscdk.CfnCondition
	// Returns the CfnHook object from the 'Hooks' section of the included CloudFormation template with the given logical ID.
	//
	// Any modifications performed on the returned object will be reflected in the resulting CDK template.
	//
	// If a Hook with the given logical ID is not present in the template,
	// an exception will be thrown.
	GetHook(hookLogicalId *string) awscdk.CfnHook
	// Returns the CfnMapping object from the 'Mappings' section of the included template.
	//
	// Any modifications performed on that object will be reflected in the resulting CDK template.
	//
	// If a Mapping with the given name is not present in the template,
	// an exception will be thrown.
	GetMapping(mappingName *string) awscdk.CfnMapping
	// Returns a loaded NestedStack with name logicalId.
	//
	// For a nested stack to be returned by this method,
	// it must be specified either in the `CfnIncludeProps.loadNestedStacks` property,
	// or through the `loadNestedStack` method.
	GetNestedStack(logicalId *string) *IncludedNestedStack
	// Returns the CfnOutput object from the 'Outputs' section of the included template.
	//
	// Any modifications performed on that object will be reflected in the resulting CDK template.
	//
	// If an Output with the given name is not present in the template,
	// throws an exception.
	GetOutput(logicalId *string) awscdk.CfnOutput
	// Returns the CfnParameter object from the 'Parameters' section of the included template.
	//
	// Any modifications performed on that object will be reflected in the resulting CDK template.
	//
	// If a Parameter with the given name is not present in the template,
	// throws an exception.
	GetParameter(parameterName *string) awscdk.CfnParameter
	// Returns the low-level CfnResource from the template with the given logical ID.
	//
	// Any modifications performed on that resource will be reflected in the resulting CDK template.
	//
	// The returned object will be of the proper underlying class;
	// you can always cast it to the correct type in your code:
	//
	//     // assume the template contains an AWS::S3::Bucket with logical ID 'Bucket'
	//     const cfnBucket = cfnTemplate.getResource('Bucket') as s3.CfnBucket;
	//     // cfnBucket is of type s3.CfnBucket
	//
	// If the template does not contain a resource with the given logical ID,
	// an exception will be thrown.
	GetResource(logicalId *string) awscdk.CfnResource
	// Returns the CfnRule object from the 'Rules' section of the CloudFormation template with the given name.
	//
	// Any modifications performed on that object will be reflected in the resulting CDK template.
	//
	// If a Rule with the given name is not present in the template,
	// an exception will be thrown.
	GetRule(ruleName *string) awscdk.CfnRule
	// Includes a template for a child stack inside of this parent template.
	//
	// A child with this logical ID must exist in the template,
	// and be of type AWS::CloudFormation::Stack.
	// This is equivalent to specifying the value in the `CfnIncludeProps.loadNestedStacks`
	// property on object construction.
	//
	// Returns: the same `IncludedNestedStack` object that `getNestedStack` returns for this logical ID.
	LoadNestedStack(logicalId *string, nestedStackProps *CfnIncludeProps) *IncludedNestedStack
	// Overrides the auto-generated logical ID with a specific ID.
	OverrideLogicalId(newLogicalId *string)
	// Returns a string representation of this construct.
	ToString() *string
}

Construct to import an existing CloudFormation template file into a CDK application.

All resources defined in the template file can be retrieved by calling the `getResource` method. Any modifications made on the returned resource objects will be reflected in the resulting CDK template.

Example:

cfnTemplate := cfn_inc.NewCfnInclude(this, jsii.String("Template"), &CfnIncludeProps{
	TemplateFile: jsii.String("my-template.json"),
})

func NewCfnInclude

func NewCfnInclude(scope constructs.Construct, id *string, props *CfnIncludeProps) CfnInclude

type CfnIncludeProps

type CfnIncludeProps struct {
	// Path to the template file.
	//
	// Both JSON and YAML template formats are supported.
	TemplateFile *string `field:"required" json:"templateFile" yaml:"templateFile"`
	// Specifies whether to allow cyclical references, effectively disregarding safeguards meant to avoid undeployable templates.
	//
	// This should only be set to true in the case of templates utilizing cloud transforms (e.g. SAM) that
	// after processing the transform will no longer contain any circular references.
	// Default: - will throw an error on detecting any cyclical references.
	//
	AllowCyclicalReferences *bool `field:"optional" json:"allowCyclicalReferences" yaml:"allowCyclicalReferences"`
	// Specifies the template files that define nested stacks that should be included.
	//
	// If your template specifies a stack that isn't included here, it won't be created as a NestedStack
	// resource, and it won't be accessible from the `CfnInclude.getNestedStack` method
	// (but will still be accessible from the `CfnInclude.getResource` method).
	//
	// If you include a stack here with an ID that isn't in the template,
	// or is in the template but is not a nested stack,
	// template creation will fail and an error will be thrown.
	// Default: - no nested stacks will be included.
	//
	LoadNestedStacks *map[string]*CfnIncludeProps `field:"optional" json:"loadNestedStacks" yaml:"loadNestedStacks"`
	// Specifies parameters to be replaced by the values in this mapping.
	//
	// Any parameters in the template that aren't specified here will be left unmodified.
	// If you include a parameter here with an ID that isn't in the template,
	// template creation will fail and an error will be thrown.
	//
	// If you are importing a parameter from a live stack, we cannot know the value of that
	// parameter. You will need to supply a value for your parameters, else the default
	// value will be used.
	// Default: - parameters will retain their original definitions.
	//
	Parameters *map[string]interface{} `field:"optional" json:"parameters" yaml:"parameters"`
	// Whether the resources should have the same logical IDs in the resulting CDK template as they did in the original CloudFormation template file.
	//
	// If you're vending a Construct using an existing CloudFormation template,
	// make sure to pass this as `false`.
	//
	// **Note**: regardless of whether this option is true or false,
	// the `CfnInclude.getResource` and related methods always uses the original logical ID of the resource/element,
	// as specified in the template file.
	// Default: true.
	//
	PreserveLogicalIds *bool `field:"optional" json:"preserveLogicalIds" yaml:"preserveLogicalIds"`
}

Construction properties of `CfnInclude`.

Example:

parentTemplate := cfn_inc.NewCfnInclude(this, jsii.String("ParentStack"), &CfnIncludeProps{
	TemplateFile: jsii.String("path/to/my-parent-template.json"),
	LoadNestedStacks: map[string]cfnIncludeProps{
		"ChildStack": &cfnIncludeProps{
			"templateFile": jsii.String("path/to/my-nested-template.json"),
		},
	},
})

type IncludedNestedStack

type IncludedNestedStack struct {
	// The CfnInclude that represents the template, which can be used to access Resources and other template elements.
	IncludedTemplate CfnInclude `field:"required" json:"includedTemplate" yaml:"includedTemplate"`
	// The NestedStack object which represents the scope of the template.
	Stack awscdk.NestedStack `field:"required" json:"stack" yaml:"stack"`
}

The type returned from `CfnInclude.getNestedStack`. Contains both the NestedStack object and CfnInclude representations of the child stack.

Example:

var parentTemplate cfnInclude

includedChildStack := parentTemplate.GetNestedStack(jsii.String("ChildStack"))
childStack := includedChildStack.Stack
childTemplate := includedChildStack.IncludedTemplate

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL