Quantcast
Channel: kakakakakku blog
Viewing all articles
Browse latest Browse all 920

正式リリースになった AWS SAM CLI の Terraform サポート機能を試す

$
0
0

2023年9月5日に AWS SAM CLI の Terraform サポート機能が GA (正式リリース)になった👏

Amazon API Gateway や AWS Lambda 関数などサーバーレス関連のコンポーネントは Terraform で統一的に管理しつつも,AWS SAM CLI の開発支援機能(sam local invokeコマンドや sam local start-apiコマンドでローカルデバッグ)は使いたい❗️という場面はあって非常に便利な組み合わせだと思う.

aws.amazon.com

実際にどういう開発体験なのかを確認するために AWS ブログに載っていたサンプルを試してみる \( 'ω')/

aws.amazon.com

検証環境

今回は macOS 上で SAM CLI 1.97.0(最新)と Terraform 1.5.7(最新)を使う.

$ sam --version
SAM CLI, version 1.97.0

$ terraform version
Terraform v1.5.7
on darwin_arm64

GitHub リポジトリ確認

サンプルコードは GitHub aws-samples/aws-sam-terraform-examplesリポジトリの gaディレクトリにある❗️

github.com

gaディレクトリには3種類のサンプルがあって,今回は Amazon API Gateway の REST API 前提の api_gateway_v1を使う📝

$ tree ga -L1
ga
├── README.md
├── api_gateway_v1
├── api_gateway_v2
└── api_gateway_v2_tf_cloud

4 directories, 1 file

さらに ga/api_gateway_v1ディレクトリ配下のファイルをまとめた.tf-resourcesディレクトリに main.tfなど Terraform コードがあって,srcディレクトリに AWS Lambda 関数コード(今回は Python 実装)があって,eventsディレクトリに AWS Lambda 関数 authの動作確認に使うイベント情報がある👌

$ tree ga/api_gateway_v1
ga/api_gateway_v1
├── events
│   └── auth.json
├── src
│   ├── auth
│   │   ├── app.py
│   │   └── requirements.txt
│   └── responder
│       ├── app.py
│       └── requirements.txt
└── tf-resources
    ├── api.tf
    ├── functions.tf
    ├── main.tf
    ├── samconfig.yaml
    └── variables.tf

6 directories, 10 files

事前準備

サンプルコードを確認したところ,Python 3.9 の AWS Lambda 関数になっていた.せっかくなら最新の Python 3.11 で動作確認をしておきたくて ga/api_gateway_v1/tf-resources/functions.tfを修正した.

