We currently have "base images" that our downstream containers pull from and we are in the process of upgrading from bespoke CDK pipelines to simple CDK pipelines using aws-cdk-lib/aws-ecr-assets
. We had been doing this by sharing the URI of these base images using SSM as they are in separate projects & stacks.
I have been attempting to do this by importing the SSM into the downstream stack and passing the URI as a Buildarg, but I'm running into "Cannot use tokens in keys or values of "buildArgs" since they are needed before deployment", which as per issue #3981 is as intended.
This makes me think I'm doing this wrong, but I'm not sure what the best practice is to achieve this? I'm working around this by hard-coding the URL that is stored in the SSM, and obviously I could pre-fetch this and pass it in at runtime but that seems clunky.
The architecture I was trying to achieve is like this:
Upstream stack:
const pythonDocker = new DockerImageAsset(this, 'python-3-base-image', {
directory: join(thisDirectoryInSrc, '/python-3'),
ignoreMode: IgnoreMode.DOCKER,
});
new StringParameter(this, 'python3-ecr-arn-ssm', {
stringValue: pythonDocker.imageUri,
description: 'Python 3 image repository URI',
parameterName: '/docker-images/python-3/image-uri',
});
Downstream stack:
const baseImage = StringParameter.fromStringParameterName(this, 'python-3-base-image', '/docker-images/python-3/image-uri');
const feedReaderDocker = ContainerImage.fromAsset('src/feed-reader', {
ignoreMode: IgnoreMode.DOCKER,
buildArgs: {
BASE_IMAGE: baseImage.stringValue
},
});
Downstream Dockerfile:
ARG BASE_IMAGE
FROM BASE_IMAGE
# Install dependencies, declare entry point, etc
TL;DR Use context. The StringParameter.valueFromLookup
"context method" can retrieve and cache the previously deployed Parameter value at synth-time.
As you experienced, StringParameter.fromStringParameterName
resolves at deploy-time. But ContainerImage.fromAsset
needs the resolved base image value at synth-time. CDK gives us Runtime Context for this use case:
The AWS CDK supports several context methods that enable AWS CDK apps to obtain contextual information from the AWS environment... If a required context value isn't available, the AWS CDK app notifies the CDK Toolkit that the context information is missing. The CLI then queries the current AWS account for the information, stores the resulting context information in the cdk.context.json file
StringParameter.valueFromLookup is one such "context method". Here's an example that compares the synth-time values of the two methods:
const paramName = '/cdk-bootstrap/hnb659fds/version';
const fromName: ssm.IStringParameter = ssm.StringParameter.fromStringParameterName(this, 'FromName', paramName);
const fromLookup: string = ssm.StringParameter.valueFromLookup(this, paramName);
console.dir({ fromName: fromName.stringValue, fromLookup });
// -> { fromName: '${Token[TOKEN.196]}', fromLookup: '12' }
valueFromLookup
fetches the value once with an SDK call and caches it in cdk.context.json
. CDK recommends you commit this file to adhere to the deterministic deploy best practice.