I have a function in a vpc, and a GW V2 in front of it - but it’s on a public IP. I’d like the gateway to be restricted to just my internal network – primary driver being that we want all inbound connections coming through other content inspection/security proxies that are managed by a different team.
If I try to change the connection_type on the aws_apigatewayv2_integration from default ‘INTERNET’ to ‘VPC_LINK’ on the integration, I get this error:
Error: error updating API Gateway v2 integration: BadRequestException: For HTTP Apis with connection type VPC_LINK, integration type must be HTTP_PROXY. Current value is AWS_PROXY.
Turns out, this was much simpler problem resolved after opening case with amazon – it has to be a REST api, not an HTTP api - which means apigatewayv2 CANNOT be used. I was able to get one provisioned using the api_gateway instead.
Bit ugly, but full snippet below for reference/search benefit.
resource "aws_ecr_repository" "demo_tools" {
name = "demo-tools"
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = false
}
tags = local.common_tags
}
resource "aws_ecr_repository_policy" "demo_tools_policy" {
repository = aws_ecr_repository.demo_tools.name
policy = <<EOF
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "LambdaECRImageRetrievalPolicy",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
]
}
]
}
EOF
}
resource "aws_iam_role" "demo_tools_lambda_iam" {
name = "demo-tools-lambda-iam"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "demo_tools_lambda_iam_basic" {
role = aws_iam_role.demo_tools_lambda_iam.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy_attachment" "demo_tools_lambda_iam_vpc" {
role = aws_iam_role.demo_tools_lambda_iam.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}
resource "aws_lambda_function" "demo_tools_lambda" {
function_name = "demo-tools-lambda"
role = aws_iam_role.demo_tools_lambda_iam.arn
package_type = "Image"
image_uri = join(":", [aws_ecr_repository.demo_tools.repository_url, "latest"])
vpc_config {
security_group_ids = [aws_security_group.demo_lambda.id]
subnet_ids = data.terraform_remote_state.is.outputs.subnet_ids
}
environment {
variables = {
foo = "bar"
}
}
}
data "aws_vpc_endpoint_service" "demo_tools_lambda_vpc_endpoint_svc" {
service = "execute-api"
}
resource "aws_vpc_endpoint" "demo_tools_lambda_vpc_endpoint" {
vpc_id = data.terraform_remote_state.cal.outputs.vpc_id
service_name = data.aws_vpc_endpoint_service.demo_tools_lambda_vpc_endpoint_svc.service_name
vpc_endpoint_type = "Interface"
private_dns_enabled = true
subnet_ids = data.terraform_remote_state.is.outputs.subnet_ids
security_group_ids = [aws_security_group.demo_lambda.id]
#security_group_ids = [aws_security_group.demo_tools_lambda_default_sg]
}
resource "aws_api_gateway_rest_api" "demo_tools_lambda_api_gw" {
name = "demo-tools-lambda-api-gw"
endpoint_configuration {
types = [ "PRIVATE" ]
vpc_endpoint_ids = [ aws_vpc_endpoint.demo_tools_lambda_vpc_endpoint.id ]
}
}
resource "aws_api_gateway_rest_api_policy" "demo_tools_lambda_api_gw_policy" {
rest_api_id = aws_api_gateway_rest_api.demo_tools_lambda_api_gw.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": [
"*"
]
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": [
"*"
],
"Condition" : {
"StringNotEquals": {
"aws:SourceVpce": "${aws_vpc_endpoint.demo_tools_lambda_vpc_endpoint.id}"
}
}
}
]
}
EOF
}
resource "aws_api_gateway_resource" "demo_tools_lambda_api_gw_rsrc" {
rest_api_id = aws_api_gateway_rest_api.demo_tools_lambda_api_gw.id
parent_id = aws_api_gateway_rest_api.demo_tools_lambda_api_gw.root_resource_id
path_part = "{proxy+}"
}
# Handle general urls
resource "aws_api_gateway_method" "demo_tools_lambda_api_gw_method" {
rest_api_id = aws_api_gateway_rest_api.demo_tools_lambda_api_gw.id
resource_id = aws_api_gateway_resource.demo_tools_lambda_api_gw_rsrc.id
http_method = "ANY"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "demo_tools_lambda_api_gw_integ" {
rest_api_id = aws_api_gateway_rest_api.demo_tools_lambda_api_gw.id
resource_id = aws_api_gateway_resource.demo_tools_lambda_api_gw_rsrc.id
http_method = aws_api_gateway_method.demo_tools_lambda_api_gw_method.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.demo_tools_lambda.invoke_arn
}
# Handle root url
resource "aws_api_gateway_method" "demo_tools_lambda_api_gw_method_root" {
rest_api_id = aws_api_gateway_rest_api.demo_tools_lambda_api_gw.id
resource_id = aws_api_gateway_rest_api.demo_tools_lambda_api_gw.root_resource_id
http_method = "ANY"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "demo_tools_lambda_api_gw_integ_root" {
rest_api_id = aws_api_gateway_rest_api.demo_tools_lambda_api_gw.id
resource_id = aws_api_gateway_method.demo_tools_lambda_api_gw_method_root.resource_id
http_method = aws_api_gateway_method.demo_tools_lambda_api_gw_method_root.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.demo_tools_lambda.invoke_arn
}
resource "aws_api_gateway_deployment" "demo_tools_lambda_api_gw_deployment" {
depends_on = [
aws_api_gateway_integration.demo_tools_lambda_api_gw_integ,
aws_api_gateway_integration.demo_tools_lambda_api_gw_integ_root
]
rest_api_id = aws_api_gateway_rest_api.demo_tools_lambda_api_gw.id
triggers = {
# NOTE: The configuration below will satisfy ordering considerations,
# but not pick up all future REST API changes. More advanced patterns
# are possible, such as using the filesha1() function against the
# Terraform configuration file(s) or removing the .id references to
# calculate a hash against whole resources. Be aware that using whole
# resources will show a difference after the initial implementation.
# It will stabilize to only change when resources change afterwards.
redeployment = sha1(jsonencode([
aws_api_gateway_resource.demo_tools_lambda_api_gw_rsrc.id,
aws_api_gateway_method.demo_tools_lambda_api_gw_method_root.id,
aws_api_gateway_method.demo_tools_lambda_api_gw_method.id,
aws_api_gateway_integration.demo_tools_lambda_api_gw_integ.id,
aws_api_gateway_integration.demo_tools_lambda_api_gw_integ_root.id,
]))
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_api_gateway_stage" "demo_tools_lambda_api_gw_stage" {
deployment_id = aws_api_gateway_deployment.demo_tools_lambda_api_gw_deployment.id
rest_api_id = aws_api_gateway_rest_api.demo_tools_lambda_api_gw.id
stage_name = "demo-tools-lambda-api-gw-stage"
}
resource "aws_lambda_permission" "demo_tools_lambda_api_gw_perm" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.demo_tools_lambda.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.demo_tools_lambda_api_gw.execution_arn}/*/*"
}
output "demo_tools_gw_url_vpce" {
description = "Base URL for demo Tools API Gateway stage."
value = "https://${aws_api_gateway_rest_api.demo_tools_lambda_api_gw.id}-${aws_vpc_endpoint.demo_tools_lambda_vpc_endpoint.id}.execute-api.us-west-2.amazonaws.com/${aws_api_gateway_stage.demo_tools_lambda_api_gw_stage.stage_name}"
}