Custom IAM Permissions in an Elastic Beanstalk Application through AWS CDK
This article builds on my introduction to building a custom Elastic Beanstalk application using AWS CDK (in TypeScript). Reading that story first will help with the context of the following but it is not required.
By way of brief intro, AWS CDK is a very powerful framework for building entire infrastructures in code — it generates CloudFormation templates for exceptionally complex and interlinked stacks for you. Moreover, since it uses TypeScript (other languages are supported), any respectable IDE will be provide hints/auto-completes to avoid the cumbersome task of checking the CloudFormation documentation every 30 seconds.
The purpose of this article is to highlight some of the powerful features it exposes with particular regard to its IAM control and Elastic Beanstalk deployment functionality.
The architecture displayed below is designed to handle up to one million IoT devices (assuming a one hour duty cycle). However, the beauty of this design, and the main reason Kinesis is present, is to scale the system horizontally — if your business needs require support for twice as much traffic, just double the number of Elastic Beanstalk instances and the Kinesis shard count and you’re good to go. Each shard-instance pair (if using a T3-small) costs just $20/month so expanding is cheap and simple.
The basic workflow of the architecture is:
- Multiple IoT devices post messages over MQTT to AWS IoT Core
- IoT Act (Rules engine) forwards these messages to a Kinesis Stream to act a buffer
- An Elastic Beanstalk application polls Kinesis and…
- Sends a response over MQTT back to the IoT device.
IAM Control in Elastic Beanstalk
When using the Elastic Beanstalk console to create an application, a default role will be created for you, aws-elasticbeanstalk-ec2-role — however, for the architecture above, the default permissions assigned are insufficient (no IoT or Kinesis access) so an alternative “instance profile” must be created with the specific permissions required by the Elastic Beanstalk application. The instance profile is “a container for an IAM role” — it is the IAM role used by your application.
There’s something hugely satisfying about a CloudFormation stack which can be deployed, torn down, and re-deployed without ever having to log on to the AWS console to perform manual tasks like creating roles, adding permissions or changing other instance settings. This app does just that.
After you have installed the AWS CDK, let’s begin building the CDK app:
$ mkdir custom-iam
$ cd custom-iam
$ cdk init --language typescript
$ yarn add @aws-cdk/aws-s3-assets @aws-cdk/aws-kinesis @aws-cdk/aws-iam @aws-cdk/aws-elasticbeanstalk @aws-cdk/aws-iot @types/node
Note: If you have already upgraded to aws-cdk 1.16.0, the @types/node should already be included (see this commit).
Once everything has installed, open up lib/custom-iam-stack.ts and start adding the Elastic Beanstalk application, IoT rules, Kinesis stream and IAM permissions.
There’s quite obviously a huge amount going on above there so let’s break it down:
- Lines 19–25: Construct the Instance Profile (which is what the application will assume when it runs) and the Role (which actually holds the permissions)
- Lines 27–30: Create a Kinesis stream with a constant number of shards — declared
NUM_SHARDS_AND_INSTANCESat the top since it will be re-used later
- Lines 34–55: Make a new role for the IoT Rule to use to post to Kinesis.
- Lines 58–76: Permissions for Elastic Beanstalk application — Kinesis reading, IoT publishing and the standard managed policies.
- Lines 80–118: Options and environment variables. The important line in the options part is 94:
value: ebInstanceProfile.attrArn— this makes the Elastic Beanstalk app use the custom profile, not the default one.
- Lines 120–136: Create a version of the Elastic Beanstalk app (from the app.zip file in the root of your project). Without this, the “sample application” (Hello World sort of thing) will be deployed. This syntax allows for deploying your custom app (in Node.js in this case)
- Lines 139-end: Create the environment with the relevant app version.
And that’s it! You can now run
touch app.zip && yarn build && cdk synth to preview your CloudFormation template (almost 400 lines long). (Obviously the app.zip should be replaced with an actual application before deploying!)
I won’t go into details of the contents of the Node.js application here. This article is supposed to provide a broad introduction to how CDK can help manage complicated and wordy IAM policies through the use of
grantWrite() as well document how to specify a custom role for your application. That said, for completeness, the way I currently ZIP my Elastic Beanstalk application is quite simply
zip -r src/* package.json package-lock.json — no need to store chunky
node_modules in there, the Elastic Beanstalk service will take care of the rest of that!
Happy CDK-ing and if you have any questions about anything in this article, please do comment below.