terraform-aws-modules / terraform-aws-apigateway-v2

Terraform module to create AWS API Gateway v2 (HTTP/WebSocket) πŸ‡ΊπŸ‡¦
https://registry.terraform.io/modules/terraform-aws-modules/apigateway-v2/aws
Apache License 2.0
147 stars 199 forks source link

Custom domain name setup creates the wrong cert and mapping #112

Closed richard5mith closed 2 weeks ago

richard5mith commented 1 month ago

Description

This is a bit of a follow-on from issue #105.

When using v5.1.0, if you set:

domain_name = "api.example.com"

And your hosted zone is example.com then you get the error listed in that previous issue:

Error: no matching Route 53 Hosted Zone found

As it's looking for a zone called api.example.com.

The answer in that question was to do:

domain_name = "example.com"
subdomains = ["api"]

Which then does create your resources. The problem is that it makes mistakes.

It correctly creates the DNS entry of api.example.com pointing at the API which was created.

But the ACM certificate is for example.com, not api.example.com. And the custom domain mapping it creates is also from example.com to your API, not api.example.com to your API.

So you have the correct DNS entry, but everything else is wrong. In fact it looks like it ignored the subdomain completely when creating the ACM and mapping resources.

My first time using this module and it's taken me many hours to work out this has been the cause of my 403 errors.

If your request is for a new feature, please use the Feature request template.

bryantbiggs commented 1 month ago

if you are going to use sub-domains, you would use the wildcard:

domain_name = "*.example.com"
subdomains = ["api"]
richard5mith commented 1 month ago

The result of that is weird. You get a cert called "example.com" with alternate names of "example.com" and ".example.com". And then the domain mapping on the API GW is from .example.com to your API.

It still completely ignores the subdomain.

It would be better as hosted_zone instead of domain_name. And then subdomains should actually be used to create the ACM and the mappings.

machulav commented 1 month ago

I have the same issue, and the suggested workaround by @bryantbiggs doesn't work for me because I have two services on different subdomains and separate API Gateways, and if I use the config like the following:

Service A:

domain_name = "*.example.com"
subdomains = ["a"]

Service B:

domain_name = "*.example.com"
subdomains = ["b"]

Then service A has a custom domain *.example.com in API Gateway config.

And the service B returns error during creation:

module.api_gateway.aws_apigatewayv2_domain_name.this[0]: Creating...
β•·
β”‚ Error: creating API Gateway v2 Domain Name (*.example.com): operation error ApiGatewayV2: CreateDomainName, https response error StatusCode: 400, RequestID: 202db311-5ca3-4e38-88e6-73767d2fb716, BadRequestException: The domain name you provided already exists.
β”‚ 
β”‚   with module.api_gateway.aws_apigatewayv2_domain_name.this[0],
β”‚   on .terraform/modules/api_gateway/main.tf line 86, in resource "aws_apigatewayv2_domain_name" "this":
β”‚   86: resource "aws_apigatewayv2_domain_name" "this" {

This is because the *.example.com can not be used on the second API Gateway once it has been used.

@antonbabenko may I ask you to check this?

bryantbiggs commented 1 month ago

I mean, in all honesty - its doubtful we will be able to cover every scenario within this module. API gateway supports wildcard domains, so does this module. However, as you pointed out, how you use and consume that wildcard domain may vary and therefore you might have to create some resources outside of this module and pass them as inputs

The wildcard pattern that was top of mind when adding support to this module was for using multiple sub-domains on the same API gateway. I think the pattern you are looking at using is sort of the opposite - multiple sub-domains across multiple gateways. In that scenario, I don't think it makes sense to create the cert and custom domain in this module configuration - those should probably be created on their own and shared across the module definitions

machulav commented 1 month ago

I mean, in all honesty - its doubtful we will be able to cover every scenario within this module. I don't really think this is a special case.

Once I specify the config like this:

domain_name = "a.example.com"

I just need a subdomain to be created properly in two places:

So once it's deployed, I can call my Lambda function through the API Gateway on a.example.com.

I don't really need the subdomains. I use the subdomains property as a workaround because the domain_name property doesn't work as expected.

I get this error if I specify domain_name = "a.example.com":

Error: no matching Route 53 Hosted Zone found
β”‚ 
β”‚   with module.api_gateway.data.aws_route53_zone.this[0],
β”‚   on .terraform/modules/api_gateway/main.tf line 134, in data "aws_route53_zone" "this":
β”‚  134: data "aws_route53_zone" "this" {

In that scenario, I don't think it makes sense to create the cert and custom domain in this module configuration - those should probably be created on their own and shared across the module definitions

Actually, it totally makes sense (and not just in my case) to create a subdomain in (Route53 and API Gateway) as I create all the resources for my service (Lambda + API Gateway), and I also want the domain to be managed in the same terraform file, so it's created and removed together with the service.

Note, a certificate is not created in this setup. I just reuse the one I have.

richard5mith commented 1 month ago

It just seems like this is so close to being really flexible, if you could only specify the exact domain you wanted (wildcard or not) separate from the zone id that the record should be created in, then that would surely resolve the issues.

machulav commented 1 month ago

Unfortunately, it does not work like this:

module "api_gateway" {
  source  = "terraform-aws-modules/apigateway-v2/aws"
  version = "~> 5.1"

  name                  = "${var.service_name}-${var.environment}-gateway"
  protocol_type         = "HTTP"
  domain_name           = "a.example.com"

  create_certificate          = false
  domain_name_certificate_arn = "..."

  routes = {
    "$default" = {
      integration = {
        uri = module.aws_lambda.lambda_function_arn
      }
    }
  }

  tags = {
    Service     = var.service_name
    Environment = var.environment
  }
}

I get this error:

Error: no matching Route 53 Hosted Zone found
β”‚ 
β”‚   with module.api_gateway.data.aws_route53_zone.this[0],
β”‚   on .terraform/modules/api_gateway/main.tf line 134, in data "aws_route53_zone" "this":
β”‚  134: data "aws_route53_zone" "this" {

Maybe I missed something?

separate from the zone id that the record should be created in

How can I specify the zone ID? I can't find such a property in the list of inputs for the module here: https://registry.terraform.io/modules/terraform-aws-modules/apigateway-v2/aws/latest?tab=inputs

richard5mith commented 1 month ago

How can I specify the zone ID

You can't. If you see my earlier comment that was my suggestion for fixing my problem and I think it would fix yours too.

Then you could say this is my zone id, this is my domain. And then it would just create a record in that zone id for your domain, get the cert for your domain and attach your domain to the gateway. If a user wanted it to be "a.example.com" they could put that along with the zone_id of example.com. If they wanted it to be "*.example.com" then they'd specify that, again along with the zone_id of example.com.

machulav commented 1 month ago

It just seems like this is so close to being really flexible, if you could only specify the exact domain you wanted (wildcard or not) separate from the zone id that the record should be created in, then that would surely resolve the issues.

ok, yes, I agree! I believe this would resolve my issue as well.

tonnenpinguin commented 4 weeks ago

I opened #119 that provides a possible fix for this since I kept banging my head against a wall trying to get this to work πŸ™‚

antonbabenko commented 2 weeks ago

This issue has been resolved in version 5.2.0 :tada: