ちょっとしたサーバレスアプリケーションを作るとき、AWS SAM は便利です。
AWS SAM のテンプレートは AWS CloudFormation テンプレートの拡張機能なので、リソースのインポートも簡単にできると思ったのですが、ひと手間必要だったのでその手順を紹介したいと思います。
背景
- AWS SAM で Lambda 関数を作成する際、テンプレートに記載しなくても CloudWatch Logs のロググループが作成されます。
- テンプレートに記載しない場合、このロググループは AWS SAM のリソースとして管理されないので、スタックを削除してもロググループが残り続けます。
- AWS SAM の管理対象ではないリソースに対して、変更を加えたい場合は別の手段で行う必要があり、管理面で非効率です。
- CloudFormation のようにリソースをインポートして、関連リソースを管理できるようにしたいですよね。
※ 今回は、CloudWatch Logs を対象にしていますが、リソースのインポート方法自体は他のリソースでも変わらないので、作業手順の参考にしていただければと思います。
サンプルの Lambda 関数を AWS SAM で作成する
最初に AWS SAM で単純な 「Lambda 関数だけ」 を作成します。
sam init \
--runtime python3.9 \
--name blog-test-function \
--app-template hello-world \
--package-type Zip
cd blog-test-function
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
blog-test-function
Sample SAM Template for blog-test-function
Globals:
Function:
Timeout: 3
Resources:
BlogTestFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.9
Architectures:
- x86_64
AutoPublishAlias: dev
- Lambda 関数
- Lambda の動作は今回の件とは関係ないので、コードの中身は何でも構いません。
import json
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": json.dumps({
"message": "hello world",
}),
}
- ビルド & デプロイ
sam build
sam package \
--output-template-file packaged.yaml \
--s3-bucket [アーティファクト用の S3 バケット名]
sam deploy \
--template-file packaged.yaml \
--stack-name blog-sam-import-stack \
--s3-bucket [アーティファクトがある S3 バケット名] \
--capabilities CAPABILITY_NAMED_IAM \
--no-fail-on-empty-changeset
CloudWatch Logs のログは Lambda 関数が実行されないと生成されないので、一度 Lambda 関数を実行しておきます。
Lambda 関数を実行することで CloudWatch のロググループが生成されて、ログストリームができていることが確認できます。
今回デフォルトで生成されたロググループの名前は、/aws/lambda/blog-sam-import-stack-BlogTestFunction-6qrGlr8YueM0
となっていました。
ロググループの名前の構造は、/aws/lambda/<Lambda 関数の名前>
となっています。Lambda のコンソールから確認してみると関数と同じ名前であることが分かります。
SAM テンプレートを使ってインポートしてみる(失敗するパターン)
次に、AWS SAM テンプレートに CloudWatch Logs のロググループをインポートしてみたいと思います。
CloudFormation 単体でリソースをインポートする手順として考えると、SAM テンプレートをそのままインポートすればいいように思います。しかし、この方法ではインポートに失敗します。
試しに先程作成した SAM テンプレート(template.yaml
)をインポートしてみます。
そのまま「Next」をクリックして進めます。
AWS SAM で使った template.yaml
をアップロードします。
ファイルをアップロードすると画像のようにエラーとなってしまいました。
これは、CloudFormation コンソールでは AWS SAM で利用される AWS::Serverless
トランスフォームセクションがサポートされないためです。詳細は下記に記載されていますので、参考にして下さい。
サポート対象の一覧は下記に記載があります。
リソースインポートの手順
AWS SAM のテンプレートをそのまま使ってインポートすることはできないことが分かりました。
AWS SAM のテンプレートにリソースをインポートするには、下記の作業を実施します。
- インポートしたいリソースを追記した CloudFormation テンプレートファイル
template.yml
を用意 - インポートするリソース情報を記載したファイル(リソースインポートファイル)
import.txt
を作成 - 上記2つのファイルを使って、AWS CLI で CloudFormation の「変更セット」を作成
- 作成された「変更セット」を適用してスタックにインポート
公式の情報では次のページが参考になります。(先程掲載したものと同じです)
CloudFormation テンプレートを用意
最初に、インポートしたいリソースを追記したテンプレートを用意します。
すでに AWS SAM テンプレートがありますが、これは CloudFormation のテンプレートを拡張したものなので、純粋な CloudFormation テンプレートとして用意します。
用意するといってもゼロから作成する必要は無く、すでにデプロイ済みのスタックから取得しましょう。CloudFormation のコンソールからコピペします。
このテンプレートに、CloudWatch Logs のリソースを追記したものが下記になります。
25 〜 29 行目に追記しています。
(15 行目の CodeUri
は環境に応じて異なるので伏せ字にしています。)
このファイルを適当な作業フォルダに template.yml
として保存しておきます。
このファイルは、リソースをインポートするために使うものなので、AWS SAM で使用した template.yaml
とは別ファイルとして作成して下さい。
27 行目に記載している通り、インポートするリソースは、CFnテンプレート内で 「DeletionPolicy」属性を指定する必要がある点に注意して下さい。
また、LogGroupName
で指定しているロググループの名前は、インポートしたい既存のグループ名になるようにしておきます。
template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: 'blog-test-function
Sample SAM Template for blog-test-function
'
Globals:
Function:
Timeout: 3
Resources:
BlogTestFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: s3://xxxxx/xxxxxx
Handler: app.lambda_handler
Runtime: python3.9
Architectures:
- x86_64
AutoPublishAlias: dev
Metadata:
SamResourceId: BlogTestFunction
# インポートするリソースを追記
LambdaFuncLogGroup:
Type: AWS::Logs::LogGroup
DeletionPolicy: Retain
Properties:
LogGroupName: !Sub /aws/lambda/${BlogTestFunction}
リソースインポートファイルを用意
次に、下記のような内容でリソースインポートファイルを作成します。
ResourceType
: インポートしたいリソースのリソースタイプ- 今回は
AWS::Logs::LogGroup
- 今回は
LogicalResourceId
: インポートしたいリソースの論理ID- 先程の
template.yml
でLambdaFuncLogGroup
としているのでコレを指定
- 先程の
ResourceIdentifier
: インポートしたいリソースの識別名- インポートしたいリソースである CloudWatch Logs のロググループ名を指定
この内容で、import.txt
というファイル名で 先程のtemplate.yml
と同じ作業ディレクトリに保存します。
import.txt
[
{
"ResourceType": "AWS::Logs::LogGroup",
"LogicalResourceId":
"LambdaFuncLogGroup"
,
"ResourceIdentifier": {
"LogGroupName":"/aws/lambda/blog-sam-import-stack-BlogTestFunction-6qrGlr8YueM0"
}
}
]
ここまでで、作業ディレクトリには以下の 2 ファイルがある状態になっています。
template.yml
import.txt
変更セットの作成
ファイルの準備ができたので、CloudFormation の「変更セット」を作成します。 変更セットの作成では、作成した2つのファイルを利用するので、AWS CLI で行います。 これまでと同じ作業ディレクトリで下記のコマンドを実行します。
--stack-name
にはインポートしたいスタックの名前を指定します。
aws cloudformation create-change-set \
--stack-name blog-sam-import-stack \
--change-set-name import-test-set \
--resources-to-import file://import.txt \
--change-set-type IMPORT \
--template-body file://template.yml \
--capabilities CAPABILITY_IAM
これで変更セットが無事作成できました。
上記の 「import-test-set」 をクリックして確認すると、追加したロググループが表示されていることが分かります。
変更セットを適用してスタックにインポート
最後にこの「変更セット」を実行してロググループをインポートします。
(ロググループは DeletionPolicy: Retain
と指定しているのでリソースは作成されず、指定のスタックにインポートされるという訳です)
そのまま「Execute change set」をクリックして実行します。
スタックの「Event」タブで状態が 「IMPORT_COMPLETE」 になればインポート完了です。
次の画像ように、CloudWatch のロググループのタグ情報も更新されて、CloudFormation(AWS SAM)の管理対象になったことが確認できました。
インポート前は何もタグがありません。
インポート後は3つのタグがセットされています。
AWS SAM からスタックを更新して確認してみる
これでリソースのインポートができたので、AWS SAM を使って対象のロググループの設定を更新してみましょう。
AWS SAM のテンプレートを下記のように更新します。(25 〜 29 行目)
ロググループのリソースを追加していますが、RetentionInDays: 30
も記載している点がポイントです。
デフォルトで作成されたロググループはログの保持期限の設定が無く「無期限」になっています。インポートしたリソースが AWS SAM の管理対象になっていれば、「1ヶ月」に更新されることを期待したものになります。
(インポート用の template.yml
と同様に、LogGroupName
で指定しているロググループの名前をインポートされた既存のグループ名になるようにしておきましょう。)
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
blog-test-function
Sample SAM Template for blog-test-function
Globals:
Function:
Timeout: 3
Resources:
BlogTestFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.9
Architectures:
- x86_64
AutoPublishAlias: dev
# CloudWatch Logs for Lambda
# 明示的にCloudWatch Logsを作成してretentionを指定
LambdaFuncLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${BlogTestFunction}
RetentionInDays: 30
一応、変更前の保持期限を確認してみます。
aws logs describe-log-groups \
--log-group-name-prefix \
/aws/lambda/blog-sam-import-stack-BlogTestFunction-6qrGlr8YueM0 \
|jq '{Retention: .logGroups[].retentionInDays}'
null
で無期限となっていますね。
{
"Retention": null
}
ちなみに、AWS CLI では null
と表示されますが、コンソール上では Never expire
と表示されます。
この状態で、AWS SAM を使ってスタックを更新してから再度、保持期間を確認してみます。
aws logs describe-log-groups \
--log-group-name-prefix \
/aws/lambda/blog-sam-import-stack-BlogTestFunction-6qrGlr8YueM0 \
|jq '{Retention: .logGroups[].retentionInDays}'
正しく「1ヶ月」に変わりました。
{
"Retention": 30
}
以上で、AWS SAM のテンプレートに既存リソースをインポートできたことが確認できました。
最後に
AWS SAM でも簡単にリソースのインポートができるかと思いましたが、意外と手間がかかることが分かりました。
手順を事前に確認しておけば、何かあったときに慌てずに対応できますね。
以上です。