AWS SAM/Cloudformation "Malformed integration at path' error - aws-lambda

I get the following error when trying to deploy lambda and API gateway configuration
"malformed integration at path /profile/v1. (Service:
AmazonApiGateway; Status Code: 400; Error Code: BadRequestException"
What can cause this error and how can it be resolved.
swagger: '2.0'
info:
version: v1
title: ProfileAPI
paths:
"/profile/v1":
get:
tags:
- Values
operationId: ProfileV1Get
consumes: []
produces:
- text/plain
- application/json
- text/json
parameters: []
responses:
'200':
description: Success
schema:
type: array
items:
type: string
x-amazon-apigateway-integration:
httpMethod: post
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProfileFunction.Arn}/invocations
definitions: {}

Got a final working setup working with AWS Support that y'all might be interested in:
When we reference the cloudformation resource details in the external swagger template, we do not get the resource details and hence receive the above error. For example: “ Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations “ will not work when you try to create API gateway integration endpoint uri in swagger definition using resource : "LambdaFunction.Arn” (which is the CloudFormation resource).
In order to resolve these issues, I made the below changes in the cloudformation template:
To reference the swagger file in the cloudformation template, I uploaded the swagger template in the s3 bucket and then used the below definition. I used :
ZazzoAPI:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Variables:
LambdaFunctionName: !Ref LambdaFunction
#Configure API settings and options
MethodSettings: [{
LoggingLevel: "INFO",
MetricsEnabled: True ,
HttpMethod: "GET",
ResourcePath: "/"
}]
DefinitionBody:
'Fn::Transform':
Name: 'AWS::Include'
Parameters:
Location: "s3://s3.code-deploy/swagger_SAM.yaml"
The AWS::Include transform lets you create a reference to a transform snippet in an Amazon S3 bucket. It allows to reference the cloudformation resource details in an external swagger file. You can refer to the documentation at [1] for more details regarding "AWS::Include” Transform.
I then checked the swagger template and could see that you are using shorthand notations for specifying the integration uri. However, "AWS::Include” does not currently support using shorthand notations for YAML snippets as mentioned in the documentation [2]. Therefore, I used the intrinsic function "Fn::Sub" and was able to reference the required cloudformation parameters in the swagger template.
Previous definition:
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations”
New definition:
uri:
Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations"
References:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/create-reusable-transform-function-snippets-and-add-to-your-template-with-aws-include-transform.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/create-reusable-transform-function-snippets-and-add-to-your-template-with-aws-include-transform.html#aws-include-transform-remarks
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-stage-methodsetting.html

Related

Multiple microservices inside same AWS API Gateway

