Skip to content

Commit 49b1638

Browse files
committed
fix: Correct custom domain use; add support for multiple subdomains with wildcard
1 parent 4d58196 commit 49b1638

File tree

9 files changed

+41
-108
lines changed

9 files changed

+41
-108
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ module "api_gateway" {
132132
| [aws_apigatewayv2_stage.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_stage) | resource |
133133
| [aws_apigatewayv2_vpc_link.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_vpc_link) | resource |
134134
| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
135-
| [aws_route53_record.api](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
135+
| [aws_route53_record.alias_ipv4](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
136136
| [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source |
137137

138138
## Inputs
@@ -171,7 +171,7 @@ module "api_gateway" {
171171
| <a name="input_stage_name"></a> [stage\_name](#input\_stage\_name) | The name of the stage. Must be between 1 and 128 characters in length | `string` | `"$default"` | no |
172172
| <a name="input_stage_tags"></a> [stage\_tags](#input\_stage\_tags) | A mapping of tags to assign to the stage resource | `map(string)` | `{}` | no |
173173
| <a name="input_stage_variables"></a> [stage\_variables](#input\_stage\_variables) | A map that defines the stage variables for the stage | `map(string)` | `{}` | no |
174-
| <a name="input_subdomain"></a> [subdomain](#input\_subdomain) | An optional subdomain to use for API gateway (prepended to the `domain_name` when the records are created) | `string` | `null` | no |
174+
| <a name="input_subdomains"></a> [subdomains](#input\_subdomains) | An optional list of subdomains to use for API gateway | `list(string)` | `[]` | no |
175175
| <a name="input_tags"></a> [tags](#input\_tags) | A mapping of tags to assign to API gateway resources | `map(string)` | `{}` | no |
176176
| <a name="input_target"></a> [target](#input\_target) | Part of quick create. Quick create produces an API with an integration, a default catch-all route, and a default stage which is configured to automatically deploy changes. For HTTP integrations, specify a fully qualified URL. For Lambda integrations, specify a function ARN. The type of the integration will be HTTP\_PROXY or AWS\_PROXY, respectively. Applicable for HTTP APIs | `string` | `null` | no |
177177
| <a name="input_vpc_link_tags"></a> [vpc\_link\_tags](#input\_vpc\_link\_tags) | A map of tags to add to the VPC Links created | `map(string)` | `{}` | no |

examples/complete-http/README.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,38 +21,32 @@ Note that this example may create resources which cost money. Run `terraform des
2121
|------|---------|
2222
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3 |
2323
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.37 |
24-
| <a name="requirement_tls"></a> [tls](#requirement\_tls) | >= 3.1 |
2524

2625
## Providers
2726

2827
| Name | Version |
2928
|------|---------|
3029
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.37 |
31-
| <a name="provider_tls"></a> [tls](#provider\_tls) | >= 3.1 |
3230

3331
## Modules
3432

3533
| Name | Source | Version |
3634
|------|--------|---------|
3735
| <a name="module_api_gateway"></a> [api\_gateway](#module\_api\_gateway) | ../../ | n/a |
3836
| <a name="module_lambda_function"></a> [lambda\_function](#module\_lambda\_function) | terraform-aws-modules/lambda/aws | ~> 7.0 |
39-
| <a name="module_s3_bucket"></a> [s3\_bucket](#module\_s3\_bucket) | terraform-aws-modules/s3-bucket/aws | ~> 4.0 |
4037
| <a name="module_step_function"></a> [step\_function](#module\_step\_function) | terraform-aws-modules/step-functions/aws | ~> 4.0 |
4138

4239
## Resources
4340

4441
| Name | Type |
4542
|------|------|
4643
| [aws_cognito_user_pool.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cognito_user_pool) | resource |
47-
| [aws_s3_object.truststore](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource |
48-
| [tls_private_key.private_key](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource |
49-
| [tls_self_signed_cert.example](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/self_signed_cert) | resource |
5044

5145
## Inputs
5246

5347
| Name | Description | Type | Default | Required |
5448
|------|-------------|------|---------|:--------:|
55-
| <a name="input_domain_name"></a> [domain\_name](#input\_domain\_name) | Custom domain name to use on API Gateway endpoint | `string` | `"sharedservices.clowd.haus"` | no |
49+
| <a name="input_domain_name"></a> [domain\_name](#input\_domain\_name) | Custom domain name to use on API Gateway endpoint | `string` | `"*.sharedservices.clowd.haus"` | no |
5650

5751
## Outputs
5852

examples/complete-http/api.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
openapi: "3.0.1"
22
info:
33
version: 0.0.1
4-
title: My awesome API
54
description: My awesome API
65
paths:
76
/debug:

examples/complete-http/main.tf

Lines changed: 7 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,9 @@ module "api_gateway" {
5252
# Domain Name
5353
create_domain_name = true
5454
domain_name = var.domain_name
55+
subdomains = ["customer1", "customer2"]
5556
create_domain_records = true
5657
create_certificate = true
57-
subdomain = "example"
58-
59-
mutual_tls_authentication = {
60-
truststore_uri = "s3://${module.s3_bucket.s3_bucket_id}/${aws_s3_object.truststore.id}"
61-
truststore_version = aws_s3_object.truststore.version_id
62-
}
6358

6459
# Routes & Integration(s)
6560
integrations = {
@@ -93,39 +88,31 @@ module "api_gateway" {
9388
authorization_scopes = ["user.id", "user.email"]
9489
}
9590

96-
"GET /some-route-with-authorizer-and-different-scope" = {
97-
lambda_arn = module.lambda_function.lambda_function_arn
98-
payload_format_version = "2.0"
99-
authorization_type = "JWT"
100-
authorizer_key = "cognito"
101-
authorization_scopes = ["user.id", "user.email"]
102-
}
103-
10491
"POST /start-step-function" = {
10592
integration_type = "AWS_PROXY"
10693
integration_subtype = "StepFunctions-StartExecution"
10794
credentials_arn = module.step_function.role_arn
10895

10996
# Note: jsonencode is used to pass argument as a string
110-
request_parameters = jsonencode({
97+
request_parameters = {
11198
StateMachineArn = module.step_function.state_machine_arn
11299
Input = jsonencode({
113100
"key1" : "value1",
114101
"key2" : "value2"
115102
})
116-
})
103+
}
117104

118105
payload_format_version = "1.0"
119106
timeout_milliseconds = 12000
120107
}
121108

122109
"$default" = {
123110
lambda_arn = module.lambda_function.lambda_function_arn
124-
tls_config = jsonencode({
111+
tls_config = {
125112
server_name_to_verify = var.domain_name
126-
})
113+
}
127114

128-
response_parameters = jsonencode([
115+
response_parameters = [
129116
{
130117
status_code = 500
131118
mappings = {
@@ -139,7 +126,7 @@ module "api_gateway" {
139126
"append:header.error" = "$stageVariables.environmentId"
140127
}
141128
}
142-
])
129+
]
143130
}
144131
}
145132

@@ -248,54 +235,3 @@ module "lambda_function" {
248235

249236
tags = local.tags
250237
}
251-
252-
module "s3_bucket" {
253-
source = "terraform-aws-modules/s3-bucket/aws"
254-
version = "~> 4.0"
255-
256-
bucket_prefix = "${local.name}-"
257-
258-
# Allow deletion of non-empty bucket
259-
# Example usage only - not recommended for production
260-
force_destroy = true
261-
262-
attach_deny_insecure_transport_policy = true
263-
attach_require_latest_tls_policy = true
264-
265-
server_side_encryption_configuration = {
266-
rule = {
267-
apply_server_side_encryption_by_default = {
268-
sse_algorithm = "AES256"
269-
}
270-
}
271-
}
272-
273-
tags = local.tags
274-
}
275-
276-
resource "aws_s3_object" "truststore" {
277-
bucket = module.s3_bucket.s3_bucket_id
278-
key = "truststore.pem"
279-
content = tls_self_signed_cert.example.cert_pem
280-
}
281-
282-
resource "tls_private_key" "private_key" {
283-
algorithm = "RSA"
284-
}
285-
286-
resource "tls_self_signed_cert" "example" {
287-
is_ca_certificate = true
288-
private_key_pem = tls_private_key.private_key.private_key_pem
289-
290-
subject {
291-
common_name = "example.com"
292-
organization = "ACME Examples, Inc"
293-
}
294-
295-
validity_period_hours = 12
296-
297-
allowed_uses = [
298-
"cert_signing",
299-
"server_auth",
300-
]
301-
}

examples/complete-http/variables.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ variable "domain_name" {
22
description = "Custom domain name to use on API Gateway endpoint"
33
type = string
44
# default = "terraform-aws-modules.modules.tf"
5-
default = "sharedservices.clowd.haus"
5+
default = "*.sharedservices.clowd.haus"
66
}

examples/complete-http/versions.tf

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,5 @@ terraform {
66
source = "hashicorp/aws"
77
version = ">= 5.37"
88
}
9-
tls = {
10-
source = "hashicorp/tls"
11-
version = ">= 3.1"
12-
}
139
}
1410
}

main.tf

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ resource "aws_apigatewayv2_api" "this" {
2828
}
2929
}
3030

31-
credentials_arn = local.is_http ? var.credentials_arn : null
32-
description = var.description
33-
disable_execute_api_endpoint = var.disable_execute_api_endpoint
31+
credentials_arn = local.is_http ? var.credentials_arn : null
32+
description = var.description
33+
# https://docs.aws.amazon.com/apigateway/latest/developerguide/rest-api-disable-default-endpoint.html
34+
disable_execute_api_endpoint = local.is_http && local.create_domain_name ? true : var.disable_execute_api_endpoint
3435
fail_on_warnings = local.is_http ? var.fail_on_warnings : null
3536
name = var.name
3637
protocol_type = var.protocol_type
@@ -104,7 +105,7 @@ resource "aws_apigatewayv2_domain_name" "this" {
104105
}
105106

106107
resource "aws_apigatewayv2_api_mapping" "this" {
107-
count = local.create_domain_name && local.create_stage && var.api_mapping_key != null ? 1 : 0
108+
count = local.create_domain_name && local.create_stage ? 1 : 0
108109

109110
api_id = aws_apigatewayv2_api.this[0].id
110111
api_mapping_key = var.api_mapping_key
@@ -116,18 +117,23 @@ resource "aws_apigatewayv2_api_mapping" "this" {
116117
# Domain - Route53 Record
117118
################################################################################
118119

120+
locals {
121+
# https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains.html
122+
stripped_domain_name = replace(var.domain_name, "*.", "")
123+
}
124+
119125
data "aws_route53_zone" "this" {
120126
count = local.create_domain_name && var.create_domain_records ? 1 : 0
121127

122-
name = var.domain_name
128+
name = local.stripped_domain_name
123129
}
124130

125-
resource "aws_route53_record" "api" {
126-
for_each = { for k, v in toset(["A", "AAAA"]) : k => v if local.create_domain_name && var.create_domain_records }
131+
resource "aws_route53_record" "alias_ipv4" {
132+
for_each = { for k, v in toset(var.subdomains) : k => v if local.create_domain_name && var.create_domain_records }
127133

128134
zone_id = data.aws_route53_zone.this[0].zone_id
129-
name = var.subdomain != null ? "${var.subdomain}.${aws_apigatewayv2_domain_name.this[0].id}" : aws_apigatewayv2_domain_name.this[0].id
130-
type = each.value
135+
name = each.value
136+
type = "A"
131137

132138
alias {
133139
name = aws_apigatewayv2_domain_name.this[0].domain_name_configuration[0].target_domain_name
@@ -142,6 +148,8 @@ resource "aws_route53_record" "api" {
142148

143149
locals {
144150
create_certificate = local.create_domain_name && var.create_certificate
151+
152+
is_wildcard = startswith(var.domain_name, "*.")
145153
}
146154

147155
module "acm" {
@@ -150,9 +158,9 @@ module "acm" {
150158

151159
create_certificate = local.create_certificate
152160

153-
domain_name = var.domain_name
161+
domain_name = local.stripped_domain_name
154162
zone_id = data.aws_route53_zone.this[0].id
155-
subject_alternative_names = var.subdomain != null ? ["${var.subdomain}.${var.domain_name}"] : []
163+
subject_alternative_names = local.is_wildcard ? [var.domain_name] : [for sub in var.subdomains : "${sub}.${local.stripped_domain_name}"]
156164

157165
validation_method = "DNS"
158166

@@ -179,11 +187,11 @@ resource "aws_apigatewayv2_integration" "this" {
179187
integration_uri = try(each.value.lambda_arn, try(each.value.integration_uri, null))
180188
passthrough_behavior = try(each.value.passthrough_behavior, null)
181189
payload_format_version = try(each.value.payload_format_version, null)
182-
request_parameters = try(jsondecode(each.value.request_parameters), each.value.request_parameters, null)
183-
request_templates = try(jsondecode(each.value.request_templates), each.value.request_templates, null)
190+
request_parameters = try(each.value.request_parameters, null)
191+
request_templates = try(each.value.request_templates, null)
184192

185193
dynamic "response_parameters" {
186-
for_each = flatten([try(jsondecode(each.value.response_parameters), each.value.response_parameters, [])])
194+
for_each = try(each.value.response_parameters, [])
187195

188196
content {
189197
mappings = response_parameters.value.mappings
@@ -195,10 +203,10 @@ resource "aws_apigatewayv2_integration" "this" {
195203
timeout_milliseconds = try(each.value.timeout_milliseconds, null)
196204

197205
dynamic "tls_config" {
198-
for_each = flatten([try(jsondecode(each.value.tls_config), each.value.tls_config, [])])
206+
for_each = try([each.value.tls_config], [])
199207

200208
content {
201-
server_name_to_verify = tls_config.value.server_name_to_verify
209+
server_name_to_verify = replace(tls_config.value.server_name_to_verify, "*.", "")
202210
}
203211
}
204212

@@ -219,7 +227,7 @@ resource "aws_apigatewayv2_integration_response" "this" {
219227

220228
content_handling_strategy = try(each.value.content_handling_strategy, null)
221229
integration_response_key = each.value.integration_response_key
222-
response_templates = try(jsondecode(each.value.response_templates), each.value.response_templates, null)
230+
response_templates = try(each.value.response_templates, null)
223231
template_selection_expression = try(each.value.template_selection_expression, null)
224232
}
225233

variables.tf

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,10 @@ variable "create_domain_records" {
152152
default = false
153153
}
154154

155-
variable "subdomain" {
156-
description = "An optional subdomain to use for API gateway (prepended to the `domain_name` when the records are created)"
157-
type = string
158-
default = null
155+
variable "subdomains" {
156+
description = "An optional list of subdomains to use for API gateway"
157+
type = list(string)
158+
default = []
159159
}
160160

161161
################################################################################

wrappers/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ module "wrapper" {
3535
stage_name = try(each.value.stage_name, var.defaults.stage_name, "$default")
3636
stage_tags = try(each.value.stage_tags, var.defaults.stage_tags, {})
3737
stage_variables = try(each.value.stage_variables, var.defaults.stage_variables, {})
38-
subdomain = try(each.value.subdomain, var.defaults.subdomain, null)
38+
subdomains = try(each.value.subdomains, var.defaults.subdomains, [])
3939
tags = try(each.value.tags, var.defaults.tags, {})
4040
target = try(each.value.target, var.defaults.target, null)
4141
vpc_link_tags = try(each.value.vpc_link_tags, var.defaults.vpc_link_tags, {})

0 commit comments

Comments
 (0)