Serverless Frameworkで自動生成されたリソースに対して設定を加えたいとき、「あれ?どうやるんだ?」となり、調べてみました。
また、Serverless Frameworkにおけるリソースの論理名の変換ルール(と罠)についてもついでに触れたりしました。
リソースの自動生成
Serverless Frameworkでは、サーバーレスアプリを開発するために色々な便利な仕組みが組まれていて、そのうちの一つが「リソースの自動生成」です。
例えば、CloudFormationでLambdaファンクションを作成する場合、CloudWatch Logsであったり場合によってはLambda Permissionであったりと、Lambda以外のものも全てテンプレートで定義して作成する必要があります。
それがServerless Frameworkでは、Lambdaなどメインのリソースの定義をするだけでそのような付随するものを勝手に生成してくれます(リソースの種類によりますが)。
疑問
そこで、「この自動生成されたリソースに設定を加えたい場合、どうすればいいのか?」という疑問が生じました。
自動生成に頼らずテンプレートに作成しようとすると名前被りや紐付けなどでエラー出ないかなとか、できれば自動で作成してくれるものは定義しないで追加で上書きしたいな、とか考えていました。
resources.extensions
とりあえずServerless Frameworkのリファレンスを漁っていたら普通にありました。
resources.extensionsのルール
使用できる属性の種類
resources.extensionsで使えるリソース属性の種類は限られているようです。
- Condition
- CreationPolicy
- DeletionPolicy
- DependsOn
- Metadata
- Properties
- UpdatePolicy
- UpdateReplacePolicy
論理名変換
これはresources.extensions
のルールではなくServerless Frameworkのルールですが、Serverless Frameworkが作成する一部のリソースの論理名は命名ルールに沿って定義・変換されます。
例えば、「doSomething
という論理名のLambda::Function
はDoSomethingLambdaFunction
という論理名で作成される」というような命名変換ルールがあります。
functions: doSomething: handler: handler.doSomething
各リソースの種類によって定義ルールが違うのですが、どれも論理名で指定した名前はnormalizedFunctionName
という変数として扱われます。
その上で、Lambdaの場合は「{normalizedFunctionName}LambdaFunction」というような命名ルールになります。
上記の記載例だと、normalizedFunctionName=doSomething
です。
また、normalizedFunctionName変換には、以下の特殊な変換ルールがあります。
- 頭文字は大文字に置き換わる
-
,_
は以下の文字に置き換わる-
->Dash
_
->Underscore
つまり、以下のような論理名に変換されます。
doSomething
->DoSomethingLambdaFunction
do-Something
->DoDashSomethingLambdaFunction
do_Something
->DoUnderscoreSomethingLambdaFunction
細かい変換ルールの詳細は公式リファレンスをご覧下さい。
使用できるテンプレートのブロックの種類
resources.extensions
は、Resources
ブロックでのみ使用可能と記載されてありました。
resources.extensionsの具体例
例えばリファレンスにあった例ですが、Lambdaを作成したときに自動作成されたCloudWatchロググループに対して有効期限をつけるような拡張をする場合、以下のようになります。
※functions
ブロックで作成したLambdaには自動でロググループが作成されます。
functions: write-post: handler: handler.writePost events: - httpApi: 'POST /api/posts/new' resources: extensions: WriteDashPostLogGroup: Properties: RetentionInDays: '30'
CloudWatch Logsのロググループにおける命名ルールは、「{normalizedFunctionName}LogGroup」になります。
つまり、write-post
というLambdaに付随して作成されたロググループは、上記変換ルールに沿ってWriteDashPostLogGroup
という論理名で生成されるため、そのリソースをresources.extensions
によって上書き設定します。
このようにして、自動作成されたロググループに、RetentionInDays: '30'
という設定を後から追加することができるようになります。
ところが・・・
エラー発生
Error: The CloudFormation template is invalid: Template format error: [/Resources/WriteDashPostLogGroup] Every Resources object must contain a Type member.
あれ・・・?上記リファレンス通りに実行するとエラーになるぞ・・・?
よく上記「論理名変換」の、normalizedFunctionName変換ルールを読み直してみます。
- 頭文字は大文字に置き換わる
-
,_
は以下の文字に置き換わる-
->Dash
_
->Underscore
あくまで頭文字を大文字にしてくれるだけであって、-
,_
の後の文字も大文字にしてくれるわけではないのです。
つまり、ケバブケースやスネークケースを完全にキャメルケースに変換してくれるわけではないのです。
実際は
write-post
というLambdaに付随して作成されたロググループは、上記変換ルールに沿ってWriteDashPostLogGroup
という論理名で生成されるため、そのリソースをresources.extensions
によって上書き設定します。
このように先ほど記載しましたが、write-post
というLambdaの論理リソース名はWriteDashpost
になり、自動生成されるロググループ名はWriteDashPostLogGroup
ではなく、WriteDashpostLogGroup
が正しいわけですね(post
が小文字)。
リファレンスによくある「実行するとエラーになる」例でした。
ちょっとわかりづらくなるので、そもそも論理名はキャメルケースで統一して作成する開発・運用ルールにしておいた方が良さそうです。
最後に
これで自動生成されたリソースに対して、追加で設定を加えることができました。
Serverless Frameworkは結構使っているのですが、リファレンスを調べると実はこんなことができるみたいなことが結構あります。よく調べてから使わないといけませんね。