Stratosphere: AWS CloudFormation in Haskell

CI sponsors hackage

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, it is possible 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/YAML.
  • Have a simple checking/linting system outside of the types that can find common errors in templates.

Funding / Sponsoring

This library is maintained by mbj and any pledge is greatly apprechiated.

Example

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

module Main where

import Stratosphere

import qualified Data.ByteString.Lazy.Char8 as B

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

template :: Template
template
  = mkTemplate [ec2Instance]
  & set @"Description" "EC2 Example template"
  & set @"Parameters"  [keyName]

keyName :: Parameter
keyName
  = mkParameter "KeyName" "AWS::EC2::KeyPair::KeyName"
  & set @"Description"           "Name of an existing EC2 KeyPair to enable SSH access to the instance"
  & set @"ConstraintDescription" "Must be the name of an existing EC2 KeyPair."

ec2Instance :: Resource
ec2Instance
  = set @"DeletionPolicy" Retain
  . resource "EC2Instance"
  $ EC2.mkInstance
  & set @"ImageId" "ami-22111148"
  & set @"KeyName" (toRef keyName)
{
  "Description": "EC2 Example 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",
      "Properties": {
        "ImageId": "ami-22111148",
        "KeyName": {
          "Ref": "KeyName"
        }
      },
      "Type": "AWS::EC2::Instance"
    }
  }
}

Please see the examples directory for more in-depth examples (including this one). The stratosphere-example package produces a same named binary with a minimal CLI for exploration.

Its encouraged to use it as a playground while exploring this library.

STACK_YAML=stack-9.2.yaml stack build --copy-bins --test stratosphere-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 Value a type.

It is recommend using the OverloadedStrings and OverloadedLists extensions to reduce the number of Literals that have to be written.

Optional and required properties

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 builder that accepts required resource properties as arguments. This allows the user to succinctly specify the resource properties they actually use without adding too much noise to their code.

To specify optional arguments, stratosphere exposes the set function that takes the type level symbol of the property to set and the value as argument. Its recommended to use the & function to chain these updates. See examples.

Auto-generation

All of the resources and resource properties are auto-generated from a JSON schema file and are placed in services/. The generator/ directory contains the auto-generator package stratosphere-generator code and the JSON model file. The services/ directory is included in git so the build process is simplified. To build stratosphere-generator from scratch and then build all of stratosphere, build the stratosphere-generator package via stack and execute the stratosphere-generator binary from the project root.

Contributing

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.

Development Build

# Warning this takes a while ;)

# Compile all packages
STACK_YAML=stack-9.12.yaml stack test

# Run the generator
STACK_YAML=stack-9.12.yaml stack build stratosphere-manager
STACK_YAML=stack-9.12.yaml stack exec stratosphere-manager -- generate

Changes

Change Log

1.0.1 2025-11-07

Fix misleading note in readme about old API.

1.0 2025-11-07

TLDR: Loads of breaking changes, with good reasons.

Overall this release is meant to serve the future, not the past. So this release has many “bunched up” breaking changes which should reduce the need for breakng changes in the future, and improve maintainability and thus release frequency dramatically.

  • Drop generated field prefixes, making usage of the library way more natural. Especially its now possible to use this library just from the AWS provided cloudformation documentation without having to lookup the field prefixes in a second tab.

  • Drop support for GHC < 9.2. This simplifies the generator significantly. If you need older GHCs use the old versions of stratosphere. Dependency of dropping field prefixes. This library relies now on NoFieldSelectors internally.

  • Fully re-written generator, which works directly on the raw, un-preprocessed AWS specification, this should increase maintainabiltiy and release frequency.

  • Re-aligned generated module / builder function structure, removing all manual overwrites for dismabiguation.

  • Drop of the lens dependency, replaced with the set @"PropertyName" value idiom.

  • Split to one package per AWS service. This induces a small overhead on the user to select the packages the user needs, but reduces compilation time significantly. Also this works around limitations on OSX linker issues on many modules. Fixes: #111 #102

  • Change Val to Value, its recommended to include stratosphere qualified where it clashes with aeson imports.

  • Remove support for custom enum types stratosphere used to tag on to the raw spec. Fixes: #195

  • Update resource specification document to version 112.0.0 and re-generate all services. Fixes: #140

