🔰

CDK のクロススタック参照での循環参照エラー

2023/09/20に公開
  • 状況: AWS CDK で他のスタックのリソースを参照する際、参照が循環していると Error: 'xxx' depends on 'yyy' (xxx -> yyy/...). Adding this dependency (yyy -> xxx/...) would create a cyclic reference. というエラーが発生する。
  • 対処法: スタック xxx 内のリソースがスタック yyy で定義されたリソースを参照している箇所を、yyy 内のリソースが xxx で定義されたリソースを参照するように修正する。

状況

  • CommonStack で VPC と RDS インタンスを定義する。
lib/common-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as rds from 'aws-cdk-lib/aws-rds';
import { Construct } from 'constructs';

export class CommonStack extends cdk.Stack {
  public vpc: ec2.Vpc;
  public db: rds.DatabaseInstance;

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const vpc = new ec2.Vpc(this, 'Vpc', {
      maxAzs: 1,
      subnetConfiguration: [
        {
          name: 'public',
          subnetType: ec2.SubnetType.PUBLIC,
        },
        {
          name: 'private-isolated',
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
        },
      ],
    });

    const db = new rds.DatabaseInstance(this, 'Db', {
      engine: rds.DatabaseInstanceEngine.MYSQL,
      vpc: vpc,
      vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },      
    });

    this.vpc = vpc;
    this.db = db;
  }
}
  • props 経由で、CommonStack から AppStack へ VPC と RDS インスタンスを渡す。
bin/cdk.ts
import * as cdk from 'aws-cdk-lib';
import { CommonStack } from '../lib/common-stack';
import { AppStack } from '../lib/app-stack';

const cdkApp = new cdk.App();
const commonStack = new CommonStack(cdkApp, 'CommonStack');
const appStack = new AppStack(cdkApp, 'AppStack', {
  vpc: commonStack.vpc,
  db: commonStack.db,
});
  • AppStack で EC2 インタンスを定義する。
  • EC2 インスタンスからのみ RDS インスタンスに接続できるように、 Connections.allowFrom でセキュリティグループの設定を作成する(// ★の部分)。
lib/app-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as rds from 'aws-cdk-lib/aws-rds';
import { Construct } from 'constructs';

interface AppStackProps extends cdk.StackProps {
  vpc: ec2.Vpc;
  db: rds.DatabaseInstance;
};

export class AppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: AppStackProps) {
    super(scope, id, props);

    const instance = new ec2.Instance(this, 'Instance', {
      vpc: props.vpc,
      instanceType: ec2.InstanceType.of(ec2.InstanceClass.T4G, ec2.InstanceSize.NANO),
      machineImage: ec2.MachineImage.latestAmazonLinux(),
    });

    props.db.connections.allowFrom(instance, ec2.Port.tcp(3306)); // ★
  }
}
  • cdk synth すると Error: 'xxx' depends on 'yyy' (xxx -> yyy/...). Adding this dependency (yyy -> xxx/...) would create a cyclic reference. というエラーが発生する。
node ➜ /workspace $ npx cdk synth
/workspace/node_modules/aws-cdk-lib/core/lib/stack.ts:908
      throw new Error(`'${target.node.path}' depends on '${this.node.path}' (${cycleDescription}). Adding this dependency (${reason.description}) would create a cyclic reference.`);
            ^
Error: 'CommonStack' depends on 'AppStack' (CommonStack -> AppStack/AppInstance/InstanceSecurityGroup/Resource.GroupId). Adding this dependency (AppStack -> CommonStack/Vpc/Resource.Ref) would create a cyclic reference.
    at AppStack._addAssemblyDependency (/workspace/node_modules/aws-cdk-lib/core/lib/stack.ts:908:13)
    at operateOnDependency (/workspace/node_modules/aws-cdk-lib/core/lib/deps.ts:90:24)
    at addDependency (/workspace/node_modules/aws-cdk-lib/core/lib/deps.ts:26:3)
    at AppStack.addDependency (/workspace/node_modules/aws-cdk-lib/core/lib/stack.ts:615:18)
    at resolveValue (/workspace/node_modules/aws-cdk-lib/core/lib/private/refs.ts:130:12)
    at resolveReferences (/workspace/node_modules/aws-cdk-lib/core/lib/private/refs.ts:34:24)
    at prepareApp (/workspace/node_modules/aws-cdk-lib/core/lib/private/prepare-app.ts:30:20)
    at synthesize (/workspace/node_modules/aws-cdk-lib/core/lib/private/synthesis.ts:45:13)
    at App.synth (/workspace/node_modules/aws-cdk-lib/core/lib/stage.ts:217:33)
    at process.<anonymous> (/workspace/node_modules/aws-cdk-lib/core/lib/app.ts:195:45)

対処法

  • AppStack の EC2 が CommonStack で定義された VPC を参照し、同時に CommonStack の RDS が AppStack で定義された EC2 を参照する状態になっているため、循環参照が発生している。
  • セキリュティグループのインバウンドルールの定義部分(// ★)を、EC2 が RDS を参照するように修正する。
修正前
props.db.connections.allowFrom(instance, ec2.Port.tcp(3306)); // ★
修正跡
instance.connections.allowTo(props.db, ec2.Port.tcp(3306));

Discussion