AWS::ApiGatewayV2::Api - Simple WebSocket configuration throwing INVALID_API_KEY (forbidden) - amazon-web-services

I did not set any authorization for the Websocket and it throws INVALID_API_KEY.
Here's SAM template:
##############################################################
# API GATEWAY: BrokerAuthenticateSocketApi
##############################################################
BrokerAuthenticateSocketApi:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: !Sub "${AWS::StackName}-BrokerAuthenticateSocketApi"
ProtocolType: WEBSOCKET
RouteSelectionExpression: "$request.body.action"
Tags:
'Joba:Product': !Ref Product
'Joba:Environment': !Ref Environment
ApiStage: # Why would we need this: https://medium.com/#TomKeeber/aws-api-gateways-c048cec63046
Type: AWS::ApiGatewayV2::Stage
Properties:
StageName: !Ref ApiStageName
AutoDeploy: true
ApiId: !Ref BrokerAuthenticateSocketApi
AccessLogSettings:
DestinationArn: !GetAtt ApiGatewayAccessLogGroup.Arn
Format: '{"requestTime":"$context.requestTime","requestId":"$context.requestId","routeKey":"$context.routeKey","status":$context.status,"responseLatency":$context.responseLatency,"integrationRequestId":"$context.integration.requestId","functionResponseStatus":"$context.integration.status","integrationLatency":"$context.integration.latency","integrationServiceStatus":"$context.integration.integrationStatus","ip":"$context.identity.sourceIp","userAgent":"$context.identity.userAgent","principalId":"$context.authorizer.principalId","validationErrorString":"$context.error.validationErrorString","integrationErrorMessage":"$context.integrationErrorMessage","errorMessage":"$context.error.message","errorResponseType":"$context.error.responseType"}'
Tags:
'Joba:Product': !Ref Product
'Joba:Environment': !Ref Environment
ApiAuthenticateRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref BrokerAuthenticateSocketApi
RouteKey: authenticate
AuthorizationType: NONE # TODO: change this for Auth0
OperationName: AuthenticateRoute
Target: !Join
- /
- - integrations
- !Ref ApiAuthenticateRouteIntegration
ApiAuthenticateRouteIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref BrokerAuthenticateSocketApi
IntegrationType: AWS
IntegrationMethod: POST
IntegrationUri: !Sub "arn:aws:apigateway:${AWS::Region}:states:action/StartExecution"
CredentialsArn: !Sub "${ApiIntegrationStateMachineExecutionRole.Arn}"
TemplateSelectionExpression: \$default
RequestTemplates: # see: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-mapping-template-reference.html
"$default" :
Fn::Sub: >
#set($statesInput='{"data": ' + $input.body + ',"webSocket": {"connectionId": "' + $context.connectionId + '", "domainName": "' + $context.domainName + '"}' + '}')
#set($statesInput=$util.escapeJavaScript($statesInput).replaceAll("\\'","'"))
{
"input": "$statesInput",
"stateMachineArn": "${DownloadBrokerageNotesStateMachine}"
}
ApiAuthenticateRouteResponse: # https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-route-response.html
Type: AWS::ApiGatewayV2::RouteResponse
Properties:
RouteId: !Ref ApiAuthenticateRoute
ApiId: !Ref BrokerAuthenticateSocketApi
RouteResponseKey: $default
ApiAuthenticateRouteIntegrationResponse:
Type: AWS::ApiGatewayV2::IntegrationResponse
Properties:
ApiId: !Ref BrokerAuthenticateSocketApi
IntegrationId: !Ref ApiAuthenticateRouteIntegration
IntegrationResponseKey: $default
ApiIntegrationStateMachineExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Effect: Allow
Principal:
Service:
- !Sub apigateway.${AWS::Region}.amazonaws.com
Path: "/"
Policies:
- PolicyName: StateMachineExecutionAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "states:StartExecution"
Resource: !Ref DownloadBrokerageNotesStateMachine
Tags:
- Key: 'Joba:Product'
Value: !Ref Product
- Key: 'Joba:Environment'
Value: !Ref Environment
ApiGatewayAccessLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join [ "/", [ "joba", "apigateway", !Ref BrokerAuthenticateSocketApi, "access-logs"]]
Using Postman to test, it connects successfully to the websocket:
When I try to send a message (with action=authenticate as configured in the SAM template), it returns Forbidden
The apigatway log shows INVALID_API_KEY.
{
"requestTime":"28/Jan/2023:22:32:34 +0000",
"requestId":"feZUdEH2oAMFe9A=",
"routeKey":"-",
"status":403,
"responseLatency":-,
"integrationRequestId":"-",
"functionResponseStatus":"-",
"integrationLatency":"-",
"integrationServiceStatus":"-",
"userAgent":"-","principalId":"-",
"validationErrorString":"-",
"integrationErrorMessage":"-",
"errorMessage":"Forbidden",
"errorResponseType":"INVALID_API_KEY"
}
In no section of the SAM template, I specified Authorization. In fact, I specified AuthorizationType: NONE in the AWS::ApiGatewayV2::Route.
What's wrong with my configuration?

