What's?
前にこんな記事を書いたのですが、環境変数がふつうに見えてしまうことに困っていました。
Terraformで、AWS Lambda関数を登録して動かしてみる
「転送時の暗号化に使用するヘルパー」という話もあるのですが、そういえばと思い、設定をAWS Systems Managerのパラメータストアから取得する方法も試してみようかなと。
AWS Systems Manager パラメータストアを使う場合は、AWS Lambdaの環境変数とは別に設定することになります。
Lambda 関数のグループに共通の環境変数を設定できますか?
というわけで、こんなことをやってみます。
- AWS Lambda関数は、Terraformでzip圧縮してアップロードする
- AWS Lambda関数からログを出力する
- Amazon CloudWatch Logsへ出力するようにロググループを作成する
- AWS Lambda関数から、AWS Systems Manager パラメータストアを使って設定を読み込む
- AWS Systems Manager パラメーターストアに格納する値は、AWS KMSのCMKで暗号化する
- 動作確認はAWS CLIで行う
前に書いた記事の内容から、AWS Systems Manager パラメータストアを使用するように変更しただけですね。
AWS Lambda関数から、AWS Systems Manager パラメータストアを使う時の注意点
通常は、AWS Lambda関数からAWS Systems Manager パラメータストアにアクセスするには、権限を追加する以外の作業は不要です。
これが、VPC内で動作するAWS Lambda関数の場合は、話が変わってきます。
VPC の Lambda 関数で Systems Manager パラメータストアにアクセスするにはどうしたらよいですか?
この場合は、AWS Lambda関数にインターネットアクセスが可能なように設定、またはVPCエンドポイントを使用する必要があります。
環境
今回の環境は、こちらです。
$ terraform version
Terraform v0.12.29
+ provider.archive v1.3.0
+ provider.aws v2.70.0
AWSのクレデンシャルは、環境変数で設定します。
$ export AWS_ACCESS_KEY_ID=...
$ export AWS_SECRET_ACCESS_KEY=...
$ export AWS_DEFAULT_REGION=ap-northeast-1
AWS Lambda関数を作成する
作成するAWS Lambda関数も、前回とほぼ同じ。リクエストで受け取った内容から、メッセージを作って返すものにします。
ここで、メッセージの一部を前回は環境変数から受け取っていたのですが、ここをAWS Systems Manager パラメータストア経由にします。
ソースコードは、こちら。
app/lambda.py
import boto3
import logging
logger = logging.getLogger('lambda_logger')
logger.setLevel(logging.INFO)
client = boto3.client('ssm')
base_message_parameter = client.get_parameter(
Name = '/App/Lambda/Config/BASE_MESSAGE',
WithDecryption = True
)
base_message = base_message_parameter['Parameter']['Value']
def handler(event, context):
logger.info('function = %s, version = %s, request_id = %s', context.function_name, context.function_version, context.aws_request_id)
logger.info('event = %s', event)
last_name = event['last_name']
first_name = event['first_name']
return { 'message': f'{base_message}, {first_name} {last_name}!!' }
AWS LambdaのPythonランタイムには、Boto3が含まれているため、そのまま使えます。
import boto3
今回は、起動時に値を取得してしまうことにしました。
client = boto3.client('ssm')
base_message_parameter = client.get_parameter(
Name = '/App/Lambda/Config/BASE_MESSAGE',
WithDecryption = True
)
base_message = base_message_parameter['Parameter']['Value']
今回はAWS Systems Manager パラメータストアから単一の値を取得していますが、ある程度一括で取得することも可能です。
Terraformの構成ファイルを書く
基本的には、前の記事と同じです。先に、ポイントだけ絞って書いていきます。
ソースコード全体は、最後に載せることにします。
AWS Lambda関数のリソース定義は、こんな感じに。環境変数(environment
)の定義はありません。
resource "aws_lambda_function" "function" {
function_name = local.function_name
handler = "lambda.handler"
role = aws_iam_role.lambda_role.arn
runtime = "python3.8"
filename = data.archive_file.function_source.output_path
source_code_hash = data.archive_file.function_source.output_base64sha256
depends_on = [aws_iam_role_policy_attachment.lambda_policy, aws_cloudwatch_log_group.lambda_log_group]
}
AWS KMSキーおよびエイリアスを作成すると共に、AWS Systems Manager パラメータストアに設定を追加します。
resource "aws_kms_key" "lambda_key" {
description = "My Lambda Function Customer Master Key"
enable_key_rotation = true
deletion_window_in_days = 7
}
resource "aws_kms_alias" "lambda_key_alias" {
name = "alias/my-lambda-key"
target_key_id = aws_kms_key.lambda_key.id
}
resource "aws_ssm_parameter" "lambda_variable_message_base" {
name = "/App/Lambda/Config/BASE_MESSAGE"
type = "SecureString"
value = "Hello"
key_id = aws_kms_key.lambda_key.id
}
前の記事では、AWS Lambda関数のリソース定義に、こんな感じで入っていました。
kms_key_arn = aws_kms_key.lambda_key.arn
...
environment {
variables = {
BASE_MESSAGE = "Hello"
}
}
あと、AWS Lambdaに付与するIAMロールに、AWS Systems Manager パラメータストアにアクセスできるように権限設定も必要ですね。ssm:GetParameter
を追加します。
data "aws_iam_policy_document" "lambda_policy" {
source_json = data.aws_iam_policy.lambda_basic_execution.policy
statement {
effect = "Allow"
actions = [
"kms:Decrypt",
"ssm:GetParameter"
]
resources = ["*"]
}
}
確認
これで、apply
して
$ terraform apply
ログをtail
しつつ
$ aws logs tail /aws/lambda/my_lambda_function --follow
確認。
$ aws lambda invoke --function-name my_lambda_function \
--payload '{"first_name": "Taro", "last_name": "Tanaka"}' \
--cli-binary-format raw-in-base64-out \
--log-type Tail output.txt
$ cat output.txt
{"message": "Hello, Taro Tanaka!!"}
OKですね。
これで、AWS Lambda関数からAWS Systems Manager パラメータストアから、値を取得できるようにTerraformで構成できました、と。
オマケ
以下、Terraformの構成ファイル全体です。
main.tf
terraform {
required_version = "0.12.29"
}
provider "aws" {
version = "2.70.0"
}
provider "archive" {
version = "1.3.0"
}
locals {
function_name = "my_lambda_function"
}
data "archive_file" "function_source" {
type = "zip"
source_dir = "app"
output_path = "archive/my_lambda_function.zip"
}
resource "aws_lambda_function" "function" {
function_name = local.function_name
handler = "lambda.handler"
role = aws_iam_role.lambda_role.arn
runtime = "python3.8"
filename = data.archive_file.function_source.output_path
source_code_hash = data.archive_file.function_source.output_base64sha256
depends_on = [aws_iam_role_policy_attachment.lambda_policy, aws_cloudwatch_log_group.lambda_log_group]
}
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
effect = "Allow"
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
data "aws_iam_policy" "lambda_basic_execution" {
arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
data "aws_iam_policy_document" "lambda_policy" {
source_json = data.aws_iam_policy.lambda_basic_execution.policy
statement {
effect = "Allow"
actions = [
"kms:Decrypt",
"ssm:GetParameter"
]
resources = ["*"]
}
}
resource "aws_iam_policy" "lambda_policy" {
name = "MyLambdaPolicy"
policy = data.aws_iam_policy_document.lambda_policy.json
}
resource "aws_iam_role_policy_attachment" "lambda_policy" {
role = aws_iam_role.lambda_role.name
policy_arn = aws_iam_policy.lambda_policy.arn
}
resource "aws_iam_role" "lambda_role" {
name = "MyLambdaRole"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
resource "aws_cloudwatch_log_group" "lambda_log_group" {
name = "/aws/lambda/${local.function_name}"
}
resource "aws_kms_key" "lambda_key" {
description = "My Lambda Function Customer Master Key"
enable_key_rotation = true
deletion_window_in_days = 7
}
resource "aws_kms_alias" "lambda_key_alias" {
name = "alias/my-lambda-key"
target_key_id = aws_kms_key.lambda_key.id
}
resource "aws_ssm_parameter" "lambda_variable_message_base" {
name = "/App/Lambda/Config/BASE_MESSAGE"
type = "SecureString"
value = "Hello"
key_id = aws_kms_key.lambda_key.id
}