I have my main CDK stack in eu-west-1
, but an EdgeFunction
is created in us-east-1
.
I have noticed two weird things with EdgeFunction
:
NestedStack
they appear in cdk ls
as edge-lambda-stack-nnnnnnnnn
(unless given an explicit name).primary
, it does not remove the lambda stack. Probably because (1) above tells me it's not a part of the NestedStack
.I have tried putting the EdgeFunction
in a separate stack created in us-east-1
explicitly and then cross-referencing it from primary
but that fails with "Cannot use resource in a cross-environment fashion" (amongst others).
cloudfront.experimental.EdgeFunction
respect the NestedStack
boundary?Stack
that exists in us-east-1
and cross-reference the lambda into my main stack in eu-west-1
?primary
stack also automatically deletes the lambda stack.The reason I'm asking is because I have a number of environments and a number of lambdas and the combinatorial explosion of stacks makes things a bit unwieldly.
CDK version 1.116
import "source-map-support/register";
import * as cdk from "@aws-cdk/core";
import * as cloudfront from "@aws-cdk/aws-cloudfront";
import * as lambda from "@aws-cdk/aws-lambda";
import * as path from "path";
class PrimaryStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new SecondaryStack(this, "secondary-stack");
}
}
class SecondaryStack extends cdk.NestedStack {
constructor(scope: cdk.Construct, id: string) {
super(scope, id);
new cloudfront.experimental.EdgeFunction(this, "my-lambda", {
runtime: lambda.Runtime.NODEJS_14_X,
functionName: "my-lambda",
handler: "index.handler",
code: lambda.Code.fromAsset(
path.join(__dirname, "..", "lib", "my-lambda"),
),
});
}
}
const app = new cdk.App();
new PrimaryStack(app, "primary", {
env: { account: "123", region: "eu-west-1" },
});
cdk ls
output:
edge-lambda-stack-cnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
primary
I talked to AWS premium support and the answer is that it doesn't seem like CDK can handle this by using nested stacks.
Instead, create a separate stack for the EdgeLambdas in us-east-1
and then export the ARNs of the lambdas to SSM.
In the other stack, import the ARNs from SSM and use those.
This will result in the minimum amount of stacks to handle. You will still need to deploy the two stacks separately.
The code I ended up using looks like this:
export function exportLambdaSsm(
func: cloudfront.experimental.EdgeFunction,
): ssm.StringParameter {
const funcName = // ...
return new ssm.StringParameter(this, `${funcName}-param`, {
// Parameter with slash must start with a slash
parameterName: `/${funcName}`,
stringValue: func.functionArn,
});
}
export function importLambdaFromSsm(stack: Stack): lambda.IVersion {
const funcName = // ...
const funcResource = new cr.AwsCustomResource(stack, `${funcName}-param`, {
policy: cr.AwsCustomResourcePolicy.fromStatements([
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["ssm:GetParameter*"],
resources: [
stack.formatArn({
service: "ssm",
region: "us-east-1",
resource: "parameter",
resourceName: funcName,
}),
],
}),
]),
onUpdate: {
// will also be called for a CREATE event
service: "SSM",
action: "getParameter",
parameters: {
Name: `/${funcName}`,
},
region: "us-east-1",
// Update physical id to always fetch the latest version
physicalResourceId: cr.PhysicalResourceId.of(Date.now().toString()),
},
});
return lambda.Version.fromVersionArn(stack, funcName, funcResource.getResponseField("Parameter.Value"));
}
Please be aware that when fetching and storing SSM values there are a lot of rules around where slashes go. I tried to keep the code above correct.