CloudFormation 跨区域引用

30
当您在同一地区运行多个CloudFormation堆栈时,可以使用CloudFormation Outputs跨堆栈共享引用。
但是,正如该文档所述,输出不能用于跨地区引用。
引用CloudFormation中不同地区的值应该如何处理?
例如,我在us-east-1部署了Route 53托管区域。然而,我有一个后端在us-west-2,我想创建一个DNS验证ACM证书,需要引用托管区域才能创建适当的CNAME以证明所有权。
我该如何从us-west-2引用在us-east-1创建的托管区域ID?
4个回答

38

我发现最简单的方法是将您想要共享的引用(即在此情况下的托管区域 ID)写入Systems Manager Parameter Store,然后在不同区域中的“子”堆栈中使用自定义资源引用该值。

幸运的是,如果您使用Cloud Development Kit (CDK)创建模板,这非常容易。

对于自定义资源从 SSM 读取,您可以使用类似以下内容的东西:

// ssm-parameter-reader.ts

import { Construct } from '@aws-cdk/core';
import { AwsCustomResource, AwsSdkCall } from '@aws-cdk/custom-resources';

interface SSMParameterReaderProps {
  parameterName: string;
  region: string;
}

export class SSMParameterReader extends AwsCustomResource {
  constructor(scope: Construct, name: string, props: SSMParameterReaderProps) {
    const { parameterName, region } = props;

    const ssmAwsSdkCall: AwsSdkCall = {
      service: 'SSM',
      action: 'getParameter',
      parameters: {
        Name: parameterName
      },
      region,
      physicalResourceId: Date.now().toString() // Update physical id to always fetch the latest version
    };

    super(scope, name, { onUpdate: ssmAwsSdkCall });
  }

  public getParameterValue(): string {
    return this.getData('Parameter.Value').toString();
  }
}

要将托管区域ID写入参数存储,您可以简单地执行以下操作:

// route53.ts (deployed in us-east-1)

import { PublicHostedZone } from '@aws-cdk/aws-route53';
import { StringParameter } from '@aws-cdk/aws-ssm';

export const ROUTE_53_HOSTED_ZONE_ID_SSM_PARAM = 'ROUTE_53_HOSTED_ZONE_ID_SSM_PARAM';

/**
 * Other Logic
 */

const hostedZone = new PublicHostedZone(this, 'WebsiteHostedZone', { zoneName: 'example.com' });

new StringParameter(this, 'Route53HostedZoneIdSSMParam', {
  parameterName: ROUTE_53_HOSTED_ZONE_ID_SSM_PARAM,
  description: 'The Route 53 hosted zone id for this account',
  stringValue: hostedZone.hostedZoneId
});

最后,您可以使用刚刚创建的自定义资源从参数存储区中读取该区域的值,并将其用于在us-west-2创建证书。

// acm.ts (deployed in us-west-2)

import { DnsValidatedCertificate } from '@aws-cdk/aws-certificatemanager';
import { PublicHostedZone } from '@aws-cdk/aws-route53';

import { ROUTE_53_HOSTED_ZONE_ID_SSM_PARAM } from './route53';
import { SSMParameterReader } from './ssm-parameter-reader';

/**
 * Other Logic
 */

const hostedZoneIdReader = new SSMParameterReader(this, 'Route53HostedZoneIdReader', {
  parameterName: ROUTE_53_HOSTED_ZONE_ID_SSM_PARAM,
  region: 'us-east-1'
});
const hostedZoneId: string = hostedZoneIdReader.getParameterValue();
const hostedZone = PublicHostedZone.fromPublicHostedZoneId(this, 'Route53HostedZone', hostedZoneId);

const certificate = new DnsValidatedCertificate(this, 'ApiGatewayCertificate', { 'pdx.example.com', hostedZone });

1
运行得非常好。我将其用于全局加速器,因为需要来自不同地区的 ARN。 - Jake
如何使用类似于Serverless框架的工具来完成类似的事情? - Marzouk
1
@Marzouk 是的。Serverless框架提供了免费的仪表板,其中有一个名为“outputs”的功能,可以在部署时导出值,例如CloudFormation ARN等,然后使用${outputs}语法进行导入。更多细节请参见:https://www.serverless.com/framework/docs/dashboard/output-variables/ - Gareth McCumskey
很遗憾,如果存储中的参数发生更改,我不认为CloudFormation会检测到此情况并使用更新后的值重新部署。 - Adrian Baker

