I want to be able to use startSyncExecution with a Step Function via a SAM template.
Here is my SAM template snippet for my Step Function and API Gateway:
RootApi:
Type: AWS::Serverless::Api
Properties:
Cors:
AllowMethods: "'*'"
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
Auth:
Authorizers:
CognitoAuth:
UserPoolArn: !FindInMap [AuthMap, !Ref Stage, authorizer]
Identity:
Header: Authorization
AuthorizationScopes:
- email
Domain:
DomainName: !FindInMap [DomainMap, !Ref Stage, DomainName]
CertificateArn: !FindInMap [DomainMap, !Ref Stage, CertificateArn]
EndpointConfiguration: EDGE
StateMachineSample:
Type: AWS::Serverless::StateMachine
Properties:
DefinitionUri: code/
Type: EXPRESS
Logging:
Destinations:
- CloudWatchLogsLogGroup:
LogGroupArn: !GetAtt [ StateMachineSample, Arn ]
IncludeExecutionData: true
Level: 'ALL'
DefinitionSubstitutions:
SampleFunction: !Ref SampleFunction
Policies:
- LambdaInvokePolicy:
FunctionName: !Ref SampleFunction
- Statement:
- Sid: PushLogs
Effect: Allow
Action: [
"logs:CreateLogDelivery",
"logs:GetLogDelivery",
"logs:UpdateLogDelivery",
"logs:DeleteLogDelivery",
"logs:ListLogDeliveries",
"logs:PutResourcePolicy",
"logs:DescribeResourcePolicies",
"logs:DescribeLogGroups"
]
Resource: "*"
Events:
SampleAPI:
Type: Api
Properties:
RestApiId: RootApi
Path: /v1/sample
Method: POST
Auth:
Authorizer: Auth
Related
I'm trying to deploy an AWS SAM application using Gitlab CICD which was successful at first but failing after that. The failure is happening with API Gateway resource. The deployment would be successful if I delete the stack and deploy it again, but then on checking the API Gateway in console, there would be no Methods associated with it.
Mappings:
StackEnv:
dev-stack:
envm: "dev"
uat-stack:
envm: "qa"
Resources:
MessageBodyAPI: #API
Type: AWS::Serverless::Api
Properties:
DefinitionBody:
swagger: 2.0
info:
title: TestApi
basePath: /
schemes:
- https
x-amazon-apigateway-policy:
Version: '2012-10-17'
Statement:
- Effect: Deny
Principal: "*"
Action: execute-api:Invoke
Resource: "*"
Condition:
StringNotEquals:
aws:sourceVpce:
- vpce-xxxxxx
- Effect: Allow
Principal: "*"
Action: execute-api:Invoke
Resource: "*"
Description: description
EndpointConfiguration: Private
MethodSettings:
- HttpMethod: POST
ResourcePath: /sap-inventory
Name: myAPI
StageName: !FindInMap [StackEnv, !Ref AWS::StackName, envm]
POSTMethod: #Method for MessageBodyAPI
Type: 'AWS::ApiGateway::Method'
Properties:
RestApiId:
Ref: MessageBodyAPI
ResourceId:
Fn::GetAtt: [
"MessageBodyAPI",
"RootResourceId"
]
HttpMethod: POST
AuthorizationType: NONE
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:xxxxxx:function:myfunction/invocations
MessageBodyAPIDeployment: #Deployment corresponding to MessageBodyAPI
DependsOn: POSTMethod
Type: AWS::ApiGateway::Deployment
Properties:
Description: description
RestApiId:
Ref: MessageBodyAPI
StageName: !FindInMap [StackEnv, !Ref AWS::StackName, envm]
The error I'm receiving:
Resource handler returned message: "The REST API doesn't contain any methods (Service: ApiGateway, Status Code: 400)
Any help?
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/
I built an cloudFormation template. It perform a simple workflow: client make a request -> api gateway handle it and send to lambda fucntion via proxy integration. There is a role between api gateway an a lambda.
My template consist of:
root resource (AudienceApi)
nested resource (Segment)
method post (PostMethod) with lambda`s integration
lambda fucntion (lambdaFunction)
lambda invoker (it is a permission for api gateway to invoke the lambda)
lambda role
And during testing the whole workflow I have faced with a problem - api responce with an error 500 Internal Server Error. From log I found message:
Invalid permissions on Lambda function.
I continue testing and find out when I remove nested resource (Segment) and connect method post resource (PostMethod) directly to root resource (AudienceApi) my workflow started to working.
Question: What wrong with my template? why it is not working with nested resource path?
Maybe someone look at my template and find an error?
template:
AWSTemplateFormatVersion: "2010-09-09"
Description: "My API Gateway and Lambda function"
Parameters:
apiGatewayStageName:
Type: "String"
AllowedPattern: "^[a-z0-9]+$"
Default: "call"
lambdaFunctionName:
Type: "String"
AllowedPattern: "^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$"
Default: "my-function"
Resources:
AudienceApi:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "my-api"
Description: "My API"
Segment:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref AudienceApi
ParentId: !GetAtt
- AudienceApi
- RootResourceId
PathPart: segment
PostMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
HttpMethod: POST
# RequestModels:
# application/json: !Ref SegmentationRequestModel
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "lambdaFunction.Arn"
ResourceId: !Ref Segment
RestApiId: !Ref AudienceApi
lambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Code:
ZipFile: |
def handler(event,context):
return {
'body': 'Hello there {0}'.format(event['requestContext']['identity']['sourceIp']),
'headers': {
'Content-Type': 'text/plain'
},
'statusCode': 200
}
Description: "My function"
FunctionName: !Ref "lambdaFunctionName"
Handler: "index.handler"
MemorySize: 128
Role: !GetAtt "lambdaIAMRole.Arn"
Runtime: "python2.7"
Timeout: 10
lambdaApiGatewayInvoke:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt "lambdaFunction.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${AudienceApi}/*/POST/"
lambdaIAMRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Effect: "Allow"
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${lambdaFunctionName}:*"
PolicyName: "lambda"
lambdaLogGroup:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: !Sub "/aws/lambda/${lambdaFunctionName}"
RetentionInDays: 90
Based on the comments, the solution was to add * to the ARN after the POST/ in SourceArn:
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${AudienceApi}/*/POST/*"
I am trying to request parameters using API Gateway in AWS CloudFormation. The parameter that I want to pass down from API gateway to Lambda function is 'action'. I have tried the following code and so far I ran into the error, mention below. Can someone please help me with identifying the issue and a possible resolution?
"Invalid mapping expression specified: Validation Result: warnings : [], errors : [Invalid mapping expression specified: Integration.request.path.action] (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 037f4753-52b5-4276-979a-131a0f903e63)"
AWSTemplateFormatVersion: "2010-09-09"
Description: "API Gateway and Lambda function"
Resources:
SampleApi:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: Sample
SampleApiMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
HttpMethod: "GET"
RequestParameters:
method.request.path.action: true
RequestTemplates:
application/yaml
Integration:
IntegrationHttpMethod: "POST"
Type: "AWS_PROXY"
RequestParameters:
Integration.request.path.action: method.request.path.action
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "SampleLambda.Arn"
CacheKeyParameters:
- method.request.path.action
ResourceId: !GetAtt "SampleApi.RootResourceId"
RestApiId: !Ref "SampleApi"
SampleApiDeployment:
Type: "AWS::ApiGateway::Deployment"
DependsOn: "SampleApiMethod"
Properties:
RestApiId: !Ref "SampleApi"
StageName: test
SampleLambda:
Type: "AWS::Lambda::Function"
Properties:
Code:
ZipFile: |
import yaml
import boto3
cf_client = boto3.client('cloudformation')
cf_client.create_stack(
StackName='your-stack',
TemplateURL='Some URL',
Parameters=[
{
'ParameterKey':'action',
'ParameterValue': 'kms:*'
},
]
)
Handler: "index.handler"
Role: !GetAtt "SampleLambdaRole.Arn"
Runtime: python3.7
LambdaApiGatewayInvoke:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt "SampleLambda.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${SampleApi}/*/GET/"
SampleLambdaRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action: ["sts:AssumeRole"]
Effect: "Allow"
Principal:
Service: ["lambda.amazonaws.com"]
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Action: ["cloudwatch:*", "logs:*"]
Effect: "Allow"
Resource: "*"
PolicyName: "lambdaLogPolicy"
Outputs:
apiGatewayInvokeURL:
Value: !Sub 'https://Sample.execute-api.${AWS::Region}.amazonaws.com/test'
According to the docs, the key for RequestParameters should be like integration.request.<location>.<name>, with a lowercase i for integration. You are using an uppercase I. From the AWS CloudFormation docs:
Specify the destination by using the following pattern integration.request.location.name, where location is query string, path, or header, and name is a valid, unique parameter name.
Also, your template from above contains a RequestTemplates property which is placed in the wrong hierarchy level. It must be placed below Integration as noted in the AWS CloudFormation docs as well. Here is the correct template for AWS::ApiGateway::Method for you:
SampleApiMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
HttpMethod: "GET"
RequestParameters:
method.request.path.action: true
Integration:
IntegrationHttpMethod: "POST"
Type: "AWS_PROXY"
RequestParameters:
integration.request.path.action: method.request.path.action
RequestTemplates:
"application/yaml": "<define your template here>"
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "SampleLambda.Arn"
CacheKeyParameters:
- method.request.path.action
ResourceId: !GetAtt "SampleApi.RootResourceId"
RestApiId: !Ref "SampleApi"
More information about defining a request template can be found in the developer reference.
We have an API Gateway utilising a custom Token authoriser. We have 2 lambdas - Greetings and GenerateToken.
We only want the Greetings lambda to be behind a authoriser - requires to be called in the following way utilising SAM:
curl -X GET \
https://<apigatewayid>.execute-api.eu-west-1.amazonaws.com/Prod/generateToken \
-H 'X-API-KEY: allow'
How can we achieve that the GenerateToken path does not require a HTTP token for authenticating?
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: API Gateway with Lambda Token Authorizer
Resources:
GreetingsApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
DefinitionBody:
swagger: 2.0
x-amazon-apigateway-policy:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: "*"
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
paths:
"/hello":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GreetingsLambda.Arn}/invocations
responses: {}
"/generateToken":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GenerateTokenLambda.Arn}/invocations
responses: {}
Auth:
DefaultAuthorizer: CustomAuthorizer
Authorizers:
MyAuthorizer:
FunctionArn: !GetAtt AuthLambda.Arn
Identity:
Header: X-API-KEY
GenerateTokenLambda:
Type: AWS::Serverless::Function
Properties:
Role: !GetAtt LambdaRole.Arn
CodeUri: "s3://<bucket-name>/code.zip"
Handler: src/generateToken.handler
Events:
GetRoot:
Type: Api
Properties:
RestApiId: !Ref GreetingsApiGateway
Path: /generateToken
Method: get
GreetingsLambda:
Type: AWS::Serverless::Function
Properties:
Role: !GetAtt LambdaRole.Arn
CodeUri: "s3://<bucket-name>/code.zip"
Handler: src/greetings.handler
Events:
GetRoot:
Type: Api
Properties:
RestApiId: !Ref GreetingsApiGateway
Path: /hello
Method: get
AuthLambda:
Type: AWS::Serverless::Function
Properties:
Role: !GetAtt LambdaRole.Arn
CodeUri: "s3://<bucket-name>/code.zip"
Handler: src/auth.handler
Globals:
Function:
Runtime: nodejs8.10
Outputs:
ApiURL:
Description: "OUR API URL"
Value: !Sub "https://${GreetingsApiGateway}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
I am not very sure if I completely understood what you want, but here's a Cloudformation template to create api-gateway resources with & without authorization enabled. I am using Cognito User Pool authorization method but it can be just as easily be a custom authorizer.
RestAPI:
Type: AWS::ApiGateway::RestApi
DeletionPolicy: Delete
Properties:
Name: {"Ref": "AWS::StackName"}
ApiKeySourceType: HEADER
EndpointConfiguration:
Types:
- EDGE
ApiAuthorizer:
Type: AWS::ApiGateway::Authorizer
DeletionPolicy: Retain
DependsOn: UserPoolList
Properties:
Name: !Join ["-", [{"Ref": "AWS::StackName"}, "authorizer"]]
RestApiId: !Ref RestAPI
Type: COGNITO_USER_POOLS
AuthType: cognito_user_pools
IdentitySource: "method.request.header.Authorization"
ProviderARNs: <User Pool ARN>
ResourceSignin:
Type: AWS::ApiGateway::Resource
DeletionPolicy: Delete
Properties:
RestApiId: !Ref RestAPI
ParentId: !GetAtt RestAPI.RootResourceId
PathPart: "signin"
SigninPostMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestAPI
ResourceId: !Ref ResourceSignin
HttpMethod: POST
AuthorizationType: NONE
ApiKeyRequired: <true/false>
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UserHandlerFunction.Arn}/invocations
Credentials: !GetAtt GenApiGatewayRole.Arn
ResourceUserCreate:
Type: AWS::ApiGateway::Resource
DeletionPolicy: Delete
Properties:
RestApiId: !Ref RestAPI
ParentId: !GetAtt RestAPI.RootResourceId
PathPart: "create"
CreatePostMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestAPI
ResourceId: !Ref ResourceUserCreate
HttpMethod: POST
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId: !Ref ApiAuthorizer
ApiKeyRequired: <true/false>
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UserHandlerFunction.Arn}/invocations
Credentials: !GetAtt UserApiGatewayRole.Arn
Here resource signin has a POST method with no authorization while create resource has a POST method with authorization enabled.
If you are planning to use API keys this might be the only way possible. I could not get API keys to work with SAM (I believe API keys with SAM is not yet supported - this was about a month back, but you can double check).
We can achieve this via swagger within API Gateway:
The World lambda is a public API and the Hello lambda resides behind the AuthLambda authorizer
OurApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Auth:
Authorizers:
MyAuthorizer:
FunctionPayloadType: REQUEST
FunctionArn: !GetAtt AuthLambda.Arn
DefinitionBody:
swagger: 2.0
basePath: /prod
info:
title: AwsSamExample
x-amazon-apigateway-policy:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: "*"
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
schemes:
- https
paths:
"/hello":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloLambda.Arn}/invocations
responses: {}
security:
- MyAuthorizer: []
"/world":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WorldLambda.Arn}/invocations
responses: {}
security: []
Use the Serverless framework and easily manage this with just an attribute.
https://www.npmjs.com/package/serverless