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

时间:2015-09-27 19:26:20

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

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

2 个答案:

答案 0 :(得分:7)

在与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<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"]
  }
}

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

    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网址(稍后会详细介绍)
  • (Map<String,Object>)input.get("ResourceProperties")返回您的CloudFormation自定义资源的地图&#34;属性&#34;从CF模板传递到Lambda函数。我提供了一个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();
    }

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

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

答案 1 :(得分:0)

尼尔,

我非常感谢您提供的精彩文档。我会添加一些我觉得有用的东西:

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

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

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

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

Gradle AWS Plugin