17
< p > cdk库已更新,需要将上面的代码更改为以下内容:< /p >
import { Construct } from '@aws-cdk/core';
import { AwsCustomResource, AwsSdkCall } from '@aws-cdk/custom-resources';
import iam = require("@aws-cdk/aws-iam");

interface SSMParameterReaderProps {
  parameterName: string;
  region: string;
}

export class SSMParameterReader extends AwsCustomResource {
  constructor(scope: Construct, name: string, props: SSMParameterReaderProps) {
    const { parameterName, region } = props;

    const ssmAwsSdkCall: AwsSdkCall = {
      service: 'SSM',
      action: 'getParameter',
      parameters: {
        Name: parameterName
      },
      region,
      physicalResourceId: {id:Date.now().toString()} // Update physical id to always fetch the latest version
    };

    super(scope, name, { onUpdate: ssmAwsSdkCall,policy:{
        statements:[new iam.PolicyStatement({
        resources : ['*'],
        actions   : ['ssm:GetParameter'],
        effect:iam.Effect.ALLOW,
      }
      )]
    }});
  }

  public getParameterValue(): string {
    return this.getResponseField('Parameter.Value').toString();
  }
}

7

CDK 2.x

CDK 2.x版本中新增了一个名为crossRegionReferences的Stack属性,您可以启用它以添加跨区域引用。操作非常简单:

const stack = new Stack(app, 'Stack', {
  crossRegionReferences: true,
});

在幕后,这个功能类似于上面的答案,使用自定义资源和系统管理器。根据CDK文档
交叉区域引用?
启用此标志以允许本地跨区域堆栈引用。
启用此功能将在生成堆栈和消费堆栈中创建云形成自定义资源,以执行导出/导入操作。
此功能目前处于实验阶段。
来自CDK核心包自述文件的更多详细信息:
您可以启用 Stack 属性 crossRegionReferences,以便访问不同区域的资源。启用此功能标志后,您可以创建一个在 us-east-2 中的 CloudFront 分发和一个在 us-east-1 中的 ACM 证书。

当 AWS CDK 确定资源位于不同的堆栈和不同的区域时,它将通过在生产堆栈中创建自定义资源来“导出”该值,为每个导出的值在消费区域创建 SSM 参数。参数的名称将使用 '/cdk/exports/${consumingStackName}/${export-name}'。

为了将导出内容“导入”到消费堆栈中,使用 SSM Dynamic reference 引用已创建的 SSM 参数。

为了模拟强引用,还会在消费堆栈中创建自定义资源,将 SSM 参数标记为“已导入”。当成功导入参数时,生产堆栈无法更新该值。

CDK 1.x

如果您正在使用CDK 1.x,请继续使用其他人分享的解决方法。


1

2023-01-16 更新,在由 projen 生成的项目中使用 cdkv2 版本 2.56.0(因此遵循 eslint 规则和最佳实践进行格式化等):

import {
  aws_iam as iam,
  custom_resources as cr,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';

interface SSMParameterReaderProps {
  parameterName: string;
  region: string;
}

export class SSMParameterReader extends cr.AwsCustomResource {
  constructor(scope: Construct, name: string, props: SSMParameterReaderProps) {
    const { parameterName, region } = props;

    const ssmAwsSdkCall: cr.AwsSdkCall = {
      service: 'SSM',
      action: 'getParameter',
      parameters: {
        Name: parameterName,
      },
      region,
      physicalResourceId: { id: Date.now().toString() }, // Update physical id to always fetch the latest version
    };

    super(scope, name, {
      onUpdate: ssmAwsSdkCall,
      policy: {
        statements: [
          new iam.PolicyStatement({
            resources: ['*'],
            actions: ['ssm:GetParameter'],
            effect: iam.Effect.ALLOW,
          }),
        ],
      },
    });
  }

  public getParameterValue(): string {
    return this.getResponseField('Parameter.Value').toString();
  }
};

Could not edit the post above ...


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