Skip to content

Commit c5c589c

Browse files
committed
feat!: acm certificate validation
BREAKING CHANGE: The `acm` sub-module creates single ACM certificate henceforth.
1 parent 8bcdcd4 commit c5c589c

File tree

11 files changed

+165
-93
lines changed

11 files changed

+165
-93
lines changed

examples/complete/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Configuration in this directory creates:
55

66
- ECS Service in a pre-configured ECS Cluster and corresponding ECS Capacity Providers
77
- Internet-facing Application Load Balancer to access the deployed services, and
8-
- ACM to generate an Amazon-issued certificate for a base domain, and then create a Route53 A-type record with an endpoint
8+
- ACM to generate an Amazon-issued certificate for a base domain, set up Route53 Records for Certificate validation, and then create a Route53 A-type record with an endpoint
99

1010
## Usage
1111

examples/complete/main.tf

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,11 @@ module "ecs_deployment" {
8484

8585
# Amazon Certificates Manager
8686
create_acm = true
87-
acm_amazon_issued_certificates = {
87+
acm_certificates = {
8888
base_domain = {
89-
domain_name = var.base_domain
89+
domain_name = var.domain_name
9090
validation_method = local.acm_validation_method
91+
record_zone_id = data.aws_route53_zone.base_domain.zone_id
9192
}
9293
}
9394

@@ -132,24 +133,7 @@ module "ecs_deployment" {
132133
################################################################################
133134

134135
data "aws_route53_zone" "base_domain" {
135-
name = var.base_domain
136-
}
137-
138-
resource "aws_route53_record" "endpoint" {
139-
zone_id = data.aws_route53_zone.base_domain.zone_id
140-
name = var.endpoint
141-
type = "A"
142-
143-
alias {
144-
name = module.ecs_deployment.alb_dns_name
145-
zone_id = module.ecs_deployment.alb_zone_id
146-
evaluate_target_health = true
147-
}
148-
}
149-
150-
resource "aws_acm_certificate_validation" "base_domain_certificate" {
151-
certificate_arn = module.ecs_deployment.amazon_issued_acm_certificates_arns["base_domain"]
152-
validation_record_fqdns = [aws_route53_record.endpoint.fqdn]
136+
name = var.domain_name
153137
}
154138

155139
################################################################################

examples/complete/outputs.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ output "listener_arn" {
6666

6767
output "acm_amazon_issued_certificate_arn" {
6868
description = "ARN of the ACM Amazon-issued certificate for the base domain"
69-
value = module.ecs_deployment.amazon_issued_acm_certificates_arns["base_domain"]
69+
value = module.ecs_deployment.acm_certificates_arns["base_domain"]
7070
}
7171

7272
################################################################################

examples/complete/variables.tf

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,7 @@ variable "security_group_alb" {
118118
type = string
119119
}
120120

121-
variable "base_domain" {
122-
description = "Base domain name for ACM"
123-
type = string
124-
}
125-
126-
variable "endpoint" {
127-
description = "DNS endpoint for the application"
121+
variable "domain_name" {
122+
description = "Domain name for ACM"
128123
type = string
129124
}

main.tf

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
locals {
2-
# ACM
3-
acm_certificates_arns = var.create_acm ? module.acm[0].amazon_issued_acm_certificates_arns : {}
4-
52
# ALB
63
alb_target_groups = {
74
for k, v in try(var.load_balancer.target_groups, {}) :
@@ -17,10 +14,10 @@ locals {
1714
k => merge(
1815
{
1916
certificate_arn = lookup(
20-
local.acm_certificates_arns,
17+
var.acm_certificates,
2118
try(v.certificate, null) != null ? try(v.certificate, "") : "",
2219
null
23-
) != null ? local.acm_certificates_arns[try(v.certificate, null)] : try(v.certificate_arn, null)
20+
) != null ? module.acm[try(v.certificate, null)].acm_certificate_arn : try(v.certificate_arn, null)
2421
},
2522
v
2623
)
@@ -259,6 +256,8 @@ module "alb" {
259256
listener_rules = try(var.load_balancer.listener_rules, {})
260257

261258
tags = try(var.load_balancer.tags, {})
259+
260+
depends_on = [module.acm]
262261
}
263262

264263
################################################################################
@@ -268,7 +267,16 @@ module "alb" {
268267
module "acm" {
269268
source = "./modules/acm"
270269

271-
count = var.create_acm ? 1 : 0
270+
for_each = var.create_acm ? var.acm_certificates : {}
271+
272+
certificate_domain_name = each.value.domain_name
273+
certificate_subject_alternative_names = try(each.value.subject_alternative_names, null)
274+
certificate_validation_method = try(each.value.validation_method, null)
275+
certificate_key_algorithm = try(each.value.key_algorithm, null)
276+
certificate_validation_option = try(each.value.validation_option, null)
277+
278+
record_zone_id = try(each.value.record_zone_id, null)
279+
record_allow_overwrite = try(each.value.record_allow_overwrite, null)
272280

273-
amazon_issued_certificates = try(var.acm_amazon_issued_certificates, {})
281+
tags = try(each.value.tags, {})
274282
}

modules/acm/.header.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
# acm
22

3-
This sub-module creates the Amazon-issued certificates for given domains with `validation_option` configuration.
3+
This sub-module creates the Amazon-issued certificate for a given domain with `validation_option` configuration.
44

55
## Presets
66

7+
### ACM Certificate
8+
79
- The `validation_method` is set to `DNS` as the recommended method, and can be overridden to use `EMAIL` method if required.
810

11+
### Route53 Record
12+
13+
- The `allow_override` is set to `true` as the default option, and can be overridden to `false` if required.
14+
915
## Notes
1016

11-
- ACM certificates are created before destroying existing ones (to update the configuration), which is the recommended practice.
12-
- The sub-module outputs the corresponding validation records for every Amazon-issued ACM certificate created. This can be further used to complete the validation by creating the Route53 DNS records.
17+
- ACM certificate is created before destroying the existing one (to update the configuration), which is the recommended practice.

modules/acm/main.tf

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,57 @@
1+
locals {
2+
acm_certificate_validation_records = [
3+
for record in aws_acm_certificate.this.domain_validation_options :
4+
{
5+
name = record.resource_record_name
6+
type = record.resource_record_type
7+
value = record.resource_record_value
8+
domain = record.domain_name
9+
}
10+
]
11+
}
12+
113
################################################################################
2-
# ACM Amazon issued certificates
14+
# ACM Certificate
315
################################################################################
416

5-
resource "aws_acm_certificate" "amazon_issued" {
6-
for_each = var.amazon_issued_certificates
7-
8-
domain_name = try(each.value.domain_name, null)
9-
subject_alternative_names = try(each.value.subject_alternative_names, null)
10-
validation_method = try(each.value.validation_method, null)
11-
key_algorithm = try(each.value.key_algorithm, null)
17+
resource "aws_acm_certificate" "this" {
18+
domain_name = var.certificate_domain_name
19+
subject_alternative_names = try(var.certificate_subject_alternative_names, null)
20+
validation_method = try(var.certificate_validation_method, null)
21+
key_algorithm = try(var.certificate_key_algorithm, null)
1222

1323
dynamic "validation_option" {
14-
for_each = try(each.value.validation_option, null) != null ? [1] : []
24+
for_each = try(var.certificate_validation_option, null) != null ? [1] : []
1525

1626
content {
17-
domain_name = each.value.validation_option.domain_name
18-
validation_domain = each.value.validation_option.validation_domain
27+
domain_name = var.certificate_validation_option.domain_name
28+
validation_domain = var.certificate_validation_option.validation_domain
1929
}
2030
}
2131

2232
lifecycle {
2333
create_before_destroy = true
2434
}
2535

26-
tags = merge(var.tags, each.value.tags)
36+
tags = var.tags
37+
}
38+
39+
################################################################################
40+
# ACM Validation
41+
################################################################################
42+
43+
resource "aws_route53_record" "this" {
44+
for_each = local.acm_certificate_validation_records
45+
46+
zone_id = var.record_zone_id
47+
name = each.value.name
48+
type = each.value.type
49+
records = [each.value.value]
50+
ttl = 60
51+
allow_overwrite = var.record_allow_overwrite
52+
}
53+
54+
resource "aws_acm_certificate_validation" "this" {
55+
certificate_arn = aws_acm_certificate.this.arn
56+
validation_record_fqdns = [for route53_record in aws_route53_record.this : route53_record.fqdn]
2757
}

modules/acm/outputs.tf

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
################################################################################
2-
# ACM Amazon issued certificates
2+
# ACM Certificate
33
################################################################################
44

5-
output "amazon_issued_acm_certificates_arns" {
6-
description = "ARNs of the Amazon issued ACM certificates."
7-
value = { for k, v in aws_acm_certificate.amazon_issued : k => v.arn }
5+
output "acm_certificate_id" {
6+
description = "ARN of the ACM certificate."
7+
value = aws_acm_certificate.this.id
88
}
99

10-
output "amazon_issued_acm_certificates_validation_records" {
11-
description = "Validation Records of the Amazon issued ACM certificates."
12-
value = {
13-
for k, v in aws_acm_certificate.amazon_issued :
14-
k => [
15-
for record in v.domain_validation_options :
16-
{
17-
name = record.resource_record_name
18-
type = record.resource_record_type
19-
value = record.resource_record_value
20-
domain = record.domain_name
21-
}
22-
]
23-
}
10+
output "acm_certificate_arn" {
11+
description = "ARN of the ACM certificate."
12+
value = aws_acm_certificate.this.arn
13+
}
14+
15+
################################################################################
16+
# Route53 Record
17+
################################################################################
18+
19+
output "route53_records_ids" {
20+
description = "Identifiers of the Validation Records of the ACM certificate."
21+
value = [for record in aws_route53_record.this : record.id]
22+
}
23+
24+
################################################################################
25+
# ACM Certificate Validation
26+
################################################################################
27+
28+
output "acm_certificate_validation_id" {
29+
description = "Identifier of the ACM certificate validation resource."
30+
value = aws_acm_certificate_validation.this.id
2431
}

modules/acm/variables.tf

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,55 @@
22
# ACM Certificate
33
################################################################################
44

5-
variable "amazon_issued_certificates" {
6-
description = "List of Amazon-issued certificates to ACM create."
7-
type = map(object({
8-
domain_name = string
9-
subject_alternative_names = optional(list(string), [])
10-
validation_method = optional(string, "DNS")
11-
key_algorithm = optional(string, null)
12-
validation_option = optional(object({
13-
domain_name = string
14-
validation_domain = string
15-
}))
16-
tags = optional(map(string), {})
17-
}))
18-
default = {}
5+
variable "certificate_domain_name" {
6+
description = "(Required) Domain name for which the certificate should be issued."
7+
type = string
8+
}
9+
10+
variable "certificate_subject_alternative_names" {
11+
description = "(Optional) Set of domains that should be SANs in the issued certificate."
12+
type = list(string)
13+
default = []
14+
}
15+
16+
variable "certificate_validation_method" {
17+
description = "(Optional) Which method to use for validation. DNS or EMAIL are valid."
18+
type = string
19+
default = "DNS"
20+
}
21+
22+
variable "certificate_key_algorithm" {
23+
description = "(Optional) Specifies the algorithm of the public and private key pair that your Amazon issued certificate uses to encrypt data."
24+
type = string
25+
default = null
26+
}
27+
28+
variable "certificate_validation_option" {
29+
description = "(Optional) Configuration block used to specify information about the initial validation of each domain name."
30+
type = object({
31+
domain_name = string
32+
validation_domain = string
33+
})
34+
default = null
1935
}
2036

2137
variable "tags" {
2238
description = "(Optional) Map of tags to assign to the resource."
2339
type = map(string)
2440
default = {}
2541
}
42+
43+
################################################################################
44+
# Route53 Record
45+
################################################################################
46+
47+
variable "record_zone_id" {
48+
description = "(Required) Hosted zone ID for a CloudFront distribution, S3 bucket, ELB, or Route 53 hosted zone."
49+
type = string
50+
}
51+
52+
variable "record_allow_overwrite" {
53+
description = "(Optional) Allow creation of this record in Terraform to overwrite an existing record, if any."
54+
type = bool
55+
default = true
56+
}

outputs.tf

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ output "ecs_task_definition_arn" {
2020
# Amazon Certificates Manager
2121
################################################################################
2222

23-
output "amazon_issued_acm_certificates_arns" {
24-
description = "ARNs of the Amazon issued ACM certificates."
25-
value = try(module.acm[0].amazon_issued_acm_certificates_arns, null)
23+
output "acm_certificates_ids" {
24+
description = "Identifiers of the ACM certificates."
25+
value = try({ for k, v in module.acm : k => v.acm_certificate_id }, null)
2626
}
2727

28-
output "amazon_issued_acm_certificates_validation_records" {
29-
description = "Validation Records of the Amazon issued ACM certificates."
30-
value = try(module.acm[0].amazon_issued_acm_certificates_validation_records, null)
28+
output "acm_certificates_arns" {
29+
description = "ARNs of the ACM certificates."
30+
value = try({ for k, v in module.acm : k => v.acm_certificate_arn }, null)
3131
}
3232

3333
################################################################################

0 commit comments

Comments
 (0)