CloudFormation跨区域参考

时间:2020-01-16 17:06:33

标签: amazon-web-services amazon-cloudformation aws-cloudformation-custom-resource

在同一区域内运行多个CloudFormation堆栈时,您可以使用CloudFormation Outputs

在堆栈之间共享引用

但是,文档突出显示的内容不能用于跨区域引用。

您不能跨区域创建跨堆栈引用。您可以使用内部函数Fn :: ImportValue仅导入在同一区域内已导出的值。

您如何在CloudFormation中跨区域引用值?

下面是一个示例,我在us-east-1中部署了一个Route 53 hosted zone。但是,我在us-west-2中有一个后端,我想要创建一个DNS-validated ACM certificate,该后端需要引用托管区域,以便能够创建适当的CNAME来证明所有权。

我该如何参考us-east-1中在us-west-2中创建的托管区域ID?

2 个答案:

答案 0 :(得分:12)

我发现最简单的方法是将要共享的引用(在这种情况下,即您托管的区域ID)写入Systems Manager Parameter Store,然后在“子”堆栈中引用该值。使用custom resource分隔区域。

幸运的是,如果使用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 :(得分:2)

cdk库已更新,需要将上面的代码更改为以下内容:

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();
  }
}