It was really hard to find the error, but I did. A rather silly mistake, but not fully understood why it has to be like this.

Related

AWS API Gateway V2 Integration with SQS - CloudFormation

I am trying to create a CloudFormation template to deploy an API Gateway HTTP API integrated with SQS. I used the below CF template:
AWSTemplateFormatVersion: "2010-09-09"
Resources:
ExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: ExecutionRole
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: root
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:SendMessage
- sqs:ReceiveMessage
- sqs:DeleteMessage
Resource: !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:sample-queue"
- Effect: Allow
Action: lambda:InvokeFunction
Resource: "*"
Queue:
Type: AWS::SQS::Queue
Properties:
QueueName: sample-queue
API:
Type: AWS::ApiGatewayV2::Api
Properties:
Body:
openapi: "3.0.1"
info:
title: "HttpApi"
version: "2022-09-29 15:59:08UTC"
paths:
/:
post:
responses:
default:
description: "Default response for POST /"
x-amazon-apigateway-integration:
integrationSubtype: "SQS-SendMessage"
credentials: !Sub "arn:aws:iam::${AWS::AccountId}:role/ExecutionRole"
requestParameters:
MessageBody: "$request.body"
QueueUrl: !Sub "https://sqs.${AWS::Region}.amazonaws.com/${AWS::AccountId}/sample-queue"
payloadFormatVersion: "1.0"
type: "aws_proxy"
connectionType: "INTERNET"
timeoutInMillis: 30000
x-amazon-apigateway-importexport-version: "1.0"
Stage:
Type: AWS::ApiGatewayV2::Stage
Properties:
StageName: v1
ApiId: !Ref API
Deployment:
Type: AWS::ApiGatewayV2::Deployment
Properties:
ApiId: !Ref API
StageName: !Ref Stage
The API this generates looks good in the web console, but not sure why, any POST to this API gives an error:
{
"message": "Not Found"
}
Perhaps I am missing something very silly. But not able to locate it. Can you please help me identify?

AWS Cloudformation Lambda + API Gateway V2: Unable to deploy API because no routes exist in this API

