how.wtf

A guide for deploying CloudFormation with CLI using Rain

· Thomas Taylor

Last year, I wrote a post describing the difference between aws cloudformation deploy and aws cloudformation create-stack. I concluded that the easiest method for deploying CloudFormation templates was cloudformation deploy since it handled change sets on your behalf.

My opinion has changed; I discovered a new tool named Rain that is maintained by AWS.

What is Rain

For folks needing to deploy raw CloudFormation templates, rain is your hero. The tool brings you a lot of power:

  1. Allows input parameters
  2. Showcases real-time updates for stack deployments
  3. Filters deployment logs sensibly
  4. Provides the ability to generate CloudFormation templates via AI
  5. Manipulates stack sets
  6. Formats template files

And many more features! As a previous user of CloudFormation, this tool appears amazing.

Installing Rain

There are a few options for installing Rain, but I’ll use the golang install for this tutorial.

1go install github.com/aws-cloudformation/rain/cmd/rain@latest

For MacOS users, brew is easy:

1brew install rain

The remaining releases are featured in their GitHub repository.

Build CloudFormation stacks using Rain

The build command accepts a prompt parameter to generate CloudFormation templates using AI. Let’s use it for generating a CloudFormation template with a S3 bucket.

1rain build -p "A s3 bucket that is secure"

Output:

 1AWSTemplateFormatVersion: 2010-09-09
 2Description: Create an S3 bucket
 3
 4Resources:
 5  MyS3Bucket:
 6    Type: AWS::S3::Bucket
 7    Properties:
 8      BucketName: my-secure-bucket
 9      AccessControl: Private
10      VersioningConfiguration:
11        Status: Enabled

Although the AccessControl property is a considered legacy, it’s great that the template was generated on my behalf!

In addition, the build property will generate a skeleton for resources as well:

1rain build AWS::Route53::CidrCollection

or

1rain build CidrCollection

This outputs a template with all the parameters and dummy CHANGEME values for a Route53 CIDR collection:

 1AWSTemplateFormatVersion: "2010-09-09"
 2
 3Description: Template generated by rain
 4
 5Resources:
 6  MyCidrCollection:
 7    Type: AWS::Route53::CidrCollection
 8    Properties:
 9      Locations:
10        - CidrList:
11            - CHANGEME
12          LocationName: CHANGEME
13      Name: CHANGEME
14
15Outputs:
16  MyCidrCollectionArn:
17    Value: !GetAtt MyCidrCollection.Arn
18
19  MyCidrCollectionId:
20    Value: !GetAtt MyCidrCollection.Id

If you prefer json, there’s a flag for that:

1rain build CidrCollection --json

If you supply the command an ambigious term like “collection”, it provides you a warning:

1rain build collection

Output:

1Ambiguous resource type 'collection'; could be any of:
2  AWS::DevOpsGuru::ResourceCollection
3  AWS::Location::GeofenceCollection
4  AWS::OpenSearchServerless::Collection
5  AWS::Rekognition::Collection
6  AWS::Route53::CidrCollection

Bootstrap using Rain

Before deploying stacks into an environment, let’s run the bootstrap command:

1rain bootstrap

Output:

1Rain needs to create an S3 bucket called 'rain-artifacts-012345678901-us-east-1'. Continue? (Y/n)

This command creates an artifacts bucket that rain references when deploying stacks.

It has the following naming convention:

1rain-artifacts-{accountId}-{regionName}

Deploying stacks using Rain

In this section, we’ll explore deploying templates with rain.

Deploying an S3 bucket

Using the S3 template rain build generated, let’s deploy it!

This is my current directory structure:

1project
2└── s3.yml

This is the command I’ll use to deploy the s3.yml with a stack name of s3-bucket-stack:

1rain deploy s3.yml s3-bucket-stack

If rain bootstrap was not executed before deploying, it’ll prompt you to do so.

My deployment failed with the following message:

1CloudFormation will make the following changes:
2Stack s3-bucket-stack:
3  + AWS::S3::Bucket MyS3Bucket
4Do you wish to continue? (Y/n)
5Deploying template 's3.yml' as stack 's3-bucket-stack' in us-east-1.
6Stack s3-bucket-stack: ROLLBACK_COMPLETE
7Messages:
8  - MyS3Bucket: Resource handler returned message: "my-secure-bucket already exists (Service: S3, Status Code: 0, Request ID: null)" (RequestToken: c4a528c1-2fa0-e75e-efb1-5cfacc2aebd6, HandlerErrorCode: AlreadyExists)
9failed deploying stack 's3-bucket-stack'

Since S3 is a global service, the bucket name my-secure-bucket exists in another account or region. I modified the BucketName to be how-wtf-rain-bucket and issued the same command:

1Deleted existing, empty stack.
2CloudFormation will make the following changes:
3Stack s3-bucket-stack:
4  + AWS::S3::Bucket MyS3Bucket
5Do you wish to continue? (Y/n)
6Deploying template 's3.yml' as stack 's3-bucket-stack' in us-east-1.
7Stack s3-bucket-stack: CREATE_COMPLETE
8Successfully deployed s3-bucket-stack

A couple of awesome things to note:

  1. It handled the failure of the stack gracefully and gave me the exact issue
  2. I deployed again and it took care of everything
  3. I did this all without touching the AWS console.

I am impressed by this tool already!

Deploying a Lambda function

I want to test the rain pkg command with lambda artifacts. Like the cloudformation package command, we specify a lambda directory or handler and it’ll handle zipping and uploading it!

