resource "aws_api_gateway_rest_api" "api" { name = var.name description = var.description dynamic "endpoint_configuration" { for_each = [1] content { types = var.apigw-type == "private" ? ["PRIVATE"] : ["REGIONAL"] vpc_endpoint_ids = var.apigw-type == "private" ? [aws_vpc_endpoint.apigw-vpcep[0].id] : null } } } # private endpoint for inbound access # to connect to the private api gateway, provide the header x-apigw-api-id # curl -IX GET -H 'x-apigw-api-id:' https:/// data "aws_region" "current" {} resource "aws_vpc_endpoint" "apigw-vpcep" { count = var.apigw-type == "private" ? 1 : 0 private_dns_enabled = false security_group_ids = [var.apigw-security-group-id] service_name = "com.amazonaws.${data.aws_region.current.name}.execute-api" subnet_ids = var.apigw-subnet-ids vpc_endpoint_type = "Interface" vpc_id = var.apigw-vpc-id } # vpc link for outbound access resource "aws_api_gateway_vpc_link" "api-vpc-link" { count = var.create-vpc-link ? 1 : 0 name = "${var.name}-vpclink" description = "VPC link for apigateway ${var.name}" target_arns = var.apigw-vpc-link-target-arns } # Apigw resources, integration, method, responses resource "aws_api_gateway_resource" "api-res" { depends_on = [aws_api_gateway_rest_api_policy.api-policy] for_each = var.resources path_part = each.key parent_id = aws_api_gateway_rest_api.api.root_resource_id rest_api_id = aws_api_gateway_rest_api.api.id } resource "aws_api_gateway_method" "api-method" { depends_on = [aws_api_gateway_resource.api-res] for_each = var.resources rest_api_id = aws_api_gateway_rest_api.api.id resource_id = aws_api_gateway_resource.api-res[each.key].id http_method = each.value["method"] authorization = each.value["authorization"] } # non-proxy integration resource "aws_api_gateway_integration" "api-integration" { depends_on = [aws_api_gateway_method.api-method] for_each = var.resources rest_api_id = aws_api_gateway_rest_api.api.id resource_id = aws_api_gateway_resource.api-res[each.key].id http_method = aws_api_gateway_method.api-method[each.key].http_method integration_http_method = each.value["method"] type = each.value["integration-type"] content_handling = each.value["content-handling"] uri = aws_lambda_function.function.invoke_arn } resource "aws_api_gateway_method_response" "response_200" { depends_on = [aws_api_gateway_method.api-method] for_each = var.resources rest_api_id = aws_api_gateway_rest_api.api.id resource_id = aws_api_gateway_resource.api-res[each.key].id http_method = aws_api_gateway_method.api-method[each.key].http_method status_code = "200" } resource "aws_api_gateway_integration_response" "integration-response" { depends_on = [aws_api_gateway_integration.api-integration] for_each = var.resources rest_api_id = aws_api_gateway_rest_api.api.id resource_id = aws_api_gateway_resource.api-res[each.key].id http_method = aws_api_gateway_method.api-method[each.key].http_method status_code = aws_api_gateway_method_response.response_200[each.key].status_code } # apigw deployment and stage resource "aws_api_gateway_deployment" "apigw-deployment" { depends_on = [aws_api_gateway_integration.api-integration] rest_api_id = aws_api_gateway_rest_api.api.id triggers = { redeployment = sha1(jsonencode(aws_api_gateway_rest_api.api.body)) } lifecycle { create_before_destroy = true } } resource "aws_api_gateway_stage" "apigw-stage" { for_each = var.stages depends_on = [aws_api_gateway_rest_api_policy.api-policy, aws_cloudwatch_log_group.this] deployment_id = aws_api_gateway_deployment.apigw-deployment.id rest_api_id = aws_api_gateway_rest_api.api.id stage_name = each.key description = each.value["description"] variables = each.value["variables"] access_log_settings { destination_arn = aws_cloudwatch_log_group.this[each.key].arn # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-logging.html format = jsonencode({ "requestId" : "$context.requestId", "extendedRequestId" : "$context.extendedRequestId", "ip" : "$context.identity.sourceIp", "caller" : "$context.identity.caller", "user" : "$context.identity.user", "requestTime" : "$context.requestTime", "httpMethod" : "$context.httpMethod", "resourcePath" : "$context.resourcePath", "status" : "$context.status", "protocol" : "$context.protocol", "responseLength" : "$context.responseLength" } ) } } resource "aws_api_gateway_method_settings" "apigw-method-settings" { depends_on = [aws_api_gateway_rest_api_policy.api-policy] for_each = aws_api_gateway_stage.apigw-stage rest_api_id = aws_api_gateway_rest_api.api.id stage_name = each.value.stage_name method_path = "*/*" settings { metrics_enabled = true logging_level = "INFO" } } # Cloudwatch log group path: API-Gateway-Execution-Logs_{rest-api-id}/{stage_name} resource "aws_cloudwatch_log_group" "this" { for_each = var.stages name = "API-Gateway-Execution-Logs_${aws_api_gateway_rest_api.api.id}/${each.key}" retention_in_days = var.cloudwatchlog-retention kms_key_id = var.cwl-cmk-key-id } # lambda function resource "aws_cloudwatch_log_group" "lambda-logs" { name = "/aws/lambda/${var.name}-lambda-function" retention_in_days = var.cloudwatchlog-retention kms_key_id = var.cwl-cmk-key-id } resource "aws_lambda_function" "function" { filename = var.lambda-archive-file function_name = "${var.name}-lambda-function" handler = "main.lambda_handler" role = aws_iam_role.lambda-exec-role.arn runtime = var.lambda-runtime-version # source_code_hash = local.source_code_hash } resource "aws_lambda_permission" "allow_api_gateway" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.function.function_name principal = "apigateway.amazonaws.com" } # Lambda execution role data "aws_caller_identity" "this" {} resource "random_id" "this" { byte_length = 4 } resource "aws_iam_role" "lambda-exec-role" { name = "lambda-apigw-${var.name}-${random_id.this.dec}" assume_role_policy = jsonencode( { "Version" : "2012-10-17", "Statement" : [ { "Effect" : "Allow", "Principal" : { "Service" : "lambda.amazonaws.com" }, "Action" : "sts:AssumeRole" } ] } ) } resource "aws_iam_role_policy" "this" { policy = jsonencode( { "Version" : "2012-10-17", "Statement" : [ { "Sid" : "AllowCreationOfCloudwatchLogGroup", "Effect" : "Allow", "Action" : "logs:CreateLogGroup", "Resource" : "arn:aws:logs:ap-east-1:${data.aws_caller_identity.this.account_id}:*" }, { "Sid" : "AllowWritingToCloudwatchLogGroup", "Effect" : "Allow", "Action" : [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource" : [ "arn:aws:logs:ap-east-1:${data.aws_caller_identity.this.account_id}:log-group:/aws/lambda/*" ] } ] } ) role = aws_iam_role.lambda-exec-role.id name = "LambdaExecutionPolicy" } # apigateway policy data "aws_iam_policy_document" "api-policy" { statement { effect = "Allow" principals { type = "AWS" identifiers = ["*"] } actions = ["execute-api:Invoke"] resources = [aws_api_gateway_rest_api.api.execution_arn] condition { test = "IpAddress" variable = "aws:SourceIp" values = [data.aws_vpc.vpc.cidr_block] } } } data "aws_vpc" "vpc" { id = var.apigw-vpc-id } resource "aws_api_gateway_rest_api_policy" "api-policy" { rest_api_id = aws_api_gateway_rest_api.api.id policy = data.aws_iam_policy_document.api-policy.json }