I am going for a modular approach to deploying APIgateway and associated lambda, across multiple regions (aws and aws.secondary provider). Assuming each var variable is assigned:
primary-gateway:
module "test_api" {
source = "./modules/api-gateway"
name = "test-api"
description = "primary API"
vpc_id = var.vpc_id #vpc in primary region
region = var.region
stage_name = ""
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "execute-api:/*",
"Condition": {
"StringNotEquals": {
"aws:sourceVpce": ["${data.aws_vpc_endpoint.execute_api_endpoint.id}"]
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "execute-api:/*"
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "lambda:InvokeFunction",
"Resource": ["${module.test_lambda.lambda_arn}"]
}
]
}
EOF
}
data "aws_vpc_endpoint" "execute_api_endpoint" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.execute-api"
}
# --------------Endpoint and Authorization ------------------
module "post_endpoint" {
source = "./modules/api-gateway-endpoint"
rest_api_id = module.test_api.api_id
root_resource_id = module.test_api.root_resource_id
path = "test"
invoke_arn = module.test_lambda.invoke_arn
lambda_name = module.test_lambda.function_name
execution_arn = module.test_api.execution_arn
http_method = "POST"
authorizer_id = aws_api_gateway_authorizer.gateway-authorizer.id
}
resource "aws_api_gateway_authorizer" "gateway-authorizer" {
name = "test-gateway-authorizer"
rest_api_id = module.test_api.api_id
authorizer_uri = module.lambda_api_gateway_authorizer.invoke_arn
authorizer_credentials = aws_iam_role.authorizer_invocation_role.arn
identity_source = "method.request.header.X-SF_AD_OIDC_TOKEN"
type = "REQUEST"
}
module "lambda_api_gateway_authorizer" {
source = "./modules/lambda"
function_name = "azureAuthorizer"
policy_document_json = data.aws_iam_policy_document.lambda_api_gateway_authorizer.json
account = var.account
region = var.region
subnet_ids = local.private_subnets
security_group_ids = [local.https_outbound_sg_id]
kms_key_arn = var.kms_key_id
permissions_boundary = var.permissions_boundary
}
data "aws_iam_policy_document" "lambda_api_gateway_authorizer" {
statement {
actions = "*"
resources = ["arn:aws:logs:${var.region}:${var.account}:log-group:/aws/lambda/*:*"]
}
statement {
actions = ["kms:Decrypt", "kms:Encrypt", "kms:GenerateDataKey"]
resources = [var.kms_key_id]
}
statement {
actions = [
"ec2:DescribeInstances",
"ec2:CreateNetworkInterface",
"ec2:AttachNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
]
resources = ["*"]
}
}
resource "aws_iam_role" "authorizer_invocation_role" {
name = "test-api-gateway-auth-invocation"
path = "/"
permissions_boundary = var.permissions_boundary
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
# --------------Stages and Deployment ------------------
resource "aws_api_gateway_deployment" "test_api" {
depends_on = [module.post_endpoint.endpoint_integration]
rest_api_id = module.test_api.api_id
triggers = {
redeployment = sha1(join(
"",
[
file("api-test-endpoint.tf") #endpoints are declared in this file
]
))
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_api_gateway_stage" "stage" {
deployment_id = aws_api_gateway_deployment.test_api.id
rest_api_id = module.test_api.api_id
stage_name = "sandbox"
access_log_settings {
destination_arn = module.test_api.api_logs_arn
format = "{ \"requestId\":\"$context.requestId\", \"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" "test_api" {
rest_api_id = module.test_api.api_id
stage_name = aws_api_gateway_stage.stage.stage_name
method_path = "*/*"
}
module "test_lambda" {
source = "./modules/lambda"
function_name = "testAPILambda"
policy_document_json = data.aws_iam_policy_document.test_policy.json
tags = local.common_tags
workspace = terraform.workspace
account = var.account
region = var.region
subnet_ids = local.private_subnets
security_group_ids = [local.https_outbound_sg_id]
kms_key_arn = var.kms_key_id
permissions_boundary = var.permissions_boundary
}
secondary-gateway:
module "test_secondary" {
source = "./modules/api-gateway"
name = "test-api"
description = "Secondary API"
vpc_id = var.secondary_vpc_id
region = var.secondary_region
stage_name = ""
providers = { aws = aws.secondary }
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "execute-api:/*",
"Condition": {
"StringNotEquals": {
"aws:sourceVpce": ["${data.aws_vpc_endpoint.execute_api_endpoint_secondary.id}"]
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "execute-api:/*"
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "lambda:InvokeFunction",
"Resource": ["${module.test_lambda_secondary.lambda_arn}"]
}
]
}
EOF
}
data "aws_vpc_endpoint" "execute_api_endpoint_secondary" {
vpc_id = var.secondary_vpc_id
service_name = "com.amazonaws.${var.secondary_region}.execute-api"
provider = aws.secondary
}
# --------------Endpoint and Authorization ------------------
module "test_endpoint_secondary" {
source = "./modules/api-gateway-endpoint"
rest_api_id = module.test_api_secondary.api_id
root_resource_id = module.test_api_secondary.root_resource_id
path = "test"
invoke_arn = module.test_lambda_secondary.invoke_arn
lambda_name = module.test_lambda_secondary.function_name
execution_arn = module.test_api_secondary.execution_arn
http_method = "POST"
authorizer_id = aws_api_gateway_authorizer.gateway-authorizer-secondary.id
}
resource "aws_api_gateway_authorizer" "gateway-authorizer-secondary" {
name = "test-gateway-authorizer-secondary"
rest_api_id = module.test_api_secondary.api_id
authorizer_uri = module.lambda_api_gateway_authorizer_secondary.invoke_arn
authorizer_credentials = aws_iam_role.authorizer_invocation_role.arn
identity_source = "method.request.header.X-SF_AD_OIDC_TOKEN"
type = "REQUEST"
}
resource "aws_iam_role_policy" "authorizer_invocation_policy_secondary" {
name = "test-api-gateway-auth-invocation-policy-secondary"
role = aws_iam_role.authorizer_invocation_role.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "lambda:InvokeFunction",
"Effect": "Allow",
"Resource": "${module.lambda_api_gateway_authorizer_secondary.lambda_arn}"
}
]
}
EOF
}
module "lambda_api_gateway_authorizer_secondary" {
source = "./modules/lambda"
function_name = "azureAuthorizer"
policy_document_json = data.aws_iam_policy_document.lambda_api_gateway_authorizer_secondary.json
account = var.account
region = var.secondary_region
providers = { aws = aws.secondary }
subnet_ids = local.secondary_private_subnets
security_group_ids = [local.https_outbound_sg_id_secondary]
kms_key_arn = var.kms_secondary_key_id
permissions_boundary = var.permissions_boundary
}
data "aws_iam_policy_document" "lambda_api_gateway_authorizer_secondary" {
statement {
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams",
"logs:PutSubscriptionFilter"
]
resources = ["arn:aws:logs:${var.secondary_region}:${var.account}:log-group:/aws/lambda/*:*"]
}
statement {
actions = ["kms:Decrypt", "kms:Encrypt", "kms:GenerateDataKey"]
resources = [var.kms_secondary_key_id]
}
statement {
actions = [
"ec2:DescribeInstances",
"ec2:CreateNetworkInterface",
"ec2:AttachNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
]
resources = ["*"]
}
}
# --------------Stages and Deployment ------------------
resource "aws_api_gateway_deployment" "test_secondary" {
depends_on = [module.test_endpoint_secondary.endpoint_integration]
rest_api_id = module.test_secondary.api_id
triggers = {
redeployment = sha1(join(
"",
[
file("api-test-endpoint.tf")
]
))
}
lifecycle {
create_before_destroy = true
}
provider = aws.secondary
}
resource "aws_api_gateway_stage" "stage_secondary" {
deployment_id = aws_api_gateway_deployment.test_secondary.id
rest_api_id = module.test_secondary.api_id
stage_name = "sandbox"
access_log_settings {
destination_arn = module.test_secondary.api_logs_arn
format = "{ \"requestId\":\"$context.requestId\", \"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\" }"
}
provider = aws.secondary
}
resource "aws_api_gateway_method_settings" "test_secondary" {
rest_api_id = module.test_secondary.api_id
stage_name = aws_api_gateway_stage.stage_secondary.stage_name
method_path = "*/*"
provider = aws.secondary
}
resource "aws_lambda_permission" "test_permission_secondary" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = module.test_lambda_secondary.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${module.test_api_secondary.execution_arn}/*/*/*"
provider = aws.secondary
}
module "test_lambda_secondary" {
source = "./modules/lambda"
function_name = "testAPILambda-secondary"
policy_document_json = data.aws_iam_policy_document.test_policy_secondary.json
workspace = terraform.workspace
account = var.account
region = var.secondary_region
providers = { aws = aws.secondary }
subnet_ids = local.secondary_private_subnets
security_group_ids = [local.https_outbound_sg_id_secondary]
kms_key_arn = var.kms_secondary_key_id
permissions_boundary = var.permissions_boundary
}
These are near identical except the secondary provider is given for the region.
I get this error:
Error creating API Gateway: AccessDeniedException: on modules\api-gateway\main.tf line 1, in resource "aws_api_gateway_rest_api" "api_gateway": 1: resource "aws_api_gateway_rest_api" "api_gateway" {
And this error only shows once the secondary resource is added.
Both resources are referencing this block template:
resource "aws_api_gateway_rest_api" "api_gateway" {
name = var.name
description = var.description
endpoint_configuration {
types = ["PRIVATE"]
}
policy = var.policy
}
# ------------- API Cloudwatch Logs --------------
resource "aws_cloudwatch_log_group" "api_logs" {
name = "test-apigateway-${aws_api_gateway_rest_api.api_gateway.id}-access-logs"
retention_in_days = 14
}
Any help for where this error can be found or what to look at is appreciated.
I'm not sure if I understand correctly, but it seems to me that you are trying to create VPC interface endpoints to API gatway in a different region. If so, this is not supported. From docs:
Endpoints are supported within the same Region only. You cannot create an endpoint between a VPC and a service in a different Region.