aws-cdk
aws-cdk copied to clipboard
aws-cloudfront: Deploying mutliple stacks on same account containing Cloudfront Function resources without `functionName` generates conflicts
Describe the bug
Deploying multiple instances of a stack (with different stackName
) on the same AWS Account generates name conflicts on Cloudfront Function resources where function name is generated by CDK.
Expected Behavior
I'd expect stackName to have an impact on the resource name. This is one of those case where the underlying L1 construct CfnFunction
requires one property name
, but L2 construct Function
makes this property optional (unlike for exemple DynamoDB Table where Cloudformation takes care of the name generation AFAIK). The unique name generation then relies solely on the CDK implementation.
Current Behavior
Name generated for function in the following file does note rely on stackName
and generate the same output for 2 different stacks.
https://github.com/aws/aws-cdk/blob/289fb96810ba8c2dd4d58dad06401c10eeddd45c/packages/%40aws-cdk/aws-cloudfront/lib/function.ts#L178-L185
Function name should indeed be unique per acccount, thus resulting in the following error:
Resource handler returned message: "Resource of type 'AWS::CloudFront::Function' with identifier 'eu-west-1TmpCdkStackMyFunction3C477B93' already exists."
Reproduction Steps
lib/tmp-cdk-stack.ts
import { Stack, StackProps } from 'aws-cdk-lib';
import { Function, FunctionCode } from 'aws-cdk-lib/aws-cloudfront';
import { Construct } from 'constructs';
export class CloudfrontStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new Function(this, 'MyFunction', {
code: FunctionCode.fromInline('function handler(event) { return event.request }')
})
}
}
bin/tmp-cdk.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CloudfrontStack } from '../lib/tmp-cdk-stack';
const app = new cdk.App();
new CloudfrontStack(app, 'TmpCdkStack', {
stackName: process.env.STACK_NAME
});
Then run successively STACK_NAME=PR-1 cdk deploy
and ``STACK_NAME=PR-2 cdk deploy`
Possible Solution
Digging around, I found this similar issue on CodePipeline https://github.com/aws/aws-cdk/issues/18828
This was discovered on https://github.com/getlift/lift/issues/247
The newly created uniqueResourceName
API on Names
can be used to instead of uniqueId
.
The resulting cloudfront.Function
L2 construct implementation would ressemble something like this
export class Function extends Resource implements IFunction {
constructor(scope: Construct, id: string, props: FunctionProps) {
super(scope, id);
this.functionName = props.functionName ?? Names.uniqueResourceName(this, {
maxLength: 64,
separator: '-',
allowedSpecialCharacters: '_-',
});
const resource = new CfnFunction(this, 'Resource', {
autoPublish: true,
functionCode: props.code.render(),
functionConfig: {
comment: props.comment ?? this.functionName,
runtime: 'cloudfront-js-1.0',
},
name: this.functionName,
});
}
}
This feature shall be released behind featureFlag to not break existing already deployed Function constructs. This is even more important as the name of a Cloudfront function is not updatable.
I'd be happy to come up with a PR if you deem this updated implementation interesting :)
Additional Information/Context
No response
CDK CLI Version
2.35.0 (build 5c23578)
Framework Version
No response
Node.js Version
v16.16.0
OS
Mac OS X
Language
Typescript
Language Version
No response
Other information
No response