How can I put condition in serverless.yml file?

Viewed 10280

I am using serverless framework to deploy api gateway. I don't want to apply VPC to every stage in serverless. Is there a way for me to add below configuration based on stage name?

provider:
  name: aws
  runtime: nodejs12.x

...

endpointType: PRIVATE
vpcEndpointIds:
    -  'Fn::ImportValue': 'api-gateway-endpoint'
resourcePolicy:
    - Effect: Deny
...
3 Answers

There are a few ways to do conditional deployments in serverless.yml, some are more brittle than others and there are pros/cons to each but here is a list of methods I have collected:

Variables to control conditionals

NOTE: We use a custom regex variable syntax to separate Serverless variables from cloudformation variable syntax. All of the following snippets use this modified syntax:

provider:
  name: aws
  # Changes serverless variable to ${{}} double curly braces
  variableSyntax: "\\${{([ ~:a-zA-Z0-9._\\'\",\\-\\/\\(\\)]+?)}}"

Serverless Custom Variables

custom:
  scheduleEnabled:
    dev: true
    stage: true
    prod: true

# Then In lambda function declaration
    events:
      - schedule:
          name: MyScheduleName
          description: SomeDescription
          rate: cron(0/5 * * * ? *)
          # Use custom variable and the serverless `stage` supplied 
          # via your deployment command to choose whether this feature is enabled
          enabled: ${self:custom.scheduleEnabled.${self:custom.stage}} 
          input: {"_keepwarm": true}

Using Conditional Operations

Setting up CloudFormation Conditionals

resources:
  - Conditions:
      # True if they are equal ==
      MyCondition: [!Equals ["${{env:SOMETHING}}","SOME_STRING"]]
      # True if they are not equal !=
      MyConditionTwo: !Not [!Equals ["${{env:SOMETHING_ELSE}}","SOME_OTHER_STRING"]]
      # Using a custom serverless variable
      IsProd: [!Equals ["${{self:provider.stage}}","prod"]]

Using Cloudformation Conditionals

If your conditional has two options, you can write it as:

# If true choose X, if False choose Y
Source:
  Type: !If
    - MyCondition # Conditional Name
    - GITHUB # If condition is true
    - GITHUB_ENTERPRISE # if condition is false

If your conditional is something you want to toggle ON or OFF, you can write it as:

# If True do nothing, If False choose X
MyCodebuildSetup:
  Type: "AWS::CodeBuild::Project"
  VpcConfig: !If
    - MyCondition # Condition Name
    - !Ref AWS::NoValue # If True, AWS will not attach a VPC Config
    - VpcId: <My_VPC_ID> # If False, Use this VPC Config
      Subnets:
        - <my_VPC_Subnet>
      SecurityGroupIds:
        - <my_VPC_security_group

Weird Conditional Hacks (document usage well if you decide to implement them)

Conditionally deploying a large series of resources

Using Serverless to selectively deploy entire files of resources using strings set via environment variables.

resources:
  - ${{file(some_dir/conditional_file_${{env:MY_CONDITION}}.yml)}}

This is also a way to toggle deployment of many resources via conditionals.

conditional_file_A.yaml can contain all the resources you toggle deployment of, while conditional_file_B.yaml can contain an empty Resources list (If you don't want a "file not found" warning from serverless:

Resources:

Using Serverless Variables in buildspec.yml files or .json files that will be sent to cloudformation (parameter store files, etc)

If you want to conditionally modify a .json or .yml file that will be sent deployed as a value for some CloudFormation key (such as deploying a .json file for parameter store, or submitting buildspec.yml files for CodePipeline), you can't actually use ${} serverless syntax in these external files. This is because serverless gets confused with it's own method of calling Key/Values from external files.

In order to use Serverless Variables in these files, you can make them .txt extensions as long as the actual file is formatted as proper .json or .yml

Source:
  Type: CODEPIPELINE
  # This file can't have any serverless variables in it
  BuildSpec: ${{file(my_buildspec_file.yml)}}
Source:
  Type: CODEPIPELINE
  # This file CAN have serverless variables because it is 
  # interpreted by serverless as a .txt, the variables
  # are resolved and then it is sent to cloudformation as a string anyway
  BuildSpec: ${{file(my_buildspec_file_yaml_formatted.txt)}}

You may check the following answers: Conditional serverless.yml based on stage?

Therefore, you get something like:

resources:
    Conditions:
        IsProd:
          Fn::Equals:
            - ${opt:stage}
            - prod  

    Resources:
        SomeIAMRole:
            Type: AWS::IAM::Role
            Condition: IsProd
            Properties:
                etc
                etc

Alternatively, there is a Serverless Plugin Ifelse

Add the plugin:

plugins:  
  - serverless-plugin-ifelse

and then add your conditions:

custom:
.....
....

  serverlessIfElse:
    - If: '"${opt:stage}" == "production"'    
      Set:
        functions.helloWorld.cors: true

see more info Serveless plugin if-else

Related