创建指向Beanstalk的Route53别名记录报错,求CDK资源创建后执行代码方法
解决方案
核心思路
CDK无法直接通过令牌获取Beanstalk环境的端点URL并用于Route53别名记录,需要借助**CloudFormation自定义资源(Custom Resource)**实现:在Beanstalk环境创建完成后,触发Lambda函数调用AWS API获取真实端点,再自动创建/更新Route53别名记录。
具体实现步骤
1. 编写Lambda处理函数
该函数负责接收CloudFormation事件,获取Beanstalk端点并操作Route53记录:
import { CloudFormationCustomResourceEvent, CloudFormationCustomResourceResponse } from 'aws-lambda'; import { ElasticBeanstalkClient, DescribeEnvironmentsCommand } from '@aws-sdk/client-elastic-beanstalk'; import { Route53Client, ChangeResourceRecordSetsCommand } from '@aws-sdk/client-route-53'; const ebClient = new ElasticBeanstalkClient({}); const route53Client = new Route53Client({}); export const handler = async (event: CloudFormationCustomResourceEvent): Promise<CloudFormationCustomResourceResponse> => { try { const { environmentName, hostedZoneId, recordName } = event.ResourceProperties; // 查询Beanstalk环境端点CNAME const ebCmd = new DescribeEnvironmentsCommand({ EnvironmentNames: [environmentName] }); const ebResp = await ebClient.send(ebCmd); const endpointUrl = ebResp.Environments?.[0]?.CNAME; if (!endpointUrl) throw new Error('无法获取Beanstalk环境端点'); // 执行Route53记录操作(创建/更新/删除) const route53Cmd = new ChangeResourceRecordSetsCommand({ HostedZoneId: hostedZoneId, ChangeBatch: { Changes: [{ Action: event.RequestType === 'Delete' ? 'DELETE' : 'UPSERT', ResourceRecordSet: { Name: recordName, Type: 'A', AliasTarget: { HostedZoneId: 'Z117KPS5GTRQ2G', // Beanstalk全局固定Hosted Zone ID DNSName: endpointUrl, EvaluateTargetHealth: false, }, }, }], }, }); await route53Client.send(route53Cmd); return { Status: 'SUCCESS', PhysicalResourceId: recordName, StackId: event.StackId, RequestId: event.RequestId, LogicalResourceId: event.LogicalResourceId, }; } catch (err) { console.error('处理失败:', err); return { Status: 'FAILED', Reason: (err as Error).message, StackId: event.StackId, RequestId: event.RequestId, LogicalResourceId: event.LogicalResourceId, }; } };
2. 在CDK栈中集成自定义资源
将上述Lambda函数和自定义资源加入CDK代码,确保依赖顺序:
import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as cr from 'aws-cdk-lib/custom-resources'; import * as route53 from 'aws-cdk-lib/aws-route53'; import * as elasticbeanstalk from 'aws-cdk-lib/aws-elasticbeanstalk'; export class EBStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // 引用已有的Hosted Zone const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, 'MyHostedZone', { hostedZoneId: '你的Hosted Zone ID', zoneName: 'example.com', }); // 创建Beanstalk环境 const ebEnv = new elasticbeanstalk.CfnEnvironment(this, 'MyEBEnv', { applicationName: '你的EB应用名称', solutionStackName: '64bit Amazon Linux 2 v5.8.0 running Node.js 18', // 其他Beanstalk配置... }); // 创建处理Lambda const updateRoute53Lambda = new lambda.Function(this, 'UpdateRoute53Fn', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'index.handler', code: lambda.Code.fromInline(`/* 把上面的Lambda代码粘贴到这里 */`), initialPolicy: [ // 赋予Lambda访问Beanstalk和Route53的权限 new cdk.aws_iam.PolicyStatement({ actions: ['elasticbeanstalk:DescribeEnvironments'], resources: ['*'], }), new cdk.aws_iam.PolicyStatement({ actions: ['route53:ChangeResourceRecordSets'], resources: [hostedZone.hostedZoneArn], }), ], }); // 创建自定义资源,依赖Beanstalk环境 new cr.CustomResource(this, 'EBRoute53Alias', { serviceToken: updateRoute53Lambda.functionArn, properties: { environmentName: ebEnv.ref, hostedZoneId: hostedZone.hostedZoneId, recordName: `${config.companyName}-alias.${hostedZone.zoneName}`, }, resourceType: 'Custom::EBRoute53Alias', }); // 确保自定义资源在Beanstalk环境后执行 this.node.addDependency(ebEnv); } }
关键注意事项
- Beanstalk别名记录必须使用AWS全局固定的Hosted Zone ID
Z117KPS5GTRQ2G,无需根据区域修改。 - Lambda权限需最小化,可将Beanstalk的资源限定为目标环境的ARN,而非
*。 - 自定义资源会自动处理栈的创建、更新、删除事件,删除栈时会同步删除Route53记录。
内容的提问来源于stack exchange,提问作者Davita
相关产品推荐
相关产品推荐