--- a/ga/api_gateway_v1/tf-resources/functions.tf+++ b/ga/api_gateway_v1/tf-resources/functions.tf@@ -5,7 +5,7 @@ module "lambda_function_responder" {
   source_path   = "../src/responder/"
   function_name = "responder"
   handler       = "app.open_handler"
-  runtime       = "python3.9"+  runtime       = "python3.11"
   create_sam_metadata = true
   publish       = true
   allowed_triggers = {
@@ -23,6 +23,6 @@ module "lambda_function_auth" {
   source_path   = "../src/auth/"
   function_name = "authorizer"
   handler       = "app.handler"
-  runtime       = "python3.9"+  runtime       = "python3.11"
   create_sam_metadata = true
 }

しかし AWS Provider 側で Python 3.11 がサポートされてなく,sam buildコマンドの実行時に以下のエラーが出てしまう🔥

Error: expected runtime to be one of [nodejs nodejs4.3 nodejs6.10 nodejs8.10 nodejs10.x nodejs12.x nodejs14.x nodejs16.x java8 java8.al2 java11 python2.7 python3.6 python3.7 python3.8 python3.9 dotnetcore1.0 dotnetcore2.0 dotnetcore2.1 dotnetcore3.1 dotnet6 nodejs4.3-edge go1.x ruby2.5 ruby2.7 provided provided.al2 nodejs18.x python3.10 java17], got python3.11

そこで AWS Provider の最新を使えるようにga/api_gateway_v1/tf-resources/main.tfを修正して terraform init -upgradeコマンドを実行しておく.

--- a/ga/api_gateway_v1/tf-resources/main.tf+++ b/ga/api_gateway_v1/tf-resources/main.tf@@ -2,7 +2,7 @@ terraform {
   required_providers {
     aws = {
       source  = "hashicorp/aws"
-      version = "~> 4.16"+      version = "~> 5.17"
     }
   }

準備完了👏

さっそく試す: sam buildsam local invoke

sam buildコマンド

Terraform コードや設定など細かいことは後ほど確認するとして,まずは試してみる❗️

ga/api_gateway_v1/tf-resourcesディレクトリに移動して,Terraform 変数にリージョンを指定して(今回は東京リージョンを使う),あとは使い慣れた sam buildコマンドを実行する.しかし今回は --hook-nameオプションで terraformを指定して実行する点に注意💡

実行すると Terraform の local-exec Provisionerの実行ログが大量に出てきて,不安になる(Ctrl+Cを押したくなる)けどそのまま待ってると Build Succeededという表示が出て完了する.

$ cd ga/api_gateway_v1/tf-resources

$ exportTF_VAR_aws_region=ap-northeast-1

$ sam build --hook-name terraform --terraform-project-root-path ../
Running Prepare Hook to prepare the current application
Executing prepare hook of hook "terraform"(中略)

module.lambda_function_auth.null_resource.archive[0](local-exec):
module.lambda_function_responder.null_resource.archive[0](local-exec):

(中略)

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

sam local invokeコマンド

そして使い慣れた sam local invokeコマンドに --hook-name terraformオプションを指定して実行すると,public.ecr.aws/lambda/python:3.11-rapid-x86_64イメージを使って AWS Lambda 関数を実行できる.今回は responderを実行したら Hello TF Worldって返ってきた \( 'ω')/ うおお〜

$ sam local invoke --hook-name terraform 'module.lambda_function_responder.aws_lambda_function.this[0]'
Skipped prepare hook. Current application is already prepared.
Invoking app.open_handler (python3.11)
Local image was not found.
Removing rapid images for repo public.ecr.aws/sam/emulation-python3.11
Building image.............................................................................................................................................................................................................................................................................................................................................................................................................................
Using local image: public.ecr.aws/lambda/python:3.11-rapid-x86_64.(中略)

START RequestId: 71b6ab8c-53f9-41ac-8fb0-62bf4ba929b4 Version: $LATEST
END RequestId: 71b6ab8c-53f9-41ac-8fb0-62bf4ba929b4
REPORT RequestId: 71b6ab8c-53f9-41ac-8fb0-62bf4ba929b4  Init Duration: 0.74 ms    Duration: 1551.46 ms  Billed Duration: 1552 ms   Memory Size: 128 MB    Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"Hello TF World\", \"location\": \"xxx.xxx.xxx.xxx\"}"}%

せっかくだから ga/api_gateway_v1/src/responder/app.pyを修正して,もう一度 sam buildコマンドと sam local invokeコマンドを実行すると,今度は Hello kakakakakku Worldって返ってきた \( 'ω')/ うおお〜

$ sam build --hook-name terraform --terraform-project-root-path ../

$ sam local invoke --hook-name terraform 'module.lambda_function_responder.aws_lambda_function.this[0]'
Skipped prepare hook. Current application is already prepared.
Invoking app.open_handler (python3.11)
Local image is up-to-date
Using local image: public.ecr.aws/lambda/python:3.11-rapid-x86_64.(中略)

START RequestId: f8ddd88f-bfd0-45c5-8643-6db922e6d798 Version: $LATEST
END RequestId: f8ddd88f-bfd0-45c5-8643-6db922e6d798
REPORT RequestId: f8ddd88f-bfd0-45c5-8643-6db922e6d798  Init Duration: 0.57 ms    Duration: 1201.38 ms  Billed Duration: 1202 ms   Memory Size: 128 MB    Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"Hello kakakakakku World\", \"location\": \"xxx.xxx.xxx.xxx\"}"}%

そして同じように authも実行できた \( 'ω')/ うおお〜

$ sam local invoke --hook-name terraform 'module.lambda_function_auth.aws_lambda_function.this[0]'-e ../events/auth.json
Skipped prepare hook. Current application is already prepared.
Invoking app.handler (python3.11)
Local image is up-to-date
Using local image: public.ecr.aws/lambda/python:3.11-rapid-x86_64.(中略)

START RequestId: c6503ad1-200d-453a-96d9-94c99d0e470a Version: $LATEST
END RequestId: c6503ad1-200d-453a-96d9-94c99d0e470a
REPORT RequestId: c6503ad1-200d-453a-96d9-94c99d0e470a  Init Duration: 0.52 ms    Duration: 269.49 ms   Billed Duration: 270 ms    Memory Size: 128 MB    Max Memory Used: 128 MB
{"principalId": "user|a1b2c3d4", "policyDocument": {"Version": "2012-10-17", "Statement": [{"Action": "execute-api:Invoke", "Effect": "Allow", "Resource": ["arn:aws:execute-api:us-east-1:123456789012:1234567890/prod/*/*"]}]}, "context": {"number": 1, "bool": true, "passed": "from authorizer"}}%

また sam local invokeコマンドの引数に実行する AWS Lambda 関数の Terraform リソース ID を指定する必要があるけど,指定なしで実行すると候補を表示してくれるようになってて親切設計だった👏

$ sam local invoke --hook-name terraform

Skipped prepare hook. Current application is already prepared.
Error: You must provide a function logical ID when there are more than one functionsin your template. Possible options in your template: ['module.lambda_function_auth.aws_lambda_function.this[0]', 'module.lambda_function_responder.aws_lambda_function.this[0]']

ちなみに Terraform と AWS SAM CLI の連携に関しては以下のドキュメントに詳しく載っている📝 現時点だと最新情報は日本語に翻訳されてなく,まだ GA 前のドキュメントになっているため,英語を確認する必要がある.

docs.aws.amazon.com

samconfig ファイル

AWS SAM CLI でよく使う samconfigにも対応していて,例えば以下のように samconfig.tomlを設定できる.

version = 0.1
[default]
[default.global]
[default.global.parameters]
hook_name = "terraform"
skip_prepare_infra = true
[default.build]
[default.build.parameters]
terraform_project_root_path = "../"

すると sam buildコマンドと sam local invokeコマンドのオプションを減らせてシンプルに実行できるようになる👏

$ sam build
$ sam local invoke 'module.lambda_function_responder.aws_lambda_function.this[0]'

ちなみに2023年7月頃から AWS SAM CLI で YAML 形式の samconfig もサポートされててサンプルには samconfig.yamlが含まれた.

version:0.1default:global:parameters:hook_name: terraform
      skip_prepare_infra:truebuild:parameters:terraform_project_root_path: ../

さっそく試す: sam local start-api

今度は sam local start-apiコマンドを実行して API の動作確認をする❗️

$ sam local start-api --hook-name terraform
Skipped prepare hook. Current application is already prepared.

AWS SAM CLI does not guarantee 100% fidelity between authorizers locally
and authorizers deployed on AWS. Any application critical behavior should
be validated thoroughly before deploying to production.

Testing application behaviour against authorizers deployed on AWS can be done using the sam sync command.

Mounting responder at http://127.0.0.1:3000/open [GET]
Mounting responder at http://127.0.0.1:3000/secure [GET](中略)

2023-09-19 22:00:00 Press CTRL+C to quit

すぐに localhost:3000/open API と /secure API を呼び出せた \( 'ω')/ うおお〜

ちなみに /secure API は Amazon API Gateway の Lambda オーソライザーを使ったアクセス制御になっているため,HTTP ヘッダーに myheader: 123456789を設定した場合にレスポンスが返るように実装されている.

$ curl http://localhost:3000/open
{"message": "Hello kakakakakku World", "location": "xxx.xxx.xxx.xxx"}

$ curl http://localhost:3000/secure
{"message":"Unauthorized"}

$ curl http://localhost:3000/secure --header'myheader: 123456789'{"message": "Hello kakakakakku World", "location": "xxx.xxx.xxx.xxx"}

$ curl http://localhost:3000/secure --header'myheader: IamInvalid'{"message":"Unauthorized"}

Terraform コード

うまく実行できたので,次は Terraform コードを確認する❗️

module"lambda_function_responder"{source        = "terraform-aws-modules/lambda/aws"version       = "~> 6.0"timeout       = 300source_path   = "../src/responder/"function_name = "responder"handler       = "app.open_handler"runtime       = "python3.11"create_sam_metadata = truepublish       = trueallowed_triggers = {APIGatewayAny = {service    = "apigateway"source_arn = "${aws_api_gateway_rest_api.api.execution_arn}/*/*"}}}module"lambda_function_auth"{source        = "terraform-aws-modules/lambda/aws"version       = "~> 6.0"timeout       = 300source_path   = "../src/auth/"function_name = "authorizer"handler       = "app.handler"runtime       = "python3.11"create_sam_metadata = true}

まず functions.tfを確認すると,AWS Provider の aws_lambda_functionではなく AWS Lambda Terraform moduleを使っていた.AWS ブログにも書いてある通り,あくまでこれは Terraform コードをシンプルに書くために使われていて,もちろん AWS SAM CLI の Terraform サポートは AWS Provider でも使える👌

github.com

そして AWS Lambda Terraform module を使うことでシンプルに書ける例として create_sam_metadata = trueがあって,これは Terraform の null_resourceを使って sam metadataリソースを自動的に作ってくれる.sam metadataは AWS Lambda 関数のアーティファクトがどこにあるかを伝えるために必要で,さらに AWS Lambda 関数の ZIP ファイルを作るビルド処理にも依存している.sam metadataに関しては以下のドキュメントに載っている❗️

docs.aws.amazon.com

また AWS Lambda Terraform module の main.tfにある null_resource.sam_metadata_aws_lambda_functionpackage.tfにある null_resource.archiveを読むと Terraform の local-exec Provisionerを使ってビルドをしてる流れ(sam buildコマンドを実行するとログが大量に出てくる部分)など理解できるからおすすめ〜👀

以下のドキュメントには AWS Provider の aws_lambda_functionを使ったサンプルも載っていた💡

docs.aws.amazon.com

まとめ

今月 GA になった AWS SAM CLI の Terraform サポート機能をさっそく試してみたけど良かった❗️

さっそく今の現場で実戦投入してみようと思う \( 'ω')/


Viewing all articles
Browse latest Browse all 920