Skip to content

Instantly share code, notes, and snippets.

@foriequal0
Last active March 7, 2024 05:05
Show Gist options
  • Save foriequal0/f1f4ea279fb64836e5fb38efefa133d7 to your computer and use it in GitHub Desktop.
Save foriequal0/f1f4ea279fb64836e5fb38efefa133d7 to your computer and use it in GitHub Desktop.
CDK StableNameStack
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;
}
@foriequal0
Copy link
Author

foriequal0 commented Nov 24, 2020

In cdk.json, you can pin logical Ids.

{
  "context": {
    "pinnedLogicalIds": {
      // You can pin a resource name in a path
      "StackA/Path/To/Resource": "OldNameToKeep",
      "StackA/Path": {
        "To/Construct": {
          // You can match type with '!'.
          // 'AWS::EC2::VPC' type resource that matches 'StackA/Path/To/Construct/*' will get 'Vpc'.
          "!AWS::EC2::VPC": "Vpc",
          // You can omit 'EC2', 'AWS' if they are not ambiguous in this context.
          "!EC2::InternetGateway": "IGW",
          "!VPCGatewayAttachment": "VPCGW",
          // You can pin a tree with '@'.
          // All resources 'StackA/Path/To/Construct/Of/Construct/*' will named as if they were in 'StackA/Previous/Path/Construct/*'
          "Of/Construct": "@Previous/Path/Construct"
        }
      }
    }
  }
}

@eladb
Copy link

eladb commented Dec 6, 2020

@foriequal0
Copy link
Author

I wanted to rename all resources in a construct tree, and wanted to make less changes as possible. What would be different with it?

@eladb
Copy link

eladb commented Dec 6, 2020

Got it, so what I would recommend is overriding Stack.allocateLogicalId.

@foriequal0
Copy link
Author

Thanks 👍

@acomagu
Copy link

acomagu commented Mar 15, 2022

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": "@"
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment