Reza Amya
Reza Amya

Reputation: 1724

How set Proxy on AWS API Gateway using Cloudformation

I have a lambda function that will handle PUT and GET requests using Amazon API Gateway {proxy+}. It is working correctly when all the settings are set manually by the Amazon Console. but I want to automate it using AWS Cloudformation.

To inform you, I will write steps to set {proxy+}:

1) create a simple Lambda function and paste this lines of code inside it:

import boto3

def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": 'text/html',
            "Access-Control-Allow-Origin": "*"
        },
        "body": "Hello Reza Amya, Your Lambda is working..!"
    }

2) goto Amazon API Gateway and click on Create API.

3) choose New API, fill API name, select Edge optimized from the list for Endpoint Type then click on Create API

4) then your API is created and you should be on it's Resources page, if you are not, go to the Resources page for the created API.

5) from Actions select Create Resource

6) Select Configure as proxy resource (then it should change other fields automatically, if it doesn't, type proxy for Resource Name and {proxy+} for Resource Path) then click on Create Resource

7) Select Lambda Function Proxy for Integration type and select your lambda function from Lambda Function and click on Save

8) on the Add Permission to Lambda Function popup, click on Ok

9) from Actions click on Deploy API

10) Select New Stage from the list for Deployment stage then type a name for Stage name (for me, I have typed 'api') and click on Deploy

11) on the stage on the root page for your deployed API, you can see Invoke URL. click on it, and it will open new tab linked to somewhere like this: https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/api/

12) add a simple segment to end of your URL like this: https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/api/test

now you should see bellow message in your browser page:

Hello Reza Amya, Your Lambda is working..!

Now the problem is I have written all these steps inside a Yaml file:

AWSTemplateFormatVersion: 2010-09-09
Description: My Lambda Function
Parameters:
  S3Bucket:
    Description: S3 Bucket where the Lambda code is
    Type: String
  S3Key:
    Description: S3 Key where the Lambda code is
    Type: String
  S3ObjectVersion:
    Description: Version of the S3 Key to use
    Type: String

Resources:
  apiGateway:
    Type: "AWS::ApiGateway::RestApi"
    Properties:
      Name: "my-api"
      Description: "My API"
      EndpointConfiguration:
        Types:
          - EDGE

  Resource: 
    Type: AWS::ApiGateway::Resource
    Properties: 
      RestApiId: 
        Ref: "apiGateway"
      ParentId: 
        Fn::GetAtt: 
          - "apiGateway"
          - "RootResourceId"
      PathPart: "{proxy+}"

  ProxyMethod:
    Type: 'AWS::ApiGateway::Method'
    Properties:
      HttpMethod: ANY
      ResourceId: !Ref Resource
      RestApiId: !Ref apiGateway
      AuthorizationType: NONE
      RequestParameters:
        method.request.path.proxy: true
      Integration:
        CacheKeyParameters:
          - 'method.request.path.proxy'
        RequestParameters:
          integration.request.path.proxy: 'method.request.path.proxy'
        Type: AWS_PROXY
        IntegrationHttpMethod: ANY
        Uri: !Sub
          - arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Arn}/invocations
          - Arn:
              Fn::GetAtt:
               - LambdaFunction
               - Arn
        PassthroughBehavior: WHEN_NO_MATCH
        IntegrationResponses:
          - StatusCode: 200 

  apiGatewayDeployment:
    Type: "AWS::ApiGateway::Deployment"
    DependsOn:
      - "ProxyMethod"
    Properties:
      RestApiId: !Ref "apiGateway"
      StageName: "dev"

  IAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: 'sts:AssumeRole'

      Policies:
        - PolicyName: Logging
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogGroup'
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: 'arn:aws:logs:*:*:*'
        - PolicyName: AccessToDynamoDB
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'dynamodb:CreateTable'
                  - 'dynamodb:DeleteItem'
                  - 'dynamodb:DeleteTable'
                  - 'dynamodb:GetItem'
                  - 'dynamodb:GetRecords'
                  - 'dynamodb:UpdateItem'
                  - 'dynamodb:UpdateTable'
                  - 'dynamodb:PutItem'
                  - 'dynamodb:UpdateTable'
                Resource: 'arn:aws:dynamodb:*:*:*'

  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: {Ref: S3Bucket}
        S3Key: {Ref: S3Key}
        S3ObjectVersion: {Ref: S3ObjectVersion}
      Handler: main.lambda_handler
      MemorySize: 128
      Role: {'Fn::GetAtt': [IAMRole, Arn]}
      Runtime: python3.6
      Timeout: 300

  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt 
        - LambdaFunction
        - Arn
      Action: 'lambda:InvokeFunction'
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/*

Outputs:
  apiGatewayInvokeURL:
    Value: !Sub "https://${apiGateway}.execute-api.${AWS::Region}.amazonaws.com/${apiGateway}"

  lambdaArn:
    Value: !GetAtt "LambdaFunction.Arn"

The above Yaml file will create the Lambda function and will deploy the API, but it will show bellow error when I am trying to test the API:

{"message": "Internal server error"}

Can you please guide me what is wrong and how I can solve the problem?

Upvotes: 2

Views: 3019

Answers (1)

jens walter
jens walter

Reputation: 14029

The issue is related to you IntegrationHttpMethod setting. Although your APIGateway method is ANY, the IntegrationHttpMethod must always be POST for AWS Lambda.

This would lead to the following method declaration.

  ProxyMethod:
    Type: 'AWS::ApiGateway::Method'
    Properties:
      HttpMethod: ANY
      ResourceId: !Ref Resource
      RestApiId: !Ref apiGateway
      AuthorizationType: NONE
      RequestParameters:
        method.request.path.proxy: true
      Integration:
        CacheKeyParameters:
          - 'method.request.path.proxy'
        RequestParameters:
          integration.request.path.proxy: 'method.request.path.proxy'
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub
          - arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Arn}/invocations
          - Arn:
              Fn::GetAtt:
               - LambdaFunction
               - Arn
        PassthroughBehavior: WHEN_NO_MATCH
        IntegrationResponses:
          - StatusCode: 200 

Upvotes: 4

Related Questions