【Terraform】Terraformで作るサーバレス構成(API Gateway、Lambda、DynamoDB)

【Terraform】 Terraformで作るサーバレス構成 (API Gateway、Lambda、DynamoDB) インフラ
スポンサーリンク

はじめに

今回は以下の構成をTerraformで作成します。

構成図

Terraformの導入については、以下の記事で解説しています。よろしければ併せてご覧ください。

フォルダ構成

フォルダ構成は以下の通りです。
赤字の部分のフォルダ、ファイルは手で作成するものです。
黒字のファイルはplan等で自動生成されるファイルです。

├── envs
│   ├── backend.tf
│   ├── main.tf
│   ├── provider.tf
│   ├── terraform.tfstate
└── modules
    ├── api-gateway
    │   └── api-gateway.tf
    ├── dynamodb
    │   ├── dynamodb.tf
    │   └── user.json
    ├── iam
    │   └── iam.tf
    └── lambda
        ├── lambda.tf
        ├── src
        │   └── lambda.py
        └── upload
            └── lambda.zip

envs配下

provider.tf

provider "aws" {
  region = "ap-northeast-1"
}

backend.tf

# ローカル管理
terraform {
  backend "local" {
    path = "./terraform.tfstate"
  }
}

main.tf

各リソースのmodules配下を作成後に記述してください。

module "dynamodb" {
  source = "../modules/dynamodb"
  prefix = "test"
}

module "iam" {
  source    = "../modules/iam"
  prefix    = "test"
  table_arn = module.dynamodb.user_table_output.arn
}

module "lambda" {
  source              = "../modules/lambda"
  prefix              = "test"
  dynamodb_table_name = module.dynamodb.user_table_output.name
  role_arn            = module.iam.lambda_role_arn_output
  api_gateway_arn     = module.api_gateway.api_gateway_arn_output
}

module "api_gateway" {
  source     = "../modules/api-gateway"
  prefix     = "users"
  lambda_arn = module.lambda.lambda_arn_output
}

modules配下

DynamoDB

dynamodb.tf

aws_dynamodb_tableがテーブルを、aws_dynamodb_table_itemがテーブルに登録するデータを表しています。データー情報はuser.jsonに切り出しています。
詳しい各パラメータは公式が詳しいです。

Terraform Registry
variable "prefix" {
  type = string
}

locals {
  json_data = file("${path.module}/user.json")
  config = jsondecode(local.json_data)
}

resource "aws_dynamodb_table" "user_table" {
  name = "${var.prefix}_user_table"
  billing_mode = "PAY_PER_REQUEST"
  hash_key = "UserId"
  attribute {
    name = "UserId"
    type = "S"
  }
}

resource "aws_dynamodb_table_item" "user_item" {
  for_each = local.config
  table_name = aws_dynamodb_table.user_table.name
  hash_key = "UserId"
  item = jsonencode(each.value)
}

output "user_table_output" {
  value = aws_dynamodb_table.user_table
}
user.json
{
  "user01": {
    "UserId": {
      "S": "A001"
    },
    "Name": {
      "S": "Taro"
    },
    "Age": {
      "S": "25"
    }
  },
  "user02": {
    "UserId": {
      "S": "A002"
    },
    "Name": {
      "S": "Jiro"
    },
    "Age": {
      "S": "20"
    }
  },
  "user03": {
    "UserId": {
      "S": "A003"
    },
    "Name": {
      "S": "Saburo"
    },
    "Age": {
      "S": "10"
    }
  }
}

IAM

iam.tf

aws_iam_role:ロール作成
aws_iam_role_policy:ポリシー作成
aws_iam_role_policy_attachment:ポリシーをロールにアタッチ

Terraform Registry
variable "prefix" {
  type = string
}

variable "table_arn" {
  type = string
}

resource "aws_iam_role" "lambda_role" {
  name = "${var.prefix}_lambda_role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Sid    = ""
        Principal = {
          Service = "lambda.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_role_policy_attach" {
  role = aws_iam_role.lambda_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_iam_role_policy" "lambda_role_policy" {
    name = "${var.prefix}_lambda_policy"
  role = aws_iam_role.lambda_role.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "dynamodb:GetItem"
        ]
        Resource = [
          var.table_arn
        ]
      }
    ]
  })
}

