The AWS CDK is AWS' "infrastructure as code" toolkit that allows you to build AWS resources using code. I've used CloudFormation extensively, and although I find the documentation to be fairly easy to follow, the developer experience just isn't there. The ability to write infrastructure in the same language you're developing your backend on is a huge advantage, and it makes AWS infrastructure much more accessible to those without extensive experience in the AWS ecosystem. I am also a huge Typescript fan so the fact that the CDK comes with Typescript out of the box is amazing. Though the AWS CDK is growing in popularity (and rightly so), the documentation is sorely lacking and very hard to read. I find I end up spending most of the time looking at the CloudFormation documentation and mapping it back to the CDK instead.
I recently setup an Elastic Beanstalk application using the CDK and ran into a couple of issues that I hope I can clear up for others. In this post I'll describe how to create a simple Elastic Beanstalk application using the AWS CDK that you can use to deploy a compatible web application.
Elastic Beanstalk
If you're familiar with Heroku or DigitalOcean's new App Platform, you should be able to get up and running quickly. EB is a simple-to-use service for deploying web applications that supports most web application frameworks. Using the EB CLI, you can deploy NodeJS, Django and many more applications with a simple eb deploy
command. EB comes with load balancing out the box and allows you to use auto-scaling configurations to handle scaling. We'll be setting up a NodeJs web application and deploying it to EB using the EB CLI.
Here's an example of what our final architecture will look like:
Prerequisites
To create and deploy the app, you'll need both the AWS CDK installed, and the Elastic Beanstalk CLI. The CDK can be installed through npm -g install aws-cdk
and the EB CLI can be installed through apt
or brew
depending on your system. We'll use the CDK to create and bootstrap our project, then we'll use the EB CLI to deploy our code. You'll also need the AWS CLI installed and your profile configured. See here for how to do that.
Getting Set Up
First, you'll want to initialize your CDK project.The CLI comes with a handy way to start new projects. I'll be using typescript for this but you can use whatever language you prefer. In a new directory, run:
cdk init --app myApp --language typescript
You should notice the CDK has created a bunch of folders: lib/
, bin/
and test
. You'll create all of your stacks in the lib
folder, write your tests in the test
folder and initialize your stacks for deployment in the bin
folder when you're ready to deploy.
Installing the CDK Libraries
The CDK packages its libraries for various resources separately. Rather than having one massive library, we can just include only what's necessary for our project. We'll be using the IAM and Elastic Beanstalk libraries which you can install by doing:
npm install @aws-cdk/aws-elasticbeanstalk @aws-cdk/aws-iam
Note: When using multiple libraries, you need to make sure the libraries you're using are on the same version. I ran into a compatibility issue when the @aws-cdk/core
package was using V1.94.0 and the @aws-cdk/aws-iam
library was using V1.85.0. Updating all packages to use the same version (E.g. V1.94.0 or V1.85.0) resolved this issue.
Creating your Stack
Next, we'll want to take a look at what's in the lib/
folder. You should see a file named after the app name you specified when you initialized the project. You should see something similar to this:
import * as cdk from '@aws-cdk/core';
import * as elasticbeanstalk from '@aws-cdk/aws-elasticbeanstalk';
import * as iam from '@aws-cdk/aws-iam';
export class AppStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
}
}
const app = new cdk.App();
new AppStack(app, 'Production');
app.synth();
The CDK works by creating "stacks". If you're not familiar with CloudFormation, a stack is a collection of AWS resource configurations (by resources, I mean databases, servers, containers etc.). The stack itself doesn't contain any resources, it is just a collection of resource configurations. When you deploy your stack, AWS then uses the configurations to create the resources. You don't pay anything for CloudFormation, but you will have to pay for anything CloudFormation creates.
To get our app up and running, we'll want to create 3 resources:
- An IAM role and an Instance Profile to give EB the permissions to manage our server
- An EB Environment
- An EB App
Let's start with our IAM permissions:
// EBS IAM Roles
const EbInstanceRole = new iam.Role(this, `${appName}-aws-elasticbeanstalk-ec2-role`, {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
});
const managedPolicy = iam.ManagedPolicy.fromAwsManagedPolicyName('AWSElasticBeanstalkWebTier')
EbInstanceRole.addManagedPolicy(managedPolicy);
const profileName = `${appName}-InstanceProfile`
const instanceProfile = new iam.CfnInstanceProfile(this, profileName, {
instanceProfileName: profileName,
roles: [
EbInstanceRole.roleName
]
});
This creates an IAM role with the AWSElasticBeanstalkWebTier
managed IAM policy attached. Next, we create the instance profile and use the role we've just created. This gives the server the permissions it needs to run as an EB server.
Next, we'll create the EB application and environment:
const node = this.node;
const platform = node.tryGetContext("platform");
const optionSettingProperties: elasticbeanstalk.CfnEnvironment.OptionSettingProperty[] = [
{
namespace: 'aws:autoscaling:launchconfiguration',
optionName: 'InstanceType',
value: 't3.small',
},
{
namespace: 'aws:autoscaling:launchconfiguration',
optionName: 'IamInstanceProfile',
value: profileName
}
];
// EBS Application and Environment
const app = new elasticbeanstalk.CfnApplication(this, 'Application', {
applicationName: `${appName}-EB-App`
});
const env = new elasticbeanstalk.CfnEnvironment(this, 'Environment', {
environmentName: `${appName}-EB-Env`,
applicationName: `${appName}-EB-App`,
platformArn: platform,
solutionStackName: '64bit Amazon Linux 2 v5.3.0 running Node.js 14',
optionSettings: optionSettingProperties
});
env.addDependsOn(app);
In our option settings, we specify the instance profile we created earlier and the instance type we'll be running. I've just selected the t3 small but you might want something bigger if you're expecting higher workloads. Setting up the application and the environment then is quite simple. Note that you may want to use a different solutionStackName
. I'll be running a NodeJs server so I've selected Amazon Linux with Node.js 14. You can view all of the available solution stacks here.
Deploying Our Stack
To deploy our stack you'll first want to run the synth
command to ensure that there's no issues translating our Typescript code into CloudFormation templates. Next, we'll call the bootstrap
command to get the CDK to initialize our Stack in AWS. This will create some additional resources that the CDK uses to manage the state of your stack. You'll likely see a stack created in CloudFormation called "CDK Toolkit". Lastly, we'll deploy our stack. You might see a warning about IAM permissions. The CDK will warn you if your stack is creating or changing IAM permissions as a security measure.
cdk synth
cdk bootstrap
cdk deploy
Now we wait. You can navigate to the CloudFormation page in the AWS Console if you want to see more details about what the stack is creating, and if any failures have occurred.
Once the stack is complete, you should be able to see your EB Environment and Application in the EB console and your server health should be "healthy". Next, we'll look at deploying some code the the server.
Deploying our Server
In order for us to deploy our code to our newly created server, we first need to initialize our project with EB. I'm using a simple Express app which I won't detail here as there's plenty of resources online for how to set that up. To start, run:
eb init
If you're using the same profile you used to deploy your CDK stack, you should see your EB environment show as a selectable option in your terminal. Select your environment and wait for the project to initialize. Once that's done, it should be as simple as:
eb deploy
Debugging EB Deployment Issues
If you're running into issues with your deployment, you can view the most recent logs or SSH into your server by running some useful commands:
eb logs
eb ssh
Testing and Conclusion
You should now be able to find the URL for your application in the AWS console. From here on, you may want to build on your stack by creating databases, S3 buckets, networks or Lambdas. You can continue to add resources to your stack, or break parts of it out into separate stacks for modularity. I would also recommend creating 2 instances of your stack, one for development/staging, and one for production. If you're setting up more than a hobby project, I'd recommend you do this in separate accounts and add your application into a VPC with security groups, and a web application firewall for increased security. But if this is a small project, this should be perfectly fine and can scale to thousands of users easily.
View the original post and more here
Top comments (1)
Thanks for shared.
Repo?