I created a file named lambda.yml with the following contents:

 1AWSTemplateFormatVersion: 2010-09-09
 2Description: Create an lambda function
 3
 4Resources:
 5  LambdaExecutionRole:
 6    Type: AWS::IAM::Role
 7    Properties:
 8      AssumeRolePolicyDocument:
 9        Version: '2012-10-17'
10        Statement:
11          - Effect: Allow
12            Principal:
13              Service:
14                - lambda.amazonaws.com
15            Action:
16              - sts:AssumeRole
17      ManagedPolicyArns:
18        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
19  MyLambdaFunction:
20    Type: AWS::Lambda::Function
21    Properties:
22      Code: lambdafunction/
23      FunctionName: MyLovelyFunction
24      Handler: index.handler
25      Role: !GetAtt LambdaExecutionRole.Arn 
26      Runtime: python3.12
27
28Outputs:
29  LambdaFunctionArn:
30    Description: The ARN of the Lambda function
31    Value: !GetAtt MyLambdaFunction.Arn

This stack creates two resources:

  1. A lambda execution role with basic permissions granted by the AWSLambdaBasicExecutionRole policy
  2. A lambda function with the lambdafunction/ contents

And outputs the lambda function arn.

I ran the fmt command with the --verify flag:

1rain fmt lambda.yml --verify

The --verify command is best saved for CI environments since it outputs a 0 or 1 depending if the formatting is correct.

Output:

1lambda.yml: would reformat

I ran it with the --write command so that it fixed them:

1rain fmt lambda.yml --write

Let’s run the pkg command with an output file named out.yml:

1rain pkg lambda.yml --output out.yml

Here is a snapshot of my directory structure after doing the pkg command:

1project
2├── lambda.yml
3├── lambdafunction
4│   └── index.py
5└── out.yml

Now let’s deploy the out.yml template:

1rain deploy out.yml lambda-function-stack

Output:

 1CloudFormation will make the following changes:
 2Stack lambda-function-stack:
 3  + AWS::IAM::Role LambdaExecutionRole
 4  + AWS::Lambda::Function MyLambdaFunction
 5Do you wish to continue? (Y/n)
 6Deploying template 'out.yml' as stack 'lambda-function-stack' in us-east-1.
 7Stack lambda-function-stack: CREATE_COMPLETE
 8  Outputs:
 9    LambdaFunctionArn: arn:aws:lambda:us-east-1:012345678901:function:MyLovelyFunction # The ARN of the Lambda function
10Successfully deployed lambda-function-stack

Using the LambdaFunctionArn output, let’s invoke the function to ensure everything worked correctly:

1aws lambda invoke --function-name MyLovelyFunction response.txt

Here is the resulting response.txt:

1"Hello world!"

View logs of deployments

Another powerful feature of rain is that it can display sensible logs for stack deployments.

Using log command

Using the lambda function stack from the prior section, we can inspect the logs using the log command:

1rain log lambda-function-stack

Output:

1No interesting log messages to display. To see everything, use the --all flag

Since there were no “interesting” log messages to display, let’s see them all!

1rain log lambda-function-stack --all

Output:

1Jan 14 20:08:52 lambda-function-stack/lambda-function-stack (AWS::CloudFormation::Stack) CREATE_COMPLETE
2Jan 14 20:08:50 lambda-function-stack/MyLambdaFunction (AWS::Lambda::Function) CREATE_COMPLETE
3Jan 14 20:08:43 lambda-function-stack/MyLambdaFunction (AWS::Lambda::Function) CREATE_IN_PROGRESS "Resource creation Initiated"
4Jan 14 20:08:42 lambda-function-stack/MyLambdaFunction (AWS::Lambda::Function) CREATE_IN_PROGRESS
5Jan 14 20:08:40 lambda-function-stack/LambdaExecutionRole (AWS::IAM::Role) CREATE_COMPLETE
6Jan 14 20:08:24 lambda-function-stack/LambdaExecutionRole (AWS::IAM::Role) CREATE_IN_PROGRESS "Resource creation Initiated"
7Jan 14 20:08:22 lambda-function-stack/LambdaExecutionRole (AWS::IAM::Role) CREATE_IN_PROGRESS
8Jan 14 20:08:19 lambda-function-stack/lambda-function-stack (AWS::CloudFormation::Stack) CREATE_IN_PROGRESS "User Initiated"
9Jan 14 20:08:12 lambda-function-stack/lambda-function-stack (AWS::CloudFormation::Stack) REVIEW_IN_PROGRESS "User Initiated"

Generating Gantt chart for deployment times

Another cool feature is viewing the Gantt chart for the deployment times of different resources:

rain log lambda-function-stack --chart > chart.html

Here is the resulting html page:

Gantt chart displaying deployment times for various resources using Rain

Destroying stacks using Rain

Using the prior sections’ stacks, let’s use the rm command to showcase the stack deletion process:

1rain rm lambda-function-stack

Output:

1Stack lambda-function-stack: CREATE_COMPLETE
2Are you sure you want to delete this stack? (y/N) y
3Successfully deleted stack 'lambda-function-stack'

and the S3 bucket stack with the -y so that we don’t have to confirm again:

1rain rm s3-bucket-stack -y

Output:

1Successfully deleted stack 's3-bucket-stack'

Other features

There are other features not covered in this guide:

  1. Stack sets
  2. Forecasting errors using rain forecast (experimental)
  3. Using rain’s instrinsic functions & modules (experimental)

As previously stated, if you need to work with raw CloudFormation - give this tool a try!

#aws   #aws-cli  

Reply to this post by email ↪