如何解决 AWS CDK v2 中 RDS 和 Secrets Manager 堆栈之间的循环依赖错误?

问题描述 投票:0回答:1

我知道有时订单会创建或破坏 CloudFormation 模板,但这应该不是问题,或者至少我不清楚为什么会出现此错误:

错误:“DataPipelinePrototypeSecretStack”取决于 'DataPipelinePrototypeDatabaseStack'(DataPipelinePrototypeSecretStack) -> DataPipelinePrototypeDatabaseStack/PrototypeDb1/Resource.Ref)。添加此依赖项(DataPipelinePrototypeDatabaseStack -> DataPipelinePrototypeSecretStack/DataPipelinePrototypeMasterUserSecret/Resource.Ref) 将创建循环引用。

我只是在 SecretStack 中创建一个秘密,完全独立于 RDS:

export class SecretStack extends Stack {
    public readonly databaseMasterUserSecret: Secret;

    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);
        this.databaseMasterUserSecret = new Secret(this, "MasterUserSecret", {
            secretName: "pg-master-user-secret",
            generateSecretString: {
                secretStringTemplate: JSON.stringify({ username: "postgres" }),
                generateStringKey: "password",
                passwordLength: 16,
                excludePunctuation: true,
            },
        });
    }
}

然后,在DatabaseStack中,创建RDS实例:

interface DatabaseStackProps extends StackProps {
    vpc: Vpc,
    securityGroup: SecurityGroup,
    secret: Secret
}

export class DatabaseStack extends Stack {
    constructor(scope: Construct, id: string, props: DatabaseStackProps) {
        super(scope, id, props);

        const engine = DatabaseInstanceEngine.postgres({ version: PostgresEngineVersion.VER_15_4 });
        const instanceType = InstanceType.of(InstanceClass.T3, InstanceSize.MICRO);
        const port = 5432;
        const dbName = "dbname";

        const dbInstance = new DatabaseInstance(this, "TestDb1", {
            vpc: props.vpc,
            vpcSubnets: { subnetType: SubnetType.PRIVATE_ISOLATED },
            instanceType,
            engine,
            port,
            securityGroups: [props.securityGroup],
            databaseName: dbName,
            credentials: Credentials.fromSecret(props.secret), //HERE...
            backupRetention: Duration.days(0),
            deleteAutomatedBackups: true,
            removalPolicy: RemovalPolicy.DESTROY,
        });
    }
}

以下是它们在入口点的调用方式:

const vpcStack = new VpcStack(app, "DataPipelinePrototypeVpc");
const secretStack = new SecretStack(app, "DataPipelinePrototypeSecretStack");
new DataBaseStack(app, "DataPipelinePrototypeDatabaseStack", {
    vpc: vpcStack.vpc,
    securityGroup: vpcStack.securityGroup,
    secret: secretStack.databaseMasterUserSecret
});
const lambdaStack = new LambdaStack(app, "DataPipelinePrototypeLambdaStack", {
    vpc: vpcStack.vpc,
    securityGroup: vpcStack.securityGroup,
    secret: secretStack.databaseMasterUserSecret
});

翻转 lambdaStack 和 DatabaseStack 顺序不起作用。

当秘密像这样嵌套在 DatabaseStack 内部时,它工作得很好:

export class DatabaseStack extends Stack {
    constructor(scope: Construct, id: string, props: DatabaseStackProps) {
        super(scope, id, props);

        const engine = DatabaseInstanceEngine.postgres({ version: PostgresEngineVersion.VER_15_4 });
        const instanceType = InstanceType.of(InstanceClass.T3, InstanceSize.MICRO);
        const port = 5432;
        const dbName = "dbname";
        
        const secret = new Secret(this, "MasterUserSecret", {
            secretName: "pg-master-user-secret",
            description: "Database master user credentials",
            generateSecretString: {
                secretStringTemplate: JSON.stringify({ username: "postgres" }),
                generateStringKey: "password",
                passwordLength: 16,
                excludePunctuation: true,
            },
        });        

        const dbInstance = new DatabaseInstance(this, "TestDb1", {
            vpc: props.vpc,
            vpcSubnets: { subnetType: SubnetType.PRIVATE_ISOLATED },
            instanceType,
            engine,
            port,
            securityGroups: [props.securityGroup],
            databaseName: dbName,
            credentials: Credentials.fromSecret(secret), //HERE...
            backupRetention: Duration.days(0),
            deleteAutomatedBackups: true,
            removalPolicy: RemovalPolicy.DESTROY,
        });
    }
}

但是,我需要向 Lambda 添加权限(请参阅上面的入口点),因此我还需要将密钥传递到 LambdaStack,以便我能够从 Lambda 代码中检索密钥:

interface LambdaStackProps extends StackProps {
    vpc: Vpc,
    securityGroup: SecurityGroup,
    secret: Secret
}

export class LambdaStack extends Stack {
    public readonly dataPipelineLambda: NodejsFunction;

    constructor(scope: Construct, id: string, props: LambdaStackProps) {
        super(scope, id, props);
        this.dataPipelineLambda = new NodejsFunction(this, "Lambda", {
            functionName: "dataPipelineHandler",
            runtime: Runtime.NODEJS_18_X,
            handler: "handler",
            entry: "./service/handler.ts"
        });
        this.dataPipelineLambda.addToRolePolicy(new PolicyStatement({
            effect: Effect.ALLOW,
            resources: [props.secret.secretArn],
            actions: [
                "ssm:Get*"
            ]
        }));
    }
}
amazon-web-services aws-lambda amazon-rds aws-cdk aws-secrets-manager
1个回答
0
投票

除非有人有更好的答案,否则我可以手动提取

credentials
中的
DatabaseInstance
值以使其正常工作:

new DatabaseInstance(this, "PrototypeDb1", {
    ///...etc...
    credentials: {
        username: props.secret.secretValueFromJson("username").unsafeUnwrap().toString(),
        password: props.secret.secretValueFromJson("password")
    }
});
© www.soinside.com 2019 - 2024. All rights reserved.