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)

FunctionUsecases
Fn::Base64UserData of EC2 instances
Fn::CidrCIDR address blocks / Networking
Fn::FindInMapLookups of values i.e. AMIs by region
Fn::GetAttCross referencing templates (including self)
Fn::GetAZsNetworking / Subnets
Fn::ImportValueCross referencing templates (including self)
Fn::JoinMerges an array into a string
Fn::SelectPicking a value from an array
Fn::SplitTurns a string into an array
Fn::SubSubstituting one value for another
Fn::TransformCalling CloudFormation Macros
RefCross referencing templates (including self)
Fn::AndConditional operation
Fn::OrConditional operation
Fn::EqualsConditional function
Fn::IfConditional function
Fn::NotConditional 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.

ParameterReturns
AWS::AccountIdThe account ID
AWS::NotificationARNsA list of notification ARNs for a stack
AWS::NoValueRemoves the corresponding resource when specified using Fn::If
AWS::PartitionThe partition a resource is in, only relevant to specialist regions like China and US Government
AWS::RegionThe current region
AWS::StackIdThe ID of the stack currently created
AWS::StackNameThe Name of the stack currently created
AWS::URLSuffixThe 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