Describe AWS API Gateway Body Mapping Templates in CloudFormation - amazon-web-services

I looked though the documentation but didn't find a way to do this. I have a API Gateway method that has a Body Mapping Template, as in the picture attached.
How do I map this template in CloudFormation? (I'm using JSON). I added "PassthroughBehavior": "WHEN_NO_TEMPLATES", but haven't found a way to add the Content-Type mapping.
Thank you.

You can do something like this:
GETMethodRequest:
Type: "AWS::ApiGateway::Method"
DependsOn: ePlanningLambdaPermission
Properties:
RestApiId: !Ref YourAPI
AuthorizationType: NONE
HttpMethod: GET
RequestParameters:
method.request.querystring.name: true
MethodResponses:
- StatusCode: 200
ResourceId: !Ref ePlanningGISLocalitymapResource
Integration:
Type: AWS
IntegrationHttpMethod: POST
RequestTemplates:
"application/json": "{
\"body\" : $input.json('$'),
\"headers\": {
#foreach($header in $input.params().header.keySet())
\"$header\": \"$util.escapeJavaScript($input.params().header.get($header))\" #if($foreach.hasNext),#end
#end
},
\"method\": \"$context.httpMethod\",
\"params\": {
#foreach($param in $input.params().path.keySet())
\"$param\": \"$util.escapeJavaScript($input.params().path.get($param))\" #if($foreach.hasNext),#end
#end
},
\"query\": {
#foreach($queryParam in $input.params().querystring.keySet())
\"$queryParam\": \"$util.escapeJavaScript($input.params().querystring.get($queryParam))\" #if($foreach.hasNext),#end
#end
}
}"
IntegrationResponses:
- StatusCode: 302

You can do this as part of the RequestTemplates property described here.
It should look something like this:
"APIMethodGet": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"RequestTemplates": {
"application/json": {
"Fn::Join": [
"",
[
"{\n \"StreamName\": \"my-kinesis\"\n",
"\n \"Data\": \"$util.base64encode($input.body)\"\n",
"\n \"PartitionKey\": \"1\"\n}"
]
]
}
},
"PassthroughBehavior": "WHEN_NO_TEMPLATES"
}
}

Related

Invalid service prefix for action 'sts.AssumeRole' [duplicate]

