はじめに
Lambda やその実行ログを出力する CloudWatch ロググループ、IAM、API Gateway などを Serverless Framework で管理していたが、V3 がサポートされなくなること、V4 からは有償化されることをきっかけに、CloudFormation(SAM)に移行することとなった。
この時の移行手順についてまとめる。
※基本的には以下を参考にさせていただきました。
https://dev.classmethod.jp/articles/aws-sam-template-import-resources/
SAM テンプレートで作成された import 用のスタックを準備する
Serverless Framework の実体は CloudFormation なので、CloudFormation の import 機能を使用して SAM に移行していく。
まずは SAM テンプレートで作成された、import 先となるスタックを準備する必要がある。(ここでは適当にリソースを作成して後で削除する)
とりあえず以下のようにしてデプロイする。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: import sam tmp stack
Globals:
Function:
Timeout: 3
Resources:
TestFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: lambda-import-tmp
Handler: app.lambda_handler
Runtime: python3.13
Architectures:
- x86_64
version = 0.1
[stg.global.parameters]
stack_name = "charita-import-sam-test"
[stg.build.parameters]
debug = true
cached = true
parallel = true
[stg.validate.parameters]
lint = true
[stg.deploy.parameters]
debug = true
capabilities = "CAPABILITY_NAMED_IAM"
confirm_changeset = true
region = "ap-northeast-1"
s3_bucket = "charita-import-sam-test"
s3_prefix = "stg"
disable_rollback = false
sam build --use-container --config-env stg
sam deploy --config-env stg --profile hogehoge
ちなみに --use-container
オプションを使用すると、指定されたランタイムの Lambda 実行環境を厳密に模倣した Docker コンテナ内でビルドプロセスを実行することができ、以下のような問題に対応できる。(イメージは ECR Public から Pull)
- Python のパッケージの中には、純粋な Python コードだけではなく C言語で書かれた拡張ライブラリ(
NumPy
やPandas
など) を含むものがあり、実際の Lambda 実行環境で正しく動作するために、適切な Linux ベースのコンパイル が必須 - macOS や Windows でコンパイルすると、生成されるバイナリはそれぞれの OS に最適化され、Lambda の実行環境では動かない可能性がある
- 各 OS では標準でインストールされているライブラリやシステムヘッダーが異なるため、同じソースコードでもコンパイル結果が異なり、Lambda で期待通りに動作しない恐れがある
※このオプションを Proxy 接続環境で使用する場合、sam cli
の実行環境からは Proxy に接続できるが、実際にビルドする Docker コンテナからは Proxy に繋がらないというような問題が発生するため、以下記事のように対応する必要がある模様。
少し話は逸れたが、ここで作成されたスタックに対し、Serverless Framework のテンプレートを import していく。
import 用の変更セットを作成する
CloudFormation には import 機能があるため、import 用の変更セットを作成せずとも以下のようにして普通にできそうだが、どうやら SAM のテンプレートには対応していないようだった。
imoport 対応リソースは以下の通り。
そのため、import 用の変更セットを作成し適用という手順を追う必要がある。
ここで準備する必要があるのは以下。(命名規則は特になし)
- 先ほど作成したスタックの
template.yaml
- import したいリソースが定義された
import-template.yaml
- import したいリソースのリソースタイプ、論理名、識別子の対応が定義された
import.txt
import-template.yaml
Serverless Framework で管理されているスタックのテンプレートから、import 対象の Resource
ブロックをコピーし、1の template.yaml
に追記する。
以下のようにする。
(今回は CloudWatch ロググループを import 対象とする)
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: import sam tmp stack
Globals:
Function:
Timeout: 3
Resources:
TestFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: lambda-import-tmp
Handler: app.lambda_handler
Runtime: python3.13
Architectures:
- x86_64
# 以下を追記
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
で指定すべき値だということがわかる。
尚、Lambda を import したい場合、上述の通り SAM のテンプレートには対応していないため、リソースタイプを Type: AWS::Serverless::Function
のようにすることはできない。
そのため、通常の CloudFormation のテンプレートの通り Type: AWS::Lambda::Function
とする。(import 完了後に変更可能)
以上で必要なファイルは準備できたが、まだこのままでは import できない。
これは、CloudFormation は同じリソースを複数のスタックで管理することができないためである。
なので、Serverless Framework で管理しているスタックを削除する必要がある。
この時、当然リソースが削除されてしまっては困るため、全てのリソースに対し DeletionPolicy: Retain
を付与した状態でスタックを削除することで、一度スタックの管理外にしてやる。(以下参考)
スタックを削除する際、実際に DeletionPolicy: Retain
が効いて削除されずに済むのかが確認できないところが心臓に悪いので、本当に DeletionPolicy: Retain
が対象リソースに適用できているかというのは、改めてコンソール上からテンプレートを確認しておくことをお勧めする。
無事にスタックのみ削除し、リソースをスタックの管理外にできたら、AWS CLI コマンドで import 用の変更セットを作成する。
コマンドは以下のようになる。
aws cloudformation create-change-set \
--stack-name charita-import-sam-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 完了となる。
あとは自由に samconfig.toml
をいい感じにしてみたり、Lambda のリソースタイプを Type: AWS::Serverless::Function
から Type: AWS::Lambda::Function
に変えてみたりと、快適な SAM ライフを楽しむだけ。
Lambda を管理する上では非常に便利な SAM だが、Terraform の import より色々考慮事項が多いし、スタックの削除は本当にやりたくないからもっと簡単にしてほしい、、、