Skip to content

Instantly share code, notes, and snippets.

@foriequal0
Last active November 21, 2024 07:59
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;
}
@acomagu
Copy link

acomagu commented Nov 21, 2024

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