Skip to content

Commit 5cd32e0

Browse files
feat: Added support for Authorizers (#64)
Co-authored-by: Anton Babenko <[email protected]>
1 parent cb86a80 commit 5cd32e0

File tree

5 files changed

+123
-55
lines changed

5 files changed

+123
-55
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,28 @@ module "api_gateway" {
4646
payload_format_version = "2.0"
4747
timeout_milliseconds = 12000
4848
}
49+
50+
"GET /some-route-with-authorizer" = {
51+
integration_type = "HTTP_PROXY"
52+
integration_uri = "some url"
53+
authorizer_key = "azure"
54+
}
4955
5056
"$default" = {
5157
lambda_arn = "arn:aws:lambda:eu-west-1:052235179155:function:my-default-function"
5258
}
5359
}
5460
61+
authorizers = {
62+
"azure" = {
63+
authorizer_type = "JWT"
64+
identity_sources = "$request.header.Authorization"
65+
name = "azure-auth"
66+
audience = ["d6a38afd-45d6-4874-d1aa-3c5c558aqcc2"]
67+
issuer = "https://sts.windows.net/aaee026e-8f37-410e-8869-72d9154873e4/"
68+
}
69+
}
70+
5571
tags = {
5672
Name = "http-apigateway"
5773
}
@@ -112,6 +128,7 @@ No modules.
112128
|------|------|
113129
| [aws_apigatewayv2_api.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_api) | resource |
114130
| [aws_apigatewayv2_api_mapping.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_api_mapping) | resource |
131+
| [aws_apigatewayv2_authorizer.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_authorizer) | resource |
115132
| [aws_apigatewayv2_domain_name.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_domain_name) | resource |
116133
| [aws_apigatewayv2_integration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_integration) | resource |
117134
| [aws_apigatewayv2_route.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_route) | resource |
@@ -124,6 +141,7 @@ No modules.
124141
|------|-------------|------|---------|:--------:|
125142
| <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 |
126143
| <a name="input_api_version"></a> [api\_version](#input\_api\_version) | A version identifier for the API | `string` | `null` | no |
144+
| <a name="input_authorizers"></a> [authorizers](#input\_authorizers) | Map of API gateway authorizers | `map(any)` | `{}` | no |
127145
| <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 |
128146
| <a name="input_cors_configuration"></a> [cors\_configuration](#input\_cors\_configuration) | The cross-origin resource sharing (CORS) configuration. Applicable for HTTP APIs. | `any` | `{}` | no |
129147
| <a name="input_create"></a> [create](#input\_create) | Controls if API Gateway resources should be created | `bool` | `true` | no |

examples/complete-http/main.tf

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ module "api_gateway" {
5050
throttling_rate_limit = 100
5151
}
5252

53+
authorizers = {
54+
"cognito" = {
55+
authorizer_type = "JWT"
56+
identity_sources = "$request.header.Authorization"
57+
name = "cognito"
58+
audience = ["d6a38afd-45d6-4874-d1aa-3c5c558aqcc2"]
59+
issuer = "https://${aws_cognito_user_pool.this.endpoint}"
60+
}
61+
}
62+
5363
integrations = {
5464

5565
"ANY /" = {
@@ -65,6 +75,12 @@ module "api_gateway" {
6575
authorizer_id = aws_apigatewayv2_authorizer.some_authorizer.id
6676
}
6777

78+
"GET /some-route-with-authorizer" = {
79+
lambda_arn = module.lambda_function.lambda_function_arn
80+
payload_format_version = "2.0"
81+
authorizer_key = "cognito"
82+
}
83+
6884
"POST /start-step-function" = {
6985
integration_type = "AWS_PROXY"
7086
integration_subtype = "StepFunctions-StartExecution"
@@ -259,7 +275,7 @@ module "lambda_function" {
259275

260276
resource "aws_s3_bucket" "truststore" {
261277
bucket = "${random_pet.this.id}-truststore"
262-
acl = "private"
278+
# acl = "private"
263279
}
264280

265281
resource "aws_s3_bucket_object" "truststore" {

main.tf

Lines changed: 66 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ resource "aws_apigatewayv2_api" "this" {
2222
for_each = length(keys(var.cors_configuration)) == 0 ? [] : [var.cors_configuration]
2323

2424
content {
25-
allow_credentials = lookup(cors_configuration.value, "allow_credentials", null)
26-
allow_headers = lookup(cors_configuration.value, "allow_headers", null)
27-
allow_methods = lookup(cors_configuration.value, "allow_methods", null)
28-
allow_origins = lookup(cors_configuration.value, "allow_origins", null)
29-
expose_headers = lookup(cors_configuration.value, "expose_headers", null)
30-
max_age = lookup(cors_configuration.value, "max_age", null)
25+
allow_credentials = try(cors_configuration.value.allow_credentials, null)
26+
allow_headers = try(cors_configuration.value.allow_headers, null)
27+
allow_methods = try(cors_configuration.value.allow_methods, null)
28+
allow_origins = try(cors_configuration.value.allow_origins, null)
29+
expose_headers = try(cors_configuration.value.expose_headers, null)
30+
max_age = try(cors_configuration.value.max_age, null)
3131
}
3232
}
3333

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

4949
dynamic "mutual_tls_authentication" {
5050
for_each = length(keys(var.mutual_tls_authentication)) == 0 ? [] : [var.mutual_tls_authentication]
51+
5152
content {
5253
truststore_uri = mutual_tls_authentication.value.truststore_uri
53-
truststore_version = lookup(mutual_tls_authentication.value, "truststore_version", null)
54+
truststore_version = try(mutual_tls_authentication.value.truststore_version, null)
5455
}
5556
}
5657

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

6869
dynamic "access_log_settings" {
6970
for_each = var.default_stage_access_log_destination_arn != null && var.default_stage_access_log_format != null ? [true] : []
71+
7072
content {
7173
destination_arn = var.default_stage_access_log_destination_arn
7274
format = var.default_stage_access_log_format
@@ -75,12 +77,13 @@ resource "aws_apigatewayv2_stage" "default" {
7577

7678
dynamic "default_route_settings" {
7779
for_each = length(keys(var.default_route_settings)) == 0 ? [] : [var.default_route_settings]
80+
7881
content {
79-
data_trace_enabled = lookup(default_route_settings.value, "data_trace_enabled", false)
80-
detailed_metrics_enabled = lookup(default_route_settings.value, "detailed_metrics_enabled", false)
81-
logging_level = lookup(default_route_settings.value, "logging_level", null)
82-
throttling_burst_limit = lookup(default_route_settings.value, "throttling_burst_limit", null)
83-
throttling_rate_limit = lookup(default_route_settings.value, "throttling_rate_limit", null)
82+
data_trace_enabled = try(default_route_settings.value.data_trace_enabled, false)
83+
detailed_metrics_enabled = try(default_route_settings.value.detailed_metrics_enabled, false)
84+
logging_level = try(default_route_settings.value.logging_level, null)
85+
throttling_burst_limit = try(default_route_settings.value.throttling_burst_limit, null)
86+
throttling_rate_limit = try(default_route_settings.value.throttling_rate_limit, null)
8487
}
8588
}
8689

@@ -89,11 +92,11 @@ resource "aws_apigatewayv2_stage" "default" {
8992
# for_each = var.create_routes_and_integrations ? var.integrations : {}
9093
# content {
9194
# route_key = route_settings.key
92-
# data_trace_enabled = lookup(route_settings.value, "data_trace_enabled", null)
93-
# detailed_metrics_enabled = lookup(route_settings.value, "detailed_metrics_enabled", null)
94-
# 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
95-
# throttling_burst_limit = lookup(route_settings.value, "throttling_burst_limit", null)
96-
# throttling_rate_limit = lookup(route_settings.value, "throttling_rate_limit", null)
95+
# data_trace_enabled = try(route_settings.value.data_trace_enabled, null)
96+
# detailed_metrics_enabled = try(route_settings.value.detailed_metrics_enabled, null)
97+
# 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
98+
# throttling_burst_limit = try(route_settings.value.throttling_burst_limit, null)
99+
# throttling_rate_limit = try(route_settings.value.throttling_rate_limit, null)
97100
# }
98101
# }
99102

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

124-
api_key_required = lookup(each.value, "api_key_required", null)
125-
authorization_type = lookup(each.value, "authorization_type", "NONE")
126-
authorizer_id = lookup(each.value, "authorizer_id", null)
127-
model_selection_expression = lookup(each.value, "model_selection_expression", null)
128-
operation_name = lookup(each.value, "operation_name", null)
129-
route_response_selection_expression = lookup(each.value, "route_response_selection_expression", null)
127+
api_key_required = try(each.value.api_key_required, null)
128+
authorization_type = try(each.value.authorization_type, "NONE")
129+
authorizer_id = try(aws_apigatewayv2_authorizer.this[each.value.authorizer_key].id, each.value.authorizer_id, null)
130+
model_selection_expression = try(each.value.model_selection_expression, null)
131+
operation_name = try(each.value.operation_name, null)
132+
route_response_selection_expression = try(each.value.route_response_selection_expression, null)
130133
target = "integrations/${aws_apigatewayv2_integration.this[each.key].id}"
131134

132135
# Not sure what structure is allowed for these arguments...
133-
# authorization_scopes = lookup(each.value, "authorization_scopes", null)
134-
# request_models = lookup(each.value, "request_models", null)
136+
# authorization_scopes = try(each.value.authorization_scopes, null)
137+
# request_models = try(each.value.request_models, null)
135138
}
136139

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

140143
api_id = aws_apigatewayv2_api.this[0].id
141-
description = lookup(each.value, "description", null)
144+
description = try(each.value.description, null)
142145

143-
integration_type = lookup(each.value, "integration_type", lookup(each.value, "lambda_arn", "") != "" ? "AWS_PROXY" : "MOCK")
144-
integration_subtype = lookup(each.value, "integration_subtype", null)
145-
integration_method = lookup(each.value, "integration_method", lookup(each.value, "integration_subtype", null) == null ? "POST" : null)
146-
integration_uri = lookup(each.value, "lambda_arn", lookup(each.value, "integration_uri", null))
146+
integration_type = try(each.value.integration_type, try(each.value.lambda_arn, "") != "" ? "AWS_PROXY" : "MOCK")
147+
integration_subtype = try(each.value.integration_subtype, null)
148+
integration_method = try(each.value.integration_method, try(each.value.integration_subtype, null) == null ? "POST" : null)
149+
integration_uri = try(each.value.lambda_arn, try(each.value.integration_uri, null))
147150

148-
connection_type = lookup(each.value, "connection_type", "INTERNET")
149-
connection_id = try(aws_apigatewayv2_vpc_link.this[each.value["vpc_link"]].id, lookup(each.value, "connection_id", null))
151+
connection_type = try(each.value.connection_type, "INTERNET")
152+
connection_id = try(aws_apigatewayv2_vpc_link.this[each.value["vpc_link"]].id, try(each.value.connection_id, null))
150153

151-
payload_format_version = lookup(each.value, "payload_format_version", null)
152-
timeout_milliseconds = lookup(each.value, "timeout_milliseconds", null)
153-
passthrough_behavior = lookup(each.value, "passthrough_behavior", null)
154-
content_handling_strategy = lookup(each.value, "content_handling_strategy", null)
155-
credentials_arn = lookup(each.value, "credentials_arn", null)
154+
payload_format_version = try(each.value.payload_format_version, null)
155+
timeout_milliseconds = try(each.value.timeout_milliseconds, null)
156+
passthrough_behavior = try(each.value.passthrough_behavior, null)
157+
content_handling_strategy = try(each.value.content_handling_strategy, null)
158+
credentials_arn = try(each.value.credentials_arn, null)
156159
request_parameters = try(jsondecode(each.value["request_parameters"]), each.value["request_parameters"], null)
157160

158161
dynamic "tls_config" {
159162
for_each = flatten([try(jsondecode(each.value["tls_config"]), each.value["tls_config"], [])])
163+
160164
content {
161165
server_name_to_verify = tls_config.value["server_name_to_verify"]
162166
}
163167
}
164168

165169
dynamic "response_parameters" {
166170
for_each = flatten([try(jsondecode(each.value["response_parameters"]), each.value["response_parameters"], [])])
171+
167172
content {
168173
status_code = response_parameters.value["status_code"]
169174
mappings = response_parameters.value["mappings"]
@@ -175,13 +180,35 @@ resource "aws_apigatewayv2_integration" "this" {
175180
}
176181
}
177182

183+
# Authorizers
184+
resource "aws_apigatewayv2_authorizer" "this" {
185+
for_each = var.create && var.create_routes_and_integrations ? var.authorizers : {}
186+
187+
api_id = aws_apigatewayv2_api.this[0].id
188+
189+
authorizer_type = try(each.value.authorizer_type, null)
190+
identity_sources = try(flatten([each.value.identity_sources]), null)
191+
name = try(each.value.name, null)
192+
authorizer_uri = try(each.value.authorizer_uri, null)
193+
authorizer_payload_format_version = try(each.value.authorizer_payload_format_version, null)
194+
195+
dynamic "jwt_configuration" {
196+
for_each = length(try(each.value.audience, [each.value.issuer], [])) > 0 ? [true] : []
197+
198+
content {
199+
audience = try(each.value.audience, null)
200+
issuer = try(each.value.issuer, null)
201+
}
202+
}
203+
}
204+
178205
# VPC Link (Private API)
179206
resource "aws_apigatewayv2_vpc_link" "this" {
180207
for_each = var.create && var.create_vpc_link ? var.vpc_links : {}
181208

182-
name = lookup(each.value, "name", each.key)
209+
name = try(each.value.name, each.key)
183210
security_group_ids = each.value["security_group_ids"]
184211
subnet_ids = each.value["subnet_ids"]
185212

186-
tags = merge(var.tags, var.vpc_link_tags, lookup(each.value, "tags", {}))
213+
tags = merge(var.tags, var.vpc_link_tags, try(each.value.tags, {}))
187214
}

outputs.tf

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,68 @@
11
output "apigatewayv2_api_id" {
22
description = "The API identifier"
3-
value = element(concat(aws_apigatewayv2_api.this.*.id, [""]), 0)
3+
value = try(aws_apigatewayv2_api.this[0].id, "")
44
}
55

66
output "apigatewayv2_api_api_endpoint" {
77
description = "The URI of the API"
8-
value = element(concat(aws_apigatewayv2_api.this.*.api_endpoint, [""]), 0)
8+
value = try(aws_apigatewayv2_api.this[0].api_endpoint, "")
99
}
1010

1111
output "apigatewayv2_api_arn" {
1212
description = "The ARN of the API"
13-
value = element(concat(aws_apigatewayv2_api.this.*.arn, [""]), 0)
13+
value = try(aws_apigatewayv2_api.this[0].arn, "")
1414
}
1515

1616
output "apigatewayv2_api_execution_arn" {
1717
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."
18-
value = element(concat(aws_apigatewayv2_api.this.*.execution_arn, [""]), 0)
18+
value = try(aws_apigatewayv2_api.this[0].execution_arn, "")
1919
}
2020

2121
# default stage
2222
output "default_apigatewayv2_stage_id" {
2323
description = "The default stage identifier"
24-
value = element(concat(aws_apigatewayv2_stage.default.*.id, [""]), 0)
24+
value = try(aws_apigatewayv2_stage.default[0].id, "")
2525
}
2626

2727
output "default_apigatewayv2_stage_arn" {
2828
description = "The default stage ARN"
29-
value = element(concat(aws_apigatewayv2_stage.default.*.arn, [""]), 0)
29+
value = try(aws_apigatewayv2_stage.default[0].arn, "")
3030
}
3131

3232
output "default_apigatewayv2_stage_execution_arn" {
3333
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."
34-
value = element(concat(aws_apigatewayv2_stage.default.*.execution_arn, [""]), 0)
34+
value = try(aws_apigatewayv2_stage.default[0].execution_arn, "")
3535
}
3636

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

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

4747
# domain name
4848
output "apigatewayv2_domain_name_id" {
4949
description = "The domain name identifier"
50-
value = element(concat(aws_apigatewayv2_domain_name.this.*.id, [""]), 0)
50+
value = try(aws_apigatewayv2_domain_name.this[0].id, "")
5151
}
5252

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

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

6363
output "apigatewayv2_domain_name_configuration" {
6464
description = "The domain name configuration"
65-
value = element(concat(aws_apigatewayv2_domain_name.this.*.domain_name_configuration, [""]), 0)
65+
value = try(aws_apigatewayv2_domain_name.this[0].domain_name_configuration, "")
6666
}
6767

6868
output "apigatewayv2_domain_name_target_domain_name" {
@@ -78,13 +78,13 @@ output "apigatewayv2_domain_name_hosted_zone_id" {
7878
# api mapping
7979
output "apigatewayv2_api_mapping_id" {
8080
description = "The API mapping identifier."
81-
value = element(concat(aws_apigatewayv2_api_mapping.this.*.id, [""]), 0)
81+
value = try(aws_apigatewayv2_api_mapping.this[0].id, "")
8282
}
8383

8484
# route
8585
# output "apigatewayv2_route_id" {
8686
# description = "The default route identifier."
87-
# value = element(concat(aws_apigatewayv2_route.this.*.id, [""]), 0)
87+
# value = try(aws_apigatewayv2_route.this[0].id, "")
8888
# }
8989

9090
# VPC link

variables.tf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ variable "integrations" {
194194
default = {}
195195
}
196196

197+
# authorrizers
198+
variable "authorizers" {
199+
description = "Map of API gateway authorizers"
200+
type = map(any)
201+
default = {}
202+
}
203+
197204
# vpc link
198205
variable "vpc_links" {
199206
description = "Map of VPC Links details to create"

0 commit comments

Comments
 (0)