When developing applications, it’s common practice to run the same application in multiple environments. Usually a development environment for general volatile changes, a test environment for integration testing and quality assurance, and a production environment for a final release. With multiple environments all using the same infrastructure, they require consistent replication. A common challenge is that over time each environment can drift from the standard and become too unique to accurately replicate. An example of drift is resource IAM permissions being changed in one environment, but the change isn’t documented and replicated in the other environments upon deployment.
In this post we will cover how Infrastructure as Code (IaC) is a great solution for ensuring consistent replication of infrastructure across multiple environments. We’ll cover the benefits of IaC and explore an IaC framework specifically for AWS resources, providing some good examples along the way.
Infrastructure as Code
Managing and provisioning application infrastructure is usually done manually through a GUI. This process is slow, error-prone, and requires substantial change documentation. It is likely that each new environment becomes a unique configuration with minor (or major) differences that cannot be automatically or easily replicated.
This is where IaC helps. Infrastructure written in code follows a principle that regardless of the deployment environment, it will always generate the same environment – reducing the risk of manual errors or ambiguity.
IaC must undergo the same development cycle as application development. This includes source controlling the code and subjecting it to peer reviews through pull requests. Source controlling code allows CI/CD, automated rollbacks, and parallel feature development; these are especially important at all layers of an application including the infrastructure.
Below is an example CI/CD process of IaC deployed across four accounts. This could be deploying or updating an infrastructure stack across the environments as would normally be done with application code.
Figure 1: Cross account infrastructure deployment through Infrastructure as Code using CloudFormation stacks
Cloud Development Kit
AWS’s Cloud Development Kit (CDK) is an IaC framework specifically designed for AWS resources. It provides high-level helper classes, called Constructs, that abstract the low-level challenges in the creation of AWS resources, and creates the resources using AWS best practice. Using Constructs greatly reduces the complexity of using raw CloudFormation templates.
Extendable and Flexible
Because the CDK-provided classes follow AWS best practice, they can dictate how resources are created and the configuration options available. However, the library is object-oriented and provides an abstraction layer for development teams to extend or implement their own high-level modules, by extending existing classes or using the low-level classes for direct access to the raw CloudFormation resources.
The below example demonstrates how a high-level Lambda class “Function” creates a Lambda function and an IAM role with basic privileges for the Lambda execution. It also creates a Dead Letter Queue and exports the function name and ARN from the stack. The configuration options allows the addition of event hooks, or layer definition, and configuration of the basic Lambda options. All of this is created in just four lines of code.All these high-level classes use a lower-level class for interfacing more directly with the raw CloudFormation code. By investigating the open-source code for the CDK, you can create custom resources and modules for use across projects, increasing development productivity. Custom resources created by the large and active developer community can also be accessed and used.
While the CDK is written in a language that provides additional benefits, its compilation synthesises into the raw CloudFormation JSON template files and utilises the AWS CloudFormation provisioning engine for deployment into AWS accounts.
Synthesizing CDK code into CloudFormation templates validates and checks for errors e.g. circular references in cross-stack outputs usage. This additional validation keeps the environment clean and reduces risk.
Deployment and destruction of the infrastructure is done in a few simple CLI commands:
- cdk synth – compiles the code into CloudFormation JSON templates
- cdk deploy – deploys the template into the AWS account
- cdk destroy – deletes the CloudFormation stack and removes all resources created by that stack.
This makes it easy to automate and integrate validation and deployment of the infrastructure into any CI/CD process.
In this example, a simple REST API that will proxy all requests to a Lambda is created. This Lambda will access DynamoDB and return data to the client. Below is the AWS diagram:
In only a couple of lines, all resources are created and linked. Note that security and permissions are ignored at this stage (authentication to the API, IAM permissions for resource access, etc).
The sample code is in Typescript.
Now all that needs to be done is to build and synthesise into CloudFormation JSON templates, and then deploy the CloudFormation into an AWS account using the commands defined earlier.
Software as a Service (SaaS) Applications
All the discussed benefits can be applied to Software as a Service (SaaS) projects, regardless of the type of SaaS architecture that is in use or whether it is single-tenanted or multi-tenanted. A pooled tenancy that uses a single database, network, and application layers follow from the examples listed above as the responsibility is mostly on the application’s code. For a bridge architecture, the codebase can be split into multiple stacks or multiple CDK Applications, each with a single focus. A siloed architecture, can utilise many features of IaC and the CDK such as: mapping, conditional code, input parameters, etc.
Given that IaC provides consistent and easy creation of resources, achieving tenant isolation at the infrastructure level is a matter of deploying the architecture under a different naming scheme, e.g. tenant name prefix on all the resources. IAM policies can be constructed from the code by programmatically referencing the resources under the tenant’s control, enforcing least-privileged access controls, in addition to the creation of dynamic IAM statements that affect the different tenant data.
This method of using IaC creates a centralised source of truth for the application across all tenants. Drift detection and a codebase history can be used to track per-tenant customisations and reliably update them, reducing the need for excessive documentation and maintenance costs.
Controlling the billing per tenant would generally become a problem in these architectures, however, the IaC is able to consistently apply resource tagging strategies to the individual tenant resources and data, allowing billing and reporting to be more reliable and accurate.
As development teams move towards using DevOps approaches, they are looking for easy ways to manage and provision application infrastructure. By using IaC and AWS’s CDK, developers can create consistent and repeatable source controlled and code reviewed systems extensible to multiple environments. At the same time, AWS resources can be created, or frameworks extended, building an internal library of reusable and flexible components to further ease new infrastructure development.