EDSL for AWS CloudFormation

Version on this page:0.4.0
LTS Haskell 8.2:0.4.0
Stackage Nightly 2017-02-22:0.4.0
Latest on Hackage:0.4.0
MIT licensed
Maintained by David Reaver

Module documentation for 0.4.0

Stratosphere: AWS CloudFormation in Haskell

Circle CI

AWS CloudFormation is a system that provisions and updates Amazon Web Services (AWS) resources based on declarative templates. Common criticisms of CloudFormation include the use of JSON as the template language and limited error-checking, often only available in the form of run-time errors and stack rollbacks. By wrapping templates in Haskell, we are able to easily construct them and help ensure correctness.

The goals of stratosphere are to: Build a Haskell EDSL to specify CloudFormation templates. Since it is embedded in Haskell, it is type-checked and generally much easier to work with than raw JSON. Have a simple checking/linting system outside of the types that can find common errors in templates. * Be able to also read valid CloudFormation JSON templates so they can be type-checked. This also gives us free integration tests by using the huge amount of example templates available in the AWS docs.


Here is an example of a Template that creates an EC2 instance, along with the JSON output:

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import qualified Data.ByteString.Lazy.Char8 as B
import Stratosphere

main :: IO ()
main = B.putStrLn $ encodeTemplate instanceTemplate

instanceTemplate :: Template
instanceTemplate =
  [ resource "EC2Instance" (
    EC2InstanceProperties $
    & eciKeyName ?~ (Ref "KeyName")
    & deletionPolicy ?~ Retain
  & description ?~ "Sample template"
  & parameters ?~
  [ parameter "KeyName" "AWS::EC2::KeyPair::KeyName"
    & description ?~ "Name of an existing EC2 KeyPair to enable SSH access to the instance"
    & constraintDescription ?~ "Must be the name of an existing EC2 KeyPair."
  "Description": "Sample template",
  "Parameters": {
    "KeyName": {
      "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instance",
      "ConstraintDescription": "Must be the name of an existing EC2 KeyPair.",
      "Type": "AWS::EC2::KeyPair::KeyName"
  "Resources": {
    "EC2Instance": {
      "DeletionPolicy": "Retain",
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "KeyName": {
          "Ref": "KeyName"
        "ImageId": "ami-22111148"

Please see the examples directory for more in-depth examples.

Value Types

CloudFormation resource parameters can be literals (strings, integers, etc), references to another resource or a Parameter, or the result of some function call. We encapsulate all of these possibilities in the Val a type.

We recommend using the OverloadedStrings extension to reduce the number of Literals you have to use.

Note that CloudFormation represents numbers and bools in JSON as strings, so we had to some types called Integer' and Bool' to override the aeson instances. In a future version we plan on using our own JSON encoder/decoder to get around this.


Almost every CloudFormation resource has a handful of required arguments, and many more optional arguments. Each resource is represented as a record type with optional arguments wrapped in Maybe. Each resource also comes with a constructor that accepts required resource parameters as arguments. This allows the user to succinctly specify the resource parameters they actually use without adding too much noise to their code.

To specify optional arguments, we recommend using the lens operators & and ?~. In the example above, the ec2Instance function takes the AMI as an argument, since it is required by the EC2Instance resource type. Then, the optional EC2 key name is specified using the & and ?~ lens operators.

This approach is very similar to the approach taken by the amazonka library. See this blog post for an explanation.


All of the resources and resource properties are auto-generated from a JSON schema file and are placed in library-gen/. The gen/ directory contains the auto-generator code and the JSON model file. We include the library-gen/ directory in git so the build process is simplified. To build library-gen from scratch and then build all of stratosphere, just run the very short script. You can pass stack args to the script too, so run ./ --fast to build the library without optimization. This is useful for development.

In the future, it would be great to not have to include the auto-generated code in git.


Feel free to raise any issues, or even just make suggestions, by filing a Github issue.

Future Work

  • Implement basic checker for things like undefined Refs and duplicate field names. This stuff would be too unwieldy to do in types, and performing a checking pass over a template should be pretty straightforward.
  • Use a custom JSON encoder so the templates look a little more idiomatic. We also create a lot of empty whitespace and newlines using aeson-pretty. There are limits on the size of CloudFormation templates, and we want readable output without hitting the limits. Also, we have some newtypes that just exist to override aeson instances, and we could get rid of those.
  • Use a custom JSON decoder with useful error messages. Although we don't use them, we have implemented FromJSON instances for everything. Theoretically, stratosphere could be used as a checker/linter for existing JSON CloudFormation templates.


Change Log


  • Derive the Eq type class for everything. This is useful when comparing two templates.
  • Added the new Fn::Split function.
  • Fix error in resource specification document where AWS::AutoScaling::AutoScalingGroup.Tags was mistakenly called AsTags.
  • Fix error in resource specification document where the two properties Image and Name in AWS::ECS::TaskDefinition.ContainerDefinition were incorrectly marked as not required, even though they are required.
  • Fix JSON instances for auto scaling group update policies


  • Updated resource specification document to version 1.1.1.


  • Backwards-incompatible: We now use the official AWS JSON spec document to auto-generate types. This means there is no more Python scraper and custom JSON schemas. The behavior of the library is exactly the same, but a ton of resource names changed to match official the official AWS names. On the plus side, we now have 100% service coverage!


  • Fixed a test suite failure caused by bleeding edge HLint version.


  • Added Dynamo DB table resources (@ababkin)
  • Fix the Python docs scraper mishandling the required value in some cases, and also missing some properties of resources (@amar47shah)
  • Added a ton of SNS and SQS resources (@ababkin)
  • Added a experimental checker for duplicate resource names (@amar47shah)


  • Breaking change: The DependsOn property previously allowed lists of Val Text, when in fact CloudFormation only accepts literal Text values. The new type of DependsOn is Maybe [Text].
  • Added AWS::ElastiCache::CacheCluster resource (@MichaelXavier)
  • Added many AWS::Lambda resources and associated resource properties (@ababkin)
  • Added new ImportValue CloudFormation function (@timmytofu)
  • Added tons of AWS Kineses resources (@MichaelXavier)
  • Added a lot of Api Gateway resources (@ababkin)
  • Allow setting LensPrefix in JSON model files to avoid name collisions (


  • Fix Haddock parsing for FindInMap. We now run haddock in CircleCI so we shouldn't see a regression like this in the future.


  • Added S3 buckets and bucket policies
  • Added CloudTrail Trail (Thanks @timmytofu!)
  • Added the FindInMap intrinsic function. (Thanks @MichaelXavier!)
  • Added SecurityGroupEgress and SecurityGroupIngress rules. (Thanks @MichaelXavier!)
  • Fixed type of ELB policy using the more specific NameAndValue type. (Thanks @MichaelXavier!)


  • Added UserName property to the IAM User resource. (Thanks @timmytofu!)
  • Added IAM Group and Role name parameters. (Thanks again @timmytofu!)


  • Update aeson-pretty to version 0.8 so we can stay in stackage nightly.
  • Fix not exporting resource name lens.

  • Removed some dependencies that crept in so we can build against stackage nightly and use GHC 8. These were actually dependencies of stratosphere-gen and aren't needed for stratosphere.


  • Added all of the resources and resource properties for Auto Scaling Groups.
  • New AutoScalingGroup example
  • Added UpdatePolicy, CreationPolicy, and DependsOn


  • Small bug fix for "style" test when using the cabal distribution

0.1 (initial release)

  • Initial release with all Template components implemented along with a huge set of Resources.
comments powered byDisqus