Last active
November 21, 2024 07:59
-
-
Save foriequal0/f1f4ea279fb64836e5fb38efefa133d7 to your computer and use it in GitHub Desktop.
CDK StableNameStack
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { CfnElement, CfnResource, Stack } from "@aws-cdk/core"; | |
import { makeUniqueId } from "@aws-cdk/core/lib/private/uniqueid"; | |
import { Node } from "constructs"; | |
import * as assert from "assert"; | |
const PINNED_RESOURCE_NAMES_CONTEXT_KEY = "pinnedLogicalIds"; | |
type PinnedLogicalIds = { [segment: string]: PinnedLogicalIds | string }; | |
export class StableNameStack extends Stack { | |
protected allocateLogicalId(element: CfnElement): string { | |
const pinnedLogicalIds: PinnedLogicalIds = Node.of(this).tryGetContext(PINNED_RESOURCE_NAMES_CONTEXT_KEY) ?? {}; | |
if (CfnResource.isCfnResource(element)) { | |
const path = element.node.path; | |
const found = find(pinnedLogicalIds, path.split("/"), element); | |
if (found) { | |
return found; | |
} | |
} | |
return super.allocateLogicalId(element); | |
} | |
} | |
function find(pinnedLogicalIds: PinnedLogicalIds, pathSegments: string[], resource: CfnResource): string | undefined { | |
// You can match type with "!AWS::EC2::VPC", or "!EC2::VPC", or "!VPC", if it is not ambiguous | |
const typeSegments = resource.cfnResourceType.split("::"); | |
for (let i = typeSegments.length - 1; i >= 0; i--) { | |
const tails = typeSegments.slice(i).join("::"); | |
const findByType = pinnedLogicalIds[`!${tails}`]; | |
if (findByType !== undefined) { | |
assert(typeof findByType === "string"); | |
return findByType; | |
} | |
} | |
for (let i = pathSegments.length; i > 0; i--) { | |
const prefix = pathSegments.slice(0, i).join("/"); | |
const rest = pathSegments.slice(i); | |
const lookup = pinnedLogicalIds[prefix]; | |
if (typeof lookup === "string") { | |
if (lookup.startsWith("@")) { | |
// path prefix rewrite | |
const path = lookup.slice(1).split("/"); | |
return makeUniqueId([...path, ...rest]); | |
} | |
return lookup; | |
} else if (lookup !== undefined) { | |
const found = find(lookup, rest, resource); | |
if (found) { | |
return found; | |
} | |
} | |
} | |
return undefined; | |
} |
What about stack.renameLogicalId()
I wanted to rename all resources in a construct tree, and wanted to make less changes as possible. What would be different with it?
Got it, so what I would recommend is overriding Stack.allocateLogicalId
.
Thanks 👍
I'll share my modification:
if (typeof lookup === 'string') {
if (lookup.startsWith('@')) {
// path prefix rewrite
- const path = lookup.slice(1).split("/");
+ const path = lookup.slice(1).split("/").filter(s => !!s);
return makeUniqueId([...path, ...rest]);
}
return lookup;
to handle configuration like:
{
"context": {
"pinnedLogicalIds": {
"Stack/A": "@"
}
}
}
CDK v2 version:
import { CfnElement, CfnResource, Names, Stack } from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import * as assert from 'node:assert';
const PINNED_RESOURCE_NAMES_CONTEXT_KEY = 'pinnedLogicalIds';
type PinnedLogicalIds = { [segment: string]: PinnedLogicalIds | string };
export class StableNameStack extends Stack {
protected allocateLogicalId(element: CfnElement): string {
const pinnedLogicalIds: PinnedLogicalIds = this.node.tryGetContext(PINNED_RESOURCE_NAMES_CONTEXT_KEY) ?? {};
if (CfnResource.isCfnResource(element)) {
const path = element.node.path;
const found = find(pinnedLogicalIds, path.split('/'), element);
if (found) {
return found;
}
}
return super.allocateLogicalId(element);
}
}
function find(pinnedLogicalIds: PinnedLogicalIds, pathSegments: string[], resource: CfnResource): string | undefined {
// You can match type with '!AWS::EC2::VPC', or '!EC2::VPC', or '!VPC', if it is not ambiguous
const typeSegments = resource.cfnResourceType.split('::');
for (let i = typeSegments.length - 1; i >= 0; i--) {
const tails = typeSegments.slice(i).join('::');
const findByType = pinnedLogicalIds[`!${tails}`];
if (findByType !== undefined) {
assert.ok(typeof findByType === 'string');
return findByType;
}
}
for (let i = pathSegments.length; i > 0; i--) {
const prefix = pathSegments.slice(0, i).join('/');
const rest = pathSegments.slice(i);
const lookup = pinnedLogicalIds[prefix];
if (typeof lookup === 'string') {
if (lookup.startsWith('@')) {
// path prefix rewrite
const path = lookup.slice(1).split('/').filter(s => !!s);;
return makeUniqueId([...path, ...rest]);
}
return lookup;
} else if (lookup !== undefined) {
const found = find(lookup, rest, resource);
if (found) {
return found;
}
}
}
return undefined;
}
// makeUniqueId function must behave the same as the one at core lib.
// https://github.com/aws/aws-cdk/blob/7373cb9fd35cd4b0ddcbab3837c18babfd8e1b6f/packages/aws-cdk-lib/core/lib/private/uniqueid.ts#L32
export function makeUniqueId(components: string[]) {
const tree = dummyConstructTree(components);
if (tree == undefined) return '';
return Names.uniqueId(tree);
}
function dummyConstructTree(components: string[]): Construct {
const last = components.at(-1);
if (last == undefined) return new Construct(undefined as any, 'dummy');
return new Construct(dummyConstructTree(components.slice(0, -1)), last);
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In
cdk.json
, you can pin logical Ids.