I'm trying to create a API Gateway HTTP API + Lambda integration using CloudFormation, but I'm stuck on this error:
Unable to deploy API because no routes exist in this API (Service: AmazonApiGatewayV2; Status Code: 400; Error Code: BadRequestException; Request ID: f606986f-d3e6-4dfd-bc20-77011b15a3f9; Proxy: null)
Here's my CloudFormation template:
AWSTemplateFormatVersion: 2010-09-09
Resources:
LambdaRole:
Type: AWS::IAM::Role
Properties:
Policies:
- PolicyName: LambdaPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource:
- 'arn:aws:logs:*:*:*'
Effect: Allow
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Lambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: 'getFruit'
Role: !GetAtt LambdaRole.Arn
Handler: index.handler
Runtime: nodejs16.x
MemorySize: 128
Code:
ZipFile: |
exports.handler = async (event) => {
const response = {
body: JSON.stringify([
{ id: 1, name: 'banana', price: 1 },
{ id: 2, name: 'pineapple', price: 2 },
]),
statusCode: 200
}
return response
}
LambdaInvokePermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref Lambda
Action: "lambda:InvokeFunction"
Principal: apigateway.amazonaws.com
LambdaIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref MyApiGateway
Description: Lambda proxy integration
IntegrationType: AWS_PROXY
IntegrationMethod: POST
PayloadFormatVersion: "2.0"
IntegrationUri: !Sub 'arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Lambda.Arn}/invocations'
MyApiGateway:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: "MyApiGateway"
ProtocolType: "HTTP"
MyApiGatewayStage:
Type: AWS::ApiGatewayV2::Stage
Properties:
AutoDeploy: true
DeploymentId: !Ref MyApiGatewayDeployment
StageName: '$default'
ApiId: !Ref MyApiGateway
MyApiGatewayDeployment:
Type: AWS::ApiGatewayV2::Deployment
Properties:
ApiId: !Ref MyApiGateway
MyApiRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref MyApiGateway
RouteKey: "GET /"
AuthorizationType: NONE
Target: !Join
- /
- - integrations
- !Ref LambdaIntegration
Try adding a DependsOn properties to the deployment for the routes you create.
MyApiGatewayDeployment:
Type: AWS::ApiGatewayV2::Deployment
DependsOn:
- MyApiRoute
Properties:
ApiId: !Ref MyApiGateway

Glue connection not working through cloudformation. It is giving error : Unable to resolve any valid connection

My glue connection created through cloudformation is not working whereas if create a glue connection with same configuration through the console it works perfectly fine.
Please find the code for the same :
Resources:
Policy1:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: Policy for glue
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: '*'
Resource: '*'
Role1:
Type: AWS::IAM::Role
DependsOn: Policy1
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- glue.amazonaws.com
- rds.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- !Ref Policy1
GlueConnection:
Type: AWS::Glue::Connection
Properties:
CatalogId: !Ref AWS::AccountId
ConnectionInput:
ConnectionProperties:
JDBC_CONNECTION_URL: "jdbc:mysql://database-2.cxs4adwnjt5i.ap-south-1.rds.amazonaws.com:3306/mydb"
USERNAME: "admin"
PASSWORD: "Admin123"
JDBC_ENFORCE_SSL: False
ConnectionType: JDBC
PhysicalConnectionRequirements:
SecurityGroupIdList:
- sg-065781f0ee39344fa
SubnetId: subnet-0cdac25848264fdb8
Name: rds-1
GlueDatabase:
Type: AWS::Glue::Database
Properties:
CatalogId: !Ref AWS::AccountId
DatabaseInput:
Name: my-rds-glue-1
To fix this you need to include AvailabilityZone in PhysicalConnectionRequirements.
PhysicalConnectionRequirements:
AvailabilityZone: !Select [ 0, !GetAZs '' ]
SecurityGroupIdList:
- !Ref GlueSG
SubnetId: !Ref SubnetId
See more details here:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-connection-physicalconnectionrequirements.html
Until this gets fix the workaround is, like you mentioned, to go in the console and just edit and save the connection.

Lambda permission not being created when using cloudformation

