- Notifications
You must be signed in to change notification settings - Fork80
A development workflow tool for working with AWS CloudFormation.
License
aws-cloudformation/rain
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
- Documentation:https://aws-cloudformation.github.io/rain/
Rain is what happens when you have a lot of CloudFormation
Rain is also a command line tool for working withAWS CloudFormation templates and stacks.
Join us on Discord to discuss rain and all things CloudFormation! Connect and interact with CloudFormation developers andexperts, find channels to discuss rain, the CloudFormation registry, StackSets,cfn-lint, Guard and more:
Interactive deployments: With
rain deploy
, rain packages your CloudFormation templates, prompts you for any parameters that have not yet been defined, shows you a summary of the changes that will be made, and then displays real-time updates as your stack is being deployed. Once finished, you get a summary of the outcome along with any error messages collected along the way - including errors messages for stacks that have been rolled back and no longer exist.Consistent formatting of CloudFormation templates: Using
rain fmt
, you can format your CloudFormation templates to a consistent standard or reformat a template from JSON to YAML (or YAML to JSON if you prefer). Rain preserves your comments when using YAML and switches use ofintrinsic functions to use the short syntax where possible.Combined logs for nested stacks with sensible filtering: When you run
rain log
, you will see a combined stream of logs from the stack you specified along with any nested stack associated with it. Rain also filters out uninteresting log messages by default so you just see the errors that require attention. You can also userain log --chart
to see a Gantt chart that shows you how long each operation took for a given stack.Build new CloudFormation templates:
rain build
generates new CloudFormation templates containing skeleton resources that you specify. This saves you having to look up which properties are available and which are required vs. optional. Build skeleton templates by specifying a resource name likeAWS::S3::Bucket
, or enable the Bedrock Claude model in your account to use generative AI with a command likerain build --prompt "A VPC with 2 subnets"
. (Note that Bedrock is not free, and requires somesetup).NEW Use therain build --recommend
command to pick from a list of functional templates that will pass typical compliance checks by default. These templates are great starting points for infrastructure projects.Build policy validation files: The
rain build
command can now send prompts to the Bedrock Cloude3 Haiku and Sonnet models, which can write Open Policy Agent (OPA) Rego files or CloudFormation Guard files, to verify the compliance of your templatesManipulate CloudFormation stack sets:
rain stackset deploy
creates a new stackset, updates an existing one or adds a stack instance(s) to an existing stack set. You can list stack sets usingrain stackset ls
, review stack set details withrain stackset ls <stack set name>
and delete stack set and\or its instances withrain stackset rm <stack set name>
Predict deployment failures (EXPERIMENTAL):
rain forecast
analyzes a template and the target deployment account to predict things that might go wrong when you attempt to create, update, or delete a stack. This command speeds up development by giving you advanced notice for issues like missing permissions, resources that already exist, and a variety of other common resource-specific deployment blockers.Modules (EXPERIMENTAL):
rain pkg
supports client-side module development. Rain modules are partial templates that are inserted into the parent template, with some extra functionality added to enable extending existing resource types. This feature integrates with CodeArtifact to enable package publish and install.Content Deployment (EXPERIMENTAL):
rain deploy
andrain rm
support metadata commands that can upload static assets to a bucket and then delete those assets when the bucket is deleted. Rain can also run build scripts before and after stack deployment to prepare content like web sites and lambda functions before uploading to S3.
Note that in order to use experimental commands, you have to add--experimental
or-x
as an argument.
If you havehomebrew installed,brew install rain
Or you can download the appropriate binary for your system fromthe releases page.
Or if you're aGopher, you cango install github.com/aws-cloudformation/rain/cmd/rain@latest
Usage: rain [command]Stack commands: cat Get the CloudFormation template from a running stack cc Interact with templates using Cloud Control API instead of CloudFormation deploy Deploy a CloudFormation stack or changeset from a local template logs Show the event log for the named stack ls List running CloudFormation stacks or changesets rm Delete a CloudFormation stack or changeset stackset This command manipulates stack sets. watch Display an updating view of a CloudFormation stackTemplate commands: bootstrap Creates the artifacts bucket build Create CloudFormation templates diff Compare CloudFormation templates fmt Format CloudFormation templates forecast Predict deployment failures merge Merge two or more CloudFormation templates module Interact with Rain modules in CodeArtifact pkg Package local artifacts into a template tree Find dependencies of Resources and Outputs in a local templateOther Commands: console Login to the AWS console help Help about any command info Show your current configuration
You can find shell completion scripts indocs/bash_completion.sh anddocs/zsh_completion.sh.
Rain is written inGo and uses theAWS SDK for Go v2.
To contribute a change to Rain,fork this repository, make your changes, and submit a Pull Request.
TheREADME.md
, documentation indocs/
, the auto completion scripts and a copy of the cloudformation specification incft/spec/cfn.go
are generated throughgo generate
.
Rain is licensed under the Apache 2.0 License.
Therain pkg
command can be used as a replacement for theaws cloudformation package
CLI command. When packaging a template,rain
looks for specificdirectives to appear in resources.
The!Rain::Embed
directive simply inserts the contents of a file into the template as a string.
The template:
Resources:Test:Type:AWS::CloudFormation::WaitConditionHandleMetadata:Comment:!Rain::Embed embed.txt
The contents ofembed.txt
, which is in the same directory as the template:
This is a test
The resulting packaged template:
Resources:Test:Type:AWS::CloudFormation::WaitConditionHandleMetadata:Comment:This is a test
The!Rain::Include
directive parses a YAML or JSON file and inserts the object into the template.
The template:
Resources:Test:!Rain::Include include-file.yaml
The file to be included:
Type:AWS::S3::BucketProperties:BucketName:test
The resulting packaged template:
Resources:Test:Type:AWS::S3::BucketProperties:BucketName:test
The!Rain::Env
directive reads environment variables and inserts them into the template as strings.
The template:
Resources:Test:Type:AWS::S3::BucketProperties:BucketName:!Rain::Env BUCKET_NAME
The resulting packaged template, if you have exported an environment variable namedBUCKET_NAME
with valueabc
:
Resources:Test:Type:AWS::S3::BucketProperties:BucketName:abc
The!Rain::S3Http
directive uploads a file or directory to S3 and inserts theHTTPS URL into the template as a string.
The template:
Resources:Test:Type:A::B::CProperties:TheS3URL:!Rain::S3Http s3http.txt
If you have a file calleds3http.txt
in the same directory as the template,rain will use your current default profile to upload the file to the artifactbucket that rain creates as a part of bootstrapping. If the path provided is adirectory and not a file, the directory will be zipped first.
Resources:Test:Type:A::B::CProperties:TheS3URL:https://rain-artifacts-012345678912-us-east-1.s3.us-east-1.amazonaws.com/a84b588aa54068ed4b027b6e06e5e0bb283f83cf0d5a6720002d36af2225dfc3
The!Rain::S3
directive is basically the same asS3Http
, but it inserts the S3 URI instead of an HTTPS URL.
The template:
Resources:Test:Type:A::B::CProperties:TheS3URI:!Rain::S3 s3.txt
If you have a file calleds3.txt
in the same directory as the template,rain will use your current default profile to upload the file to the artifactbucket that rain creates as a part of bootstrapping. If the path provided is adirectory and not a file, the directory will be zipped first.
Resources:Test:Type:A::B::CProperties:TheS3URI:s3://rain-artifacts-755952356119-us-east-1/a84b588aa54068ed4b027b6e06e5e0bb283f83cf0d5a6720002d36af2225dfc3
If instead of providing a path to a file, you supply an object with properties, you can exercise more control over how the object is uploaded to S3. The following example is a common pattern for uploading Lambda function code. The optionalRun
property is a local script that you want Rain to run before uploading the content atPath
.
Resources:MyFunction:Type:AWS::Lambda::FunctionProperties:Code:!Rain::S3Path:lambda-distZip:trueBucketProperty:S3BucketKeyProperty:S3KeyRun:buildscript.sh
The packaged template:
Resources:MyFunction:Type:AWS::Lambda::FunctionProperties:Code:S3Bucket:rain-artifacts-012345678912-us-east-1S3Key:1b4844dacc843f09941c11c94f80981d3be8ae7578952c71e875ef7add37b1a7
Sometimes you require that objects uploaded to S3 have a specific extension, use theExtension
property to ensure the artifact in S3 ends ..
Resources:Test:Type:A::B::CProperties:TheS3URI:!Rain::S3Path:testExtension:sh
The packaged template:
Resources:Test:Type:A::B::CProperties:TheS3URI:s3://rain-artifacts-012345678912-us-east-1/a84b588aa54068ed4b027b6e06e5e0bb283f83cf0d5a6720002d36af2225dfc3.sh
You can add a metadata section to anAWS::S3::Bucket
resource to take additional actions during deployment, such as running pre and post build scripts, uploading content to the bucket after stack deployment completes, and emptying the contents of the bucket when the stack is deleted.
Resources:Bucket:Type:AWS::S3::BucketMetadata:Rain:EmptyOnDelete:trueContent:site/distVersion:2DistributionLogicalId:SiteDistributionRunBefore:Command:buildsite.shArgs: -ALiteralArgumentRunAfter:Command:buildsite.shArgs: -Rain::OutputValue AStackOutputKey
EmptyOnDelete
: If true, the bucket's contents, including all versions, will be deleted so that the bucket itself can be deleted. This can be useful for development environments, but be careful about using it in production!
Version
: Rain doesn't do anything with this, but incrementing the number can force the stack to deploy if there have been no infrastructure changes to the stack.
RunBefore
: Rain will run this command before the stack deploys. Useful to run your website build script before bothering to deploy, to make sure it builds successfully.
RunAfter
: Rain will run this command after deployment, and it is capable of doing stack output lookups to provide arguments to the script. This is useful if you deployed a resource like an API Gateway and need to know the stage URL to plug in to your website configuration. UseRain::OutputValue OutputKey
to pass one of the arguments to the script.
DistributionLogicalId
: Supply the logical id of a CloudFront distribution to invalidate all files in it after the content upload completes.
Seetest/webapp/README.md
for a complete example of using these commands with Rain modules.
You can use Rain to package templates with client-side modules, which givesCloudFormation multi-file support. This feature is compatible with (upcoming)functionality in the AWS CLIcloudformation package
command. This feature isstill considered experimental, so you need to use the--experimental
flag touse it. The UX is fairly stable at this point, but we still reserve the rightto change the behavior in a minor version release.
Therain pkg
command does not actually deploy any resources if the templatedoes not upload any objects to S3, so you always have a chance to review thepackaged template. It's recommended to run linters and scanners on the packagedtemplate, rather than a pre-processed template that makes use of these advanceddirectives. (We are working on native support for modules in cfn-lint and inIDE plugins)
A prior version of modules in Rain made use of theRain::Module
resource typedirective, which is still available for backwards compatibility.
Modules are imported into the parent template or parent module via a newModules
section. This is a departure from registry modules and from the priorversion of Rain modules, which are configured as resources.
Modules: Content: Source: ./module.yaml
Modules are very similar to CloudFormation templates. They supportParameters
,Resources
,Conditions
, andOutputs
.Parameters
areconfigured with theProperties
attribute in the parent, and act much likenormal parameters, except it is possible to pass in objects and lists to amodule. Any valid CloudFormation template can be used as a module.
A sample module:
Parameters: Name: Type: ScalarResources: Bucket: Type: AWS::S3::Bucket Metadata: OverrideMe: abc Properties: BucketName: !Ref NameOutputs: BucketArn: Value: !GetAtt Bucket.Arn
TheOutputs
of a module are reference-able in the parent by usingGetAtt
orSub
for scalars andRef
for objects.
An example of using the module above:
Modules: Content: Source: ./module.yaml Properties: Name: foo Overrides: Bucket: Metadata: OverrideMe: defOutputs: TheArn: Value: !GetAtt Content.BucketArn
TheOverrides
attribute is a new feature that distinguishes local modulesfrom registry modules. This allows the consumer to override content from themodule that is included in the parent template, if they know the internalstructure of the module. Instead of forcing a module author to anticipate everypossible use case with numerous parameters, the author can focus on basic usecases and allow the consumer to get creative if they want to change certainelements of the output. UsingOverrides
carries a risk of breaking changeswhen a module author changes things, so it will generally be safer to rely onParameters
andReferences
, but the overrides provide a flexible andeasy-to-use escape hatch that can solve some tricky design challengesassociated with a declarative language like YAML.
The packaged output of the above template:
Resources: ContentBucket: Type: AWS::S3::Bucket Metadata: OverrideMe: def Properties: BucketName: foo
You can also define constant values in modules. TheConstants
section is asimple key-value list of strings or objects that are referred to later withFn::Sub
orRef
. When referencing an object, only the entire object can bereferenced, not any sub-elements of the object.
Constants: S3Arn: "arn:${AWS::Partition}:s3:::" BucketName: "${Name}-${AWS::Region}-${AWS::AccountId}" BucketArn: "${Const::S3Arn}${Const::BucketName}" AnObject: Foo: bar
Before any processing of the template happens, all instances of constants in!Sub
strings are replaced, including in subsequent constant declarations.Constant references are prefixed byConst::
. Constants are supported notjust in modules, but in the parent template as well. For constants that areobjects, they are referenced with!Ref Const::name
.
After constants are processed, theModules
section is processed. Modulesource files are specified using a path that is relative to the parent templateor module. The path is not relative to where thepackage
command is beingrun. So, if a module is in the same directory as the parent, it is simplyreferred to asmodule.yaml
or./module.yaml
. Modules can also reference anHTTPS URL as the source location.
Modules can contain other modules, with no enforced maximum limit on nesting.Modules are not allowed to refer to themselves directly or in cycles. Module Acan’t import Module A, and it can’t import Module B if that module importsModule A.
Modules support a basic form of looping/foreach by either using the familiarFn::ForEach
syntax, or with a shorthand by adding aForEach
attribute tothe module configuration. Special variables$Identifier
and$Index
can beused to refer to the value and list index. With the shorthand, or if you don'tput the Identifier in the logical id, logical ids are auto-incremented byadding an integer starting at zero. Since this is a client-side-only feature,list values must be fully resolved scalars, not values that must be resolved atdeploy time.
Parameters: List: Type: CommaDelimitedList Default: A,B,CModules: Content: Source: ./map-module.yaml ForEach: !Ref List Properties: Name: !Sub my-bucket-$MapValue
Assuming the module itself simply creates a bucket, the output would be:
Parameters: List: Type: CommaDelimitedList Default: A,B,CResources: Content0Bucket: Type: AWS::S3::Bucket Properties: BucketName: my-bucket-A Content1Bucket: Type: AWS::S3::Bucket Properties: BucketName: my-bucket-B Content2Bucket: Type: AWS::S3::Bucket Properties: BucketName: my-bucket-C
It’s also possible to refer to elements within aMap
using something like!GetAtt Content[0].Arn
for a single element, or!GetAtt Content[].Arn
,which resolves to a list of all of theArn
outputs from that module.
When a module is processed, the first thing that happens is parsing of theConditions within the module. Any Resources, Modules, or Outputs marked with afalse condition are removed, and any property nodes with conditions areprocessed. Any values of !Ref AWS::NoValue are removed. Any unresolvedconditions (for example, a condition that references a paramter in the parenttemplate, or something like AWS::Region) are emitted into the parent template,prefixed with the module name.
Much of the value of module output is in the smart handling ofRef
,Fn::GetAtt
, andFn::Sub
. For the most part, we want these to “just work” inthe way that an author would expect them to. Since the logical IDs in themodule result in a concatenation of the module name and the ID specified insidethe module, we have to modify self-references within the module. And we have tofix up any references to parameter values, so that in the final renderedoutput, they match up with actual IDs and property names.
From the parent, the author has two options for referring to module properties.If they know the structure of the module, they can use the predicted final namefor resources, in which case we leave the strings alone. For example, in amodule that creates a bucket, if the logical id within the module isBucket
and the name of the module in the parent isContent
, the author could write!Ref ContentBucket
or!GetAtt ContentBucket.BucketName
. The second, saferway would be for the module author to specify an output reference calledName
that refs the bucket name, so the parent author could write!GetAtt Content.Name
. Module authors are encouraged to provideOutputs
that provideaccess to all needed values, but there will be cases when they cannot predicteverything a consumer needs. ForSub
strings, if we can fully resolve thevalue, we get rid of theFn::Sub
and simply write the string to the output.The parent can also refer directly to theProperties
defined for a moduleimport in theModules
section.
Complex objects are merged when an override is specified, so it’s possible todo things like add statements to a policy without actually overwriting theentire thing. This is usually what the author wants, but the tradeoff is thatthere is no way to delete an element from a list in the module.
As a part of this design, we are adding support for intrinsic functions that donot exist for normal CloudFormation templates, and can only be processed by thepackage
command. We also augment the behavior of some intrinsics. Thesefunctions are fully resolved locally and won’t need server-side support.
Fn::Merge
merges the contents of multiple objects. This is useful for thingslike tagging, where each module might want to add a set of tags, in addition toany tags defined by the parent module.
Fn::Select
is collapsed if we can fully resolve the return value as a string.We do the same forFn::Join
.
Fn::InsertFile
inserts the contents of a local file directly into thetemplate as a string.
Fn::Invoke
allows modules to be treated sort of like functions. A module canbe created with onlyParameters
andOutputs
, and by usingInvoke
later,you can get different outputs. An example use case is a module that createsstandard resource names, accepting things likeEnv
andApp
andName
asparameters, and returning a concatenated string.
When processing modules, the package command adds metadata to the template formetrics gathering.
Metadata: AWSToolsMetrics: Rain: '{"Version":"v1.21.0","Experimental":true,"HasModules":true,"HasRainSection":false}'
Each rendered resource has an added Metadata property to indicate where itoriginated. This can be useful for tooling such as IDE integration, and fortroubleshooting in the console.
Resources: ContentBucket: Metadata: SourceMap: "./modules/bucket.yaml:Bucket:35"
In order to reference a collection of modules, you can add aPackages
sectionto the template. Packages are zip files containing modules.
Packages: abc: Source: ./package.zip def: Source: https://example.com/packages/package.zip
Packages are referenced by using an alias to the package name that starts with$
.
Modules: Foo: Source: $abc/foo.yaml Bar: Source: $def/a/b/bar.yaml
Rain integrates with AWS CodeArtifact to enable an experience similar to npmpublish and install. A directory that includes Rain module YAML files can bepackaged up withrain module publish
, and then the directory can be installedby developers withrain module install
.
A module package is published and released from this repository separately fromthe Rain binary release. This allows the package to be referenced by versionnumbers using tags, such asm0.1.0
as shown in the example above. The majorversion number will be incremented if any breaking changes are introduced tothe modules. The available modules in the release package are listed below.Treat these modules as samples to be used as a proof-of-concept for buildingyour own module packages.
A VPC with just two availability zones. This module is useful for POCs and simple projects.
A simple bucket with encryption enabled and public access blocked
A bucket, plus extra buckets for access logs and replication and a bucket policy that should pass most typical compliance checks.
A bucket policy that denies requests not made with TLS.
An ELBv2 load balancer
An S3 bucket and a CloudFront distribution to host content for a web site
A Cognito User Pool and associated resources
An API Gateway REST API
A Lambda function and associated API Gateway resources
Inside a module, you can add a Metadata attribute to show or hide resources,depending on whether the parent template sets a parameter value. This issimilar to the Conditional section in a template, but somewhat simpler, and itonly works in modules. Modules also support normal Conditions.IfParam
isbasically just a shorthand for writing the complete Condition.
Resources:Bucket:Type:AWS::S3::BucketMetadata:Rain:IfParam:Foo
If the parent template does not set a value for theFoo
property, the module willomit the resource. The opposite is true forIfNotParam
.
IfParam
can be useful to make flexible modules that can optionally do things likeconfigure permissions for related resources, like allowing access to a bucket or table.
IfNotParam
is useful if you have pre-created a resource and you don't want the moduleto create it for you.
Output a chart to an HTML file that you can view with a browser to look at how long stack operations take for each resource.
rain log --chart CDKToolkit > ~/Desktop/chart.html
You can now write CloudFormation templates in Apple's new configurationlanguage, Pkl. Rain commands that accept input as JSON or YAML now also acceptfiles with the.pkl
extension. We host a Pkl package in aseparaterepo that isgenerated based on the CloudFormation registry. This package has classes thatcan be imported for each registry resource type, in addition to higher levelpatterns. This allows you to write a type-safe template and create your ownclient-side modules, in a way that is similar to CDK, but with declarativecode.
Example Pkl template:
amends"package://github.com/aws-cloudformation/cloudformation-pkl/releases/download/cloudformation@0.1.1/cloudformation@0.1.1#/template.pkl"import"package://github.com/aws-cloudformation/cloudformation-pkl/releases/download/cloudformation@0.1.1/cloudformation@0.1.1#/cloudformation.pkl"ascfnimport"package://github.com/aws-cloudformation/cloudformation-pkl/releases/download/cloudformation@0.1.1/cloudformation@0.1.1#/aws/s3/bucket.pkl"asbucketDescription ="Create a bucket"Parameters { ["Name"] {Type ="String"Default ="baz" }}Resources { ["MyBucket"]=newbucket.Bucket {BucketName = cfn.Ref("Name") }}
You can put constants into theRain
section in the template and then refer tothose constants later in the template. Constants can be strings or entireobjects (but only strings can be used later in Sub functions). Use the!Rain::Constant
directive to refer to a constant in the template. Forstrings, you can add constants to!Sub
strings with the${Rain::ConstantName}
pseudo-parameter. Constants can contain previosulydeclared constants in Sub strings using the same format. TheRain
sectiongets removed during packaging.
Parameters:Prefix:Type:StringRain:Constants:Test1:ezbeard-rain-test-constantsTest2:!Sub ${Prefix}-${Rain::Test1}-SubTestResources:Bucket:Type:AWS::S3::BucketProperties:BucketName:!Rain::Constant Test1Bucket2:Type:AWS::S3::BucketProperties:BucketName:!Rain::Constant Test2Bucket3:Type:AWS::S3::BucketProperties:BucketName:!Sub "pre-${Prefix}-${Rain::Test1}-suffix"
Runrain pkg
on that template to produce the following output:
Parameters:Prefix:Type:StringResources:Bucket:Type:AWS::S3::BucketProperties:BucketName:ezbeard-rain-test-constantsBucket2:Type:AWS::S3::BucketProperties:BucketName:!Sub ${Prefix}-ezbeard-rain-test-constants-SubTestBucket3:Type:AWS::S3::BucketProperties:BucketName:!Sub pre-${Prefix}-ezbeard-rain-test-constants-suffix
Validate CloudFormation yaml/json templates against the CloudFormation spec and additional checks. Includes checking valid values for resource properties and best practices.
Guard is a policy evaluation tool that allows you to build your own rules with a custom DSL. You can also pull rules from theguard registry to scan your templates for resources that don't comply with common best practices.
taskcat is a tool that tests AWS CloudFormation templates. It deploys your AWS CloudFormation template in multiple AWS Regions and generates a report with a pass/fail grade for each region. You can specify the regions and number of Availability Zones you want to include in the test, and pass in parameter values from your AWS CloudFormation template. taskcat is implemented as a Python class that you import, instantiate, and run.
Are we missing an excellent tool? Let us know via a GitHub issue.
About
A development workflow tool for working with AWS CloudFormation.
Resources
License
Code of conduct
Security policy
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.