This is the first part of a series of posts that provides an introduction to the Lono CloudFormation Framework. I’ve already provided an introduction to raw CloudFormation in the Simple Introduction to AWS CloudFormation Series. This series will focus on Lono.

Note: Source Code for this tutorial is available at: tongueroo/lono-cloudformation-examples

Lono CloudFormation Framework

With the Lono CloudFormation Framework, we can build CloudFormation templates with a DSL. The DSL gives us the full power of a programming language: Ruby. At the same time, the Lono DSL stays close to the declarative nature of CloudFormation. So we get the best of both worlds.

Lono has additional powerful concepts to help with AWS CloudFormation:

Concept Description
Configs Customize the blueprints to fit your needs. Customize at run-time with params or compile-time with variables.
Layering Use layering to build multiple environments like development, production, and more.
DRY Designed to allow you to reuse CloudFormation templates. The configs and templates are separated in a structured and organized way.
Helpers Powerful way to extend Lono and keep your code readable and maintained.

Install

You can install lono with the gem command:

gem install lono

More info: Lono Install

Basic Lono Example

Let’s start by creating a new lono project. We’ll use the lono new command to generate a lono project quickly.

$ lono new infra
=> Creating new project called infra.
      create  infra
      create  infra/.gitignore
      create  infra/Gemfile
      create  infra/Guardfile
      create  infra/README.md
      create  infra/configs/settings.yml
$

Next we’ll generate a blueprint with the lono blueprint new command.

$ cd infra
$ lono blueprint new demo
=> Creating new blueprint called demo.
      create  blueprints/demo
      create  blueprints/demo/demo.gemspec
      create  blueprints/demo/.gitignore
      create  blueprints/demo/.meta/config.yml
      create  blueprints/demo/CHANGELOG.md
      create  blueprints/demo/Gemfile
      create  blueprints/demo/README.md
      create  blueprints/demo/Rakefile
      create  blueprints/demo/seed/configs.rb
       exist  blueprints/demo
      create  blueprints/demo/app/templates/demo.rb
      create  blueprints/demo/app/user_data/bootstrap.sh
       exist  blueprints/demo/app/templates
       exist
      create  configs/demo/params/development.txt
      create  configs/demo/params/production.txt
      create  configs/demo/variables/development.rb
      create  configs/demo/variables/production.rb
$

Check out the generated code.

blueprints/demo/app/templates/demo.rb:

# Simple Starter Demo Example
aws_template_format_version "2010-09-09"
description "Demo stack"

parameter("InstanceType", "t3.micro")

mapping("AmiMap",
  "ap-northeast-1": { ami: "ami-0f9ae750e8274075b" },
  "ap-northeast-2": { ami: "ami-047f7b46bd6dd5d84" },
  # ...
  "us-west-2":      { ami: "ami-061392db613a6357b" }
)

resource("Instance", "AWS::EC2::Instance",
  instance_type: ref("InstanceType"),
  image_id: find_in_map("AmiMap", ref("AWS::Region"), :ami),
  security_group_ids: [get_att("SecurityGroup.GroupId")],
  user_data: base64(user_data("bootstrap.sh"))
)
resource("SecurityGroup", "AWS::EC2::SecurityGroup",
  group_description: "demo security group",
)

output("Instance")
output("SecurityGroup", get_att("SecurityGroup.GroupId"))

The starter blueprint creates an EC2 instance and security group associated with it. The resource method is use to create the AWS::EC2::Instance and AWS::EC2::SecurityGroup.

There are a few built-in helper methods and intrinsic functions also being used here:

Method Description
ref The intrinsic function Ref returns the value of the specified parameter or resource.
find_in_map The intrinsic function Fn::FindInMap returns the value corresponding to keys in a two-level map that is declared in the Mappings section.
get_att The Fn::GetAtt intrinsic function returns the value of an attribute from a resource in the template.
base64 The intrinsic function Fn::Base64 returns the Base64 representation of the input string.
user_data When you launch an instance in Amazon EC2, you have the option of passing user data to the instance that can be used to perform common automated configuration tasks and even run scripts after the instance starts.

