从 SSM 参数生成文件并包含在无服务器框架 lambda 包中

Generate files from SSM parameters and include in Serverless Framework lambda bundle

我有一些 JSON 存储在 SSM Parameter Store 中,我想在我的无服务器框架 lambda 函数中使用它们(包含一些关于早期由 Terraform 生成的基础设施的细节)。我可以调用 SSM 以在运行时获取数据,但 Parameter Store 的吞吐量限制非常低 (40tps by default),因此我可能会立即超过该限制,即使更高的限制仍然太低而无法在生产中这样做。

更一般地说,我想避免调用外部服务来检索此信息的开销,因为它将在自定义 lambda 授权方中使用,所以我希望它速度快并且不依赖任何外部依赖项可能。

我正在考虑从 Parameter Store 中检索 JSON,然后在执行 serverless deploy 时将其烘焙到我的 lambda 包中。我很高兴在它们发生变化时不得不重新部署我的后端。

我可以使用环境变量,但是 maximum size of all environment variables is 4kb,所以我不能把 JSON 放在那里。

我正在使用 Serverless Webpack Plugin,我认为这可能是关键,但我是 Webpack 新手,不知道从哪里开始!

也许有更好的方法,但我设法通过编写 custom webpack loader 来实现它。这个答案专门针对我的情况,我需要将多个认知池的详细信息导出到单个文件以供自定义 lambda 授权方使用,但该模式应该适用于任何场景,并且不一定与 SSM 相关联(您可以使用任何方法生成文件,因为加载程序很简单 Javascript)。它使我的 Lambda 执行时间从约 40 毫秒(使用 SSM)减少到约 2 毫秒。

模板文件和用法

首先,我创建了一个示例模板 .json 文件,其结构与我存储在 SSM 中的数据相匹配。这可以在任何地方,但我把它放在 generated/cognitoConfig.json 中。这对于使用点的文档和代码辅助很有用。

{
    "pools": [
        {
            "_note_": "This is just an example. This file gets totally replaced with the real pool config by cognitoConfigLoader on deploy",
            "clientId":"example-client",
            "name":"example",
            "poolArn":"arn:aws:cognito-idp:eu-west-2:account-id:userpool/example-pool",
            "poolEndpoint":"cognito-idp.eu-west-2.amazonaws.com/example-pool",
            "poolId":"example-pool",
            "poolKeysUrl":"https://cognito-idp.eu-west-2.amazonaws.com/example-pool/.well-known/jwks.json",
            "region":"eu-west-2"
        }
    ]
}

然后可以在 lambda 代码 (ES6) 中导入和使用它。例如:

import * as cognitoData from '../../generated/cognitoConfig.json';

function getPoolConfig(name) {
    const poolConfig = cognitoData.pools.filter(pool => pool.name === name)[0]
}

Webpack 加载器

我配置了一个针对此模板文件运行的自定义 webpack 加载程序:

const path = require('path');

module.exports = {
    //...other webpack config

    module: {
        // These execute from bottom to top
        rules: [
            // ...other rules (e.g. babel)

            // Retrieve cognito pool information from SSM and store
            {
                test: /cognitoConfig\.json$/,
                include: path.resolve(__dirname, "generated"),
                loader: path.resolve(__dirname, "webpack-loaders/cognitoConfigLoader.js"),
            },
        ]
    }
}

然后我编写了一个 webpack 加载程序,用于搜索所有匹配的 SSM 参数,并将内容写入 JSON 文件。无服务器 webpack 插件提供对 the underlying serverless object 的访问,因此可以访问当前的 AWS 凭证。

对于奖励积分,我还得到了它来下载签名密钥,但我没有在此处包含它,因为我不想弄乱答案:

const slsw = require("serverless-webpack");
const { SSMClient, GetParametersByPathCommand } = require("@aws-sdk/client-ssm")
const { fromIni } = require("@aws-sdk/credential-provider-ini")

module.exports = function () {
    const callback = this.async()

    buildPoolsJson().then(
        poolsJson => callback(undefined, poolsJson),
        error => callback(error)
    )
}

async function buildPoolsJson() {
    const poolsParameters= await loadPoolsParameters()
    return JSON.stringify({
        pools: poolsParameters
    })
}

async function loadPoolsParameters() {
    const awsProvider = slsw.lib.serverless.service.provider
    const ssmClient = new SSMClient({
        credentials: fromIni({ profile: awsProvider.profile }),
        region: awsProvider.region,
    })

    const poolParamsResponse = await ssmClient.send(new GetParametersByPathCommand({
        Path: "/terraform/cognito-pools",
    }))

    return poolParamsResponse.Parameters.map(parameter => {
        return JSON.parse(parameter.Value);
    })
}