0.60.0

  • Add and require aeson-2 support
  • Maintainer change to Markus Schirp
  • Drop support for pre GHC 9.0, user older releases if you are on these GHCs. This reduces maintainer overhead.
  • Add github actions based CI

0.59.1

  • Fix missing .cabal file in distribution

0.59.0

  • Update resource specification document to version 18.2.0

0.58.0

  • Update resource specification document to version 17.0.0
  • Added .cabal files to repo (#179)
  • Added PATCH constructor for HttpMethod (#176)

0.57.0

  • Update resource specification document to version 16.3.0

0.56.0

  • Update resource specification document to version 16.2.0

0.55.0

  • Update resource specification document to version 15.3.0

0.54.0

  • Update resource specification document to version 15.1.0

0.53.0

  • Update resource specification document to version 14.3.0
  • Update CI for GHC 8.8

0.52.0

  • Update resource specification document to version 12.3.0

0.51.0

  • Update resource specification document to version 11.6.0

0.50.0

  • Update resource specification document to version 11.4.0

0.49.0

  • Fixed ImportValue not allowing Val as argument (thanks @mbj!)
  • Update resource specification document to version 10.4.0

0.48.0

  • Update resource specification document to version 10.3.0

0.47.0

  • Update resource specification document to version 10.2.0

0.46.0

  • Update resource specification document to version 8.1.0

0.45.0

  • Update lambda runtimes list

0.44.0

  • Update resource specification document to version 7.1.0
  • Added NodeJS10x Lambda runtime (@Gosha)

0.43.0

  • Update resource specification document to version 6.3.0

0.42.0

  • Update resource specification document to version 6.0.0

0.41.0

  • Update resource specification document to version 5.0.0

0.40.0

  • Update resource specification document to version 4.1.0

0.39.0

  • Update resource specification document to version 3.4.0

0.38.0

  • Update resource specification document to version 3.3.0

0.37.0

  • Update resource specification document to version 3.0.0

0.36.0

  • Update resource specification document to version 2.29.0

0.35.0

0.34.0

0.33.0

  • BREAKING CHANGE: We nuked the huge ResourceProperties sum type, which greatly reduced compile times, memory usage, and binary sizes for programs that depend on stratosphere. See:

    To migrate to this version, you should just have to remove any uses of the old ResourceProperties sum type and pass your resource configuration directly into Resource:

    @@ -32,7 +32,6 @@ dbTemplate =
     rdsMaster :: Resource
     rdsMaster =
       resource "RDSMaster" $
    -  RDSDBInstanceProperties $
       rdsdbInstance
       "db.t2.micro"
    

0.32.0

0.31.0

  • Update resource specification document to version 2.25.0
  • Added Condition field to resources

0.30.1

  • Update resource specification document to version 2.21.0

0.30.0

  • Update resource specification document to version 2.19.0

0.29.1

  • Update resource specification document to version 2.18.1

0.29.0

  • Update resource specification document to version 2.18.0

0.28.1

  • Update resource specification document to version 2.16.0

0.28.0

  • Update resource specification document to version 2.15.0

0.27.0

  • Update resource specification document to version 2.12.0

0.26.2

  • Update resource specification document to version 2.10.0

0.26.1

  • Remove dependency on aeson-qq

0.26.0

  • Update resource specification document to version 2.8.0

0.25.0

  • Update resource specification document to version 2.6.0

0.24.4

  • Update resource specification document to new unspecified version

0.24.3

  • Update resource specification document to new unspecified version

0.24.2

  • Update resource specification document to version 2.5.0

0.24.1

  • Update resource specification document to support EKS

0.24.0

  • Update resource specification document to version 2.4.0

0.23.0

  • Update resource specification document to version 2.3.0

0.22.3

  • Update resource specification document to new unspecified version

0.22.2

  • Update resource specification document to version 2.2.0

0.22.1

  • Update resource specification document to version 2.1.0

0.22.0

  • Bug fix: AWSTemplateFormatVersion was being formatted incorrectly in JSON.

0.21.0

  • Update resource specification document to some new unspecified version.
  • Add new nodejs8.10 to lambda

0.20.0

  • Fix name of AutoScalingRollingUpdatePolicy SuspendProcesses parameter
  • Update list of AWS Lambda Runtime values

0.19.1

  • Add compatibility with GHC 8.4.1

0.19.0

  • Fix Equals function requiring Val Bool arguments instead of Val a
  • Add Metadata field to Resource type

0.18.0

  • Update resource specification document to version 2.0.0
  • Fix missing AWS::ElasticLoadBalancingV2::ListenerCertificate resource

0.17.0

  • Fix JSON instances for non-codegen code.
  • Allow the Split function to use Val a in its second argument.

0.16.0

  • BREAKING CHANGE: Renamed lenses for non-generated code to match naming conventions of generated code.
  • Update resource specification document to 1.14.0

0.15.2

  • Update resource specification document to some unspecified new version.
  • stratosphere now compiles with no warnings.

0.15.1

  • Update resource specification document to version 1.13.0

0.15.0

  • Update resource specification document to some unspecified new version.

0.14.0

  • Update resource specification document to version 1.12.0

0.13.0

  • Update resource specification document to version 1.11.0

0.12.0

  • Update resource specification document to version 1.10.0

0.11.0

  • Update resource specification document to version 1.8.0

0.10.0

  • Update resource specification document to version 1.7.0

0.9.0

  • Update resource specification document to version 1.6.0

0.8.0

  • Update resource specification document (no version given in doc)
  • GHC 8.2.1 compatibility

0.7.1

  • Add Export field to the Output type. This allows you to declare cross-stack references.

0.7.0

  • Made Val and ValList more type-safe by moving some constructors to ValList and being more specific with types in functions that only accept Text parameters. Specific examples include:
    • Join and Select now require a ValList argument
    • Base64 and Join now work only on Val Text, not Val a
    • GetAZs and Split are now in ValList, not Val
  • Created ImportValueList as a ValList alternative to ImportValue.
  • Added support for Fn::Sub intrinsic function.

0.6.0

  • BREAKING CHANGE: Added ValList type. This new type allows you to reference parameters that are already list types. Previously you had to use some kludgy workarounds. For example, you can now Ref a parameter of type List<AWS::EC2::AvailabilityZone::Name>.

    Every type that used to be [Val a] is now ValList a. If you use the OverloadedLists pragma, you might not have to change any of your code. Otherwise, you must wrap existing lists in the ValList constructor.

  • BREAKING CHANGE: The newtype wrappers Integer', Bool', and Double' are no longer required. CloudFormation expects numbers and bools to be JSON strings. These newtypes used to be necessary so we didn’t use JSON numbers/bools. Now the conversion is handled internally, and users don’t need to worry about this when using stratosphere.

0.5.0

  • Update resource specification document (no version given)

0.4.4

  • Update the resource specification document to version 1.4.2

0.4.3

  • Update the resource specification document to version 1.4.1

0.4.2

  • Update the resource specification document to version 1.2.1

0.4.1

  • Manually write out all JSON instances to speed up compilation
  • Fix bug in CloudFormation specification where EvaluationPeriods was accidentally set to Double.

0.4.0

  • 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

0.3.1

  • Updated resource specification document to version 1.1.1.

0.3.0

  • 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!

0.2.2

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

0.2.1

  • 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)

0.2.0

  • 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 (https://github.com/frontrowed/stratosphere/issues/27)

0.1.6

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

0.1.5

  • 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!)

0.1.4

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

0.1.3

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

0.1.2.1

  • 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.

0.1.2

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

0.1.1

  • 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.