3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS Systems Manager パラメータストアを参照するような、AWS Lambda関数を登録して動かしてみる

Last updated at Posted at 2020-08-02

What's?

前にこんな記事を書いたのですが、環境変数がふつうに見えてしまうことに困っていました。

Terraformで、AWS Lambda関数を登録して動かしてみる

「転送時の暗号化に使用するヘルパー」という話もあるのですが、そういえばと思い、設定をAWS Systems Managerのパラメータストアから取得する方法も試してみようかなと。

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

Python による Lambda 関数のビルド

今回は、起動時に値を取得してしまうことにしました。

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']

SSM / Client

get_parameter

今回はAWS Systems Manager パラメータストアから単一の値を取得していますが、ある程度一括で取得することも可能です。

get_parameters

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
}
3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?