Как мы получаем доступ к пользовательским ресурсам CloudFormation и реагируем на них, используя функцию AWS Lambda, написанную на Java?

96
13

У меня есть функция AWS Lambda, написанная на Java, которую я хотел бы использовать как часть ответа на функцию AWS CloudFormation. Amazon предоставляет два подробных примера о том, как создать пользовательский ресурс CloudFormation, который возвращает свое значение на основе AWS Лямбда-функция, написанная в Node.js, однако мне трудно перевести примеры Lambda в Java. Как мы можем настроить нашу функцию AWS Java так, чтобы она считывала значение предварительно подписанного URL-адреса S3, переданного в качестве параметра функции Lambda из CloudFormation, и отсылает наш желаемый ответ на ожидающий шаблон CloudFormation?

спросил(а) 2015-09-27T22:26:00+03:00 4 года, 9 месяцев назад
1
Решение
100

После беседы с AWS и назад, вот несколько примеров кода, которые я создал, которые выполняют это.


Прежде всего, если вы хотите использовать предопределенные интерфейсы для создания обработчиков, вы можете реализовать 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);
}

Ключевыми моментами здесь, помимо того, что объяснено в примерах NodeJS в документации AWS, являются


    (String)input.get("ResponseURL") - это предварительно подписанный URL-адрес S3, на который вам нужно ответить (подробнее об этом позже)
    (Map<String,Object>)input.get("ResourceProperties") возвращает карту вашего настраиваемого ресурса CloudFormation "Свойства", переданного в функцию Lambda из вашего шаблона CF. Я предоставил 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();
}

Особо следует отметить node "Данные" выше, который ссылается на дополнительный com.amazonaws.util.json.JSONObject, в который я включаю любые атрибуты, которые требуются в моем шаблоне CloudFormation. В этом случае он будет извлечен в шаблоне CF с чем-то вроде { "Fn::GetAtt": [ "MyCustomResource", "CFAttributeRefName" ] }


Наконец, вы можете просто return null, поскольку из этой функции ничего не будет возвращено, так как она HTTPUrlConnection, которая фактически отвечает на вызов CF.

ответил(а) 2015-09-27T22:26:00+03:00 4 года, 9 месяцев назад
39

Нил,


Я очень ценю вашу отличную документацию. Я бы добавил несколько вещей, которые я нашел полезными:


input.get( "RequestType" ) - это возвращается как "Создать", "Удалить" и т.д. Вы можете использовать это значение, чтобы определить, что делать, когда стек создается, удаляется и т.д.


Что касается безопасности, я загрузил Lambda Functions и вручную установил VPC, подсети и группу безопасности (по умолчанию), чтобы я мог повторно использовать его с несколькими сценариями cloudformationn. Кажется, что все работает нормально.

Я создал одну функцию Lambda, вызываемую сценариями CF, и один из них можно запустить вручную, если первый из них завершился неудачей.


Этот отличный gradle aws-плагин позволяет легко загружать функции Java Lambda в AWS.


Gradle Плагин AWS

ответил(а) 2017-02-05T16:14:00+03:00 3 года, 4 месяца назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема