Skip to content

Commit

Permalink
feat: Added support for Authorizers (#64)
Browse files Browse the repository at this point in the history
Co-authored-by: Anton Babenko <anton@antonbabenko.com>
  • Loading branch information
bobbydeveaux and antonbabenko authored Mar 25, 2022
1 parent cb86a80 commit 5cd32e0
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 55 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,28 @@ module "api_gateway" {
payload_format_version = "2.0"
timeout_milliseconds = 12000
}
"GET /some-route-with-authorizer" = {
integration_type = "HTTP_PROXY"
integration_uri = "some url"
authorizer_key = "azure"
}
"$default" = {
lambda_arn = "arn:aws:lambda:eu-west-1:052235179155:function:my-default-function"
}
}
authorizers = {
"azure" = {
authorizer_type = "JWT"
identity_sources = "$request.header.Authorization"
name = "azure-auth"
audience = ["d6a38afd-45d6-4874-d1aa-3c5c558aqcc2"]
issuer = "https://sts.windows.net/aaee026e-8f37-410e-8869-72d9154873e4/"
}
}
tags = {
Name = "http-apigateway"
}
Expand Down Expand Up @@ -112,6 +128,7 @@ No modules.
|------|------|
| [aws_apigatewayv2_api.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_api) | resource |
| [aws_apigatewayv2_api_mapping.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_api_mapping) | resource |
| [aws_apigatewayv2_authorizer.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_authorizer) | resource |
| [aws_apigatewayv2_domain_name.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_domain_name) | resource |
| [aws_apigatewayv2_integration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_integration) | resource |
| [aws_apigatewayv2_route.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_route) | resource |
Expand All @@ -124,6 +141,7 @@ No modules.
|------|-------------|------|---------|:--------:|
| <a name="input_api_key_selection_expression"></a> [api\_key\_selection\_expression](#input\_api\_key\_selection\_expression) | An API key selection expression. Valid values: $context.authorizer.usageIdentifierKey, $request.header.x-api-key. | `string` | `"$request.header.x-api-key"` | no |
| <a name="input_api_version"></a> [api\_version](#input\_api\_version) | A version identifier for the API | `string` | `null` | no |
| <a name="input_authorizers"></a> [authorizers](#input\_authorizers) | Map of API gateway authorizers | `map(any)` | `{}` | no |
| <a name="input_body"></a> [body](#input\_body) | An OpenAPI specification that defines the set of routes and integrations to create as part of the HTTP APIs. Supported only for HTTP APIs. | `string` | `null` | no |
| <a name="input_cors_configuration"></a> [cors\_configuration](#input\_cors\_configuration) | The cross-origin resource sharing (CORS) configuration. Applicable for HTTP APIs. | `any` | `{}` | no |
| <a name="input_create"></a> [create](#input\_create) | Controls if API Gateway resources should be created | `bool` | `true` | no |
Expand Down
18 changes: 17 additions & 1 deletion examples/complete-http/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ module "api_gateway" {
throttling_rate_limit = 100
}

authorizers = {
"cognito" = {
authorizer_type = "JWT"
identity_sources = "$request.header.Authorization"
name = "cognito"
audience = ["d6a38afd-45d6-4874-d1aa-3c5c558aqcc2"]
issuer = "https://${aws_cognito_user_pool.this.endpoint}"
}
}

integrations = {

"ANY /" = {
Expand All @@ -65,6 +75,12 @@ module "api_gateway" {
authorizer_id = aws_apigatewayv2_authorizer.some_authorizer.id
}

"GET /some-route-with-authorizer" = {
lambda_arn = module.lambda_function.lambda_function_arn
payload_format_version = "2.0"
authorizer_key = "cognito"
}

"POST /start-step-function" = {
integration_type = "AWS_PROXY"
integration_subtype = "StepFunctions-StartExecution"
Expand Down Expand Up @@ -259,7 +275,7 @@ module "lambda_function" {

resource "aws_s3_bucket" "truststore" {
bucket = "${random_pet.this.id}-truststore"
acl = "private"
# acl = "private"
}

resource "aws_s3_bucket_object" "truststore" {
Expand Down
105 changes: 66 additions & 39 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ resource "aws_apigatewayv2_api" "this" {
for_each = length(keys(var.cors_configuration)) == 0 ? [] : [var.cors_configuration]

content {
allow_credentials = lookup(cors_configuration.value, "allow_credentials", null)
allow_headers = lookup(cors_configuration.value, "allow_headers", null)
allow_methods = lookup(cors_configuration.value, "allow_methods", null)
allow_origins = lookup(cors_configuration.value, "allow_origins", null)
expose_headers = lookup(cors_configuration.value, "expose_headers", null)
max_age = lookup(cors_configuration.value, "max_age", null)
allow_credentials = try(cors_configuration.value.allow_credentials, null)
allow_headers = try(cors_configuration.value.allow_headers, null)
allow_methods = try(cors_configuration.value.allow_methods, null)
allow_origins = try(cors_configuration.value.allow_origins, null)
expose_headers = try(cors_configuration.value.expose_headers, null)
max_age = try(cors_configuration.value.max_age, null)
}
}

Expand All @@ -48,9 +48,10 @@ resource "aws_apigatewayv2_domain_name" "this" {

dynamic "mutual_tls_authentication" {
for_each = length(keys(var.mutual_tls_authentication)) == 0 ? [] : [var.mutual_tls_authentication]

content {
truststore_uri = mutual_tls_authentication.value.truststore_uri
truststore_version = lookup(mutual_tls_authentication.value, "truststore_version", null)
truststore_version = try(mutual_tls_authentication.value.truststore_version, null)
}
}

Expand All @@ -67,6 +68,7 @@ resource "aws_apigatewayv2_stage" "default" {

dynamic "access_log_settings" {
for_each = var.default_stage_access_log_destination_arn != null && var.default_stage_access_log_format != null ? [true] : []

content {
destination_arn = var.default_stage_access_log_destination_arn
format = var.default_stage_access_log_format
Expand All @@ -75,12 +77,13 @@ resource "aws_apigatewayv2_stage" "default" {

dynamic "default_route_settings" {
for_each = length(keys(var.default_route_settings)) == 0 ? [] : [var.default_route_settings]

content {
data_trace_enabled = lookup(default_route_settings.value, "data_trace_enabled", false)
detailed_metrics_enabled = lookup(default_route_settings.value, "detailed_metrics_enabled", false)
logging_level = lookup(default_route_settings.value, "logging_level", null)
throttling_burst_limit = lookup(default_route_settings.value, "throttling_burst_limit", null)
throttling_rate_limit = lookup(default_route_settings.value, "throttling_rate_limit", null)
data_trace_enabled = try(default_route_settings.value.data_trace_enabled, false)
detailed_metrics_enabled = try(default_route_settings.value.detailed_metrics_enabled, false)
logging_level = try(default_route_settings.value.logging_level, null)
throttling_burst_limit = try(default_route_settings.value.throttling_burst_limit, null)
throttling_rate_limit = try(default_route_settings.value.throttling_rate_limit, null)
}
}

Expand All @@ -89,11 +92,11 @@ resource "aws_apigatewayv2_stage" "default" {
# for_each = var.create_routes_and_integrations ? var.integrations : {}
# content {
# route_key = route_settings.key
# data_trace_enabled = lookup(route_settings.value, "data_trace_enabled", null)
# detailed_metrics_enabled = lookup(route_settings.value, "detailed_metrics_enabled", null)
# logging_level = lookup(route_settings.value, "logging_level", null) # Error: error updating API Gateway v2 stage ($default): BadRequestException: Execution logs are not supported on protocolType HTTP
# throttling_burst_limit = lookup(route_settings.value, "throttling_burst_limit", null)
# throttling_rate_limit = lookup(route_settings.value, "throttling_rate_limit", null)
# data_trace_enabled = try(route_settings.value.data_trace_enabled, null)
# detailed_metrics_enabled = try(route_settings.value.detailed_metrics_enabled, null)
# logging_level = try(route_settings.value.logging_level, null) # Error: error updating API Gateway v2 stage ($default): BadRequestException: Execution logs are not supported on protocolType HTTP
# throttling_burst_limit = try(route_settings.value.throttling_burst_limit, null)
# throttling_rate_limit = try(route_settings.value.throttling_rate_limit, null)
# }
# }

Expand Down Expand Up @@ -121,49 +124,51 @@ resource "aws_apigatewayv2_route" "this" {
api_id = aws_apigatewayv2_api.this[0].id
route_key = each.key

api_key_required = lookup(each.value, "api_key_required", null)
authorization_type = lookup(each.value, "authorization_type", "NONE")
authorizer_id = lookup(each.value, "authorizer_id", null)
model_selection_expression = lookup(each.value, "model_selection_expression", null)
operation_name = lookup(each.value, "operation_name", null)
route_response_selection_expression = lookup(each.value, "route_response_selection_expression", null)
api_key_required = try(each.value.api_key_required, null)
authorization_type = try(each.value.authorization_type, "NONE")
authorizer_id = try(aws_apigatewayv2_authorizer.this[each.value.authorizer_key].id, each.value.authorizer_id, null)
model_selection_expression = try(each.value.model_selection_expression, null)
operation_name = try(each.value.operation_name, null)
route_response_selection_expression = try(each.value.route_response_selection_expression, null)
target = "integrations/${aws_apigatewayv2_integration.this[each.key].id}"

# Not sure what structure is allowed for these arguments...
# authorization_scopes = lookup(each.value, "authorization_scopes", null)
# request_models = lookup(each.value, "request_models", null)
# authorization_scopes = try(each.value.authorization_scopes, null)
# request_models = try(each.value.request_models, null)
}

resource "aws_apigatewayv2_integration" "this" {
for_each = var.create && var.create_routes_and_integrations ? var.integrations : {}

api_id = aws_apigatewayv2_api.this[0].id
description = lookup(each.value, "description", null)
description = try(each.value.description, null)

integration_type = lookup(each.value, "integration_type", lookup(each.value, "lambda_arn", "") != "" ? "AWS_PROXY" : "MOCK")
integration_subtype = lookup(each.value, "integration_subtype", null)
integration_method = lookup(each.value, "integration_method", lookup(each.value, "integration_subtype", null) == null ? "POST" : null)
integration_uri = lookup(each.value, "lambda_arn", lookup(each.value, "integration_uri", null))
integration_type = try(each.value.integration_type, try(each.value.lambda_arn, "") != "" ? "AWS_PROXY" : "MOCK")
integration_subtype = try(each.value.integration_subtype, null)
integration_method = try(each.value.integration_method, try(each.value.integration_subtype, null) == null ? "POST" : null)
integration_uri = try(each.value.lambda_arn, try(each.value.integration_uri, null))

connection_type = lookup(each.value, "connection_type", "INTERNET")
connection_id = try(aws_apigatewayv2_vpc_link.this[each.value["vpc_link"]].id, lookup(each.value, "connection_id", null))
connection_type = try(each.value.connection_type, "INTERNET")
connection_id = try(aws_apigatewayv2_vpc_link.this[each.value["vpc_link"]].id, try(each.value.connection_id, null))

payload_format_version = lookup(each.value, "payload_format_version", null)
timeout_milliseconds = lookup(each.value, "timeout_milliseconds", null)
passthrough_behavior = lookup(each.value, "passthrough_behavior", null)
content_handling_strategy = lookup(each.value, "content_handling_strategy", null)
credentials_arn = lookup(each.value, "credentials_arn", null)
payload_format_version = try(each.value.payload_format_version, null)
timeout_milliseconds = try(each.value.timeout_milliseconds, null)
passthrough_behavior = try(each.value.passthrough_behavior, null)
content_handling_strategy = try(each.value.content_handling_strategy, null)
credentials_arn = try(each.value.credentials_arn, null)
request_parameters = try(jsondecode(each.value["request_parameters"]), each.value["request_parameters"], null)

dynamic "tls_config" {
for_each = flatten([try(jsondecode(each.value["tls_config"]), each.value["tls_config"], [])])

content {
server_name_to_verify = tls_config.value["server_name_to_verify"]
}
}

dynamic "response_parameters" {
for_each = flatten([try(jsondecode(each.value["response_parameters"]), each.value["response_parameters"], [])])

content {
status_code = response_parameters.value["status_code"]
mappings = response_parameters.value["mappings"]
Expand All @@ -175,13 +180,35 @@ resource "aws_apigatewayv2_integration" "this" {
}
}

# Authorizers
resource "aws_apigatewayv2_authorizer" "this" {
for_each = var.create && var.create_routes_and_integrations ? var.authorizers : {}

api_id = aws_apigatewayv2_api.this[0].id

authorizer_type = try(each.value.authorizer_type, null)
identity_sources = try(flatten([each.value.identity_sources]), null)
name = try(each.value.name, null)
authorizer_uri = try(each.value.authorizer_uri, null)
authorizer_payload_format_version = try(each.value.authorizer_payload_format_version, null)

dynamic "jwt_configuration" {
for_each = length(try(each.value.audience, [each.value.issuer], [])) > 0 ? [true] : []

content {
audience = try(each.value.audience, null)
issuer = try(each.value.issuer, null)
}
}
}

# VPC Link (Private API)
resource "aws_apigatewayv2_vpc_link" "this" {
for_each = var.create && var.create_vpc_link ? var.vpc_links : {}

name = lookup(each.value, "name", each.key)
name = try(each.value.name, each.key)
security_group_ids = each.value["security_group_ids"]
subnet_ids = each.value["subnet_ids"]

tags = merge(var.tags, var.vpc_link_tags, lookup(each.value, "tags", {}))
tags = merge(var.tags, var.vpc_link_tags, try(each.value.tags, {}))
}
30 changes: 15 additions & 15 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -1,68 +1,68 @@
output "apigatewayv2_api_id" {
description = "The API identifier"
value = element(concat(aws_apigatewayv2_api.this.*.id, [""]), 0)
value = try(aws_apigatewayv2_api.this[0].id, "")
}

output "apigatewayv2_api_api_endpoint" {
description = "The URI of the API"
value = element(concat(aws_apigatewayv2_api.this.*.api_endpoint, [""]), 0)
value = try(aws_apigatewayv2_api.this[0].api_endpoint, "")
}

output "apigatewayv2_api_arn" {
description = "The ARN of the API"
value = element(concat(aws_apigatewayv2_api.this.*.arn, [""]), 0)
value = try(aws_apigatewayv2_api.this[0].arn, "")
}

output "apigatewayv2_api_execution_arn" {
description = "The ARN prefix to be used in an aws_lambda_permission's source_arn attribute or in an aws_iam_policy to authorize access to the @connections API."
value = element(concat(aws_apigatewayv2_api.this.*.execution_arn, [""]), 0)
value = try(aws_apigatewayv2_api.this[0].execution_arn, "")
}

# default stage
output "default_apigatewayv2_stage_id" {
description = "The default stage identifier"
value = element(concat(aws_apigatewayv2_stage.default.*.id, [""]), 0)
value = try(aws_apigatewayv2_stage.default[0].id, "")
}

output "default_apigatewayv2_stage_arn" {
description = "The default stage ARN"
value = element(concat(aws_apigatewayv2_stage.default.*.arn, [""]), 0)
value = try(aws_apigatewayv2_stage.default[0].arn, "")
}

output "default_apigatewayv2_stage_execution_arn" {
description = "The ARN prefix to be used in an aws_lambda_permission's source_arn attribute or in an aws_iam_policy to authorize access to the @connections API."
value = element(concat(aws_apigatewayv2_stage.default.*.execution_arn, [""]), 0)
value = try(aws_apigatewayv2_stage.default[0].execution_arn, "")
}

output "default_apigatewayv2_stage_invoke_url" {
description = "The URL to invoke the API pointing to the stage"
value = element(concat(aws_apigatewayv2_stage.default.*.invoke_url, [""]), 0)
value = try(aws_apigatewayv2_stage.default[0].invoke_url, "")
}

output "default_apigatewayv2_stage_domain_name" {
description = "Domain name of the stage (useful for CloudFront distribution)"
value = replace(element(concat(aws_apigatewayv2_stage.default.*.invoke_url, [""]), 0), "/^https?://([^/]*).*/", "$1")
value = replace(try(aws_apigatewayv2_stage.default[0].invoke_url, ""), "/^https?://([^/]*).*/", "$1")
}

# domain name
output "apigatewayv2_domain_name_id" {
description = "The domain name identifier"
value = element(concat(aws_apigatewayv2_domain_name.this.*.id, [""]), 0)
value = try(aws_apigatewayv2_domain_name.this[0].id, "")
}

output "apigatewayv2_domain_name_arn" {
description = "The ARN of the domain name"
value = element(concat(aws_apigatewayv2_domain_name.this.*.arn, [""]), 0)
value = try(aws_apigatewayv2_domain_name.this[0].arn, "")
}

output "apigatewayv2_domain_name_api_mapping_selection_expression" {
description = "The API mapping selection expression for the domain name"
value = element(concat(aws_apigatewayv2_domain_name.this.*.api_mapping_selection_expression, [""]), 0)
value = try(aws_apigatewayv2_domain_name.this[0].api_mapping_selection_expression, "")
}

output "apigatewayv2_domain_name_configuration" {
description = "The domain name configuration"
value = element(concat(aws_apigatewayv2_domain_name.this.*.domain_name_configuration, [""]), 0)
value = try(aws_apigatewayv2_domain_name.this[0].domain_name_configuration, "")
}

output "apigatewayv2_domain_name_target_domain_name" {
Expand All @@ -78,13 +78,13 @@ output "apigatewayv2_domain_name_hosted_zone_id" {
# api mapping
output "apigatewayv2_api_mapping_id" {
description = "The API mapping identifier."
value = element(concat(aws_apigatewayv2_api_mapping.this.*.id, [""]), 0)
value = try(aws_apigatewayv2_api_mapping.this[0].id, "")
}

# route
# output "apigatewayv2_route_id" {
# description = "The default route identifier."
# value = element(concat(aws_apigatewayv2_route.this.*.id, [""]), 0)
# value = try(aws_apigatewayv2_route.this[0].id, "")
# }

# VPC link
Expand Down
7 changes: 7 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ variable "integrations" {
default = {}
}

# authorrizers
variable "authorizers" {
description = "Map of API gateway authorizers"
type = map(any)
default = {}
}

# vpc link
variable "vpc_links" {
description = "Map of VPC Links details to create"
Expand Down

0 comments on commit 5cd32e0

Please sign in to comment.