output "lambda_role_arn_output" {
  value = aws_iam_role.lambda_role.arn
}

Lambda

lambda.tf

aws_lambda_functionはLambda関数を定義します。
aws_lambda_permissionはLambda関数に対するアクセス許可を定義します。

Terraform Registry
variable "prefix" {
  type = string
}

variable "dynamodb_table_name" {
  type = string
}

variable "role_arn" {
  type = string
}

variable "api_gateway_arn" {
  type = string
}

data "archive_file" "lambda_file" {
  type = "zip"
  source_dir = "${path.module}/src"
  output_path = "${path.module}/upload/lambda.zip"
}

resource "aws_lambda_function" "lambda_function" {
  filename = data.archive_file.lambda_file.output_path
  function_name = "${var.prefix}_lambda"
  role = var.role_arn
  handler = "lambda.handler"
  source_code_hash = data.archive_file.lambda_file.output_base64sha256
  runtime = "python3.9"
  timeout = 29
  environment {
    variables = {
      table_name = var.dynamodb_table_name
    }
  } 
}

resource "aws_lambda_permission" "lambda_permission" {
  statement_id = "AllowAPIGatewayGetTrApi"
  action = "lambda:InvokeFunction"
  function_name = aws_lambda_function.lambda_function.arn
  principal = "apigateway.amazonaws.com"
  source_arn = "${var.api_gateway_arn}/test/GET/"
}

output "lambda_arn_output" {
  value = aws_lambda_function.lambda_function.invoke_arn
}
lambda.py

Lambdaに記述するコード(Python)です。
DynamoDBからユーザーを取得し、クライアントに返しています。

import os
import json
import boto3

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.getenv('table_name'))

def handler(event, context):
    user_info = table.get_item(Key={'UserId': 'A001'})['Item']
    user_id = user_info['UserId']
    name = user_info['Name']
    age = user_info['Age']
    return {
        'statusCode': 200,
        'body': json.dumps({
            'Id': user_id,
            'Name': name,
            'Age': age,
        })
    }

API Gateway

api-gateway.tf

aws_api_gateway_rest_apiはAPI GatewayのREST APIを定義します。
aws_api_gateway_methodはAPI Gatewayのメソッド(HTTPメソッド)を定義します。
aws_api_gateway_integrationはAPI Gatewayのメソッドとバックエンド(Lambda)の統合を定義します。
aws_api_gateway_deploymentはAPI Gatewayのデプロイ情報を定義します。このリソースタイプを使用して、デプロイメントのステージ(開発、本番など)、APIの設定などを指定できます。

Terraform Registry
variable "prefix" {
  type = string
}

variable "lambda_arn" {
  type = string
}

resource "aws_api_gateway_rest_api" "api" {
  name = "${var.prefix}_api"
}

resource "aws_api_gateway_method" "api_gateway_get" {
  authorization = "NONE"
  http_method = "GET"
  resource_id = aws_api_gateway_rest_api.api.root_resource_id
  rest_api_id = aws_api_gateway_rest_api.api.id
}

resource "aws_api_gateway_integration" "api_gateway_get" {
  http_method = aws_api_gateway_method.api_gateway_get.http_method
  resource_id = aws_api_gateway_rest_api.api.root_resource_id
  rest_api_id = aws_api_gateway_rest_api.api.id
  integration_http_method = "POST"
  type = "AWS_PROXY"
  uri = var.lambda_arn
}

resource "aws_api_gateway_deployment" "api_deploy" {
  depends_on = [ 
    aws_api_gateway_integration.api_gateway_get
   ]
   rest_api_id = aws_api_gateway_rest_api.api.id
   stage_name = "test"
   triggers = {
         redeployment = filebase64("${path.module}/api-gateway.tf")
   }
}

output "api_gateway_arn_output" {
  value = aws_api_gateway_rest_api.api.execution_arn
}

終わりに

本記事はここまでとなります。

AWSの学習にはハンズオンで学習できるUdemyが非常に効率的でおすすめです。
以下におすすめ教材を紹介しておりますので、是非ご活用ください。


ご覧いただきありがとうございました。ご指摘等がございましたら頂けますと嬉しいです。
引き続き、プログラミングについて定期的に発信していきますのでよろしくお願いします!
また、もしよろしければtwitterもフォローしていただけると嬉しいです!🐢

コメント