AM DEV
AM DEV

Reputation: 339

Error creating cloudfront distribution with terraform. InvalidViewerCertificate

I'm trying to deploy a ReactJS project statically to s3 using Terraform

My s3 bucket terraform config to create the bucket with the policy:

resource "aws_s3_bucket" "site" {
  bucket = var.domain
  acl = "public-read"

  policy = <<EOF
{
  "Version":"2012-10-17",
  "Statement":[{
        "Sid":"PublicReadForGetBucketObjects",
        "Effect":"Allow",
          "Principal": "*",
      "Action":["s3:GetObject"],
      "Resource":["arn:aws:s3:::${var.domain}/*"]
    }
  ]
}
  EOF

  website {
      index_document = "index.html"
      error_document = "404.html"
  }
}

route53 config with the necessary dns:

resource "aws_route53_zone" "main" {
  name = var.domain
}

resource "aws_route53_record" "root_domain" {
  zone_id = aws_route53_zone.main.zone_id
  name = var.domain
  type = "A"

  alias {
    name = aws_cloudfront_distribution.cdn.domain_name
    zone_id = aws_cloudfront_distribution.cdn.hosted_zone_id
    evaluate_target_health = false
  }
}

cloudfront config:

resource "aws_cloudfront_distribution" "cdn" {
  origin {
    origin_id   = var.domain
    domain_name = aws_s3_bucket.site.bucket_regional_domain_name

    custom_origin_config {
      http_port = 80
      https_port = 443
      origin_protocol_policy = "match-viewer"
      origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
    }
  }

  aliases = [var.domain]

  enabled             = true
  wait_for_deployment = false
  default_root_object = "index.html"

  custom_error_response {
      error_caching_min_ttl = 0
      error_code = 404
      response_code = 200
      response_page_path = "/index.html"
  }

  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = var.domain

    forwarded_values {
      query_string = true
      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  price_class = "PriceClass_100"

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

Even though I'm trying to use cloudfront default certificate i keep getting the following error after running terraform apply:

Error: error creating CloudFront Distribution: InvalidViewerCertificate: To add an alternate domain name (CNAME) to a CloudFront distribution, you must attach a trusted certificate that validates your authorization to use the domain name.

Upvotes: 11

Views: 14948

Answers (4)

willcwf
willcwf

Reputation: 722

I had this issue too, found that I had accidentally created the cert through the ACM in the wrong region. The certificate must be in the same region as the distribution

Upvotes: 1

AM DEV
AM DEV

Reputation: 339

Finally fixed it, if you want to use the default certificate you cannot add alternate domain names to the CloudFront distribution, you will need to generate an SSL certificate using Amazon certificate manager. In other words, to make this work you need to comment out aliases = [var.domain] in the CloudFront config part

Upvotes: 13

klimksh
klimksh

Reputation: 129

Terraform version of the @RtmY reply could look something like this. In order to get a certificate and validate it you have to do it in us-east-1 region, therefore we need to introduce it in your settings:

provider "aws" {
  version = "~> 2.46.0"
}

provider "aws" {
  version = "~> 2.46.0"
  region = "us-east-1"
  alias = "us-east-1"
}

Then we have to create a certificate:

resource "aws_acm_certificate" "cert" {
  provider = aws.us-east-1
  domain_name       = local.bucket_name
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

To validate a certificate we have to create a DNS entry using Route53:

resource "aws_route53_record" "cert_validation" {
  name = "${aws_acm_certificate.cert.domain_validation_options.0.resource_record_name}"
  type = "${aws_acm_certificate.cert.domain_validation_options.0.resource_record_type}"
  zone_id = var.zone-id-of-your-route-53
  records = ["${aws_acm_certificate.cert.domain_validation_options.0.resource_record_value}"]
  ttl = 60
} 

Last step, we have to validate newly created certificate, again in us-east-1 region:

resource "aws_acm_certificate_validation" "cert" {
  provider = aws.us-east-1
  certificate_arn         = aws_acm_certificate.cert.arn
  validation_record_fqdns = ["${aws_route53_record.cert_validation.fqdn}"]
}

Now, in our aws_cloudfront_distribution we keep aliases where they are, and have to add another configuration for the viewer certificate:

viewer_certificate {
    acm_certificate_arn = aws_acm_certificate_validation.cert.certificate_arn
    ssl_support_method = "sni-only"
}

I assume, such validation works for one alias only and not for many. In case of many aliases, the example from the docs might suit better: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation

The solution was borrowed from here:

Terraform AWS ACM certificates in us-east-1 for resources in eu-west-1

Upvotes: 4

Rotem jackoby
Rotem jackoby

Reputation: 22198

This problem is of course not related to Terraform.

As mentoied in How do I resolve the "InvalidViewerCertificate" error exception while creating or updating a CloudFront distribution:

This error message:

To add an alternate domain name (CNAME) to a CloudFront distribution, you must attach a trusted certificate that validates your authorization to use the domain name.

Indicates that the alternate domain names (CNAMEs) on the distribution aren't covered by the Subject Alternative Name (SAN) of the certificate that you provided.
You can request a public certificate with ACM, or you can contact your certificate authority (CA) for an updated certificate that covers the alternate domain names on the distribution.

(*) Regarding the provided answer above - Notice that you are not forced to use Amazon certificate manager - you can use external provider (for example the free LetsEncrypt) as long as you follow the mentioned rules.

Upvotes: 2

Related Questions