こんにちは、SRE の中村です。
弊社では Lambda とその周辺リソースは基本的には Serverless Framework で管理してきましたが、V3 がサポートされなくなること、V4 からは有償化されることをきっかけに、CloudFormation(SAM)に移行することになりました。 Serverless Framework も実体は CloudFormation なので、文法などの違いはあるものの、特に問題なく進められるだろうと思っていましたが、やってみると意外にも面倒な点がありました。 以下にやり方をご紹介します。
SAM テンプレートで作成された import 用のスタックを準備する
CloudFormation にも Terraform と同じように import 機能があります。 なので、まずは SAM のテンプレートで作成された import 先となるスタックを準備して、それに対し import していく方針にします。
とりあえず以下のようにしてデプロイします。 空のテンプレートではスタックを作成できないので、ここでは適当にリソースを作成して後で削除します。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: import to sam stack Resources: TestSQS: Type: AWS::SQS::Queue Properties: QueueName: sam-import-tmp
余談ですが、SAM テンプレートのデプロイ工程に置いて、sam build
コマンドの --use-container
オプションを使用すると、指定されたランタイムの Lambda 実行環境を厳密に模倣した Docker コンテナ内でビルドプロセスを実行することができるのでオススメです。(Docker を起動させておく必要あり)
import 用の変更セットを作成する
最初は import 用の変更セットを作成せずとも以下ドキュメントのようにして普通に import できそうだと思ったのですが、どうやら SAM のテンプレートには対応していないようでした。
スタックへの既存リソースのインポート - AWS CloudFormation
そのため、Lambda を import したい場合でもリソースタイプを Type: AWS::Serverless::Function
のようにすることはできません。
通常の CloudFormation のテンプレートの通り Type: AWS::Lambda::Function
として、import 完了後に変更する形になります。
ちなみに import に対応しているリソースは以下の通りです。
Resource type support - AWS CloudFormation
今回は SAM を使用したいので、import 用の変更セットを作成 -> 適用
という手順を追う必要があります。
ここで準備する必要があるのは以下の3つです。(命名規則は特になし)
- 先ほど作成したスタックの
template.yaml
- import したいリソースが定義された
import-template.yaml
- import したいリソースのリソースタイプ、論理名、識別子の対応が定義された
import.txt
import-template.yaml
Serverless Framework で管理されているスタックのテンプレートから import 対象の Resource
ブロックをコピーして、1の template.yaml
に追加した内容のもの( import-template.yaml
)を準備します。
serverless.yml
は CloudFormation の記法になっていませんが、コンソールからスタックのテンプレートを見ると、serverless.yml
が CloudFormation の記法に変換されているので、ここからコピーすれば OK です。
以下のようにしてみます。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: import to sam stack Resources: TestSQS: Type: AWS::SQS::Queue Properties: QueueName: sam-import-tmp # import 対象リソースを追記 TestLogGroup: Type: AWS::Logs::LogGroup DeletionPolicy: Retain Properties: LogGroupName: /aws/lambda/lambda-import-tmp
この時、import 対象のリソースには必ず DeletionPolicy
を付与する必要があります。( Retain
である必要はなし)
import.txt
import したいリソースのリソースタイプ、論理名、識別子を以下の形式で記述します。
[ { "ResourceType": "AWS::Logs::LogGroup", "LogicalResourceId": "TestLogGroup" , "ResourceIdentifier": { "LogGroupName":"/aws/lambda/lambda-import-tmp" } } ]
この時指定する ResourceIdentifier
は、一度コンソール上からリソースの import を進めてみるとわかります。
例えば以下のようにしてみると、LogGroupName
というのが ResourceIdentifier
で指定すべき値だということがわかります。
以上で必要なファイルは準備できましたが、実はまだこのままでは import できません。
これは、CloudFormation は同じリソースを複数のスタックで管理することができないためです。
なので、Serverless Framework で管理しているスタックを削除する必要があります。
この時、当然リソースが削除されてしまっては困るので、全てのリソースに対し DeletionPolicy: Retain
を付与した状態でスタックを削除することで、一度スタックの管理外にしてあげます。
スタックを丸ごと削除するのは本当に痺れますね。
無事にスタックのみ削除し、リソースをスタックの管理外にできたら、AWS CLI コマンドで import 用の変更セットを作成します。
コマンドは以下のようになります。
aws cloudformation create-change-set \ --stack-name sam-import-test \ --change-set-name import-changeset \ --resources-to-import file://import.txt \ --change-set-type IMPORT \ --template-body file://import-template.yaml \ --capabilities CAPABILITY_NAMED_IAM \ --profile hogehoge
作成された変更セットは以下のようになり、「アクション」が「import」となっていることを確認したら、この変更セットを実行してあげれば無事 import 完了です。
※ここでは上述までで例示したリソースではなく、実際に弊社で管理しているリソースの import 画面のためリソース数などが異なります。
あとは template.yaml
にも imoport-template.yaml
に追記したリソースを定義してあげる、もしくはそのまま imoport-template.yaml
を使用しても良いです。ひとまずこれ以降は SAM でリソースの変更などが可能となります。
imoport-template.yaml
と imoport.txt
はあくまで import 用なので、この時点で削除しても構いません。
尚、初めに SAM 用のスタックを用意するために作成したリソースの削除と、全リソースに DeletionPolicy: Retain
を付与していることを忘れてはいけませんね。
また、複数環境を運用されている場合は、samconfig.toml
を活用して環境ごとのリソース定義ができるようにしたり、Lambda のリソースタイプを Type: AWS::Serverless::Function
から Type: AWS::Lambda::Function
に変えてみたりと、SAM の恩恵を十分に受けられるように多少手を加えて完成です。
Terraform ならもっと簡単なのに、、、と思うところは多々ありましたが、Serverless Framework から SAM への移行は社内で初めての試みだったので、他のプロジェクトでの移行作業はこれでスムーズにいきそうです。
最後に、ユニファで一緒に働く仲間を探しています!