Skip to content

Commit a9a3c23

Browse files
authored
feat: Allow validating the certificate with CloudFlare (#101)
1 parent 02ca0fa commit a9a3c23

File tree

13 files changed

+206
-10
lines changed

13 files changed

+206
-10
lines changed

README.md

+31-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,32 @@ module "acm" {
2525
}
2626
```
2727

28+
## Usage with external DNS validation (e.g. CloudFlare)
29+
30+
```hcl
31+
module "acm" {
32+
source = "terraform-aws-modules/acm/aws"
33+
version = "~> 3.0"
34+
35+
domain_name = "weekly.tf"
36+
zone_id = "b7d259641bf30b89887c943ffc9d2138"
37+
38+
subject_alternative_names = [
39+
"*.weekly.tf",
40+
]
41+
42+
create_route53_records = false
43+
validation_record_fqdns = [
44+
"_689571ee9a5f9ec307c512c5d851e25a.weekly.tf",
45+
]
46+
47+
tags = {
48+
Name = "weekly.tf"
49+
}
50+
}
51+
52+
```
53+
2854
## [Usage with CloudFront](https://aws.amazon.com/premiumsupport/knowledge-center/install-ssl-cloudfront/)
2955

3056
```hcl
@@ -56,6 +82,7 @@ module "acm" {
5682
## Examples
5783

5884
- [Complete example with DNS validation (recommended)](https://github.com/terraform-aws-modules/terraform-aws-acm/tree/master/examples/complete-dns-validation)
85+
- [Complete example with DNS validation via external DNS provider (CloudFlare)](https://github.com/terraform-aws-modules/terraform-aws-acm/tree/master/examples/complete-dns-validation-with-cloudflare)
5986
- [Complete example with EMAIL validation](https://github.com/terraform-aws-modules/terraform-aws-acm/tree/master/examples/complete-email-validation)
6087

6188
## Conditional creation and validation
@@ -92,7 +119,7 @@ module "acm" {
92119

93120
| Name | Version |
94121
|------|---------|
95-
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.12.26 |
122+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13.1 |
96123
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 2.53 |
97124

98125
## Providers
@@ -119,15 +146,17 @@ No modules.
119146
|------|-------------|------|---------|:--------:|
120147
| <a name="input_certificate_transparency_logging_preference"></a> [certificate\_transparency\_logging\_preference](#input\_certificate\_transparency\_logging\_preference) | Specifies whether certificate details should be added to a certificate transparency log | `bool` | `true` | no |
121148
| <a name="input_create_certificate"></a> [create\_certificate](#input\_create\_certificate) | Whether to create ACM certificate | `bool` | `true` | no |
149+
| <a name="input_create_route53_records"></a> [create\_route53\_records](#input\_create\_route53\_records) | When validation is set to DNS, define whether to create the DNS records internally via Route53 or externally using any DNS provider | `bool` | `true` | no |
122150
| <a name="input_dns_ttl"></a> [dns\_ttl](#input\_dns\_ttl) | The TTL of DNS recursive resolvers to cache information about this record. | `number` | `60` | no |
123151
| <a name="input_domain_name"></a> [domain\_name](#input\_domain\_name) | A domain name for which the certificate should be issued | `string` | `""` | no |
124152
| <a name="input_subject_alternative_names"></a> [subject\_alternative\_names](#input\_subject\_alternative\_names) | A list of domains that should be SANs in the issued certificate | `list(string)` | `[]` | no |
125153
| <a name="input_tags"></a> [tags](#input\_tags) | A mapping of tags to assign to the resource | `map(string)` | `{}` | no |
126154
| <a name="input_validate_certificate"></a> [validate\_certificate](#input\_validate\_certificate) | Whether to validate certificate by creating Route53 record | `bool` | `true` | no |
127155
| <a name="input_validation_allow_overwrite_records"></a> [validation\_allow\_overwrite\_records](#input\_validation\_allow\_overwrite\_records) | Whether to allow overwrite of Route53 records | `bool` | `true` | no |
128156
| <a name="input_validation_method"></a> [validation\_method](#input\_validation\_method) | Which method to use for validation. DNS or EMAIL are valid, NONE can be used for certificates that were imported into ACM and then into Terraform. | `string` | `"DNS"` | no |
157+
| <a name="input_validation_record_fqdns"></a> [validation\_record\_fqdns](#input\_validation\_record\_fqdns) | When validation is set to DNS and the DNS validation records are set externally, provide the fqdns for the validation | `list(string)` | `[]` | no |
129158
| <a name="input_wait_for_validation"></a> [wait\_for\_validation](#input\_wait\_for\_validation) | Whether to wait for the validation to complete | `bool` | `true` | no |
130-
| <a name="input_zone_id"></a> [zone\_id](#input\_zone\_id) | The ID of the hosted zone to contain this record. | `string` | `""` | no |
159+
| <a name="input_zone_id"></a> [zone\_id](#input\_zone\_id) | The ID of the hosted zone to contain this record. Required when validating via Route53 | `string` | `""` | no |
131160

132161
## Outputs
133162

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Complete ACM example with external CloudFlare DNS validation
2+
3+
Configuration in this directory creates an ACM certificate (valid for the domain name and wildcard) while the DNS validation is done via an external DNS provider.
4+
5+
For this example CloudFlare DNS is used but any DNS provider could be used instead.
6+
7+
This is a complete example which fits most of scenarios.
8+
9+
## Usage
10+
11+
To run this example you need to execute:
12+
13+
```bash
14+
$ terraform init
15+
$ terraform plan
16+
$ terraform apply
17+
```
18+
19+
Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources.
20+
21+
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
22+
## Requirements
23+
24+
| Name | Version |
25+
|------|---------|
26+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13.1 |
27+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 2.53 |
28+
| <a name="requirement_cloudflare"></a> [cloudflare](#requirement\_cloudflare) | >= 3.4.0 |
29+
30+
## Providers
31+
32+
| Name | Version |
33+
|------|---------|
34+
| <a name="provider_cloudflare"></a> [cloudflare](#provider\_cloudflare) | >= 3.4.0 |
35+
36+
## Modules
37+
38+
| Name | Source | Version |
39+
|------|--------|---------|
40+
| <a name="module_acm"></a> [acm](#module\_acm) | ../../ | n/a |
41+
42+
## Resources
43+
44+
| Name | Type |
45+
|------|------|
46+
| [cloudflare_record.validation](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/record) | resource |
47+
| [cloudflare_zone.this](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/data-sources/zone) | data source |
48+
49+
## Inputs
50+
51+
No inputs.
52+
53+
## Outputs
54+
55+
| Name | Description |
56+
|------|-------------|
57+
| <a name="output_acm_certificate_arn"></a> [acm\_certificate\_arn](#output\_acm\_certificate\_arn) | The ARN of the certificate |
58+
| <a name="output_acm_certificate_domain_validation_options"></a> [acm\_certificate\_domain\_validation\_options](#output\_acm\_certificate\_domain\_validation\_options) | A list of attributes to feed into other resources to complete certificate validation. Can have more than one element, e.g. if SANs are defined. Only set if DNS-validation was used. |
59+
| <a name="output_acm_certificate_validation_emails"></a> [acm\_certificate\_validation\_emails](#output\_acm\_certificate\_validation\_emails) | A list of addresses that received a validation E-Mail. Only set if EMAIL-validation was used. |
60+
| <a name="output_distinct_domain_names"></a> [distinct\_domain\_names](#output\_distinct\_domain\_names) | List of distinct domains names used for the validation. |
61+
| <a name="output_validation_domains"></a> [validation\_domains](#output\_validation\_domains) | List of distinct domain validation options. This is useful if subject alternative names contain wildcards. |
62+
| <a name="output_validation_route53_record_fqdns"></a> [validation\_route53\_record\_fqdns](#output\_validation\_route53\_record\_fqdns) | List of FQDNs built using the zone domain and name. |
63+
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
locals {
2+
domain = "terraform-aws-modules.modules.tf"
3+
4+
# Removing trailing dot from domain - just to be sure :)
5+
domain_name = trimsuffix(local.domain, ".")
6+
}
7+
8+
module "acm" {
9+
source = "../../"
10+
11+
domain_name = local.domain_name
12+
zone_id = data.cloudflare_zone.this.id
13+
14+
subject_alternative_names = [
15+
"*.alerts.${local.domain_name}",
16+
"new.sub.${local.domain_name}",
17+
"*.${local.domain_name}",
18+
"alerts.${local.domain_name}",
19+
]
20+
21+
create_route53_records = false
22+
validation_record_fqdns = cloudflare_record.validation.*.hostname
23+
24+
tags = {
25+
Name = local.domain_name
26+
}
27+
}
28+
29+
resource "cloudflare_record" "validation" {
30+
count = length(module.acm.distinct_domain_names)
31+
32+
zone_id = data.cloudflare_zone.this.id
33+
name = element(module.acm.validation_domains, count.index)["resource_record_name"]
34+
type = element(module.acm.validation_domains, count.index)["resource_record_type"]
35+
value = replace(element(module.acm.validation_domains, count.index)["resource_record_value"], "/.$/", "")
36+
ttl = 60
37+
proxied = false
38+
39+
allow_overwrite = true
40+
}
41+
42+
data "cloudflare_zone" "this" {
43+
name = local.domain_name
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
output "acm_certificate_arn" {
2+
description = "The ARN of the certificate"
3+
value = module.acm.acm_certificate_arn
4+
}
5+
6+
output "acm_certificate_domain_validation_options" {
7+
description = "A list of attributes to feed into other resources to complete certificate validation. Can have more than one element, e.g. if SANs are defined. Only set if DNS-validation was used."
8+
value = module.acm.acm_certificate_domain_validation_options
9+
}
10+
11+
output "acm_certificate_validation_emails" {
12+
description = "A list of addresses that received a validation E-Mail. Only set if EMAIL-validation was used."
13+
value = module.acm.acm_certificate_validation_emails
14+
}
15+
16+
output "validation_route53_record_fqdns" {
17+
description = "List of FQDNs built using the zone domain and name."
18+
value = module.acm.validation_route53_record_fqdns
19+
}
20+
21+
output "distinct_domain_names" {
22+
description = "List of distinct domains names used for the validation."
23+
value = module.acm.distinct_domain_names
24+
}
25+
26+
output "validation_domains" {
27+
description = "List of distinct domain validation options. This is useful if subject alternative names contain wildcards."
28+
value = module.acm.validation_domains
29+
}

examples/complete-dns-validation-with-cloudflare/variables.tf

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
terraform {
2+
required_version = ">= 0.13.1"
3+
4+
required_providers {
5+
aws = {
6+
source = "hashicorp/aws"
7+
version = ">= 2.53"
8+
}
9+
cloudflare = {
10+
source = "cloudflare/cloudflare"
11+
version = ">= 3.4.0"
12+
}
13+
}
14+
}

examples/complete-dns-validation/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Note that this example may create resources which cost money. Run `terraform des
2323

2424
| Name | Version |
2525
|------|---------|
26-
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.12.26 |
26+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13.1 |
2727
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 2.53 |
2828

2929
## Providers

examples/complete-dns-validation/versions.tf

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
terraform {
2-
required_version = ">= 0.12.26"
2+
required_version = ">= 0.13.1"
33

44
required_providers {
55
aws = {

examples/complete-email-validation/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Note that this example may create resources which cost money. Run `terraform des
3636

3737
| Name | Version |
3838
|------|---------|
39-
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.12.26 |
39+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13.1 |
4040
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 2.53 |
4141

4242
## Providers

examples/complete-email-validation/versions.tf

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
terraform {
2-
required_version = ">= 0.12.26"
2+
required_version = ">= 0.13.1"
33

44
required_providers {
55
aws = {

main.tf

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ resource "aws_acm_certificate" "this" {
3232
}
3333

3434
resource "aws_route53_record" "validation" {
35-
count = var.create_certificate && var.validation_method == "DNS" && var.validate_certificate ? length(local.distinct_domain_names) : 0
35+
count = var.create_certificate && var.validation_method == "DNS" && var.create_route53_records && var.validate_certificate ? length(local.distinct_domain_names) : 0
3636

3737
zone_id = var.zone_id
3838
name = element(local.validation_domains, count.index)["resource_record_name"]
@@ -53,5 +53,5 @@ resource "aws_acm_certificate_validation" "this" {
5353

5454
certificate_arn = aws_acm_certificate.this[0].arn
5555

56-
validation_record_fqdns = aws_route53_record.validation.*.fqdn
56+
validation_record_fqdns = flatten([aws_route53_record.validation.*.fqdn, var.validation_record_fqdns])
5757
}

variables.tf

+18-1
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,27 @@ variable "validation_method" {
4444
description = "Which method to use for validation. DNS or EMAIL are valid, NONE can be used for certificates that were imported into ACM and then into Terraform."
4545
type = string
4646
default = "DNS"
47+
48+
validation {
49+
condition = contains(["DNS", "EMAIL", "NONE"], var.validation_method)
50+
error_message = "Valid values are DNS, EMAIL or NONE."
51+
}
52+
}
53+
54+
variable "create_route53_records" {
55+
description = "When validation is set to DNS, define whether to create the DNS records internally via Route53 or externally using any DNS provider"
56+
type = bool
57+
default = true
58+
}
59+
60+
variable "validation_record_fqdns" {
61+
description = "When validation is set to DNS and the DNS validation records are set externally, provide the fqdns for the validation"
62+
type = list(string)
63+
default = []
4764
}
4865

4966
variable "zone_id" {
50-
description = "The ID of the hosted zone to contain this record."
67+
description = "The ID of the hosted zone to contain this record. Required when validating via Route53"
5168
type = string
5269
default = ""
5370
}

versions.tf

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
terraform {
2-
required_version = ">= 0.12.26"
2+
required_version = ">= 0.13.1"
33

44
required_providers {
55
aws = {

0 commit comments

Comments
 (0)