Serverless Framework(以後sls)を利用したLambdaを作成する際に、AWSアカウント毎にデプロイするLambdaをコントロールしたい事があり、方法を探していたところ serverless-plugin-ifelse というプラグインが便利だったのでそのプラグインの紹介です。

そもそも何故こんなものが必要なのか?

AWSでslsを使った場合、作成されるリソースはCloudFormationテンプレートにマッピングされCloudFormationのStackが作成されます。
CloudFormationでは各リソースにCondition属性が用意されており、 Conditionは値がtrueの場合のみリソース作成する ので環境毎に作成するリソースをコントロールする際はCondition属性を使って行います。
先日slsを使って複数のアカウントにLambdaをデプロイするユースケースで、本番アカウントのみへデプロイしたいLambdaと全てのアカウントにデプロイが必要なLambdaが、1つのテンプレートに混在している状態が起こってしまったため、Conditionに当たる属性値を探していたのですが、slsのテンプレートではLambdaを定義するfunctionsセクションでCondition属性やその代わりになるような属性が定義されていませんでした。
slsのテンプレートを分割したり、resourcesセクションにLambdaを定義すれば可能だと思いますが、もっとスマートな解決策を探していた所 serverless-plugin-ifelse を見つけました。

serverless-plugin-ifelseのサンプル

特定のステージのみへデプロイされるLambdaを定義する

例えば1つのテンプレートにLambda Functionが2つ記述されており、「開発環境では片方のLambdaだけをデプロイしたい」などといった場合、以下のように記述する事で環境分けを行う事ができます。
サンプルではstage属性を使って環境を決定するケースを想定しています。

service: serverless_ifelse_sample
provider:
  name: aws
  runtime: python3.6
  memorySize: 128
  timeout: 60
  stage: ${opt:stage, self:custom.defaultStage}
  profile: ${opt:profile, self:custom.defaultProfile}
  region: ${opt:region, self:custom.defaultRegion}

custom:
  defaultStage: dev
  defaultProfile: default
  defaultRegion: ap-northeast-1

  # serverless-plugin-ifelse configration
  # provider.stage = prod の時以外はfunc2をデプロイしない
  serverlessIfElse:
  - If: '"${self:provider.stage}" != "prod"'
      Exclude:
        - functions.func2

plugins:
  - serverless-plugin-ifelse

functions:
  func1:
    handler: index.lambda_handler1
    events:
      - schedule: cron(0 2 1 * ? *)
  func2:
    handler: index.lambda_handler2
    events:
      - schedule: cron(0 2 1 * ? *)

ステージ毎の属性値を変更する

このプラグインではcustomセクションにserverlessIfElse属性を定義し、中で条件文を書く事で特定の条件の場合のみ動作する振る舞いを定義する事ができます。今回はデプロイされるLambdaのコントロールに利用しましたが、特定の場合のみtimeoutを長くするなど、属性値の上書きも可能となっています。

# provider.stage = prod の時のみtimeoutを90秒にする
custom:
  defaultStage: dev
  defaultProfile: default
  defaultRegion: ap-northeast-1
  serverlessIfElse:
  - If: '"${self:provider.stage}" != "prod"'
      Set:
        provider.timeout: 90

ただし、単純に属性値の条件分岐を行いたいだけであればslsの構文をうまく利用する事で記述する事もできるので、個人的には利用箇所は必要最低限にし、なるべくslsの記述方法へ寄せた方が良いと思っています。

# slsの表現で書いた属性値の条件分岐
service: serverless_ifelse_sample
provider:
  name: aws
  runtime: python3.6
  memorySize: 128
  timeout: ${self:custom.${self:provider.stage}.timeout} # provider.stage = prodの場合は90秒、dev(デフォルト値)は60秒
  stage: ${opt:stage, self:custom.defaultStage}
  profile: ${opt:profile, self:custom.defaultProfile}
  region: ${opt:region, self:custom.defaultRegion}

custom:
  defaultStage: dev
  defaultProfile: default
  defaultRegion: ap-northeast-1
  prod:
    timeout: 90
  dev:
    timeout: 60

まとめ

serverless-plugin-ifelseを使って特定の場合のみデプロイされるLambda Functionを作成する事ができました。今回の紹介で利用した構文はExcludeやSetでしたが、他にもいくつか細かく設定を行うための構文があるので気になった方は 本家リポジトリのREADME を参照してください。
pluginの利用は利便性が増す分、仕組みが複雑になるので用法用量は正しく守って利用しましょう。

冒頭で少し紹介したCloudFormationのConditionはslsのresourcesセクションでは使えると思うので、詳しく知りたい方は AWSのドキュメント 条件 - AWS CloudFormationを参照してください。