在与 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 URL(稍后会详细介绍)
-
(Map<String,Object>)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。