Polyfilling AWS CloudFormation with a Lambda-backed Custom Resource

Barrett Harber
5 min readJun 11, 2016

--

Icons courtesy of Amazon Web Services I draw like a 4-year-old.

There are a ton of tools out there for doing deployments. If you’re working with AWS, you probably know about CloudFormation. It’s been called “a saner API” by Austen Collins, founder of Serverless. I would definitely agree.

CloudFormation is a fantastic service, but it doesn’t cover all of the services that AWS offers. However, CloudFormation does provide one crazy-awesome feature: Custom Resources. More specifically, Lambda-backed custom resources. This means you can invoke a Lambda function whenever you do an operation on a CloudFormation stack.

Amazon Cognito is a service that enables enables identity management and user data synchronization. It’s not only great for mobile (iOS and Android) but works very well on the web.

CloudFormation does not currently have a Cognito Identity Pool Resource type, but with custom resources, we can polyfill it. This is exactly what I did and the code is posted on GitHub. It’s released under the Unlicense, meaning that you can pretty much do whatever you want with it and not have to worry about it license restrictions.

You may ask, “Why is this necessary?” There are other ways, but it does provide a few key benefits:

  • Easily creating Identity Pools in different accounts.
  • Easily creating multiple Identity Pools within the same account (you can have up to 60 per account). You may want separate identity pools for dev, test, and prod environments.
  • It follows the philosophy of managing Infrastructure as Code.

You could do the same thing by running scripts, using one of the SDKs, using the CLI, or just manually through the AWS Console. These aren’t bad options, but they take a lot more work, and you’ll likely end up writing the same code over and over, or clicking a lot of buttons.

Usage

The README gives instructions on what to do. I’ve provided gulp tasks to assist with the stack creation. Basically, just git clone, npm install, drop in a configuration, and run gulp.

It doesn’t fit well as an NPM module (please comment if you disagree), so I added it as a git submodule in my project.

The only other thing you need to do is attach policies to the IAM roles. I provided this example, as a separate CloudFormation template which uses IAM Managed Policies that attach themselves to the IAM Roles. The example provides the same permissions as using the walkthrough in the console. I didn’t want to include a default set of permissions for obvious reasons. You decide what you want your users to be able to do.

Breakdown of the CloudFormation template

To explain why all of the resources are needed, here’s a breakdown of the resources within the template:

Lambda Execution Role

Every Lambda function needs an execution role and execution role policy, so it can inherit permissions to be able to perform the actions that it’s supposed to perform. All Lambda functions require access to CloudWatch logs, and, in this case, require access to Create/Update/Delete Identity Pools as well as set Identity Pool Roles.

Lambda Function

The Lambda function is essentially the code gets executed when it’s called. Here, it’s performing three tasks — Create/Update/Delete — for two separate resource types. For the Identity Pool, it will create, update, or delete the identity pool, depending on the state of the stack. For the Identity Pool Roles, it will associate the roles with the identity pool during create and update operations, but during delete operations, it will do nothing — as it should.

This Lambda function is written in JavaScript for the Node.js 4.3 runtime. I chose to use a separate file, because it’s easier to write and maintain. If you are writing your own, you can inline it inside the template, but only for Node.js (I prefer the separate zip file approach still). You can also use one of the other runtimes available in Lambda — it just needs to be able to parse the signed URL and do the S3 put for the response to CloudFormation.

Cognito Identity Pool

The identity pool itself is created by invoking the Lambda function. Essentially the same thing happens for updates and deletes, just with a different call using the SDK. The template parameters are provide inputs to the SDK. Since CloudFormation can only accept a few data types, all of the parameter values have to be stringified and then parsed in the Lambda function.

Cognito Authenticated Role

There are two types of roles that can be associated with Cognito Identity Pools — Authenticated (required) and Unauthenticated (optional). This is the IAM role for authenticated identities. Authenticated users must be logged in with Facebook, Google, Amazon, an OpenID Connect provider, or a developer-authenticated identity. Typically, these users will have elevated privileges relative to their unauthenticated counterparts. For example, they would be able to call certain API gateway endpoints or write to a DynamoDB table.

Cognito Unauthenticated Role

This is the IAM role for unauthenticated identities. It will be created conditionally, meaning only if the AllowUnauthenticatedIdentities is true. While this is optional, the AWS console will prompt you to fix it if it’s not used (bug?).

Cognito Identity Pool Roles

This is a custom resource for setting the identity pool roles. This custom resource invokes the Lambda function and set the identity pool roles during create and update operations. During a delete, it simply responds with “SUCCESS”, since the roles and identity pool will be deleted by other parts of the CloudFormation template.

Future Work

  • Add CI integration (Travis most likely)
  • More tests (linting, coverage, etc)
  • Breaking out the gulp tasks into a separate module (for re-use)
  • Maybe a Yeoman generator
  • A Serverless plugin perhaps

If this was helpful to you, please recommend it on Medium and Star the repo on GitHub. Thanks!

--

--