API Gateway + LambdaでのAPIをTerraformで実装するためのテンプレートです。
API GatewayとLambdaにはそれぞれIAM Roleを作成してアタッチしています。
main.tf
variable aws_profile {}
variable aws_region {}
variable resource_prefix {}
provider "aws" {
profile = var.aws_profile
region = var.aws_region
}
################################
# LambdaにアタッチするIAM Role
################################
resource "aws_iam_role" "lambda_role" {
name = "${var.resource_prefix}-lambda-role"
assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json
}
resource "aws_iam_role_policy_attachment" "lambda_policy" {
role = aws_iam_role.lambda_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
data "aws_iam_policy_document" "lambda_assume_role" {
statement {
actions = ["sts:AssumeRole"]
effect = "Allow"
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
################################
# API GatewayにアタッチするIAM Role
################################
resource "aws_iam_role" "api_gateway_role" {
name = "${var.resource_prefix}-apigateway-role"
assume_role_policy = data.aws_iam_policy_document.api_gateway_assume_role.json
}
resource "aws_iam_role_policy_attachment" "api_gateway_policy_logs" {
role = aws_iam_role.api_gateway_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
}
resource "aws_iam_role_policy_attachment" "api_gateway_policy_lambda" {
role = aws_iam_role.api_gateway_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaRole"
}
data "aws_iam_policy_document" "api_gateway_assume_role" {
statement {
actions = ["sts:AssumeRole"]
effect = "Allow"
principals {
type = "Service"
identifiers = ["apigateway.amazonaws.com"]
}
}
}
################################
# Lambda
################################
# apiディレクトリにLambdaのソースコードがある前提
# apiディレクトリを api.zip という名前に固めて resource "aws_lambda_function" "api" から参照できるようにする
data "archive_file" "lambda_zip" {
type = "zip"
source_dir = "api"
output_path = "api.zip"
}
resource "aws_lambda_function" "api" {
depends_on = [aws_iam_role.lambda_role]
filename = data.archive_file.lambda_zip.output_path
function_name = "${var.resource_prefix}-api"
role = aws_iam_role.lambda_role.arn
handler = "index.lambda_handler"
runtime = "nodejs14.x"
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
}
################################
# API Gateway
################################
resource "aws_api_gateway_rest_api" "api" {
name = "${var.resource_prefix}-api"
body = jsonencode({
openapi = "3.0.1"
info = {
title = "api"
version = "1.0"
}
paths = {
"/path1" = {
get = {
x-amazon-apigateway-integration = {
httpMethod = "POST" # LambdaへのアクセスはPOSTでないといけないらしい
payloadFormatVersion = "1.0"
type = "AWS_PROXY"
uri = aws_lambda_function.api.invoke_arn
credentials = aws_iam_role.api_gateway_role.arn
}
}
}
}
})
}
resource "aws_api_gateway_deployment" "deployment" {
rest_api_id = aws_api_gateway_rest_api.api.id
depends_on = [aws_api_gateway_rest_api.api]
stage_name = "prod"
triggers = {
# resource "aws_lambda_function" "api" の内容が変わるごとにデプロイされるようにする
redeployment = sha1(jsonencode(aws_api_gateway_rest_api.api))
}
}
data "aws_iam_policy_document" "api_gateway_policy" {
statement {
effect = "Allow"
principals {
type = "*"
identifiers = ["*"]
}
actions = ["execute-api:Invoke"]
resources = ["${aws_api_gateway_rest_api.api.execution_arn}/*"]
}
}
resource "aws_api_gateway_rest_api_policy" "policy" {
rest_api_id = aws_api_gateway_rest_api.api.id
policy = data.aws_iam_policy_document.api_gateway_policy.json
}
次のファイルはLambdaのソースコードのサンプルです。 api/
ディレクトリに入れておきます。Node.js です。
api/index.js
exports.lambda_handler = async (event, context) => {
return new Promise(function(resolve, reject) {
resolve({
"statusCode": 200,
"headers": {
"Content-Type": "application/json",
},
"body": JSON.stringify({message: "OK", event: event}),
});
return;
});
};
main.tf
から参照されている変数の定義例です。
terraform.tfvars
aws_profile = "default"
aws_region = "ap-northeast-1"
resource_prefix = "APINAME"
terraform apply
を実行するとAPI Gateway、Lambda、IAM Roleが作成されます。
以下はAPIの実行結果例です。
$ curl 'https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/path1?q=abc' | jq .
{
"message": "OK",
"event": {
"resource": "/path1",
"path": "/path1",
"httpMethod": "GET",
"headers": {
"Accept": "*/*",
...
},
"multiValueHeaders": {
"Accept": [
"*/*"
],
...
},
"queryStringParameters": {
"q": "abc"
},
"multiValueQueryStringParameters": {
"q": [
"abc"
]
},
...,
"requestContext": {
...
},
"body": null,
"isBase64Encoded": false
}
}
追記 2023/06/22
API Gatewayの代わりにCloudFrontを使うバージョンも次の記事で書きました。