Skip to content

jcouyang/dhall-aws-cloudformation

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
app
 
 
bin
 
 
 
 
sam
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Dhall AWS CloudFormation

dhall-aws-cloudformation contains Dhall bindings to AWS CloudFormation, so you can generate CloudFormation template from Dhall expressions. This will let you easily typecheck, template and modularize your CloudFormation definitions.

πŸ” References

πŸ’‘ Examples

πŸ“– Usage

Use resource schema

AWS Cloudformation has massive amount of specifications, to load all package.dhall remotely will be very slow

It is recommended to just import the only resources you need

optionaly, if you really need all resources in package.dhall, load the binary cache to local first

let Function =
    -- import Lambda Function type definition
      https://github.com/jcouyang/dhall-aws-cloudformation/raw/0.9.69/cloudformation/AWS::Lambda::Function.dhall
        sha256:60937fd655917883d994e8593155453b823258e801792b0878b828b372946836

let Fn =
    -- Intrinsic functions
      https://github.com/jcouyang/dhall-aws-cloudformation/raw/0.9.69/Fn.dhall
        sha256:b2cf7212998902c44ba1bf1670a8e0bc40562542b9b525587cd044f317644e47

let S =
    {-
    Each AWS String field can be either a String or a Intrinsic function, we can use `Fn.renderText "abc"` to create static string

    Or `Fn.render (Ref "abc")` to create a function that ref to a string
    -}   Fn.renderText

let render =
    -- function can be nested `render (Fn.Ref (Fn.GetAtt (Fn.String "abc.property")))`
      Fn.render

let example0 =
      { Resources.HelloWorldFunction
        = Function.Resources::{
        , Properties = Function.Properties::{
          , Handler = Some (S "index.handler")
          , Code = Function.Code::{
            , S3Bucket = Some (S "lambda-functions")
            , S3Key = Some (S "amilookup.zip")
            }
          , Runtime = Some (S "nodejs12.x")
          , Role = render (Fn.Ref "role logical id")
          , Timeout = Some +25
          , TracingConfig = Some { Mode = Some (S "Active") }
          }
        }
      }

in  example0

to convert to CloudFormation JSON file just

dhall-to-json < ./template.dhall > ./template.json

generates

{
  "Resources": {
    "HelloWorldFunction": {
      "Properties": {
        "Code": {
          "S3Bucket": "lambda-functions",
          "S3Key": "amilookup.zip"
        },
        "Handler": "index.handler",
        "Role": {
          "Ref": "role logical id"
        },
        "Runtime": "nodejs12.x",
        "Timeout": 25,
        "TracingConfig": {
          "Mode": "Active"
        }
      },
      "Type": "AWS::Lambda::Function"
    }
  }
}

Intrinsic Function

The following intrinsic functions are implemented, you can find examples of using intrinsic function in Fn.dhall document

  • Fn::Base64
  • Fn::Cidr
  • Condition functions
  • Fn::FindInMap
  • Fn::GetAtt
  • Fn::GetAZs
  • Fn::ImportValue
  • Fn::Join
  • Fn::Select
  • Fn::Split
  • Fn::Sub
  • Fn::Transform
  • Ref

Type Safe Fn::GetAttr

Instead of manually looking for AWS documents to make sure the resource has what attributes, we can just use <Resource>.GetAttr.<attribute name>:

render (Role.GetAttr.Arn "HelloWorldFunctionRole")

So the compiler can just help you find the correct attributes available.

Sam Policy Templates

Cloudformation's Policy document is loosy type as just JSON, it is hard to get the policy right and too many boilerplates to create a Dhall JSON data

Thanks to AWS SAM there are some common policy documents we can laverage

All these templates are translated into Dhall functions, so you don't need to use SAM to be able to use these policy documents.

let Policy = https://github.com/jcouyang/dhall-aws-cloudformation/raw/0.9.69/cloudformation/AWS::IAM::Role/Policy.dhall
let Sam/Policy = https://github.com/jcouyang/dhall-aws-cloudformation/raw/0.9.69/sam/policy-template/package.dhall
...
  Policies = Some [Policy::{
    , PolicyDocument = Sam/Policy.DynamoDBReadPolicy (Fn.String "DBName")
    , PolicyName = s "dynamo read only"
  }]
...

will generates

{
  "Policies": [
    {
      "PolicyDocument": {
        "Statement": [
          {
            "Action": [
              "dynamodb:GetItem",
              "dynamodb:Scan",
              "dynamodb:Query",
              "dynamodb:BatchGetItem",
              "dynamodb:DescribeTable"
            ],
            "Effect": "Allow",
            "Resource": [
              {
                "Fn::Sub": [
                  "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tableName}",
                  {
                    "tableName": "DBName"
                  }
                ]
              },
              {
                "Fn::Sub": [
                  "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tableName}/index/*",
                  {
                    "tableName": "DBName"
                  }
                ]
              }
            ]
          }
        ]
      },
      "PolicyName": "dynamo read only"
    }
  ]
}

β˜• Contribute

Build and Test

> nix-shell
$ stack build
$ stack test

Generate Type Definitions

Type definitions are generated from config file ./config.dhall which contains specifications used by AWS CDK as well:

To regenerate types definition files, simply run

$ stack run

Or if you just want to regenerate dhall files without setting up haskell dev environment, just

docker run --rm -v $(pwd):/data -w /data ghcr.io/jcouyang/dhall-aws-cloudformation

⚠️ Known Issue

The following CloudFormation definitions will raise assertion error due to invalid type definition such as empty type or cyclic import

  • AWS::EMR::Cluster
  • AWS::EMR::InstanceGroupConfig
  • AWS::EMR::InstanceFleetConfig
  • AWS::Macie::FindingsFilter
  • AWS::DataBrew::Recipe
  • AWS::FIS::ExperimentTemplate
  • AWS::SageMaker::ModelBiasJobDefinition
  • AWS::SageMaker::ModelQualityJobDefinition
  • AWS::SageMaker::MonitoringSchedule
  • AWS::SageMaker::DataQualityJobDefinition
  • AWS::SageMaker::ModelExplainabilityJobDefinition
  • AWS::S3::StorageLens
  • AWS::StepFunctions::StateMachine
  • AWS::MWAA::Environment
  • AWS::WAFv2::RuleGroup
  • AWS::WAFv2::WebACL
  • AWS::ServiceDiscovery::PrivateDnsNamespace
  • AWS::ServiceDiscovery::PublicDnsNamespace
  • AWS::AmplifyUIBuilder::Component
  • AWS::AmplifyUIBuilder::Theme