A HIPAA/HITRUST compliant AWS CDK App Template based on AWS best practices for high availability and security
After many happy customers in production 😎, a lot of them in the US health sector where infrastructure is frequently reviewed against security & compliance standards like HIPAA/HITRUST, I just end up with an AWS CDK App Template that you now can also reuse to provision AWS resources based on AWS best practices for high availability and security in a safe and repeatable manner by relying on Infrastructure as a Code (IaC) through AWS CDK. The AWS CDK App Template is publically available in my GitHub account as a template-based repo so you can easily fork and deploy right away but you still might find it helpful to first understand how:
👉 it is structured;
👉 can be adjusted to fit your requirements;
👉 can be deployed either by command line or AWS CodeBuild as CI/CD tool;
⚡The AWS CDK App Template⚡
The AWS CDK App Template is based on “Reference Architecture for HIPAA on AWS”. As I said before, it supports AWS best practices for high availability and security and:
- multiple AWS regions and multiple AWS accounts. Production environment may be deployed to a different AWS (sub) account for higher resource isolation;
- multiple environments. By default, dev, test & production are coded but you can easily create and deploy more just by copy & paste a few lines of code;
- was built to optimize running costs (ex: no NAT Gateways are needed in a test environment as database and applicational layer are publicly accessible by being placed in the public subnet);
- is covered with unit tests that check for most critical security vulnerabilities;
The project code is structured in multiple stacks. Stacks (equivalent to AWS CloudFormation stacks) contain constructs, each of which defines one or more concrete AWS resources, such as Amazon S3 buckets, Lambda functions, Amazon DynamoDB tables, and so on. Stacks (and their resources) can be shared. That means you can add, edit, adjust & remove the App Template defaults stacks as you wish in order to meet your requirements even if your app is not web-based. Hopefully, in a faster way than build an AWS CDK App from scratch as this is the goal of a template 🙃. The App Template comes with default stacks that recreate the following Web Reference Architecture:
Lastly, it is coded on TypeScript because “AWS CDK is developed in one language (TypeScript) and language bindings are generated for the other languages through the use of a tool called JSII”. That means that much AWS CDK example code is written in TypeScript and TypeScript CDK updates may arrive sooner than in other programming languages. Plus, over the years, TypeScript exploded in popularity. That doesn’t mean that you will have problems if you go with another first-class supported programming language. Actually, I code mostly in c# (.netcore) and some of my colleagues have a great experience with AWS CDK for c# as part of their VisualStudio/Rider solution.
This is probably the most important one. Unless you are running a serverless app, you probably need to place some of your AWS resources in a VPC/subnets no matter if it is a web app, worker app, or something else. As you can see:
- it creates the public (ex: for load balancers), private (ex: for servers), and isolated (ex: for databases) subnets;
- only for the prod environment, it creates a single NAT Gateway (instead of one per availability zone):
If you would like to save on the cost of NAT gateways, you can use isolated subnets instead of private subnets (as described in Advanced Subnet Configuration). If you need private instances to have internet connectivity, another option is to reduce the number of NAT gateways created by setting the natGateways property to a lower value (the default is one NAT gateway per availability zone). Be aware that this may have availability implications for your application.
- In the non-prod environments, only one public subnet is used so you don’t need a NAT Gateway for outbound traffic 😉
- only for a prod environment, it also creates a bastion host so you can do SSH:
If you want to connect from the internet using SSH, you need to place the host into a public subnet. You can then configure allowed source hosts. As there are no SSH public keys deployed on this machine, you need to use EC2 Instance Connect with the command aws ec2-instance-connect send-ssh-public-key to provide your SSH public key
It creates an AWS S3 encrypted bucket (public access blocked by default);
It creates AWS Cognito-related resources. That includes (among other things):
- A user pool with some frequently used settings like sign in as case insensitive (which was a well-known complaint);
- Example code on how to configure a user pool group;
- Commented example code on how to configure a third-party identity provider (Google);
- Example code on how to use Html customizing email verification messages;
- Example code on how to use lambda triggers;
- Example code on how to provision an identity pool with a default authorization role;
You may know, AWS Cognito is a standards-based Identity Provider. Some customers I worked for, were already using other Identity Providers like Auth0 so I simply deleted this stack and move forward 🙂
It creates an AWS IAM user (& group) with programmatic access only to be used by your app/code. By default, the user can access to the User Pool on authentication stack (for example, to validate an access token), read secrets from AWS Secret Manager like DB connection string variables and write & read from the bucket created on Storage stack;
As you can guess, it creates a relational Aurora Postgres DB in AWS RDS. In most cases, you can change it to another engine just by changing one setting. By default:
- it creates a cluster for production environments and a single instance for non-prod environments;
- as I said before, the database is placed in an isolated subnet in the production environment and in the public subnet in other non-prod environments. In the production environment, it can only be reached by the private/applicational subnet or AWS CodeBuild (for CI/CD). This is just an example of many settings that can change between prod and non-pro environments;
- it creates AWS Cloud Watch metrics & alarms;
- it stores secrets (like username and password) in AWS Secret Manager;
AWS Elastic Beanstalk is an easy-to-use service for deploying and scaling web applications and services developed with Java, .NET, PHP, Node.js, Python, Ruby, Go, and Docker on familiar servers such as Apache, Nginx, Passenger, and IIS.
You can simply upload your code and Elastic Beanstalk automatically handles the deployment, from capacity provisioning, load balancing, auto-scaling to application health monitoring. At the same time, you retain full control over the AWS resources powering your application and can access the underlying resources at any time.
By default, it will use .NET core on the Linux platform (more about it here) but I also have in-production apps running in Ruby on Rails or Django. In most cases, you just need to target/specify a different platform or a different AMI in AWS EB configuration:
You may also run your app code in a different service like Amazon Elastic Container Service (Amazon ECS) which is easy! You can just update or replace this stack code and still have all other resources being created (plug-in / plug-out). If that is the case, I suggest that you take a look at the AWS CDK example code in the GitHub repo. For example, you can find an example AWS CDK code for Amazon ECS here.
Time to deploy either by command line and AWS AWS CodeBuild as a CI/CD tool 🤓 🚀
We will deploy using the command line first and, later on, using AWS CodeBuild as CI/CD tool. That means that as long as you can run commands, you should be able to deploy with any other CI/CD tool. It is possible to also set up the AWS CDK pipeline in AWS CDK App Template code but I prefer to do it manually for simplicity. All you need is:
- an AWS Account (like free tier) and credentials of an administrator use;
- AWS CLI, Node.js, AWS CDK Toolkit and IDE of your preference if you want to edit code (VSCode recommended);
Start by forking the repo and be sure that code compiles and tests successfully run:
npm run build & npx jest
Before we deploy, we need to set some contextual variables in the
cdk.json configuration file as this is a template project. There are several ways of setting/getting contextual values but I like to have in a configuration file:
Be sure that you have replaced
[YOUR_NON_PROD_AWS_SUBACCOUNT_NUMBER] with your AWS Account ID. Also notice that I’m not setting
SSLCertificateArns variables for simplicity but basically this is your verified certificate ARN in AWS Certificate Manager so you can use/terminate HTTPS at AWS EB Load Balanced.
That being said, to deploy using a command line is simple as running an AWS CDK Toolkit command:
cdk deploy —-profile [AWS_CLI_PROFILE_NAME] [STACK_NAME] 😀 (check options and other commands with
cdk -h ). To create a test environment, I simply run
aws cdk deploy "*-test" :
Remark: if your deployment fails on the compute stack, most likely is because you need to create a
aws-elasticbeanstalk-ec2-role IAM role. If that is the case, just create (and delete) an AWS Elastic Beanstalk app manually should fix the problem, or a drop me comment also works 😉.
Voilà! congrats! 👏 You now have a test environment:
You can now edit your stacks and deploy them again. Destroy and recreate the test environment (or any other environment) in a safe and repeatable manner. Usually the next step will be deploy your code in Elastic Beanstalk but this another story 😉
Setup a Continous Delivery (CD) Pipeline in AWS for a .NET Core 3.x Web API
Setup a Continuous Delivery (CD) pipeline in AWS CodePipeline + AWS CodeBuild to deploy a Asp.net core 3.x web api in…
Anyway, moving forward, to have our infrastructure / AWS CDK App Template deployed automatically when something is committed to master branch on GitHub by using AWS CodeBuild 👌, I just need to create an AWS CodeBuild project and be sure that:
aws-codebuild-buildspec.ymlfile is used;
- an environment variable that tells which environment should be deployed is set;
- assign AWS IAM
Administratorrole to the
codebuild-aws-cdk-build-service-rolethat is about to be created by default;
As you can see,
aws-codebuild-buildspec.yml actually only builds the project and runs the unit tests.
aws-codebuild-deployspec.yml is the build spec file that actually does the deployment. I have two files because usually I only deploy to production as a separate step by using a different AWS CodeBuild project based on
aws-codebuild-deployspec.yml file. Why? Safety! I manually check the output of build project first looking for differences / what will be deployed:
Last, but not least important, you can destroy the environment by running
cdk destroy "*-test" :
To sum up ✍️
When it comes to provision AWS resources, most people do it manually in the AWS Console or use Infrastructure as Code (IaC). IaC has enumerable advantages but typically requires an initial investment that not everyone is willing or able to take. Plus, AWS CloudFormation configuration files, which for a long time was the only AWS offering for IaC, look complex especially for a developer and/or when compared with some other frameworks like Terraform.
But that changed when AWS announced AWS CDK. AWS CDK provides us “high-level components called constructs that preconfigure cloud resources with proven defaults, so you can build cloud applications without needing to be an expert”, in your familiar programming language. Under the wood, AWS CDK still provides CloudFormation (CFN) Resources, which map 1:1 with base-level AWS CloudFormation resources, and provides a way to define CloudFormation with a programming language. CFN Resources provides complete coverage of CloudFormation resources and are available shortly after a CloudFormation resource is updated or newly available. In other words, you have the full flexibility of CloudFormation if needed (ex: you are using something not yet supported in AWS CDK) but it is very likely that you don’t need to look at CloudFormation configuration files anymore to find out what is about to be deployed.
So, if like me, you:
👉 need to provision AWS resources but multi-cloud provider support is not a requirement (otherwise look at Pulumi);
👉 want to reuse components across multiple projects/customers that meet your organization’s/customer’s security, compliance, and governance requirements;
👉 want to quickly deploy a new environment for a new customer;
👉 are also a developer so using your familiar programming language and your favorite IDE to code the infrastructure instead of AWS CloudFormation looks awesome 😎;
👉 are simply driven by technical excellence and Infrastructure as Code (IaC) is not a question anymore if you can code, version & deploy a new AWS environment in a matter of minutes. Is not a question anymore because you know the multiple advantages of it: your production infrastructure is more transparent to devs, code can be easily reviewed by another dev, easily covered by unit tests, easily covered by static code analysis tools like GitHub security features or SonarQube, etc;