Pass lambda function name to uri in swagger file

Viewed 439

I am creating an api gateway with cloudformation. Actually I am using a swagger.yaml which is uploaded in s3 as body. I want to keep the swagger.yaml parameterized, but I can't pass the arn of my lambda function to the file. I have tried some solutions but nothing seems to work for me. I hope anyone can help me here.

Api GW:

AWSTemplateFormatVersion: 2010-09-09
Description: API

Parameters:
  application:
    Type: String
    Default: test
  apiGatewayName:
    Type: String
    Default: hub
  apiGatewayStageName:
    Type: String
    AllowedPattern: "[a-z0-9]+"
    Default: dev
  apiGatewayHTTPMethod:
    Type: String
    Default: GET
  lambdaFunctionName:
    Type: String
    AllowedPattern: "[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+"
    Default: crawler

############################ REST API ############################

Resources:
  apiGateway:
    Type: AWS::ApiGateway::RestApi
    Properties: 
      EndpointConfiguration:
        Types:
          - REGIONAL
      BodyS3Location:
        Bucket: !Sub ${application}-${apiGatewayStageName}-${AWS::AccountId}
        Key: api_swagger.yml
      Name: !Ref apiGatewayName
      Tags: 
      -
        Key: Project
        Value: test

  apiGatewayDeployment:
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId: !Ref apiGateway
      StageName: !Ref apiGatewayStageName   
      Tags: 
      -
        Key: Project
        Value: test

############################ usagePlan ############################

  usagePlan:
    Type: 'AWS::ApiGateway::UsagePlan'
    DependsOn:
      apiKey
    Properties:
      ApiStages:
        - ApiId: !Ref apiGateway 
          Stage: !Ref apiGatewayStageName
      Description: test usage plan
      Quota:
        Limit: 1000
        Period: MONTH
      Throttle:
        BurstLimit: 200
        RateLimit: 100
      UsagePlanName: ${application}-usageplan
      Tags: 
      -
        Key: Project
        Value: test

  usagePlanKey:
    Type: 'AWS::ApiGateway::UsagePlanKey'
    DependsOn:
      usagePlan
    Properties:
      KeyId: !Ref apiKey
      KeyType: API_KEY
      UsagePlanId: !Ref usagePlan
      Tags: 
      -
        Key: Project
        Value: test

############################ apiKey ############################
 
  apiKey:
    Type: AWS::ApiGateway::ApiKey
    DependsOn:
      - apiGatewayDeployment
      - apiGateway
    Properties: 
      CustomerId: String
      Description: ApiKey for ${application}-api
      Enabled: True
      Name: ${application}-apikey
      StageKeys: 
        - RestApiId: !Ref apiGateway 
          StageName: !Ref apiGatewayStageName
      Tags: 
      -
        Key: Project
        Value: test
    
    
############################ apiGatewayRootMethod ############################

  lambdaRootMethodInvoke:
    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}:${apiGateway}/*/POST/
      Tags: 
      -
        Key: Project
        Value: test

############################ applicationRuleBufferZoneMethod ############################

  lambdaBufferZoneInvoke:
    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}:${apiGateway}/*/${apiGatewayHTTPMethod}/application/rule/bufferZoneList
      Tags: 
      -
        Key: Project
        Value: test

############################ Lambda Functions ############################

  lambdaFunction:
    Type: AWS::Lambda::Function
    DependsOn:
      - apiGateway
    Properties:
      Layers:
        - arn:aws:lambda:eu-central-1:770693421928:layer:Klayers-python38-boto3:108
      Code:
        S3Bucket: !Sub ${application}-${apiGatewayStageName}-${AWS::AccountId}
        S3Key: crawler.zip
      Description: DynamoDB Crawler
      FunctionName: !Ref lambdaFunctionName
      Handler: crawler.lambda_handler
      MemorySize: 128
      Role: !GetAtt lambdaIAMRole.Arn
      Runtime: python3.8
      Tags: 
      -
        Key: Project
        Value: test

############################ Lambda IAM Role ############################

  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:
                  - dynamodb:DeleteItem
                  - dynamodb:GetItem
                  - dynamodb:PutItem
                  - dynamodb:Query
                  - dynamodb:Scan
                  - dynamodb:UpdateItem
                Effect: Allow
                Resource: "*"
          PolicyName: dynamoDBAccess
        - 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: cloudWatchLogs
      Tags: 
      -
        Key: Project
        Value: test
          
  lambdaLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub /aws/lambda/${lambdaFunctionName}
      RetentionInDays: 90
      Tags: 
      -
        Key: Project
        Value: test

############################ Output ############################

Outputs:
  apiGatewayInvokeURL:
    Value: !Sub https://${apiGateway}.execute-api.${AWS::Region}.amazonaws.com/${apiGatewayStageName}
  BucketUrl:
    Value: !Sub s3://${application}-${apiGatewayStageName}-${AWS::AccountId}/api_swagger.yml

swagger.yaml

