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