I'm not able to use a previous created S3 Bucket (it's not a website S3 Bucket!) for one of my CloudFront Distribution Origins:
Error: error creating CloudFront Distribution: InvalidArgument: The parameter Origin DomainName does not refer to a valid S3 bucket.
status code: 400, request id: xxxx
on modules/cloudfront/main.tf line 20, in resource "aws_cloudfront_distribution" "app":
20: resource "aws_cloudfront_distribution" "app" {
Notice: I successfully created a CloudFront Distribution with an Origin using the same S3 Bucket through the Web console to verify the S3 Bucket.
resource "aws_s3_bucket" "static" {
bucket = "static.any-domain.tld"
acl = "public-read"
}
resource "aws_cloudfront_distribution" "app" {
enabled = true
is_ipv6_enabled = true
comment = "k8s test"
# without this origin, cloudfront distribution is successfully created
origin {
# tried different dns domain names:
# static.any-domain.tld.s3.eu-west-1.amazonaws.com
# or static.any-domain.tld.s3-eu-west-1.amazonaws.com
# or static.any-domain.tld.s3.amazonaws.com
domain_name = aws_s3_bucket.static.bucket_regional_domain_name
origin_id = "S3-${aws_s3_bucket.static.bucket}/any/path"
origin_path = "/any/path"
# tried with and without the following:
s3_origin_config {
origin_access_identity = "origin-access-identity/cloudfront/${aws_cloudfront_distribution.app.id}"
}
}
origin {
domain_name = "any-valid-stuff.eu-west-1.elb.amazonaws.com"
origin_id = "ELB-any-valid-stuff"
custom_origin_config {
origin_read_timeout = 30
origin_keepalive_timeout = 30
http_port = 80
https_port = 443
origin_protocol_policy = "http-only"
origin_ssl_protocols = ["TLSv1"]
}
}
...
}
Related
I am attempting to deploy two cloudfront distributions in cn-northwest-1 and I cannot seem to get ACM certificates attached to them, terraform keeps returning the following error
error creating CloudFront Distribution: InvalidViewerCertificate: The specified SSL certificate source isn't available in this region.
│ status code: 400
The ACM certificates are being generated in us-east-1 and the validation is completing successfully, but it seems that the cloudfront distribution which is created in china cannot access the certificates in the account with access to us-east-1 and RAM does not work for ACM Certificates as far as I could find.
Has anyone run into the similar issue, is the only solution here using SSL/TLS certificates and manually importing them?
You can use the alias method to create and import ACM from another region(us-east-1 as it's the only supported region).
provider "aws" {
alias = "us_east"
region = "us-east-1"
# profile = var.profile
}
And create ACM using this provider:
resource "aws_acm_certificate" "cloudfront_cdn" {
provider = aws.us_east
domain_name = "*.cdn.${var.domain_name}"
validation_method = "DNS"
tags = {
name = "certificate for cloudfront distribution"
}
lifecycle {
create_before_destroy = true
}
}
Then do your DNS validations and certificate validations(I hope you're fine with this as you said your certificate is validating successfully.). Now, create distribution:
# Add product cloudfront distribution
resource "aws_cloudfront_distribution" "product_s3_distribution" {
origin {
domain_name = "${var.bucket_name}.s3.amazonaws.com"
origin_id = var.bucket_name
# s3_origin_config {
# origin_access_identity =
# }
}
enabled = true
is_ipv6_enabled = true
comment = "CloudFront distribution for staging"
aliases = ["${var.route53_record_name}.${var.domain_name}"]
default_cache_behavior {
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = var.bucket_name
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "allow-all"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
restrictions {
geo_restriction {
restriction_type = "none"
# restriction_type = "whitelist"
# locations = ["US", "CA", "GB", "DE"]
}
}
viewer_certificate {
# cloudfront_default_certificate = true
acm_certificate_arn = aws_acm_certificate.cloudfront_cdn.arn
ssl_support_method = "sni-only"
}
depends_on = [aws_acm_certificate.cloudfront_cdn]
}
Received a response on the AWS support forms and it seems the AWS China Does not currently support ACM Certificates for CloudFront, You Have to use an already generated SSL Certificate and import it into IAM, specifically into /cloudfront/ and then it can be used by CloudFront
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.
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
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.
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
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
I can't seem to get an SSL certificate from ACM working on API-Gateway, Route53, using terraform. There seems to be an interdependency problem.
data "aws_route53_zone" "root_domain" {
name = "${var.route53_root_domain_name}"
private_zone = false
}
# The domain name to use with api-gateway
resource "aws_api_gateway_domain_name" "domain_name" {
domain_name = "${var.route53_sub_domain_name}"
certificate_arn = "${aws_acm_certificate.cert.arn}"
}
resource "aws_route53_record" "sub_domain" {
name = "${var.route53_sub_domain_name}"
type = "A"
zone_id = "${data.aws_route53_zone.root_domain.zone_id}"
alias {
name = "${aws_api_gateway_domain_name.domain_name.cloudfront_domain_name}"
zone_id = "${aws_api_gateway_domain_name.domain_name.cloudfront_zone_id}"
evaluate_target_health = false
}
}
resource "aws_acm_certificate" "cert" {
# api-gateway / cloudfront certificates need to use the us-east-1 region
provider = "aws.cloudfront-acm-certs"
domain_name = "${var.route53_sub_domain_name}"
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
}
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 = "${aws_route53_record.sub_domain.zone_id}"
records = ["${aws_acm_certificate.cert.domain_validation_options.0.resource_record_value}"]
ttl = 60
}
resource "aws_acm_certificate_validation" "cert" {
# api-gateway / cloudfront certificates need to use the us-east-1 region
provider = "aws.cloudfront-acm-certs"
certificate_arn = "${aws_acm_certificate.cert.arn}"
validation_record_fqdns = ["${aws_route53_record.cert_validation.fqdn}"]
}
The problem appears to be that:
aws_api_gateway_domain_name requires aws_acm_certificate
aws_acm_certificate has to be validated, so step 3
aws_route53_record.cert_validation requires aws_route53_record.sub_domain
aws_route53_record.subdomain requires aws_api_gateway_domain_name
Go to 1
Everytime I try to use the configuration given, I get the following error:
aws_api_gateway_domain_name.domain_name: Error creating API Gateway
Domain Name: BadRequestException: Unable to associate certificate
arn:aws:acm:us-east-1:yyyy:certificate/zzzz with CloudFront. This
error may prevent the domain name audit-log.taspli.com from being used
in API Gateway for up to 40 minutes. Please ensure the certificate
domain name matches the requested domain name, and that this user has
permission to call cloudfront:UpdateDistribution on '*' resources.
status code: 400, request id: xxxx
I seem to have fixed the problem by adding the certificate validation records to the root domain instead of the sub domain. Therefore breaking the cyclic dependency.
The problem appears to be that the sub domain can't be created without the certificate and the certificate can't be validated without the sub domain. So the situation is stuck and unresolvable.
You could manually create the sub domain, but then whats the point in automation if you have to make manual efforts to solve problems.
So I tried adding the cert validation records to the root. Suddenly it starts to work, because the root domain is something that is created externally to the project. A sort of global infrastructure project which can be handled externally. Then your individual projects can hang off of that infrastructure on a case-by-case basis.
Here is the terraform configuration which worked:
data "aws_route53_zone" "root_domain" {
name = "${var.route53_root_domain_name}"
private_zone = false
}
# The domain name to use with api-gateway
resource "aws_api_gateway_domain_name" "domain_name" {
domain_name = "${var.route53_sub_domain_name}"
certificate_arn = "${aws_acm_certificate.cert.arn}"
}
resource "aws_route53_record" "sub_domain" {
name = "${var.route53_sub_domain_name}"
type = "A"
zone_id = "${data.aws_route53_zone.root_domain.zone_id}"
alias {
name = "${aws_api_gateway_domain_name.domain_name.cloudfront_domain_name}"
zone_id = "${aws_api_gateway_domain_name.domain_name.cloudfront_zone_id}"
evaluate_target_health = false
}
}
resource "aws_acm_certificate" "cert" {
# api-gateway / cloudfront certificates need to use the us-east-1 region
provider = "aws.cloudfront-acm-certs"
domain_name = "${var.route53_sub_domain_name}"
validation_method = "DNS"
}
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 = "${data.aws_route53_zone.root_domain.zone_id}"
records = ["${aws_acm_certificate.cert.domain_validation_options.0.resource_record_value}"]
ttl = 60
}
resource "aws_acm_certificate_validation" "cert" {
# api-gateway / cloudfront certificates need to use the us-east-1 region
provider = "aws.cloudfront-acm-certs"
certificate_arn = "${aws_acm_certificate.cert.arn}"
validation_record_fqdns = ["${aws_route53_record.cert_validation.fqdn}"]
timeouts {
create = "45m"
}
}
#Christopher Thomas thank you for your initial answer
With current terraform version it needs some modifications in the certificate blocks and I propose to set a provider in case your main region is not us-east-1. Since the changes are too long for a comment I add another answer for now
# api-gateway / cloudfront certificates need to use the us-east-1 region
provider "aws" {
alias = "virginia"
region = "us-east-1"
}
resource "aws_acm_certificate" "cert" {
provider = aws.virginia
domain_name = local.api_subdomain
validation_method = "DNS"
}
resource "aws_acm_certificate_validation" "cert" {
provider = aws.virginia
certificate_arn = "${aws_acm_certificate.cert.arn}"
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
resource "aws_route53_record" "cert_validation" {
for_each = {
for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
name = each.value.name
records = [each.value.record]
type = each.value.type
zone_id = data.aws_route53_zone.root_domain.zone_id
ttl = 60
}
resource "aws_acm_certificate_validation" "cert" {
provider = aws.virginia
certificate_arn = "${aws_acm_certificate.cert.arn}"
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
# Finally Hook up Custom api domain name with api stage
resource "aws_api_gateway_base_path_mapping" "avxapi_base_path_mapping" {
api_id = aws_api_gateway_rest_api.xxxxxx.id
stage_name = aws_api_gateway_stage.xxxx.stage_name
domain_name = aws_api_gateway_domain_name.xxxx.domain_name
}
For me I have changed the aws_route53_record.cert_validation resource and it worked:
resource "aws_route53_record" "cert_validation" {
name = "${tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_name}"
type = "${tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_type}"
zone_id = "${aws_route53_record.sub_domain.zone_id}"
records = ["${tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_value}"]
ttl = 60
}
I have a plan which uses two modules: bucket-website and cloudfront-website
Among other things (policies and such) inside the bucket module, there is the following resource for creating the bucket and serve it as a website:
resource "aws_s3_bucket" "bucket-website" {
bucket = "${var.bucket_name}"
region = "${var.region}"
website {
index_document = "index.html"
}
tags = "${local.common_tags}"
}
Also this module has the following output:
output "website_endpoint" {
value = "${aws_s3_bucket.bucket-website.website_endpoint}"
}
The cloudfront-website module has a resource with all those cloudfront properties (IPs, cache stuff, etc), but the relevant part is:
resource "aws_cloudfront_distribution" "distribution" {
.....
origin {
domain_name = "${var.domain_name}"
origin_id = "${var.domain_name}"
}
.....
}
The call to the cloudfront module in the plan passes the following parameter:
domain_name = "${module.bucket-website.website_endpoint}"
I can confirm that the value is correct, because in the log of terraform apply is can see:
origin.123456.domain_name: "" => "foo.s3-website-eu-west-1.amazonaws.com"
origin.123456.origin_id: "" => "foo.s3-website-eu-west-1.amazonaws.com"
Which is the same endpoint I would use if I was doing this setup using just the AWS Console, i.e. get the bucket's static web endpoint (different to the standard bucket endpoint) and use it as the origin of Cloudfront.
However, for some reason Terraform is complaining about the domain name:
* aws_cloudfront_distribution.distribution: error creating CloudFront Distribution: InvalidArgument: The parameter Origin DomainName does not refer to a valid S3 bucket.
And I'm already out of ideas. Everything looks good. The endpoint is correct. I have checked other examples and they also use ${aws_s3_bucket.<BUCKET_RESOURCE_NAME>.website_endpoint}, so I honestly don't understand what's wrong.
Just found the solution. When serving a S3 website through CloudFront, the following code must be added to the origin section, even though it's not specified elsewhere to do so.
custom_origin_config {
http_port = "80"
https_port = "443"
origin_protocol_policy = "http-only"
origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
}
I am having trouble adding a certificate to my LB listener. Here is the code used to do so (note these is a snippets of code):
global/main.tf
resource "aws_acm_certificate" "demo_cert_east" {
provider = "aws.east"
domain_name = "*.mydomain.com"
validation_method = "DNS"
tags {
Name = "demo certificate"
Environment = "demo"
}
lifecycle {
create_before_destroy = true
}
}
stage/main.tf
data "aws_acm_certificate" "demo_cert" {
domain = "*.mydomain.com"
statuses = ["ISSUED", "PENDING_VALIDATION"]
}
resource "aws_lb_listener" "wfe_demo_ssl" {
load_balancer_arn = "${aws_lb.wfe_demo.arn}"
port = "443"
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-2016-08"
certificate_arn = "${data.aws_acm_certificate.demo_cert.arn}"
default_action {
target_group_arn = "${aws_lb_target_group.wfe_demo.arn}"
type = "forward"
}
}
I have ensured that both resources are in the aws-east region. I am getting the error:
Error creating LB Listener: CertificateNotFound: Certificate 'arn:aws:acm:us-east-1:078395932517:certificate/b83ba534-ef9d-4a07-ae13-832695dc8b5a' not found.
So the certificate is getting retrieved correctly by the data source but the listener then can't seem to find it.
To be able to attach an ACM certificate to a load balancer or other AWS resource such as Cloudfront, it must have been validated first.
Changing your data source to find only ISSUED certificates should then push the error to happen in the data source if there are no validated certificates that match your pattern:
data "aws_acm_certificate" "demo_cert" {
domain = "*.mydomain.com"
statuses = ["ISSUED"]
}
To validate the certificate you can either handle this out of band manually when you request it, use some other tool to automatically validate it for you or you can use Terraform's aws_acm_certificate_validation resource when creating the ACM certificate request:
resource "aws_acm_certificate" "cert" {
domain_name = "example.com"
validation_method = "DNS"
}
data "aws_route53_zone" "zone" {
name = "example.com."
private_zone = false
}
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 = data.aws_route53_zone.zone.id
records = [aws_acm_certificate.cert.domain_validation_options.0.resource_record_value]
ttl = 60
}
resource "aws_acm_certificate_validation" "cert" {
certificate_arn = aws_acm_certificate.cert.arn
validation_record_fqdns = [aws_route53_record.cert_validation.fqdn]
}
Because I just had the issue: certificate and resource must be in the same region. (#ydaetskcoR kind of mentioned this already).