我们如何使用Java编写的AWS Lambda函数访问和响应CloudFormation自定义资源?

Nei*_*eil 6 amazon-web-services aws-cloudformation aws-lambda

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

Nei*_*eil 8

在与AWS进行来回对话之后,以下是我创建的一些代码示例.

首先,假设您想利用预定义的接口来创建处理程序,您可以实现RequestsHandler并定义HandleRequest方法,如下所示:

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

Map<String, Object>是从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"]
  }
}
Run Code Online (Sandbox Code Playgroud)

可以使用以下代码进行分析

    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);
    }
Run Code Online (Sandbox Code Playgroud)

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

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

为了响应CloudFormation模板自定义资源实例化,您需要执行HTTP PUT回调到之前提到的ResponseURL,并在变量中包含以下大部分字段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();
    }
Run Code Online (Sandbox Code Playgroud)

特别值得注意的是上面的节点"数据"引用了一个附加节点com.amazonaws.util.json.JSONObject,其中我包含了我的CloudFormation模板中所需的任何属性.在这种情况下,它将在CF模板中检索,例如{ "Fn::GetAtt": [ "MyCustomResource", "CFAttributeRefName" ] }

最后,你可以简单地return null从因为它HTTPUrlConnection实际上响应CF调用而没有从这个函数返回任何内容.