如何在 Lambda 函数中使用 CloudFormation 资源?

How do I use CloudFormation resources in a Lambda function?

我已将 Redis ElastiCache 部分添加到我的 s-resource-cf.json(一个 CloudFormation 模板),并选择其主机名作为输出。

"Resources": {
    ...snip...
    "Redis": {
        "Type": "AWS::ElastiCache::CacheCluster",
        "Properties": {
            "AutoMinorVersionUpgrade": "true",
            "AZMode": "single-az",
            "CacheNodeType": "cache.t2.micro",
            "Engine": "redis",
            "EngineVersion": "2.8.24",
            "NumCacheNodes": "1",
            "PreferredAvailabilityZone": "eu-west-1a",
            "PreferredMaintenanceWindow": "tue:00:30-tue:01:30",
            "CacheSubnetGroupName": {
                "Ref": "cachesubnetdefault"
            },
            "VpcSecurityGroupIds": [
                {
                    "Fn::GetAtt": [
                        "sgdefault",
                        "GroupId"
                    ]
                }
            ]
        }
    }
},
"Outputs": {
    "IamRoleArnLambda": {
        "Description": "ARN of the lambda IAM role",
        "Value": {
            "Fn::GetAtt": [
                "IamRoleLambda",
                "Arn"
            ]
        }
    },
    "RedisEndpointAddress": {
        "Description": "Redis server host",
        "Value": {
            "Fn::GetAtt": [
                "Redis",
                "Address"
            ]
        }
    }
}

当 运行 sls resources deploy 时,我可以让 CloudFormation 输出 Redis 服务器主机,但是如何从 Lambda 函数中访问该输出?

此入门项目模板中没有任何内容涉及示例项目附带的 IamRoleArnLambda。根据 docs,模板仅可用于项目配置,不能从 Lambda 函数访问它们:

Templates & Variables are for Configuration Only

Templates and variables are used for configuration of the project only. This information is not usable in your lambda functions. To set variables which can be used by your lambda functions, use environment variables.

那么,如何在创建 ElastiCache 服务器后将环境变量设置为主机名?

您可以在函数 s-function.json 文件的 environment 部分设置环境变量。此外,如果您想防止将这些变量放入版本控制中(例如,如果您的代码将发布到 public GitHub 存储库),您可以将它们放在您的适当文件中_meta/variables 目录,然后引用 s-function.json 文件中的那些。只需确保将 _meta 行添加到 .gitignore 文件即可。

例如,在我最近的项目中,我需要连接到 Redis 云服务器,但不想将连接详细信息提交给版本控制。我将变量放入 _meta/variables/s-variables-[stage]-[region].json 文件中,如下所示:

{
  "redisUrl": "...",
  "redisPort": "...",
  "redisPass": "..."
}

…并引用该函数的 s-function.json 文件中的连接设置变量:

"environment": {
  "REDIS_URL": "${redisUrl}",
  "REDIS_PORT": "${redisPort}",
  "REDIS_PASS": "${redisPass}"
}

然后我将这个 redis.js 文件放在我的 functions/lib 目录中:

module.exports = () => {
  const redis = require('redis')
  const jsonify = require('redis-jsonify')
  const redisOptions = {
    host: process.env.REDIS_URL,
    port: process.env.REDIS_PORT,
    password: process.env.REDIS_PASS
  }

  return jsonify(redis.createClient(redisOptions))
}

然后,在任何需要连接到该 Redis 数据库的函数中,我导入了 redis.js:

redis = require('../lib/redis')()

(有关我的 Serverless/Redis 设置的更多详细信息以及我在使用它时遇到的一些挑战,请参阅我昨天发布的 this question。)

更新

自从在问题跟踪器中发布该评论后,CloudFormation 的使用已经有所简化。我已向 http://docs.serverless.com/docs/templates-variables, and posted a shortened version of my configuration in a gist.

提交文档更新

可以在 s-function.json Lambda 配置文件中引用 CloudFormation 输出,以使这些输出可用作环境变量。

s-resource-cf.json 输出部分:

"Outputs": {
    "redisHost": {
        "Description": "Redis host URI",
        "Value": {
            "Fn::GetAtt": [
                "RedisCluster",
                "RedisEndpoint.Address"
            ]
        }
    }
}

s-function.json 环境部分:

"environment": {
    "REDIS_HOST": "${redisHost}"
},

在 Lambda 函数中的用法:

exports.handler = function(event, context) {
    console.log("Redis host: ", process.env.REDIS_HOST);
};

旧答案

似乎在无服务器问题跟踪器中找到/实施了解决方案(link). To quote HyperBrain


CF 输出变量

要让您的 lambda 访问 CF 输出变量,您必须在 lambda IAM 角色中赋予它 cloudformation:describeStacks 访问权限。

CF.loadVars() 承诺会将所有 CF 输出变量添加到流程中' 环境作为 SERVERLESS_CF_OutVar 名称。它会增加几毫秒的时间 您的 lambda 的启动时间。

如下更改您的 lambda 处理程序:

// Require Serverless ENV vars
var ServerlessHelpers = require('serverless-helpers-js');
ServerlessHelpers.loadEnv();

// Require Logic
var lib = require('../lib');

// Lambda Handler
module.exports.handler = function(event, context) {
  ServerlessHelpers.CF.loadVars()
  .then(function() {
    lib.respond(event, function(error, response) {
      return context.done(error, response);
    });
  })
};