@sjmeverett/cloudformation-types

Basically, CloudFormation in typescript instead of JSON/YAML.

Usage no npm install needed!

<script type="module">
  import sjmeverettCloudformationTypes from 'https://cdn.skypack.dev/@sjmeverett/cloudformation-types';
</script>

README

@sjmeverett/cloudformation-types

Basically, CloudFormation in typescript instead of JSON/YAML.

Why?

CloudFormation is overwhelming at first, so I thought I'd make it a little easier. Because there are types, you get editor completion, and there are links to the AWS documentation in the JSDocs.

Since it's just Typescript, you also get expressions, conditionals, etc. You can also create NPM modules with reusable patterns, e.g. a Lambda function with API Gateway in front, and a record in Route 53.

How?

import { createLambdaFunction } from "@sjmeverett/cloudformation-types";

const fn = createLambdaFunction("MyLambdaFunction", {
  FunctionName: "...",
  // ... etc
});

const myStack = {
  AWSTemplateFormatVersion: "2010-09-09",
  Resources: getResources([fn]),
};

console.log(JSON.stringify(myStack));

The library defines a function for every CloudFormation resource type, generated by trimming the AWS:: off the front and removing other :: in the name. E.g., AWS::Lambda::Function becomes createLambdaFunction.

The create functions accept two parameters: the resource name, and an appropriately-typed Properties definition. They simply return the CloudFormation object which can be serialised to JSON. The name is stored on a non-enumerable key referenced by a symbol exported as nameSymbol.

Example:

fn ===
  {
    Type: "AWS::Lambda::Function",
    Properties: {
      FunctionName: "...",
      // ... etc
    },
    [nameSymbol]: "MyLambdaFunction",
  };

There are a few helper methods as well:

  • getName(resource) — helper to get the name, i.e., resource[nameSymbol]
  • dependsOn(resource, ...dependencies) — adds resources to the DependsOn field of the specified resource
  • getRef(resource) — just returns { Ref: getName(resource) }
  • getAttribute(resource, attributeName) — gets an attribute (output) from the specified resource, in a typesafe manner
  • getResources(resources) — builds up the map of resource name to resource definition from an array of resources
  • fnSub(str: string, vars?: Record<string, string>) — helper for Fn::Sub intrinsic function

Note that the types returned from getRef and getAttribute are "faked". getRef claims to return a string, even though it returns an object like {Ref: 'foo'}, and getAttribute claims to return whatever the type of the attribute you asked for, even though it actually returns {'Fn::GetAtt': 'foo'}. I figured this was more useful, as it means you can supply an attribute value to a Property with the matching type.

How does it work?

The build process downloads a JSON file from AWS that describes all the AWS CloudFormation types, generates a Typescript file from a handlebars template, and then compiles that Typescript.