I've been all over the web searching for an answer to this.
Essentially, we're spinning up an API using Swagger, which is awesome and works great, but one thing doesn't work... When we make a call to an Endpoint, we get a 500 error (it's not a 500 error that we're providing either it's one from AWS). The error states "Execution failed due to configuration error: Invalid permissions on Lambda function" (https://youtu.be/H4LM_jw5zzs <- This is a video, from another user, of the error I'm getting).
I've gone down many ratholes, and have found an answer... It involves using the AWS CLI and looks a bit like this:
aws lambda add-permission \
--function-name FUNCTION_NAME \
--statement-id STATEMENT_ID \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-east-1:ACCOUNT_ID:API_ID/*/METHOD/ENDPOINT"
This is great and all, but we are using CloudFormation to spin up everything and we want this to be automated. Is there an easier way to go about this? Is there something in CloudFormation that will give us the resource policy that we need?
I'm hitting a bit of a wall with this, but I've been working on it for a few hours today and it's a bit of a blocker for our API release, so any help would be much appreciated. :)
There is a CloudFormation solution to this problem. See the following CloudFormation snippet:
"Permission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": { "Fn::GetAtt": [ "Lambda", "Arn" ] },
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": { "Fn::Join": [ "", [
"arn:aws:execute-api:",
{ "Ref": "AWS::Region" }, ":",
{ "Ref": "AWS::AccountId" }, ":",
{ "Ref": "API" },
"/*/*/*"
] ] }
}
}
This grants API Gateway permissions to launch your Lambda function. Variables in this snippet you need to change are Lambda (line 4) and API (line 11).
For the invoke permissions:
"APIInvokePermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Ref": "YOUR_LAMBDA_FUNCTION_RESOURCE_NAME"
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${YOUR_REST_API_RESOURCE_NAME}/*/*/*"
}
}
},
Thanks https://twitter.com/edjgeek for helping me get this straight in my head.
This GIST shows how to use AWS::Serverless:Function with Events to automatically generate the needed AWS::Lambda::Permission to allow APIGateway (for a given route) to invoke your Lambda:
https://gist.github.com/rainabba/68df1567cbd0c4930d428c8953dc2316
Both of the following approaches assume an api such as (many fields omitted for readability):
MyApi:
Type: 'AWS::Serverless::Api'
Properties:
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: openapi.yaml
Using "SAM Events"
The most relevant bit (I've omitted many required fields):
MyLambdaFunction:
Type: 'AWS::Serverless::Function'
Properties:
Events:
MyRouteEventToProxy:
Type: Api
Properties:
Method: POST
Path: '/some-route/{pathParm}'
RestApiId: !Ref MyApi # ResourceName of AWS::Serverless::Api
Auth:
Authorizer: NONE
Using "openapi binding"
If you'd rather declare the binding in the openapi.yaml, then see the following project (no Lambda/Events required). This approach requires an explict role to allow invoke.
template.yaml relevant bits:
MyLambdaFunction:
Type: 'AWS::Serverless::Function'
Properties:
# Events: # No need for Events when binding from openapi.yaml
MyHttpApiRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "apigateway.amazonaws.com"
Action:
- "sts:AssumeRole"
openapi.yaml relevant bits:
paths:
post:
x-amazon-apigateway-integration:
httpMethod: POST
uri:
Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambdaFunction.Arn}:live/invocations"
contentHandling: "CONVERT_TO_TEXT"
type: aws_proxy
credentials:
Fn::GetAtt: [MyHttpApiRole, Arn]
https://github.com/aws-samples/sessions-with-aws-sam/tree/master/http-api-direct-integration

API Gateway -> SQS HTTP POST MessageAttributes

I have an API gateway setup which sends to SQS which fires a Lambda, I am trying to pass message attributes to the SQS but when I hit the endpoint in postman I keep getting a 400 Bad Request.. what is the right way to send the attributes over a JSON POST body
here is body from postman (have tried a few options based on this link)
"message": "Message",
"MessageAttributes": {
"Name": "Name",
"Type": "String",
"Value": "my value"
}
}
Here is how API Gateway is configured
Incase someone stumbles on this later here is worked from the CDK side
let intergation = new apiGateway.CfnIntegration(this, 'Integration', {
apiId: props.httpApi.httpApiId,
payloadFormatVersion: '1.0',
integrationType: 'AWS_PROXY',
credentialsArn: apigwRole.roleArn,
integrationSubtype: 'SQS-SendMessage',
requestParameters: {
QueueUrl: sqsqueue.queueUrl,
MessageBody: '$request.body',
MessageAttributes: '$request.body.MessageAttributes'
}
})
new apiGateway.CfnRoute(this, 'Route', {
apiId: props.httpApi.httpApiId,
routeKey: apiGateway.HttpRouteKey.with('/url/foo', apiGateway.HttpMethod.POST).key,
target: `integrations/${intergation .ref}`
}).addDependsOn(intergation);
and the cloudformation
MessageBody: $request.body
MessageAttributes: $request.body.MessageAttribute
then in post man the POST body content type as application/json
{
"message": "Message",
"MessageAttributes": {
"Attributes": {
"DataType": "String",
"StringValue": "my value"
}
}
}
the lamba would log out both separate for each Record from the event body
Records: [
{
....
body: 'Message',
attributes: [Object],
messageAttributes: [Object]
}
]
}
the messageAttributes object from above:
{
Attributes: {
stringValue: 'my value',
stringListValues: [],
binaryListValues: [],
dataType: 'String'
}
}
This is using AWS API Gateway v2 HTTP API also

AWS lambda authorizer, Custom Context values in REST API

How could I retrieve the output of custom authorizer in lambda integration?
for example, lets assume below is my swagger file with aws api gateway integration, lambda authorizer output and AwsProxyHttpServletRequest;
A question in SO here AWS API Gateway with Lambda Authorizer says it works, but not for me.
REST API
openapi: 3.0.0
info:
title: Sample Event
version: 1.0.0
# Enable request validator. See doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-validation-sample-api-swagger.html
x-amazon-apigateway-request-validators:
all:
validateRequestBody: true
validateRequestParameters: true
x-amazon-apigateway-request-validator: all
x-amazon-apigateway-gateway-responses:
# Provide more detailed error message for bad request body errors. See doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-gateway-responses.html
BAD_REQUEST_BODY:
responseTemplates:
application/json: '{"errorCode": "BadRequestBody", "message": "$context.error.validationErrorString"}'
responseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
DEFAULT_4XX:
responseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
DEFAULT_5XX:
responseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
paths:
/events:
post:
operationId: CreateEvent
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/CreateEventInput"
required: true
responses:
"201":
description: "Successfully Created an event."
content:
application/json:
schema:
$ref: "#/components/schemas/Event"
"400":
description: "Bad Request Exception"
content:
application/json:
schema:
$ref: "#/components/schemas/BadRequestException"
"401":
description: "Unauthorized Exception"
content:
application/json:
schema:
$ref: "#/components/schemas/UnauthorizedException"
"409":
description: "Conflict Exception"
content:
application/json:
schema:
$ref: "#/components/schemas/ConflictException"
"429":
description: "Too Many Requests Exception"
content:
application/json:
schema:
$ref: "#/components/schemas/TooManyRequestsException"
"500":
description: "Internal Server Error"
content:
application/json:
schema:
$ref: "#/components/schemas/InternalServerErrorException"
x-amazon-apigateway-integration:
uri:
Fn::Sub: arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${EventsApiLambda.Arn}:live/invocations
httpMethod: POST
type: aws_proxy
requestParameters:
integration.request.header.x-api-auth-user: "context.authorizer.x-api-auth-user"
integration.request.header.x-api-auth-resource-uri: "context.authorizer.x-api-auth-resource-uri"
integration.request.header.x-api-auth-type: "context.authorizer.x-api-auth-type"
integration.request.header.x-api-auth-resource-id: "context.authorizer.x-api-auth-resource-id"
integration.request.header.x-api-auth-resource-type: "context.authorizer.x-api-auth-resource-type"
integration.request.header.x-api-auth-resource-permissions: "context.authorizer.x-api-auth-resource-permissions"
passthroughBehavior: never
security:
- tokenAuthorizer: []
Lambda Authorizer Output (from API-Gateway-Execution-Logs-xxxx)
(023bd04b-e1c9-4980-ae14-xxxxx) Authorizer result body before parsing:
{
"principalId": "act-xxxxxxxxx",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:*:*:*"
}
]
},
"context": {
"x-api-auth-user": "act-xxxxxxx",
"x-api-auth-type": "email",
"x-api-auth-resource-id": "11eb2825-18cc-fb80-9d6c-xxxxx",
"x-api-auth-resource-type": "cb:event",
"x-api-auth-resource-permissions": "read,write"
}
}
I can see the output from authorizer reaching the api execution stage. But making it into neither the requestContext nor the multiValueHeaders.
API-Gateway-Execution-Logs_xxxx
(023bd04b-e1c9-4980-ae14-xxxx) Endpoint request headers: {X-Amz-Date=20210112T170314Z, x-amzn-apigateway-api-id=xxx, Accept=application/json, User-Agent=AmazonAPIGateway_xxxx, x-api-auth-type=email, Host=lambda.us-east-1.amazonaws.com, x-api-auth-resource-id=11eb2825-18cc-fb80-9d6c-xxxx, X-Amz-Content-Sha256=xxxxxx, X-Amzn-Trace-Id=Root=1-5ffdd64b-xxxxx;Parent=xxxx;Sampled=1, x-amzn-lambda-integration-tag=023bd04b-e1c9-4980-ae14-xxxxxx, Authorization=**********************282c30, X-Amz-Source-Arn=arn:aws:execute-api:us-east-1:38067 [TRUNCATED]
Lambda input
{
"path": "/events",
"isBase64Encoded": false,
"requestContext": {
"resourceId": "xxxxx",
"apiId": "xxxxx",
"resourcePath": "/events",
"httpMethod": "POST",
"requestId": "xxxxxx-15f9-4ca2-9a71-xxxxx",
"extendedRequestId": "xxxxxx=",
"accountId": "xxxx",
"identity": {
"userAgent": "PostmanRuntime/7.26.8",
"sourceIp": "xxxxx"
},
"authorizer": {
"principalId": "act-VNJQUexxxxx"
},
"stage": "v1",
"path": "/event/events",
"protocol": "HTTP/1.1",
"requestTime": "12/Jan/2021:17:53:06 +0000",
"requestTimeEpoch": xxx
},
....[TRUNCATED]....
}
Do I need to explicitly specify the authorizer result to be in each path's header/body?
Any idea?

Set API Key to be required for AWS ApiGateway endpoint (Swagger import)

I try to define my AWS Api Gateway infrastructure using Swagger/OpenAPI. Everything is working so far, however I have problems enabling the need for an API-Key for my endpoints.
My Swagger file looks like this (shortened):
---
swagger: 2.0
basePath: /dev
info:
title: My API
description: Proof of concept
schemes:
- https
securityDefinitions:
api_key:
type: apiKey
name: X-Api-Key
in: header
paths:
/example-path:
options:
consumes:
- application/json
produces:
- application/json
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Methods: "'GET,HEAD,OPTIONS'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: |
{}
responses:
200:
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
get:
security:
- api_key: []
x-amazon-apigateway-integration:
# Further definition of the endpoint, calling Lambda etc...
Linked inside a CloudFormation template the Swagger file is processed successfully. But when I open the endpoint in the AWS Web Console, the flag for API Key Required is still false.
Any suggestions? Thanks.
Found the solution: the API key has to be named x-api-key (all lowercase).
It seems like only this way the setting is recognized during import.
To enable required API Key you need to add this "x-amazon-apigateway-api-key-source" : "HEADER" in security scheme block.
See an example:
"components" : {
"securitySchemes" : {
"api-key" : {
"type" : "apiKey",
"name" : "x-api-key",
"in" : "header",
"x-amazon-apigateway-api-key-source" : "HEADER"
}
}
}
It's an example using proxy requests.
Your JSON should be like this:
openapi3
{
"openapi": "3.0.3",
"info": {
"title": "User Portal",
"description": "API focused in User Portal.",
"version": "v1"
},
"paths": {
"users/{proxy+}": {
"options": {
"x-amazon-apigateway-integration": {
"httpMethod": "OPTIONS",
"payloadFormatVersion": "1.0",
"type": "MOCK"
}
},
"x-amazon-apigateway-any-method": {
"produces":[ "application/json"],
"parameters": [
{
"name": "proxy",
"in": "path",
"required": "true",
"type": "string"
}
],
"responses": {},
"security": [
{
"api-key": []
}
],
"x-amazon-apigateway-integration": {
"uri":"https://test.com.br/users/{proxy}",
"httpMethod":"ANY",
"type": "HTTP_PROXY"
}
}
}
},
"components" : {
"securitySchemes" : {
"api-key" : {
"type" : "apiKey",
"name" : "x-api-key",
"in" : "header",
"x-amazon-apigateway-api-key-source" : "HEADER"
}
}
}
}
In openapi2 you can add this in your yml.
swagger: 2.0
basePath: /dev
info:
title: My API
description: Proof of concept
schemes:
- https
securityDefinitions:
api_key:
type: apiKey
name: X-Api-Key
in: header
x-amazon-apigateway-api-key-source: HEADER
If you have troubles using api integration with openapi you can see this article: Working with API Gateway extensions to OpenAPI

How can I grant permission to API Gateway to invoke lambda functions through CloudFormation?

I've been all over the web searching for an answer to this.
Essentially, we're spinning up an API using Swagger, which is awesome and works great, but one thing doesn't work... When we make a call to an Endpoint, we get a 500 error (it's not a 500 error that we're providing either it's one from AWS). The error states "Execution failed due to configuration error: Invalid permissions on Lambda function" (https://youtu.be/H4LM_jw5zzs <- This is a video, from another user, of the error I'm getting).
I've gone down many ratholes, and have found an answer... It involves using the AWS CLI and looks a bit like this:
aws lambda add-permission \
--function-name FUNCTION_NAME \
--statement-id STATEMENT_ID \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-east-1:ACCOUNT_ID:API_ID/*/METHOD/ENDPOINT"
This is great and all, but we are using CloudFormation to spin up everything and we want this to be automated. Is there an easier way to go about this? Is there something in CloudFormation that will give us the resource policy that we need?
I'm hitting a bit of a wall with this, but I've been working on it for a few hours today and it's a bit of a blocker for our API release, so any help would be much appreciated. :)
There is a CloudFormation solution to this problem. See the following CloudFormation snippet:
"Permission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": { "Fn::GetAtt": [ "Lambda", "Arn" ] },
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": { "Fn::Join": [ "", [
"arn:aws:execute-api:",
{ "Ref": "AWS::Region" }, ":",
{ "Ref": "AWS::AccountId" }, ":",
{ "Ref": "API" },
"/*/*/*"
] ] }
}
}
This grants API Gateway permissions to launch your Lambda function. Variables in this snippet you need to change are Lambda (line 4) and API (line 11).
For the invoke permissions:
"APIInvokePermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Ref": "YOUR_LAMBDA_FUNCTION_RESOURCE_NAME"
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${YOUR_REST_API_RESOURCE_NAME}/*/*/*"
}
}
},
Thanks https://twitter.com/edjgeek for helping me get this straight in my head.
This GIST shows how to use AWS::Serverless:Function with Events to automatically generate the needed AWS::Lambda::Permission to allow APIGateway (for a given route) to invoke your Lambda:
https://gist.github.com/rainabba/68df1567cbd0c4930d428c8953dc2316
Both of the following approaches assume an api such as (many fields omitted for readability):
MyApi:
Type: 'AWS::Serverless::Api'
Properties:
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: openapi.yaml
Using "SAM Events"
The most relevant bit (I've omitted many required fields):
MyLambdaFunction:
Type: 'AWS::Serverless::Function'
Properties:
Events:
MyRouteEventToProxy:
Type: Api
Properties:
Method: POST
Path: '/some-route/{pathParm}'
RestApiId: !Ref MyApi # ResourceName of AWS::Serverless::Api
Auth:
Authorizer: NONE
Using "openapi binding"
If you'd rather declare the binding in the openapi.yaml, then see the following project (no Lambda/Events required). This approach requires an explict role to allow invoke.
template.yaml relevant bits:
MyLambdaFunction:
Type: 'AWS::Serverless::Function'
Properties:
# Events: # No need for Events when binding from openapi.yaml
MyHttpApiRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "apigateway.amazonaws.com"
Action:
- "sts:AssumeRole"
openapi.yaml relevant bits:
paths:
post:
x-amazon-apigateway-integration:
httpMethod: POST
uri:
Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambdaFunction.Arn}:live/invocations"
contentHandling: "CONVERT_TO_TEXT"
type: aws_proxy
credentials:
Fn::GetAtt: [MyHttpApiRole, Arn]
https://github.com/aws-samples/sessions-with-aws-sam/tree/master/http-api-direct-integration