10
4

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 1 year has passed since last update.

TerraformでAPI Gateway + Lambdaの構成テンプレート

Last updated at Posted at 2023-05-23

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を使うバージョンも次の記事で書きました。

10
4
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
10
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?