如何将 CloudFormation 模板与在 RDS 中的 Aurora PostgreSQL 实例上创建新数据库的 Lambda 函数连接?

How to connect a CloudFormation Template with a Lambda Function that creates a new database on an Aurora PostgreSQL Instance in RDS?

希望能帮到你!

目标

我正在尝试在 AWS 上配置以下场景:

  1. 在 AWC CLI 中:调用 CloudFormation 模板创建新堆栈。
  2. 在模板中:调用 Lambda 函数。
  3. 在 Lambda 函数中:连接到 Aurora PostgreSQL Instance in RDS via pg 模块。
  4. 在 PostgresSQL 中:创建一个新数据库。

短篇小说

一旦我将我的 Lambda 函数添​​加到默认 VPC,它就无法再将响应发送回 CloudFormation,并且堆栈卡在 CREATE_IN_PROGRESS。当我从 Lambda 函数的配置中删除 VPC 时,它起作用了。但后来我无法再连接到另一个组件,如 RDS 数据库实例。如何处理?

长话短说

为了让它工作,我的第一步是手动连接到 PostgreSQL 并创建一个新数据库:

  1. 配置默认安全组以允许所有入站和所有出站流量。不用担心,仅用于测试。我稍后会更改它。
  2. 将安全组添加到 RDS 实例以接受来自同一 VPC 内其他组件的流量。步骤 3 所必需的。
  3. 创建了一个 Cloud9 环境以访问 VPC 中的终端。连接到 PostgreSQL 数据库并创建数据库成功!

现在我的目标是通过 Lambda 函数创建一个新数据库:

  1. 手动创建了一个新的 Lambda 函数,包括一个具有基本 Lambda 授权的新角色。还为角色添加了 AdministratorAccess。不用担心,再次仅用于测试。 Lambda 函数在 Node.js 12.x 上运行,超时为 15 秒。
  2. 将我的 Lambda 脚本和 pg 模块打包成一个 zip 文件并上传。脚本如下所示:
const { Client } = require('pg');
exports.handler = async (event, context) => {
    var databaseName = event.DatabaseName;
    var dbSuperUser = event.DBSuperUser;
    var dbSuperPassword = event.DBSuperPassword;

    const dbClient = new Client({
        host: "<Database Instance Endpoint>",
        port: 5432,
        database: "postgres",
        user: dbSuperUser,
        password: dbSuperPassword
    });

    await dbClient.connect();
    await dbClient.query("CREATE DATABASE " + databaseName);
    await dbClient.end();

    var response = {status: 'SUCCESS'};
    return response;
};
  1. 通过 aws lambda invoke 从 Cloud9 调用 Lambda 脚本会导致超时。这是预期的行为,因为 Lambda 函数与 RDS 数据库实例不在同一个 VPC 中。
  2. 在 Lambda 函数配置中添加了带有三个默认子网和默认安全组的默认 VPC。瞧,数据库已创建!我们走上正轨。

现在我尝试调用 Lambda 函数并从基本的 CloudFormation 模板创建数据库:

  1. 创建的模板包含 Custom Ressource:
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Parameters": {
        "DatabaseName": {"Type": "String"}
    },
    "Resources": {
        "SampleInstance": {
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "InstanceType": "t1.micro",
                "ImageId": {"Fn::GetAtt": ["CreateDatabase", "Id"]}
            }
        },
        "CreateDatabase": {
            "Type": "Custom::CreateDatabase",
            "Properties": {
                "ServiceToken": "<Lambda Function ARN>",
                "DBSuperUser": "<DBSuperUser>",
                "DBSuperPassword": "<DBSuperPassword>",
                "DatabaseName": {"Ref": "DatabaseName"}                
            }
        }
    }
}

EC2 实例只是一个示例,无论如何都无法创建,因为 ImageId 将无效。它只是用来测试我是否收到 Lambda 函数的响应。

  1. 更改了 Lambda 函数以从 CloudFormation 接收事件并发回响应。我可以使用 cfc-response 模块,但决定将它的代码复制到我的 Lambda 函数中,以查看底层发生了什么。请点击上面的 link 并向下滚动到 Module Source Code 以查看源代码。

我还更改了获取输入参数的方式:

var customerName = event.ResourceProperties.CustomerName;
var dbSuperUser = event.ResourceProperties.DBSuperUser;
var dbSuperPassword = event.ResourceProperties.DBSuperPassword; 

并像这样将响应发送到 CloudFormation:

responseData = {Id: '1234567890'};
send(event, context, SUCCESS, responseData);
  1. 运行 通过 AWS CLI 命令 aws cloudformation create-stack 的模板导致堆栈卡在 CREATE_IN_PROGRESS。但是数据库创建成功!

  2. 只要我从 Lambda 函数中删除 VPC 配置,问题就消失了。 CloudFormation 成功收到函数的响应并尝试创建 EC2 示例实例。但是现在Lambda函数不能再访问RDS来创建数据库了。

这会导致以下问题:

我检查过的其他内容:

期待您对此的看法!

干杯

您发布的代码是一个简单的 Lambda,并没有按照 CloudFormation custom resource 的要求报告其状态。因此,第一步是确保您符合要求。

如果您是,并且您的代码在向 S3 报告状态时被阻止,那么最可能的原因是您 运行 在既没有 NAT nor an S3 endpoint 的子网中。如果您不需要 NAT,S3 端点是免费的,因此将是最好的解决方案。如果您已经拥有 NAT,请确保 Lambda 在 NAT 可用的子网中运行。