How to resolve “All arguments to Vpc.fromLookup() must be concrete (no Tokens)” error in CDK

Summary

In CDK, when importing an existed VPC from outside CDK app, or importing a VPC ID from a stack’s output, one might get error “All arguments to Vpc.fromLookup() must be concrete (no Tokens)” during synthesis. This article walks you through a solution using SSM for this issue.

Resolution

In CDK, when importing an existed VPC from outside CDK app, or importing a VPC ID from a stack’s output, one might get error “All arguments to Vpc.fromLookup() must be concrete (no Tokens)” during synthesis.

This is due to, at this point, calling `Vpc.fromLookup()` will lead to a lookup when the CDK CLI is executed, therefore, we cannot use any values that will only be available at CloudFormation execution time (i.e., Tokens).

Given this requirement, if we exported a VPC ID in a stack’s output (`CfnOutput`), then import it in other stacks (`Fn::ImportValue`), and then call `Vpc.fromLookup()`, the above error will occur.

To achieve the use case that having a CDK stack that creates and provides a VPC, which later can be used by other stacks, we need to make sure the `Vpc.fromLookup()` is provided actual value during synthesis. However, in most cases, hard-coding VPC ID is not applicable.

Comes down to our solution here, as workaround, we can use `aws-ssm` module to store VPC ID, and then use `StringParameter.valueFromLookup` to retrieve VPC ID in SSM. `StringParameter.valueFromLookup` reads the value of an SSM parameter during synthesis through an environmental context provider, thus it satisfies the requirement of calling `Vpc.fromLookup()`.

There are some considerations for this solution:

1. `StringParameter.valueFromLookup` returns the actual value of the parameter as a Runtime context value. If the value is not already cached in cdk.json or passed on the command line, it will be retrieved from the current AWS account. For this reason, the stack must be synthesized with explicit account and region information.

2. Only plain Systems Manager strings may be retrieved, not secure strings.

3. It is not possible to request a specific version; the latest version is always returned.

Example

In stack A (`VpcProvider`), I created a VPC resource, and then stored the VPC ID to a SSM parameter. Snippet of `VpcProvider` stack:

Once stack A is created, in stack B (`VpcConsumer`), I used `StringParameter.valueFromLookup` to get the VPC ID, and used `Vpc.fromLookup()` to import VPC to the stack. Snippet of `VpcConsumer` stack:

Meanwhile, I need to explicitly provide account and region in CDK entry point:

Synthesis won’t return that error any more, since VPC ID is retrieved by `StringParameter.valueFromLookup` and stored in `cdk.context.json` at `cdk synth`.

Reference

[1] Get a value from the Systems Manager Parameter Store

[2] static fromLookup(scope, id, options)

[3] Tokens

I blog about Cloud, DevOps, Cybersecurity. Opinions are my own.