Configs

When we ran lono blueprint new demo, it also generated some starter configs for the blueprint. Here’s the structure:

configs/
└── demo
    ├── params
    │   ├── development.txt
    │   └── production.txt
    └── variables
        ├── development.rb
        └── production.rb

Let’s look an the development examples:

configs/demo/params/development.txt:

# Starter Example
# InstanceType=t3.micro

configs/demo/varaibles/development.rb:

# Starter Example
# @variable = "value"

You can see that they are just examples to help get you started. You can change them to fit with how your template works. More info: Lono Configs

Lono Summary

Another handy way to get a summary of the blueprint template is with lono summary

$ lono summary demo
Generating CloudFormation templates for blueprint demo:
  output/demo/templates/demo.yml
=> CloudFormation Template Summary for template demo:
Required Parameters:
  There are no required parameters parameters
Optional Parameters:
  InstanceType (String) Default: t3.micro
Resources:
  1 AWS::EC2::Instance
  1 AWS::EC2::SecurityGroup
  2 Total
$

Deploy Example

Now that we’ve reviewed what was generated. Let’s deploy it. We’ll use the lono cfn deploy

$ lono cfn deploy demo
Deploy demo stack...
Generating CloudFormation templates for blueprint demo:
  output/demo/templates/demo.yml
Uploading app/files...
Uploading CloudFormation templates...
Not modified: output/demo/templates/demo.yml to s3://lono-bucket-usp0x9l7fhr4/development/output/demo/templates/demo.yml
Templates uploaded to s3.
Generating parameter files for blueprint demo:
  output/demo/params/development.json
Using template: output/demo/templates/demo.yml
Using param: configs/demo/params/development.txt
Parameters passed to cfn.create_stack:
---
stack_name: demo
parameters: []
disable_rollback: false
template_url: https://lono-bucket-usp0x9l7fhr4.s3.us-west-2.amazonaws.com/development/output/demo/templates/demo.yml
template_body: 'Hidden due to size... View at: output/demo/templates/demo.yml'
Creating demo stack.
Waiting for stack to complete
11:10:31PM CREATE_IN_PROGRESS AWS::CloudFormation::Stack demo User Initiated
11:10:33PM CREATE_IN_PROGRESS AWS::EC2::SecurityGroup SecurityGroup
11:10:38PM CREATE_IN_PROGRESS AWS::EC2::SecurityGroup SecurityGroup Resource creation Initiated
11:10:38PM CREATE_COMPLETE AWS::EC2::SecurityGroup SecurityGroup
11:10:41PM CREATE_IN_PROGRESS AWS::EC2::Instance Instance
11:10:42PM CREATE_IN_PROGRESS AWS::EC2::Instance Instance Resource creation Initiated
11:10:49PM CREATE_COMPLETE AWS::EC2::Instance Instance
11:10:51PM CREATE_COMPLETE AWS::CloudFormation::Stack demo
Stack success status: CREATE_COMPLETE
Time took for stack deployment: 20s.
$

Here’s a screenshot of the CloudFormation Events.

Here’s a screenshot of the CloudFormation Resources.

For good measure, we can also go to the EC2 Console to confirm that the EC2 Instance was created:

Cleanup

Let’s destroy the resources so we do not get charged more money than we have to. This is super simple.

$ lono cfn delete demo
Are you sure you want to want to delete the demo stack? (y/N)
y
Deleted demo stack.
Waiting for stack to complete
11:17:44PM DELETE_IN_PROGRESS AWS::CloudFormation::Stack demo User Initiated
11:17:45PM DELETE_IN_PROGRESS AWS::EC2::Instance Instance
Stack demo deleted.
Time took for stack deletion: 35s.
$

Summary

There is a lot more you can do with the Lono CloudFormation Framework. This tutorial was designed to be basic intentionally since it is meant to be a simple introduction.

In the next post, we will talk about how to add an EIP and attach it to the EC2 instance.

Lono Introduction Series