I'm developing a series of microservices which need to share the same AWS API Gateway. Here's my structure:
/
/assessments
/skills
/work-values
/graphql
/skills, /work-values, and /graphql are 3 different microservices I'm trying to register with the same AWS API Gateway. The problem I'm having is getting the serverless.yaml files for /skills, /work-values routes to nest under 'assessments'. There is no functionality for /assessments in-and-of-itself. It exists just so we can organize all of our assessments under the same URL path structure.
Here's my serverless.yaml file for `/work-values':
service:
name: assessments-workvalues
...
custom:
stage: ${opt:stage, self:provider.stage}
provider:
...
apiGateway:
restApiId:
# THE FOLLOWING REFERENCES A VARIABLE FROM MY API GATEWAY ROOT
'Fn::ImportValue': ${self:custom.stage}-ApiGatewayRestApiId
restApiRootResourceId:
'Fn::ImportValue': # HOW DO I GET THE PROPER VALUE HERE TO MAP TO `/assessments`?
...
functions:
...
Here's my serverless.yaml file for `/assessments':
service:
name: assessments
custom:
stage: ${opt:stage, self:provider.stage}
provider:
...
apiGateway:
restApiId:
# THE FOLLOWING REFERENCES A VARIABLE FROM MY API GATEWAY ROOT
'Fn::ImportValue': ${self:custom.stage}-ApiGatewayRestApiId
restApiRootResourceId:
'Fn::ImportValue': ${self:custom.stage}-ApiGatewayRestApiRootResourceId
functions:
...
resources:
Outputs:
ApiGatewayRestApiId:
Value:
Ref: ApiGatewayRestApi
Export:
Name: ${self:custom.stage}-Assessments-ApiGatewayRestApiId
ApiGatewayRestApiRootResourceId:
Value:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
Export:
Name: ${self:custom.stage}-Assessments-ApiGatewayRestApiRootResourceId
The problem seems to be coding the Outputs in serverless.yaml file for assessments route. When I run serverless deploy, I get this error message:
Error: The CloudFormation template is invalid: Unresolved resource dependencies [ApiGatewayRestApi] in the Outputs block of the template
At the end of Share an API Endpoint Between Services article, the author mentions 'You HAVE TO import /billing from the billing-api, so the new service will only need to create the /billing/xyz part.' (which seems to be the situation I'm in). But, the author does not explain how to import /billing. Or in my case, how do I import /assessments into the serverless.yaml files for each assessment microservice?
After further research, I found this link:
Splitting Your Serverless Framework API on AWS
I ended up reworking my original approach following what's in the article above. The piece I was missing was having a root or base serverless file which is used to create your routing in AWS API Gateway and expose those placeholders as output which your subsequent child serverless files consume as input for wiring up your child lambda functions to routes under the API Gateway umbrella.

aws serverless - exporting output value for cognito authorizer

I'm trying to share cognito authorizer between my stacks for this I'm exporting my authorizer but when I try to reference it in another service I get the error
Trying to request a non exported variable from CloudFormation. Stack name: "myApp-services-test" Requested variable: "ExtApiGatewayAuthorizer-test".
Here is my stack where I have authorizer defined and exported:
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
# Generate a name based on the stage
UserPoolName: ${self:provider.stage}-user-pool
# Set email as an alias
UsernameAttributes:
- email
AutoVerifiedAttributes:
- email
ApiGatewayAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: CognitoAuthorizer
Type: COGNITO_USER_POOLS
IdentitySource: method.request.header.Authorization
RestApiId: { "Ref": "ProxyApi" }
ProviderARNs:
- Fn::GetAtt:
- CognitoUserPool
- Arn
ApiGatewayAuthorizerId:
Value:
Ref: ApiGatewayAuthorizer
Export:
Name: ExtApiGatewayAuthorizer-${self:provider.stage}
this is successfully exported as I can see it in stack exports list from my aws console.
I try to reference it in another stack like this:
myFunction:
handler: handler.myFunction
events:
- http:
path: /{userID}
method: put
cors: true
authorizer:
type: COGNITO_USER_POOLS
authorizerId: ${myApp-services-${self:provider.stage}.ExtApiGatewayAuthorizer-${self:provider.stage}}
my env info
Your Environment Information ---------------------------
Operating System: darwin
Node Version: 12.13.1
Framework Version: 1.60.5
Plugin Version: 3.2.7
SDK Version: 2.2.1
Components Core Version: 1.1.2
Components CLI Version: 1.4.0
Answering my own question
it looks like I should have imported by output name not output export name, which is bit weird and all the docs I have seen point to export name, but this is how I was able to make it work
replaced this -
authorizerId:${myAppservices-${self:provider.stage}.ExtApiGatewayAuthorizer-${self:provider.stage}}
with -
authorizerId: ${myApp-services-${self:provider.stage}.ApiGatewayAuthorizerId}
If you come across Trying to request a non exported variable from CloudFormation. Stack name: "myApp-services-test" Requested variable: "ExtApiGatewayAuthorizer-test"., when exporting profile i.e.,
export AWS_PROFILE=your_profile
It must be done on the terminal window where you are doing sls deploy not on another terminal window. It is a silly mistake but I don't want anyone else waste their time around that

AWS SAM : Creating/Exporting API Gateway resource and importing it in another stack

I am trying to use SAM to automate my deployment on AWS.
In the first SAM template, I want to create an API Gateway, Cognito authoriser and a lambda function which uses this API gateway.
I am trying to export this API gateway resource, from the first template.
I want to use this API gateway resource in another SAM template, using import statement.
Is there any good example which explains exporting API gateway resource from the first SAM template and then using it in the second SAM template.
Try this (not tested):
From here Fn::ImportValue
Stack A Export
Outputs:
ServerlessRestApi:
Value: !Ref MyApiGatewayResource
Export:
Name: !Sub "${AWS::StackName}-ServerlessRestApi"
Stack B Import
Import to a Lambda Function:
MyLambda:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-MyLambda"
Handler: ....
CodeUri: .....
Events:
MyApi:
Type: Api
Properties:
Path: /example
Method: post
RestApiId:
Fn::ImportValue:
!Sub "${StackANameParameter}-ServerlessRestApi"
Hope this helps

serverless-offline: remote Lambda custom authorizer for API Gateway

I have declared an HTTP Proxy method on my API Gateway resource using CloudFormation syntax (detailed in this earlier post.)
I am trying to attach a custom authorizer, of type "Request", which uses an existing AWS Lambda function. The serverless docs have a decent example of setting up custom authorizers using CloudFormation. Meanwhile, serverless-offline clearly supports (source) request-scoped custom authorizers.
I have closely cross-referenced the relevant AWS CloudFormation documentation for AWS::ApiGateway::Resource and AWS::ApiGateway::Authorizer, together with the related serverless docs, in creating the below serverless template. But so far, I am not getting the expected output or behavior from serverless-offline.
Resources are created without authorizers
When I boot up the below serverless configuration, no authorizers are loaded for my resource. I don't see what is wrong or missing in my serverless.yml file. Do you have any tips? Thanks in advance!
➜ serverless-offline-attempt git:(master) ✗ npm start
> # start /Users/freen/src/apig/serverless-offline-attempt
> ./node_modules/serverless/bin/serverless offline
Serverless: Starting Offline: dev/us-east-1.
Serverless: Routes defined in resources:
Serverless: ANY /upstream/{proxy*} -> http://upstream.company.cool/{proxy}
Serverless: Offline listening on http://localhost:3000
serverless.yml
The below template file includes the APIG resources and the authorizer configuration.
service: company-apig
provider:
name: aws
stage: dev
runtime: python2.7
plugins:
- serverless-offline
custom:
serverless-offline:
resourceRoutes: true
resources:
Resources:
# Parent APIG RestApi
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: company-apig
Description: 'The main entry point of the APIG'
# Shared Authorizers
AuthorizeCompanyJWTAccessToken:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: AuthorizeCompanyJWTAccessToken
Type: REQUEST
RestApiId:
Ref: ApiGatewayRestApi
AuthorizerUri:
Fn::Join:
- ""
-
- "arn:aws:apigateway:"
- Ref: "AWS::Region"
- ":lambda:path/2015-03-31/functions/"
- "arn:aws:lambda:us-east-1:123456789012:function:jwt-tokens-staging-AccessTokenAuthorizer"
- "/invocations"
# Resource /upstream
UpstreamResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
PathPart: 'upstream'
RestApiId:
Ref: ApiGatewayRestApi
# Resource /upstream/{proxy+}
UpstreamProxyPath:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Ref: UpstreamResource
PathPart: '{proxy+}'
RestApiId:
Ref: ApiGatewayRestApi
# Method ANY /upstream/{proxy+}
UpstreamProxyAnyMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: CUSTOM
AuthorizerId:
Ref: AuthorizeCompanyJWTAccessToken
HttpMethod: ANY
Integration:
IntegrationHttpMethod: ANY
Type: HTTP_PROXY
Uri: http://upstream.company.cool/{proxy}
PassthroughBehavior: WHEN_NO_MATCH
MethodResponses:
- StatusCode: 200
ResourceId:
Ref: UpstreamProxyPath
RestApiId:
Ref: ApiGatewayRestApi
Related:
Shared Lambda authorizer setup in Serverless Framework (only loosely; the OP seeks a CloudFormation solution, but the answer does not use CF)

SAM Template: multiple microservice lambdas sharing a single AWS::ApiGateway::DomainName

My goal is allow several independent lambda resources (microservices) to share a common custom domain for test environment. Using the below template, deploying microservice #1 went off without issue. However, when deploying microservice #2, CloudFormation fails and rolls back because the domain name is assigned to microservice #1.
Surely it must be possible to share a common custom domain among several microservices with unique paths?
Service #1 template.yaml
Resources:
ApiGatewayApi:
Type: AWS::Serverless::Api
Properties:
StageName: !Sub ${apiGatewayStageName}
DefinitionBody:
swagger: "2.0"
info:
title: !Sub ${functionName}
paths:
"/service_one":
post:
responses: {}
x-amazon-apigateway-integration:
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${functionName}:live/invocations"
httpMethod: POST
type: aws_proxy
ApiGatewayCustomDomainName:
Type: AWS::ApiGateway::DomainName
Properties:
CertificateArn: !Sub "arn:aws:acm:${AWS::Region}:${AWS::AccountId}:certificate/${apiGatewayCDNCertificateId}"
DomainName: !Sub "${envName}.${apiGatewayCustomDomainNameSuffix}"
DependsOn:
- "LogsSubscriptionFilter"
- "ApiGatewayApi"
ApiGatewayBasePathMapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
DomainName: !Ref ApiGatewayCustomDomainName
RestApiId: !Ref ApiGatewayApi
Stage: !Ref apiGatewayStageName
DependsOn: "ApiGatewayCustomDomainName"
The Service #2 template is the same, except with a different path (.e.g. /service_two.
Worked with AWS support to get to the bottom of this one. In its current form (May 2018), if you want separate SAM templates for each lambda for deployment reasons, but have a shared API Gateway resource for those lambdas, then you must have a separate dedicated API Gateway SAM template. Additionally, the Events parameter in AWS::Serverless::Function must be excluded in favor of paths definitions in AWS::Serverless::Api.
Lastly, the AWS::Serverless::Function(s) must be exported under Output and then imported by the AWS::Serverless::Api resource.

Resources