【问题标题】:How do we access and respond to CloudFormation custom resources using an AWS Lambda function written in Java?我们如何使用用 Java 编写的 AWS Lambda 函数访问和响应 CloudFormation 自定义资源?
【发布时间】:2015-12-25 01:02:18
【问题描述】:

我有一个用 Java 编写的 AWS Lambda 函数,我想将其用作对 AWS CloudFormation 函数的响应的一部分。 Amazon 提供了two detailed examples,介绍了如何创建 CloudFormation 自定义资源,该资源基于用 Node.js 编写的 AWS Lambda 函数返回其值,但是我一直难以将 Lambda 示例转换为 Java。我们如何设置我们的 AWS Java 函数,以便它读取作为参数从 CloudFormation 传递给 Lambda 函数的预签名 S3 URL 的值,并将我们想要的响应发送回等待的 CloudFormation 模板?

【问题讨论】:

    标签: amazon-web-services amazon-cloudformation aws-lambda


    【解决方案1】:

    在与 AWS 反复交谈之后,这里是我创建的一些代码示例来完成此任务。

    首先,假设你想leverage the predefined interfaces for creating Handlers,你可以像这样实现RequestsHandler并定义HandleRequest方法:

    public class MyCloudFormationResponder implements RequestHandler<Map<String, Object>, Object>{
        public Object handleRequest(Map<String,Object> input, Context context) {
            ...
        }
    }
    

    Map&lt;String, Object&gt; 是从您的 CloudFormation 资源发送到 Lambda 函数的值的映射。 CF 资源示例:

    "MyCustomResource": {
      "Type" : "Custom::String",
      "Version" : "1.0",
      "Properties": {
        "ServiceToken": "arn:aws:lambda:us-east-1:xxxxxxx:function:MyCloudFormationResponderLambdaFunction",
        "param1": "my value1",
        "param2": ["t1.micro", "m1.small", "m1.large"]
      }
    }
    

    可以用下面的代码分析

        String responseURL = (String)input.get("ResponseURL");
        context.getLogger().log("ResponseURLInput: " + responseURL);
        context.getLogger().log("StackId Input: " + input.get("StackId"));
        context.getLogger().log("RequestId Input: " + input.get("RequestId"));
        context.getLogger().log("LogicalResourceId Context: " + input.get("LogicalResourceId"));
        context.getLogger().log("Physical Context: " + context.getLogStreamName());
        @SuppressWarnings("unchecked")
        Map<String,Object> resourceProps = (Map<String,Object>)input.get("ResourceProperties");
        context.getLogger().log("param 1: " + resourceProps.get("param1"));
        @SuppressWarnings("unchecked")
        List<String> myList = (ArrayList<String>)resourceProps.get("param2");
        for(String s : myList){
            context.getLogger().log(s);
        }
    

    除了 AWS 文档中的 NodeJS 示例中解释的内容之外,这里要指出的关键点是

    • (String)input.get("ResponseURL") 是您需要回复的预签名 S3 URL(稍后会详细介绍)
    • (Map&lt;String,Object&gt;)input.get("ResourceProperties") 返回从 CF 模板传递到 Lambda 函数的 CloudFormation 自定义资源“属性”的映射。我提供了一个 String 和 ArrayList 作为可以返回的对象类型的两个示例,尽管其他几个是可能的

    为了响应 CloudFormation 模板自定义资源实例化,您需要对前面提到的 ResponseURL 执行 HTTP PUT 回调,并将以下大部分字段包含在变量 cloudFormationJsonResponse 中。以下是我的做法

        try {
            URL url = new URL(responseURL);
            HttpURLConnection connection=(HttpURLConnection)url.openConnection();
            connection.setDoOutput(true);
            connection.setRequestMethod("PUT");
            OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
            JSONObject cloudFormationJsonResponse = new JSONObject();
            try {
                cloudFormationJsonResponse.put("Status", "SUCCESS");
                cloudFormationJsonResponse.put("PhysicalResourceId", context.getLogStreamName());
                cloudFormationJsonResponse.put("StackId", input.get("StackId"));
                cloudFormationJsonResponse.put("RequestId", input.get("RequestId"));
                cloudFormationJsonResponse.put("LogicalResourceId", input.get("LogicalResourceId"));
                cloudFormationJsonResponse.put("Data", new JSONObject().put("CFAttributeRefName", "some String value useful in your CloudFormation template"));
            } catch (JSONException e) {
                e.printStackTrace();
            }
            out.write(cloudFormationJsonResponse.toString());
            out.close();
            int responseCode = connection.getResponseCode();
            context.getLogger().log("Response Code: " + responseCode);
        } catch (IOException e) {
            e.printStackTrace();
        }
    

    需要特别注意的是上面的节点“数据”,它引用了一个额外的com.amazonaws.util.json.JSONObject,我在其中包含了我的 CloudFormation 模板中所需的任何属性。在这种情况下,它将在 CF 模板中检索到类似{ "Fn::GetAtt": [ "MyCustomResource", "CFAttributeRefName" ] }

    最后,您可以简单地使用return null,因为该函数不会返回任何内容,因为它是实际响应 CF 调用的 HTTPUrlConnection

    【讨论】:

    • 您是否为发送响应的存储桶授予任何权限,因为当我将响应发送到存储桶时,我无法从自定义资源中读取响应?我的 lambda 正确发送数据,然后 CloudFormation 挂起,因为它似乎永远不会收到通知。
    • 对 responseURL 的请求失败并出现我无法弄清楚的错误:我们计算的请求签名与您提供的签名不匹配。检查您的密钥和签名方法。您是否遇到过这种情况,您知道它的实际含义吗?
    • 值得一提的是,当前版本的 AWS Java SDK 已经没有 JSONObject 类了。但是,org.json 中有一个非常相似的类,叫做 JSONObject。
    • 事件库(版本 3.8.0)现在实现了这种事件类型:github.com/aws/aws-lambda-java-libs/blob/master/…
    【解决方案2】:

    尼尔,

    我非常感谢您在这里提供的出色文档。我会添加一些我觉得有用的东西:

    input.get("RequestType") - 以“Create”、“Delete”等形式返回。您可以使用此值来确定在创建、删除堆栈等时要执行的操作。

    就安全性而言,我上传了 Lambda 函数并手动设置了 VPC、子网和安全组(默认),以便我可以将其与多个 cloudformationn 脚本一起使用。这似乎工作正常。

    我创建了一个由 CF 脚本调用的 Lambda 函数,并且我可以手动运行一个以防第一个失败。

    这个出色的 gradle aws 插件可以轻松地将 Java Lambda 函数上传到 AWS。

    Gradle AWS Plugin

    【讨论】:

      猜你喜欢
      • 2021-06-23
      • 2017-07-23
      • 2016-09-03
      • 2020-02-17
      • 2020-09-07
      • 1970-01-01
      • 1970-01-01
      • 2021-05-11
      • 1970-01-01
      相关资源
      最近更新 更多