openapi: 3.0.1
info:
  title: Label Hub
  termsOfService: http://swagger.io/terms/
  contact:
    email: apiteam@swagger.io
  license:
    name: Apache 2.0
    url: http://www.apache.org/licenses/LICENSE-2.0.html
  version: 1.0.0
externalDocs:
  description: Find out more about Swagger
  url: http://swagger.io
servers:
- url: https://example.labelhub.de/v2
security:
- api_key: []
paths:
  /application/rule/bufferZoneList:
    get:
      tags:
      - application
      summary: Returns list of buffer zones per field object for drift management
      description: Returns a map of status codes to quantities
      operationId: getApplicationRuleDrift
      parameters:
      - name: pName
        in: query
        required: true
        schema:
          type: string
      - name: cCode
        in: query
        required: true
        schema:
          type: string
      - name: cType
        in: query
        required: true
        schema:
          type: string
      - name: nType
        in: query
        schema:
          type: string
      - name: timing
        in: query
        schema:
          type: string
      - name: rate
        in: query
        schema:
          type: string
      responses:
        200:
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/bufferZoneList'
      x-amazon-apigateway-integration:
        type: "aws_proxy"
        httpMethod: "POST"
        uri: 
Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaFunction.Arn}/invocations"
        responses:
          default:
            statusCode: "200"
        passthroughBehavior: "when_no_match"
        contentHandling: "CONVERT_TO_TEXT"
components:
  schemas:
    crop:
      type: object
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
    cropTypeList:
      type: array
      items: 
        $ref: '#/components/schemas/crop'
    bufferZone:
      type: object
      properties:
        bufferZone:
          type: integer
          example: 5
        unit:
          type: string
          example: m
        areaType:
          type: string
          example: WATERBODY_VEGETATED
    bufferZoneList:
      type: array
      items:
        $ref: '#/components/schemas/bufferZone'
    layout:
      required:
      - "name"
      type: "object"
      properties:
        id:
          type: "integer"
          format: "int64"
        name:
          type: "string"
        status:
          type: "string"
          description: "label layout status in the application"
          enum:
          - "available"
          - "pending"
    MODEL444ead:
      type: "object"
      properties:
        file:
          type: "string"
          description: "file to upload"
          format: "binary"
    apiResponse:
      type: "object"
      properties:
        code:
          type: "integer"
          format: "int32"
        type:
          type: "string"
        message:
          type: "string"
    product:
      type: "object"
      properties:
        id:
          type: "integer"
          format: "int64"
        name:
          type: "string"
    MODEL6f7c6f:
      type: "object"
      additionalProperties:
        type: "integer"
        format: "int32"
  securitySchemes:
    api_key:
      type: "apiKey"
      name: "x-api-key"
      in: "header"

Any solutions to pass the uri to my swagger file ?

1 Answers

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/create-reusable-transform-function-snippets-and-add-to-your-template-with-aws-include-transform.html

The documentation includes the follow note:

We don't currently support using shorthand notations for YAML snippets.

Instead of

    uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:crawler/invocations"

use

    uri:
      Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations

!Sub is shorthand notation so won't be supported

Edit:

Issue with swagger.yaml

      x-amazon-apigateway-integration:
        type: "aws_proxy"
        httpMethod: "POST"
        uri: 
Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaFunction.Arn}/invocations"
        responses:
          default:
            statusCode: "200"
        passthroughBehavior: "when_no_match"
        contentHandling: "CONVERT_TO_TEXT"

The above is incorrect YAML due to wrong indentation of the Fn::Sub line. Change this to:

      x-amazon-apigateway-integration:
        type: "aws_proxy"
        httpMethod: "POST"
        uri:
          Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaFunction.Arn}/invocations"
        responses:
          default:
            statusCode: "200"
        passthroughBehavior: "when_no_match"
        contentHandling: "CONVERT_TO_TEXT"

Issue with template.yaml

Resources:
  apiGateway:
    Type: AWS::ApiGateway::RestApi
    Properties: 
      EndpointConfiguration:
        Types:
          - REGIONAL
      BodyS3Location:
        Bucket: !Sub ${application}-${apiGatewayStageName}-${AWS::AccountId}
        Key: api_swagger.yml
      Name: !Ref apiGatewayName
      Tags: 
      -
        Key: Project
        Value: test

should be

Resources:
  apiGateway:
    Type: AWS::ApiGateway::RestApi
    Properties: 
      EndpointConfiguration:
        Types:
          - REGIONAL
      Body:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: !Sub "s3://{application}-${apiGatewayStageName}-${AWS::AccountId}/api_swagger.yml"
      Name: !Ref apiGatewayName
      Tags: 
      -
        Key: Project
        Value: test

As the very first link of my answer mentions, the include transform is required (and was the basis of my original answer!).

After fixing both these issues, I got a circular dependency issue. Since this is outside the scope of your original question and I did not want to spend time debugging more issues, I did not make any more changes, but here are some resources to help you with that:

https://aws.amazon.com/blogs/infrastructure-and-automation/handling-circular-dependency-errors-in-aws-cloudformation/

Work around circular dependency in AWS CloudFormation

Related