I am trying to create a simple infra using cloudformation. I have to create an Rest API Gateway and Lambda function. That function will be called using API Gateway.
Api Gateway > Lambda
The cloudformation code is below ( I am not showing the code related to role creation or managed policy).
medtestFunction:
Type: "AWS::Lambda::Function"
Properties:
Description: ""
Environment:
Variables:
APIID: !Ref medTestRestapi
SLACK_VERIFICATION_TOKEN:
Ref: SlackVerificationToken
SLACK_INCOMING_WEBHOOK_URL:
Ref: SlackIncomingWebhookURL
FunctionName: "med-test2"
Handler: "index.handler"
Architectures:
- "x86_64"
Code:
S3Bucket:
Ref: S3CodeBucket
S3Key:
Ref: MedTestFunctionS3Key
MemorySize: 128
Role: !GetAtt medtestrole.Arn
Runtime: "nodejs14.x"
Timeout: 6
TracingConfig:
Mode: "PassThrough"
medTestRestapi:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "medtest2"
Description: "medtest2"
ApiKeySourceType: "HEADER"
EndpointConfiguration:
Types:
medTestApiStage:
Type: "AWS::ApiGateway::Stage"
Properties:
StageName: "a"
DeploymentId: !Ref medTestApiDeployment
RestApiId: !Ref medTestRestapi
Description: "a"
CacheClusterEnabled: false
TracingEnabled: false
medTestApiMethod:
DependsOn: medtestFunction
Type: "AWS::ApiGateway::Method"
Properties:
RestApiId: !Ref medTestRestapi
ResourceId: !GetAtt medTestRestapi.RootResourceId
HttpMethod: "POST"
AuthorizationType: "NONE"
ApiKeyRequired: false
RequestParameters: {}
MethodResponses:
-
ResponseModels:
"application/json": "Empty"
StatusCode: "200"
Integration:
ContentHandling: "CONVERT_TO_TEXT"
IntegrationHttpMethod: "POST"
IntegrationResponses:
-
ResponseTemplates: {}
StatusCode: "200"
PassthroughBehavior: "WHEN_NO_MATCH"
TimeoutInMillis: 29000
Type: "AWS_PROXY"
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:med-test2/invocations"
medTestApiDeployment:
DependsOn: medTestApiMethod
Type: "AWS::ApiGateway::Deployment"
Properties:
RestApiId: !Ref medTestRestapi
Description: "a"
medTestFunctionPermission:
DependsOn: [medTestApiDeployment, medTestApiMethod]
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt medtestFunction.Arn
Principal: "apigateway.amazonaws.com"
SourceArn: !Join [ ":", ["arn:aws:execute-api", !Ref AWS::Region, !Ref AWS::AccountId, !Ref medTestRestapi, "/*/POST/" ] ]
After stack creation when I am checking the function it says
The API with ID : could not be found.
But when I add the trigger manually using console on top of the created stack then it works. Any idea what I am doing wrong? Thanks
you've got one colon too many in SourceArn of medTestFunctionPermission
The one just after the API Gateway ID
You have:
arn:aws:execute-api:eu-west-1:<accountId>:<apiGWId>:/*/POST/
Should be:
arn:aws:execute-api:eu-west-1:<accountId>:<apiGWId>/*/POST/
You can use !Sub instead of !Join. It's easier to read:
SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${medTestRestapi}/*/POST/

AWS Lambda function is missing trigger by websocket gateway when using CloudFormation

I am trying to set-up a websocket gateway to a Lambda function in AWS. When I do this manually I can successfully deploy the websocket and try it out using wscat. However I would like to build the architecture up using CloudFormation.
The structure of my CloudFormation yaml file looks like this:
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
lambdaRole:
Type: String
Default: ...
backendRole:
Type: String
Default: ...
lambdaImage:
Type: String
Default: ...
Resources:
MyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Code:
ImageUri: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${backendRole}:${lambdaImage}
Description: lambda connect function
FunctionName: myLambdaFunction
MemorySize: 128
Role: !Sub arn:aws:iam::${AWS::AccountId}:role/${lambdaRole}
Timeout: 3
PackageType: Image
MyWebSocket:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: MyWebSocket
ProtocolType: WEBSOCKET
RouteSelectionExpression: $request.body.action
MyIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref MyWebSocket
Description: Lambda Integration
IntegrationType: AWS_PROXY
IntegrationUri: !Join
- ''
- - 'arn:'
- !Ref 'AWS::Partition'
- ':apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- !GetAtt MyLambdaFunction.Arn
- /invocations
IntegrationMethod: POST
MyConnectRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref MyWebSocket
RouteKey: $connect
Target: !Join
- /
- - integrations
- !Ref MyIntegration
MyDefaultRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref MyWebSocket
RouteKey: $default
Target: !Join
- /
- - integrations
- !Ref MyIntegration
MyResponseRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref MyWebSocket
RouteKey: add
RouteResponseSelectionExpression: $default
Target: !Join
- /
- - integrations
- !Ref MyIntegration
MyRouteResponse:
Type: AWS::ApiGatewayV2::RouteResponse
Properties:
RouteId: !Ref MyResponseRoute
ApiId: !Ref MyWebSocket
RouteResponseKey: $default
MyIntegrationResponse:
Type: AWS::ApiGatewayV2::IntegrationResponse
Properties:
IntegrationId: !Ref MyIntegration
IntegrationResponseKey: /201/
ApiId: !Ref MyWebSocket
testStage:
Type: AWS::ApiGatewayV2::Stage
DependsOn:
- MyConnectRoute
- MyDefaultRoute
- MyResponseRoute
Properties:
ApiId: !Ref MyWebSocket
StageName: testStage
MyDeployment:
Type: AWS::ApiGatewayV2::Deployment
Properties:
ApiId: !Ref MyWebSocket
Description: My Deployment
StageName: !Ref testStage
The stack is build without any errors and (almost) everything looks like I intended. However other than in the manually build version the integration of the Lambda function into the websocket does not seem to add the required trigger for the Lambda function. When I manually add a Lambda function to an API Gateway, this automatically adds the trigger.
What do I need to change in my CloudFormation Yaml file to also add the trigger to the Lambda function?
Trigger automatically added to Lambda function when Lambda function is manually added to API Gateway:
No trigger added when Lambda function is added to API Gateway using CloudFormation:
It looks like you may just need to include the Lambda permission for api gateway.
LambdaFunctionPermission:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
Principal: apigateway.amazonaws.com
FunctionName: !Ref MyLambdaFunction
DependsOn: MyWebSocket
If you are having issues with Cloudformation creation it would probably be helpful to also create an account config for logging and chain the dependsOn order of the gateway resources like this.
ApiGwAccountConfig:
Type: "AWS::ApiGateway::Account"
Properties:
CloudWatchRoleArn: !GetAtt "rRoleForCloudtrail.Arn"
rRoleForCloudtrail:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action:
- sts:AssumeRole
Policies:
-
PolicyName: "apigateway-cloudwatch"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:DescribeLogGroups
- logs:DescribeLogStreams
- logs:PutLogEvents
- logs:GetLogEvents
- logs:FilterLogEvents
Resource: "*"
testStage:
Type: AWS::ApiGatewayV2::Stage
Properties:
ApiId: !Ref MyWebSocket
StageName: testStage
DefaultRouteSettings:
DetailedMetricsEnabled: true
LoggingLevel: INFO
DataTraceEnabled: true
DependsOn: ApiGwAccountConfig
MyDeployment:
Type: AWS::ApiGatewayV2::Deployment
DependsOn:
- MyConnectRoute
- MyDefaultRoute
- MyResponseRoute
Properties:
ApiId: !Ref MyWebSocket
Description: My Deployment
StageName: !Ref testStage
I guess you need AWS::Lambda::Permission resource, e.g.:
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
FunctionName: !GetAtt MyLambdaFunction.Arn
Principal: apigateway.amazonaws.com
SourceArn:
Fn::Join:
- ''
- - 'arn:aws:execute-api:'
- Ref: AWS::Region
- ":"
- Ref: AWS::AccountId
- ":"
- Ref: MyWebSocket
- "/*"
It may need some tweaking - it works with AWS::ApiGateway::RestApi, not sure if there is difference for AWS::ApiGatewayV2::Api.