aws-cloudformation / cloudformation-coverage-roadmap

The AWS CloudFormation Public Coverage Roadmap
https://aws.amazon.com/cloudformation/
Creative Commons Attribution Share Alike 4.0 International
1.1k stars 54 forks source link

AWS::Route53RecoveryControl::Cluster - [BUG] - There is no way to access clusterEndpoints return value. #2029

Open tmokmss opened 5 months ago

tmokmss commented 5 months ago

Name of the resource

AWS::Route53RecoveryControl::Cluster

Resource Name

No response

Issue Description

Hi, AWS::Route53RecoveryControl::Cluster returns a value called ClusterEndpoints, which is an array of objects ClusterEndpoint.

So how can we reference each value of the array, e.g. the endpoint of the first element? As far as I read the document, there is no way to handle an array of objects with GetAtt intrinsic function.

Usecase: I want to pass the endpoint of the cluster to Lambda environment variable.

Expected Behavior

We can access clusterEndpoints return value just like other string return values.

Observed Behavior

We cannot access clusterEndpoints return value.

Test Cases

Template to test (Note that ARC can only be deployed in us-east-1 region):

{
 "Resources": {
  "Cluster": {
   "Type": "AWS::Route53RecoveryControl::Cluster",
   "Properties": {
    "Name": "ARC-test"
   }
  }
 },
 // THE BELOW IS INVALID
 "Outputs": {
  "Exportaa": {
   "Value": {
    "Fn::Select": [
     0,
     {
      "Fn::GetAtt": [
       "Cluster",
       "ClusterEndpoints"  // How can we reference the endpoint?
      ]
     }
    ]
   },
   "Export": {
    "Name": "aa"
   }
  }
 }
}

Other Details

Workaround: use custom resource to get these values. CDK Example:

    const cfnCluster = new route53recoverycontrol.CfnCluster(this, 'Cluster', {
      name: 'ARC-Test',
    });

    const handler = new Function(this, 'DescribeClusterHandler', {
      runtime: Runtime.NODEJS_20_X,
      handler: 'index.handler',
      timeout: cdk.Duration.seconds(30),
      code: Code.fromInline(`
const response = require('cfn-response');
const sdk = require('@aws-sdk/client-route53-recovery-control-config');
// Route53RecoveryControlConfig API is only available on us-west-2
const client = new sdk.Route53RecoveryControlConfigClient({region: 'us-west-2'});

exports.handler = async function(event, context) {
  try {
    console.log(event);
    if (event.RequestType == 'Delete') {
      return await response.send(event, context, response.SUCCESS);
    }
    const clusterArn = event.ResourceProperties.ClusterArn;
    // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/route53-recovery-control-config/command/DescribeClusterCommand/
    const command = new sdk.DescribeClusterCommand({ClusterArn: clusterArn});
    const res = await client.send(command);
    const clusterEndpoints = res.Cluster.ClusterEndpoints;
    const endpoints = clusterEndpoints.map(c=>c.Endpoint).join(',');
    const regions = clusterEndpoints.map(c=>c.Region).join(',');
    await response.send(event, context, response.SUCCESS, {endpoints, regions}, clusterArn);
  } catch (e) {
    console.log(e);
    await response.send(event, context, response.FAILED);
  }
};
`),
    });
    handler.addToRolePolicy(
      new PolicyStatement({
        actions: ['route53-recovery-control-config:DescribeCluster'],
        resources: [cfnCluster.attrClusterArn],
      }),
    );

    const describeClusterResult = new cdk.CustomResource(this, 'DescribeClusterResult', {
      serviceToken: handler.functionArn,
      resourceType: 'Custom::DescribeClusterResult',
      properties: { ClusterArn: cfnCluster.attrClusterArn },
    });

    const clusterEndpoints = describeClusterResult.getAttString('endpoints');
    const clusterEndpointRegions = describeClusterResult.getAttString('regions');