論理 ID | AWS CDK
CDK は論理 ID にハッシュ値のサフィックスを自動付与します。
- 例:WebVpc → WebVpc46147648
- 例外:スタック直下に置いた L1(
Cfnで始まるクラス)は、パスが 1 階層のためハッシュが付かず、construct ID がそのまま論理 ID になる
サフィックスのハッシュ値について
ハッシュは、スタックからの相対パス(スタック ID は含まない)から生成されます。
L2 コンストラクトの場合は、内部で作られる L1 の ID である Resource までを含んだパス(例:SampleVpc/Resource)が対象です。
- 同じコードならパスは同じなので、常に同じハッシュになる
- コンストラクト ID を変更するとパスが変わり、ハッシュ(と論理 ID)が変わるので、リソースの再作成(置換)が発生する
- 親のコンストラクトの ID を変えたり、間に階層を挟んだりしても、パスが変わるので同様に置換が発生する
- スタック ID を変更しても論理 ID は変わらない。ただしスタック自体が別物として新規作成されるため、旧スタックのリソースは引き継がれない
これが CDK でコンストラクト ID を安易に変更してはいけない理由です。
例
Stack や L2 コンストラクトなど多くの Construct のコンストラクタは (scope: Construct, id: string, props?) という形を取り、scope に親を渡すことで実行時にツリー構造が組まれる仕組みです(constructs ライブラリの Composite pattern)。
例外として App は scope を取りません。
export class SampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// this(SampleStackインスタンス自身)をscopeとして渡す
// → SampleVpcはSampleStackの子になる
const vpc = new ec2.Vpc(this, 'SampleVpc', { /* ... */ });
}
}
const app = new cdk.App();
new SampleStack(app, 'SampleStack');
このとき vpc.node.path は SampleStack/SampleVpc になります。this を渡すことで 今のクラスの中に子コンストラクトを吊るす という親子関係が作られ、さらに深くネストしたい場合は自作の Construct クラスを間に挟んで scope を渡していけば、SampleStack/MyConstruct/SampleVpc のように何段階でも階層を作れます。
論理 ID のハッシュ計算に使われるのは、スタックを除いたパスです。この例の VPC は SampleVpc/Resource から計算されるので、論理 ID は SampleVpc にハッシュが付いた形になります。
論理 ID の変更を避ける方法
コンストラクト ID の変更やリファクタリングが避けられない場合は、論理 ID を固定して、置換を防ぎます。
// 1. L1 の論理 ID を固定する
const cfnVpc = vpc.node.defaultChild as ec2.CfnVPC;
cfnVpc.overrideLogicalId('SampleVpcXXXXXXXX'); // 変更前の論理 ID
// 2. スタックで、生成される論理 ID を別の ID に置き換える
this.renameLogicalId('NewGeneratedLogicalId', 'OldLogicalId');
- どちらの方法でも、変更前後の論理 ID を
cdk diffで確認してから使う - 物理名(
bucketNameなど)を明示したリソースは、置換時に「新リソース作成 → 旧リソース削除」の順で処理されるため、同名のリソースがすでに存在して失敗する。物理名は CloudFormation の自動生成に任せるのが安全 cdk diffでreplaceと表示されたリソースは、置換が発生する