AWS CDK:如何在同一个应用程序中引用跨堆栈资源?

29

我有一个应用程序,其中有两个堆栈,都在同一区域/帐户内。其中一个堆栈需要另一个堆栈中存在的 Lambda 的 ARN。我该如何引用它?

// within stackA constructor
public StackA(Construct scope, String id, StackProps props) {
    SingletonFunction myLambda = SingletonFunction.Builder.create(this, "myLambda")
        // some code here
        .build()
    CfnOutput myLambdaArn = CfnOutput.Builder.create(this, "myLambdaArn")
        .exportName("myLambdaArn")
        .description("ARN of the lambda that I want to use in StackB")
        .value(myLambda.getFunctionArn())
        .build();
    
}

App app = new App();
Stack stackA = new StackA(app, "stackA", someAProps);    
Stack stackB = new StackB(app, "stackB", someBProps);
stackB.dependsOn(stackA);

如何将 ARN 传递给 StackB?

5个回答

27
CDK的官方文档中有一个完整的示例,用于在堆栈之间共享S3存储桶。我将其复制在下方以便快速参考。
/**
 * Stack that defines the bucket
 */
class Producer extends cdk.Stack {
  public readonly myBucket: s3.Bucket;

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

    const bucket = new s3.Bucket(this, 'MyBucket', {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });
    this.myBucket = bucket;
  }
}

interface ConsumerProps extends cdk.StackProps {
  userBucket: s3.IBucket;
}

/**
 * Stack that consumes the bucket
 */
class Consumer extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props: ConsumerProps) {
    super(scope, id, props);

    const user = new iam.User(this, 'MyUser');
    props.userBucket.grantReadWrite(user);
  }
}

const producer = new Producer(app, 'ProducerStack');
new Consumer(app, 'ConsumerStack', { userBucket: producer.myBucket });

谢谢你。否则我不会找到那个,而且文档中的示例(https://docs.aws.amazon.com/cdk/latest/guide/resources.html#resource_stack)相当有限。 - badfun

19
只要处于同一AWS区域和账户,您可以访问不同堆栈中的资源。以下示例定义了堆栈stack1,该堆栈定义了一个Amazon S3存储桶。然后它定义了第二个堆栈stack2,该堆栈将stack1中的存储桶作为构造函数属性。
// Helper method to build an environment
static Environment makeEnv(String account, String region) {
    return Environment.builder().account(account).region(region)
            .build();
}

App app = new App();

Environment prod = makeEnv("123456789012", "us-east-1");

StackThatProvidesABucket stack1 = new StackThatProvidesABucket(app, "Stack1",
        StackProps.builder().env(prod).build());

// stack2 will take an argument "bucket"
StackThatExpectsABucket stack2 = new StackThatExpectsABucket(app, "Stack,",
        StackProps.builder().env(prod).build(), stack1.getBucket());

很好,你有关于这个实现的文档吗? - Amit Baranes
您可以在以下 AWS 文档中找到更详细的信息:https://docs.aws.amazon.com/cdk/latest/guide/resources.html - Abhinaya
我更喜欢使用我的示例,因为我也可以从其他地区/帐户导入和导出,但是很好知道。谢谢分享 :) - Amit Baranes
2
stack1.getBucket 定义在哪里?不定义它意味着我们必须猜测,有时我们会猜错。 - Paul S
1
这里有一个完整示例的帖子:https://dev59.com/_lQJ5IYBdhLWcg3w3p3z#57586393 - cyberwombat
这个答案不够充分,因为它没有解释stack1如何在第一次获取到bucket的引用。这是解决问题的关键,应该包含在被接受的答案中。 - teuber789

8

选项1:

使用构造函数将数据从Stack A传递到Stack B:

您可以扩展cdk.stack并创建一个包含stackA的新类。

在该堆栈中,使用public XXX: string\number (等)(请参见示例中的第2行)公开您想要的相关数据。

稍后,只需将此数据传递到StackB构造函数(也可以使用props进行传递)。

工作代码片段:

Stack A:

    export class StackA extends cdk.Stack {
        public YourKey: KEY_TYPE;
    
        constructor(scope: cdk.Construct, id: string, props: cdk.StackProps ) {
            super(scope, id, props);
    
            Code goes here...
    
            // Output the key 
            new cdk.CfnOutput(this, 'KEY', { value: this.YourKey });
    
        }
    }

堆栈B:

export class StackB extends cdk.Stack {
    constructor(scope: cdk.Construct, id: string,importedKey: KEY_TYPE, props: cdk.props) {
        super(scope, id, props)

        Code goes here...
        
        console.log(importedKey)

    }
}

二进制时间戳:

const importedKey = new StackA(app, 'id',props).YourKey;
new StackB(app, 'id',importedKey,props);

选项2:

有时,将这种类型的内容保存在参数存储中并从那里读取会更好。

更多信息在这里


这个方案可以用于跨账户部署吗?例如,如果Stack AStack B在两个不同的账户中,我该如何引用Stack A中定义的Lambda等资源?我正在尝试使用该账户的凭据部署Stack B,但是出现了一个错误,提示我需要Stack A的凭据。 - John
这应该可以跨区域/账户正常工作.. 你能确定错误吗? - Amit Baranes
错误信息如下:"需要为账户111111111111执行AWS调用,但未找到凭据。尝试使用默认凭据",而我使用的是账户222222222222的凭据来部署堆栈B。相关问题请参见:https://stackoverflow.com/review/suggested-edits/26137203 - John
在堆栈A中,您在哪里设置YourKey的值? - Paul S
当我尝试通过new cdk.CfnOutput(this, api.url, { value: this.apiUrl });将apigateway的端点url传递给另一个stack/construct时,我总是会收到“无法在构造ID中使用令牌:https://${Token[TOKEN.54]}.execute-api.${Token[AWS.Region.4]}.${Token[AWS.URLSuffix.1]}/${Token[TOKEN.72]}/” 的错误信息。你也遇到过这个问题吗? - benito_h
显示剩余2条评论

8

我发现所有的答案都朝着正确的方向,但没有一个完全或者好好地解释它。在这个例子中,我正在将一个VPC从一个VPC堆栈传递到一个ECS集群。

首先,向原始堆栈添加一个属性。每当创建资产时,此属性将被设置:

export class VpcStack extends cdk.Stack {
    readonly vpc: Vpc;

    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
    
        // Here
        this.vpc = new Vpc(this, 'vpc', {
            maxAzs: 3,
            cidr: '10.0.0.0/16',
        });
    });
}

接下来,在使用的堆栈中将此属性作为参数要求:
// Create an interface that extends cdk.StackProps
// The VPC property is added here
interface EcsClusterStackProps extends cdk.StackProps {
    vpc: Vpc,
}

export class EcsClusterStack extends cdk.Stack {
    // Use your interface instead of the regular cdk.StackProps
    constructor(scope: cdk.Construct, id: string, props: EcsClusterStackProps) {  
        super(scope, id, props);
    
        // Use the passed-in VPC where you need it
        new Cluster(this, "myCluster", {
            capacity: {
                instanceType: InstanceType.of(InstanceClass.M6I, InstanceSize.LARGE)
            },
            clusterName: "myCluster",
            vpc: props.vpc,  // Here
        });
    }
}

第三步,将引用传递到您的应用文件中:
const app = new cdk.App();

// Create the VPC stack
const vpcStack = new VpcStack(app, 'vpc-stack', {
    env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});

// Pass the VPC directly to the consuming stack's constructor
const ecsClusterStack = new EcsClusterStack(app, 'ecs-cluster-stack', {
    vpc: vpcStack.vpc,  // Here
});

希望这能澄清一些不明确的地方。

帮助我的一些资源:Bobby的博客AWS文档 - teuber789

0
在 Alien Attack https://github.com/aws-samples/aws-alien-attack 上,我创建了一个名为 ResourceAwareStack 的类,它继承自 Stack,并实现了一些方法来添加/获取可以在堆栈之间共享的资源。你可以看一下。

1
这可能回答了问题,但当链接失效时,答案就变得多余了。你能否添加更多细节,使答案成为独立的,不依赖外部链接的内容? - Brian Tompsett - 汤莱恩

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接