Categories
AWS CDK

Sharing resources in AWS CDK

Cloud resources do not exist in isolation, as when it comes to Infrastructure as Code, we deal with infrastructure graph. It means that we need to create relationships between them, and AWS CDK has to support that – so let’s learn how to do it!

As described in my blog post about Constructs, the AWS CDK provides a rich class library of constructs, and AWS resources are a particular example of a construct. Additionally, it is sporadic when a resource exists without any context and without relations with other resources.

So, if we would like to connect our resources to the proper infrastructure graph, we need to model interdependencies between constructs. In AWS CloudFormation, we would either use Fn::Ref or Outputs, Parameters, Exports, and Fn::ImportValues. But how should that be done in AWS CDK to preserve idiomatic way?

What options do we have?

We do have two kinds of scenarios here:

  1. Internal sharing across AWS CDK Constructs and Stacks.
  2. Sharing in both ways between AWS CDK and external AWS CloudFormation templates.

Let’s discuss both in order.

Internal Sharing

Internal Sharing means that we want to share resources between higher-level and lower-level Constructs (including Stacks) inside AWS CDK Application.

Single stack

This is easy because you can access the properties, e.g., share the reference between constructs by passing it to the ConstructsAWS CDK discovers that we are inside a single stack and will use Fn::Ref via logical identifiers from AWS CloudFormation. Sweet!

Multiple stacks

If we’d like to build an infrastructure graph across multiple stacks, the solution will be basically the same as what I described in the previous paragraph, except it will use OutputsExports, and Fn::ImportValue.

If you have worked with this in AWS CloudFormation, you know this can be good and bad at the same time. The truth is that we rarely want such tight coupling between stacks as it is enforced by this functionality.

How to avoid that? There are two options:

  1. Parameters – which are problematic, as I described it here.
  2. As we now can use code and programming language, we can easily employ AWS SSM Parameter Store values, which will work in this case, much like Exports, but without this tight coupling.

External Sharing

However, that’s not all. AWS CDK was designed to cooperate in a mixed environment, with a very wise assumption that we will not rewrite everything into AWS CDK immediately. So we need to have a way to exchange information in a bidirectional way between AWS CDK and existing AWS CloudFormation stacks.

From AWS CDK to AWS CloudFormation

Let’s explain this in an example.

If you define a VPC inside a CDK Application and you’d like to use it from an AWS CloudFormation template, it actually functions much like how you would share the template between plain CFN templates. You would use Outputs and Exports in the one template and Parameters or Imports in the other.

In AWS CDK exporting works by calling vpc.export() inside your CDK Application. It creates Exports for all attributes of your VPC that another CDK stack would need to import, and those outputs are available to any AWS CloudFormation stack in the region we operate. Deploy a template via AWS CDK and pick your favorite method of getting the VPC information into the other template.

One note: if you’re not happy about the default naming convention of the Exports (it’s understandable since they are designed to be consumed transparently), you can change it. You’re free to add some additional outputs (via new Output()) to your CDK Stack, which translates directly into the AWS CloudFormation Outputs section, and you can create exported values from there as well.

From AWS CloudFormation to AWS CDK

Let’s analyze now a reverse case if you already have an existing VPC deployed through CloudFormation or otherwise, and you want to consume it in a CDK Application.

What you want to do is to import it via Vpc.fromLookup(…) (via its name or a few other options) or Vpc.fromVpcAttributes(…) (more flexible because of many attributes from VpcAttributes struct). Similarly, you can do the same thing for additional resources (e.g., Bucket.fromBucketArn(…)), and that will quickly introduce those values into the context. I explained contexts here, but one thing to remember is that it will be looked up once and stay there unless you remove it.

Alternatively, you can also use other options:

  • AWS CloudFormation Parameters – more cons, especially in the context of AWS CDK, that I explained here.
  • AWS CloudFormation Imports – it will work if resources were created via CloudFormation, and they are exported.
  • Synthesis-time parameters – not ideal in all cases, but you can choose to pass in the actual values when running the CDK Application. You can do it either as context values or as a hardcoded parameter to your constructs – in the end, the AWS CloudFormation template comes out with the identifiers already filled in.

There is also one additional option. However, it’s not applicable in all cases: you can include the whole template and manage it via AWS CDK from now on:

import * as cdk from "@aws-cdk/core"; import * as fs from "fs"; new cdk.CfnInclude(this, "ExistingInfrastructure", { template: JSON.parse(fs.readFileSync("template.json").toString()) }); // ... // And now assuming that `template.json` file contains // a resource with logical ID equal to `S3Bucket`: const bucketArn = cdk.Fn.getAtt("S3Bucket", "Arn");

However, keep in mind that from now on, you should manage the included template from the AWS CDK.

It’s also worth noting that together with overriding logical ID, this is also a great way to introduce existing resources to AWS CDK without redeploying them.

Summary

This topic is essential, especially if you are managing existing infrastructure.

As AWS CDK is based on AWS CloudFormation, it is both a blessing (that we can use existing mechanisms as-is) and a curse (that those mechanisms are constraining us in many cases). Our job is to understand and choose the proper machinery, and I hope that this post will help you with that.

By Wojciech Gawroński

Principal Cloud Architect at Pattern Match. Infrastructure as Code, DevOps culture and AWS aficionado. Functional Programming wrangler (Erlang/Elixir).

Comments

This site uses Akismet to reduce spam. Learn how your comment data is processed.