Setting provisioned concurrency for lambda conditionally using serverless

Viewed 1465

The lambda function is created using serverless(2.22) and is deployed using codebuild (standard5.0). I want to conditionally set the provisioned concurrency only for UAT and Prod env and not for dev and test env.

These are the things I tried:

serverless.yml:

    ...
    alarms:
      - functionErrors
    Conditions:
      isProdOrUAT: {"Fn::Or": [{"${opt:stage}": "prod"}, {"${opt:stage}": "uat"}]} 
functions:
  TestApi:
    handler: test.api.StreamLambdaHandler::handleRequest
    description: Lambda function for the Test API
    timeout: 20
    custom: !If [isProdOrUAT, "${file(./provisionedconcurrency.yml)}", !Ref "AWS::NoValue"]

provisionedconcurrency.yml

provisionedConcurrency: "${ssm:/${opt:stage}/TestsServiceProvisionedConcurrency}"

In this case what happens is, it imports the file successfully, build also passes. But there is no provisioned concurrency attached in the lambda configuration for UAT and Prod.

serverless.yml

 ...
    alarms:
      - functionErrors
    Conditions:
      isProdOrUAT: {"Fn::Or": [{"${opt:stage}": "prod"}, {"${opt:stage}": "uat"}]} 
functions:
  TestApi:
    handler: test.api.StreamLambdaHandler::handleRequest
    description: Lambda function for the Test API
    timeout: 20
    provisionedConcurrency: !If [isProdOrUAT, "${ssm:/${opt:stage}/TestsServiceProvisionedConcurrency}", !Ref "AWS::NoValue"]

In this case getting the following error

Error --------------------------------------------------
  Error: The CloudFormation template is invalid: [/Resources/TestApiProvConcLambdaAlias/Type/ProvisionedConcurrencyConfig/ProvisionedConcurrentExecutions] 'null' values are not allowed in templates 

Checked the serverless-state.json, found this to be there.

    "Name": "provisioned",
      "ProvisionedConcurrencyConfig": {
        "ProvisionedConcurrentExecutions": null
      }
3 Answers

Sls added support for stripping null properties about a year ago so now it's pretty strait-forward:

custom:
  stages:
    dev:
    # no need to set value for provisionedConcurrency
    uat:
      provisionedConcurrency: 5
    prod:
      provisionedConcurrency: 5

functions:    
    TestApi:
        handler: test.api.StreamLambdaHandler::handleRequest
        description: Lambda function for the Test API
        timeout: 20
        provisionedConcurrency: ${self:custom.stages.${self:provider.stage}.provisionedConcurrency, null}

Just try to keep it simple, without additional yml configs and complicated if-statement and take benefit of Conditional variable:

custom:
    provisionedConcurrency:
        prod: 5
        uat: 5
        default: 0
        
functions:    
    TestApi:
        handler: test.api.StreamLambdaHandler::handleRequest
        description: Lambda function for the Test API
        timeout: 20
        provisionedConcurrency: ${self:custom.provisionedConcurrency.${self:provider.stage}, self:custom.provisionedConcurrency.default}

I had the same question. Nowadays you can do this (YAML example)

Parameters:
 ProvisionedConcurrentExecutions:
    Description: The amount of provisioned concurrency to allocate for the function, use 0 to ignore setting
    Type: Number
    MinValue: 0
    MaxValue: 10
    Default: 0

Conditions:
  UseProvisionedConcurrentExecutions: !Not [!Equals [!Ref ProvisionedConcurrentExecutions, 0]]


Resources:
  LambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
          ProvisionedConcurrencyConfig: !If
           - UseProvisionedConcurrentExecutions
           - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrentExecutions
           - !Ref 'AWS::NoValue'here
Related