AWS DevOps Pro Certification Blog Post Series: CloudFormation


This is part of the blog post series: AWS DevOps Pro Certification

Caveat emptor

Using AWS costs money, some of these services may not be part of the AWS Free Tier. You can keep costs down by tearing down anything you've created whilst learning, but it's still possible to run up a hefty bill so pay attention to the instances you setup!

I'm very lucky to be able to use my employer's AWS account. You should ask your place of work if a similar arrangement can be made as part of your study.

Velocius quam asparagi conquantur

The format of the blog posts is liable to change as I try to refine my mental model of each domain, so be sure to revisit the blog posts on a regular basis.

What?

CloudFormation is a templating language that can be expressed as JSON or YAML. This is one tool that falls under the Infrastructure as Code (IaC) category for this domain.

The core concepts you need to be aware of are:

Intrinsic functions (helpers)

Function Usecases
Fn::Base64 UserData of EC2 instances
Fn::Cidr CIDR address blocks / Networking
Fn::FindInMap Lookups of values i.e. AMIs by region
Fn::GetAtt Cross referencing templates (including self)
Fn::GetAZs Networking / Subnets
Fn::ImportValue Cross referencing templates (including self)
Fn::Join Merges an array into a string
Fn::Select Picking a value from an array
Fn::Split Turns a string into an array
Fn::Sub Substituting one value for another
Fn::Transform Calling CloudFormation Macros
Ref Cross referencing templates (including self)
Fn::And Conditional operation
Fn::Or Conditional operation
Fn::Equals Conditional function
Fn::If Conditional function
Fn::Not Conditional function

Resource Attributes

CreationPolicy:
  ResourceSignal:
    Count: '3' # 3 instances have been created, so 3 signals will need to be generated before fulfilling the CreationPolicy requirements
    Timeout: PT15M

Timeout value is ISO8601 durations format: PT#H#M#S where # is the number of hours, minutes and seconds. Give the instances as long as possible, if the timeout is too short you will trigger rollbacks. PT15M is a timeout of 15 minutes

To send a signal, you need to install help script called cfn-signal on the resources (usually done in the User Data area of EC2 instances).

Know when to use the Wait condition over a CreationPolicy.

Pseudo Parameters

Are predefined parameters that return a valued on the current context i.e. current account or region in use.

Parameter Returns
AWS::AccountId The account ID
AWS::NotificationARNs A list of notification ARNs for a stack
AWS::NoValue Removes the corresponding resource when specified using Fn::If
AWS::Partition The partition a resource is in, only relevant to specialist regions like China and US Government
AWS::Region The current region
AWS::StackId The ID of the stack currently created
AWS::StackName The Name of the stack currently created
AWS::URLSuffix The suffix for a domain typically amazonaws.com, but may vary for specialized regions

Nested stacks

Stack updates

Stack Policy is JSON only.

Custom Resources

Custom Resources are a way to provision and track resources that are not supported directly through CloudFormation.

The request/response mechanism is either an SNS topic or Lambda backed ARN.

AWS has a [walk through][docs_custom_resource] that demonstrates how to create a custom resource to perform an AMI lookup (using Lambda) to provide the correct AMI for a given region (in this case the region where we create the stack) and CPU type.

Before custom resources, you would've had to keep a static list of AMIs in the Mappings section of a template.

Why?

This allows you to define your infrastructure as code, rather than manual steps carried out via various UIs (Console and CLI)

When?

How?

Here's a very basic example of CloudFormation, we'll use it to create an S3 bucket.

The CloudFormation template: hello-bucket.yaml.

N.B. To keep things terse, I've decided to only use YAML as the template format. CloudFormation can use JSON (in fact this was the original format, so you will still find a lot of examples in this format).

Resources:
  HelloBucket:
    Type: AWS::S3::Bucket

HelloBucket is the logical name of our resource, and the type we've gone for is an S3 bucket.

Let's use the CLI to create the stack based on this template.

aws cloudformation create-stack \
  --stack-name hellostack \
  --template-body \
  file:///path/to/hello-bucket.yaml 
{
    "StackId": "arn:aws:cloudformation:eu-west-3:xxx:stack/hellostack/4a3b0220-7552-11e9-acf0-0a230f532f04"
}  

aws cloudformation describe-stacks
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:eu-west-3:xxx:stack/hellostack/4a3b0220-7552-11e9-acf0-0a230f532f04",
            "StackName": "hellostack",
            "CreationTime": "2019-05-13T07:39:50.524Z",
            "RollbackConfiguration": {},
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Tags": [],
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}

Let's check on the status of stack creation.

 aws cloudformation describe-stack-resources --stack-name hellostack
{
    "StackResources": [
        {
            "StackName": "hellostack",
            "StackId": "arn:aws:cloudformation:eu-west-3:xxx:stack/hellostack/4a3b0220-7552-11e9-acf0-0a230f532f04",
            "LogicalResourceId": "HelloBucket",
            "PhysicalResourceId": "hellostack-hellobucket-1ux8azkoq7t0t",
            "ResourceType": "AWS::S3::Bucket",
            "Timestamp": "2019-05-13T07:40:15.289Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}

Useful fields:

We can verify the name of the bucket by using the s3api command:

aws s3api list-buckets | jq '.Buckets[] | select(.Name | contains("hellostack"))'
{
  "Name": "hellostack-hellobucket-1ux8azkoq7t0t",
  "CreationDate": "2019-05-13T07:39:55.000Z"
}

Finally let's tear down, and verify the bucket has been deleted.

aws cloudformation delete-stack --stack-name hellostack
# no output

aws s3api list-buckets | jq '.Buckets[] | select(.Name | contains("hellostack"))'
# no output

API and CLI features and verbs

Features

**Verbs (CRUD) **

Outliers

AWS DevOps Pro Certification Blog Post Series



Tweet