This module can be used to issue and validate free, auto-renewing TLS certificates using AWS Certificate
Manager (ACM). It supports issuing and validating multiple ACM certificates.
The module will create the TLS certificates, as well as
the DNS records to validate them, and output the certificates' ARNs so you can use them with other resources, such as ALBs, CloudFront, and API Gateway.
Understanding how ACM certificates are programmatically requested and verified
This module supports ordering and programmatically validating, via DNS records that are written to a public Route53 zone, ACM certificates.
To understand the Route53 certificate ordering and DNS validation process in more detail, consult the AWS documentation.
The DNS validation scheme is preferred over the alternative email validation scheme, because the DNS scheme can be orchestrated end to end via Terraform, which this module does for you.
At a high level, the process involves:
Requesting a certificate for a given domain name such as example.com.
Receiving the challenge records provided by AWS, which ask you to prove ownership of the domain by writing a programmatically generated CNAME record to the Route53 public hosted zone associated with example.com. The provided challenge CNAME record will consist of the CNAME record name and a record value.
The Route53 public hosted zone must be created if it does not already exist, so that any records written to it will resolve via external DNS queries.
Upon writing the challenge record and its value to the public Route53 hosted zone, programmatically initiate the validation process, whereby AWS will query your Route53 zone for the challenge record, ensure it resolves, and that its value is the exact value supplied in the original challenge. Note that these queries must resolve over public DNS, so you can test them yourself:
dig _a79865eb4cd1a6ab990a45779b4e0b96.example.com. cname
Once AWS is able to verify that the records resolve correctly and with the expected values, it will convert your certificate from a pending status to an issued status.
This module is capable of handling these steps for you, so that you need only supply the correct input map of desired certificates via var.acm_tls_certificates.
How to request wildcard certificates
To provision a wildcard certificate for example.com, you would create the following acm_tls_certificates input, using the subject_alternative_names
field to specify the domain prefixed with *., e.g., *.example.com:
# Example of a simple wildcard certificate that protects BOTH example.com and the first level of subdomains# such as test.example.com, mail.example.com, etc
acm_tls_certificates = {
"example.com" = {
subject_alternative_names = ["*.example.com"]
tags = {
Environment = "stage"
run_destroy_check = true
}
create_verification_record = true
verify_certificate = true
}
}
Here's an example of requesting a wildcard certificate for the next level down of subdomains:
# Example of provisioning a wildcard certificate that protects BOTH test.example.com and mail.test.example.com,# db.test.example.com, etc
acm_tls_certificates = {
"test.example.com" = {
subject_alternative_names = ["*.test.example.com"]
tags = {
Environment = "stage"
run_destroy_check = true
}
create_verification_record = true
verify_certificate = true
}
}
Requesting a certificate for a domain that doesn't match its hosted zone name
If you are requesting a a certificate for domain X, but you're attaching it to a hosted zone that is NOT named X you must specify the hosted_zone_id of the target hosted zone in the var.acm_tls_certificates input.
For example, if you are requesting a certificate for test-29283.example.com, but you are attaching it to the public zone named example.com, then you MUST provide the hosted_zone_id for the example.com public zone in your var.acm_tls_certificates input map.
Recall that, because of how the programmatic DNS validation scheme works outlined above, you need the DNS validation challenge records that will be generated for your requessted test-29283.example.com to resolve via public DNS queries, and therefore you need to write their records to the example.com hosted zone. Therefore you would provide the hosted_zone_id of example.com in your input map as in the following example:
acm_tls_certificates = {
"test-29283.example.com" = {
subject_alternative_names = ["*.test.example.com"]
tags = {
Environment = "stage"run_destroy_check = true
}
create_verification_record = trueverify_certificate = true# This is the ID of the public zone example.comhosted_zone_id = "Z04542822CKAS2ZFBUGT"
}
}
How do you use this module?
See the root README for instructions on using Terraform modules.
See vars.tf for all the variables you can set on this module.
Special handling for use with API Gateway
If you are using this module to create a TLS certificate that will be used with API Gateway, along with a custom
domain name, then you need to set a tag named exactly run_destroy_check with a value of true. Do this for every certificate you configure that will be used in this way:
Without this, terraform destroy will fail. This is because you can't delete an ACM cert while it's in use,
deleting a custom domain name mapping for API Gateway takes 10 - 30 minutes, and Terraform only waits a max of 10
minutes to delete the cert, so it times out and exits with an error every time. The run_destroy_check tells this
module to run a script that waits until the cert is no longer in use.
The script is written in Bash, so it will not work on Windows versions earlier than Windows 10, which supports a Linux Bash shell.
A note on the dependency_getter pattern implementing module_depends
This module uses a null_resource named dependency_getter to effectively implement depends_on at the module level. This is a temporary workaround as Terraform does not yet natively support depends_on at the module level. You can also see this Terraform issue on GitHub for more discussion.
Here's how this works. First, we create this optional dependencies variable for the current module, which accepts a list of strings, but defaults to an empty list. The null_resource linked above does a simple join on the dependencies list, if present. Every resource within this module also depends_on this null_resource.dependency_getter(even though it's creating no resources).
This has the desirable effect of causing the dependency graph that Terraform builds to look as you'd expect if there were in fact native support for depends_on at the module level. Let's say you had a separate Terraform module that needed to consume this current module in order to generate certificates, but you wanted this certificates module to wait to do any of its work until some of the outputs from the resources in your parent module were ready. In that case, you could pass those outputs into this module's dependencies variable, like so:
# ---------------------------------------------------------------------------------------------------------------------# CREATE PUBLIC HOSTED ZONE(S)# ---------------------------------------------------------------------------------------------------------------------module"acm-tls-certificates" {
# When using these modules in your own repos, you will need to use a Git URL with a ref attribute that pins you# to a specific version of the modules, such as the following example:# source = "git::git@github.com:gruntwork-io/terraform-aws-load-balancer.git//modules/acm-tls-certificate?ref=v0.19.0"
source = "git::git@github.com:gruntwork-io/terraform-aws-load-balancer.git//modules/acm-tls-certificate?ref=v0.20.0"
acm_tls_certificates = local.acm_tls_certificates
# Workaround Terraform limitation where there is no module depends_on.# See https://github.com/hashicorp/terraform/issues/1178 for more details.# This effectively draws an explicit dependency between the public # and private zones managed here and the ACM certificates that will be optionally # provisioned for them
dependencies = flatten([values(aws_route53_zone.public_zones).*.name_servers])
}
In this example, the acm-tls-certificates module will "wait" until your aws_route53_zone.public_zones resources have been successfully provisioned.
Questions? Ask away.
We're here to talk about our services, answer any questions, give advice, or just to chat.
{"treedata":{"name":"root","toggled":true,"children":[{"name":".circleci","children":[{"name":"config.yml","path":".circleci/config.yml","sha":"538f130f2287d50a02ca785c34c7db00e05e35a7"}]},{"name":".gitignore","path":".gitignore","sha":"8530ecdd5eddb0e1c3a9f93d106dd2e53e0cebd6"},{"name":".pre-commit-config.yaml","path":".pre-commit-config.yaml","sha":"40dde37cbe2716be382b51fccaabc08b113a5eba"},{"name":"CODEOWNERS","path":"CODEOWNERS","sha":"8c24c86ef8447a19436b38826f458c71b4da4f45"},{"name":"LICENSE.txt","path":"LICENSE.txt","sha":"154ea4429924fda533b20544c7a19d8949fc82cf"},{"name":"README.md","path":"README.md","sha":"fbb92c9436c55cb47a5080329de83a04443468ce"},{"name":"_docs","children":[{"name":"migration_guides","children":[{"name":"nlb_to_0.15.0","children":[{"name":"README.md","path":"_docs/migration_guides/nlb_to_0.15.0/README.md","sha":"1a4945a97fcd4b02ee384c4bca33799ceee5211b"},{"name":"after_migration","children":[{"name":"main.tf","path":"_docs/migration_guides/nlb_to_0.15.0/after_migration/main.tf","sha":"d05d961ae5e6ac15588b06048086cbce73b71f71"},{"name":"outputs.tf","path":"_docs/migration_guides/nlb_to_0.15.0/after_migration/outputs.tf","sha":"d967d0daa27477b626b7b28911d10b2f18fe6f76"},{"name":"vars.tf","path":"_docs/migration_guides/nlb_to_0.15.0/after_migration/vars.tf","sha":"b8c8af94a7ed9e5db9bd522bf69e0fe304efddee"}]},{"name":"before_migration","children":[{"name":"main.tf","path":"_docs/migration_guides/nlb_to_0.15.0/before_migration/main.tf","sha":"9928dc1f79cddeb66e9cb9b044ddabda3470c5a6"},{"name":"outputs.tf","path":"_docs/migration_guides/nlb_to_0.15.0/before_migration/outputs.tf","sha":"dc162b85fc673c12a9d166a1f58021eb142ac447"},{"name":"vars.tf","path":"_docs/migration_guides/nlb_to_0.15.0/before_migration/vars.tf","sha":"b8c8af94a7ed9e5db9bd522bf69e0fe304efddee"}]}]}]}]},{"name":"examples","children":[{"name":"acm-tls-certificate","children":[{"name":"README.md","path":"examples/acm-tls-certificate/README.md","sha":"0a7892a8b5d327254c91a96276273fcc975821ca"},{"name":"dependencies.tf","path":"examples/acm-tls-certificate/dependencies.tf","sha":"237767e8323ffdf0d76a976946fa24c4f12708ce"},{"name":"main.tf","path":"examples/acm-tls-certificate/main.tf","sha":"7f6eb8f82478517daef922c529ab33eb7faf3be1"},{"name":"outputs.tf","path":"examples/acm-tls-certificate/outputs.tf","sha":"083ef125b3be1f465c66f8b1e8b0ede0c782f10e"},{"name":"vars.tf","path":"examples/acm-tls-certificate/vars.tf","sha":"3b96d4f8aa3d1620494d05538069dbc5d9d4de14"}]},{"name":"alb-with-logs","children":[{"name":"README.md","path":"examples/alb-with-logs/README.md","sha":"b46a1aef114340b80c6742b9344c52f1a952a7a4"},{"name":"main.tf","path":"examples/alb-with-logs/main.tf","sha":"6d095aa660079ec4f2a679570385f781328271fc"},{"name":"outputs.tf","path":"examples/alb-with-logs/outputs.tf","sha":"518914d6b608ea2d2996cbaa9361c370bd9c9fa1"},{"name":"vars.tf","path":"examples/alb-with-logs/vars.tf","sha":"029a4cf53d07f1e6e5c04c6173df8a29b0bed98f"}]},{"name":"alb","children":[{"name":"README.md","path":"examples/alb/README.md","sha":"202d51dd9238a27061a6277a4632dce37706a3f9"},{"name":"main.tf","path":"examples/alb/main.tf","sha":"41df4c0140979c57bd85fc9c3f5c98e727ea80ef"},{"name":"outputs.tf","path":"examples/alb/outputs.tf","sha":"201a421be76f8332b212f78216417e35f8c968eb"},{"name":"vars.tf","path":"examples/alb/vars.tf","sha":"3306c4227949373dc9fed5a83dc19f7d6904e007"}]},{"name":"lb-listener-rules","children":[{"name":"README.md","path":"examples/lb-listener-rules/README.md","sha":"3d2205b57641d6e97bc28f2a8a59093c4bfc7f61"},{"name":"main.tf","path":"examples/lb-listener-rules/main.tf","sha":"364a46966bd42f79eec8f820e9e7d9e94ec59042"},{"name":"outputs.tf","path":"examples/lb-listener-rules/outputs.tf","sha":"2f227fe908f34ad51a837a85fa6a064f993fa897"},{"name":"user_data.sh","path":"examples/lb-listener-rules/user_data.sh","sha":"5b667c100463a2ce64a5827e4e9dc357509e1a4c"},{"name":"variables.tf","path":"examples/lb-listener-rules/variables.tf","sha":"6fd908ad8227c3e2b1058a3b752d245aef0c1b5f"}]},{"name":"multiple-acm-tls-certificates","children":[{"name":"main.tf","path":"examples/multiple-acm-tls-certificates/main.tf","sha":"5cb64d56308f500e14139831c3e57c3848c811f3"},{"name":"outputs.tf","path":"examples/multiple-acm-tls-certificates/outputs.tf","sha":"20ec52f607e69887ce2a0b54e68415cf36f034e6"},{"name":"vars.tf","path":"examples/multiple-acm-tls-certificates/vars.tf","sha":"55bb61a54afca24f007dfdf2aed50fb86355d56a"}]}]},{"name":"modules","children":[{"name":"acm-tls-certificate","children":[{"name":"README.md","path":"modules/acm-tls-certificate/README.md","sha":"3a5873e77d9ab92ef6b43545d577ac5911b777c7","toggled":true},{"name":"main.tf","path":"modules/acm-tls-certificate/main.tf","sha":"4b163820dd801c548ecaa31023afbf8eba5efdda"},{"name":"outputs.tf","path":"modules/acm-tls-certificate/outputs.tf","sha":"18fa9f9ce0d23ca8f81f8fa41cb20c81922f7a87"},{"name":"vars.tf","path":"modules/acm-tls-certificate/vars.tf","sha":"ca6385fbb1865ee150851f3ee165021a6712b45d"},{"name":"wait-until-tls-cert-not-in-use.sh","path":"modules/acm-tls-certificate/wait-until-tls-cert-not-in-use.sh","sha":"68296e34cc4e86fbefcae3820e6de88f2aa6f911"}],"toggled":true},{"name":"alb","children":[{"name":"README.md","path":"modules/alb/README.md","sha":"3f749a16fbe8484b7c3b56a4f980f3c470c4da1f"},{"name":"main.tf","path":"modules/alb/main.tf","sha":"dcf93d25d2f2402b4a279db193335c475770f906"},{"name":"outputs.tf","path":"modules/alb/outputs.tf","sha":"e1de70ae961208480de17161a71e1ff86d96f612"},{"name":"vars.tf","path":"modules/alb/vars.tf","sha":"b7ea99986b18399073d1610476c3adbf20ea38b6"}]},{"name":"lb-listener-rules","children":[{"name":"README.md","path":"modules/lb-listener-rules/README.md","sha":"a4a3c1a510f7958373a35ba409a6ce4eb5ba63eb"},{"name":"listener-rules-list-to-map","children":[{"name":"README.md","path":"modules/lb-listener-rules/listener-rules-list-to-map/README.md","sha":"2a61caf06789c48011607cb7976da736bfa61aaf"},{"name":"outputs.tf","path":"modules/lb-listener-rules/listener-rules-list-to-map/outputs.tf","sha":"7c4311be9c8a1d6585093227949ad3e9fbe27073"},{"name":"variables.tf","path":"modules/lb-listener-rules/listener-rules-list-to-map/variables.tf","sha":"1f81e857a5725d04cc8d3028a3d7e062bac19216"}]},{"name":"main.tf","path":"modules/lb-listener-rules/main.tf","sha":"f89a921a7ba7330293e7315687dff051cf69125a"},{"name":"outputs.tf","path":"modules/lb-listener-rules/outputs.tf","sha":"9d3efd16064ff8defdb7e8c95634f875d10832b1"},{"name":"variables.tf","path":"modules/lb-listener-rules/variables.tf","sha":"bef3aceb1f79bc43f5708163ebef0e9d210dc6db"}]},{"name":"nlb","children":[{"name":"README.md","path":"modules/nlb/README.md","sha":"41dda13c9f245bea970c54d51a21fcb1a5ca7fe9"}]}],"toggled":true},{"name":"terraform-cloud-enterprise-private-module-registry-placeholder.tf","path":"terraform-cloud-enterprise-private-module-registry-placeholder.tf","sha":"ae586c0fe830819580e1009d41a9074f16e65bed"},{"name":"test","children":[{"name":"README.md","path":"test/README.md","sha":"ded6aae1d95e6fca858a0ef196d3ecf30d4bf5f2"},{"name":"acm_tls_certificate_test.go","path":"test/acm_tls_certificate_test.go","sha":"84bead400799d0146a80d46d147cc14a6d704e3d"},{"name":"go.mod","path":"test/go.mod","sha":"ef1afcd04ad769ca58fb7c0d9edcdec099512980"},{"name":"go.sum","path":"test/go.sum","sha":"6fc0bbe0f9a6d8cada397f5cc186185de283fe25"},{"name":"lb_listener_rules_test.go","path":"test/lb_listener_rules_test.go","sha":"ea25e3f04fa321944a7031ae6c8774201f9cfade"},{"name":"lb_test.go","path":"test/lb_test.go","sha":"be64434cbd9ee3b91fe85b2d5596990fa17eb56f"}]}]},"detailsContent":"<h1 class=\"preview__body--title\" id=\"acm-tls-certificate\">ACM TLS Certificate</h1><div class=\"preview__body--border\"></div><p>This module can be used to issue and validate free, auto-renewing TLS certificates using <a href=\"https://aws.amazon.com/certificate-manager/\" class=\"preview__body--description--blue\" target=\"_blank\">AWS Certificate\nManager (ACM)</a>. It supports issuing and validating multiple ACM certificates.</p>\n<p>The module will create the TLS certificates, as well as\nthe DNS records to validate them, and output the certificates' ARNs so you can use them with other resources, such as ALBs, CloudFront, and API Gateway.</p>\n<h2 class=\"preview__body--subtitle\" id=\"understanding-how-acm-certificates-are-programmatically-requested-and-verified\">Understanding how ACM certificates are programmatically requested and verified</h2>\n<p>This module supports ordering and programmatically validating, via DNS records that are written to a public Route53 zone, ACM certificates.</p>\n<p>To understand the Route53 certificate ordering and DNS validation process in more detail, consult <a href=\"https://docs.aws.amazon.com/acm/latest/userguide/dns-validation.html\" class=\"preview__body--description--blue\" target=\"_blank\">the AWS documentation</a>.\nThe DNS validation scheme is preferred over the alternative email validation scheme, because the DNS scheme can be orchestrated end to end via Terraform, which this module does for you.</p>\n<p>At a high level, the process involves:</p>\n<ol>\n<li>Requesting a certificate for a given domain name such as example.com.</li>\n<li>Receiving the challenge records provided by AWS, which ask you to prove ownership of the domain by writing a programmatically generated CNAME record to the Route53 public hosted zone associated with example.com. The provided challenge CNAME record will consist of the CNAME record name and a record value.</li>\n<li>The Route53 public hosted zone must be created if it does not already exist, so that any records written to it will resolve via external DNS queries.</li>\n<li>Upon writing the challenge record and its value to the public Route53 hosted zone, programmatically initiate the validation process, whereby AWS will query your Route53 zone for the challenge record, ensure it resolves, and that its value is the exact value supplied in the original challenge. Note that these queries must resolve over public DNS, so you can test them yourself:\n<code>dig _a79865eb4cd1a6ab990a45779b4e0b96.example.com. cname</code></li>\n<li>Once AWS is able to verify that the records resolve correctly and with the expected values, it will convert your certificate from a <code>pending</code> status to an <code>issued</code> status.</li>\n</ol>\n<p>This module is capable of handling these steps for you, so that you need only supply the correct input map of desired certificates via <code>var.acm_tls_certificates</code>.</p>\n<h2 class=\"preview__body--subtitle\" id=\"how-to-request-wildcard-certificates\">How to request wildcard certificates</h2>\n<p>To provision a wildcard certificate for example.com, you would create the following <code>acm_tls_certificates</code> input, using the <code>subject_alternative_names</code>\nfield to specify the domain prefixed with <code>*.</code>, e.g., <code>*.example.com</code>:</p>\n<pre><span class=\"hljs-comment\"># Example of a simple wildcard certificate that protects BOTH example.com and the first level of subdomains</span>\n<span class=\"hljs-comment\"># such as test.example.com, mail.example.com, etc</span>\nacm_tls_certificates = {\n <span class=\"hljs-string\">\"example.com\"</span> = {\n subject_alternative_names = [<span class=\"hljs-string\">\"*.example.com\"</span>]\n tags = {\n Environment = <span class=\"hljs-string\">\"stage\"</span>\n run_destroy_check = true\n }\n create_verification_record = true\n verify_certificate = true\n }\n }\n</pre>\n<p>Here's an example of requesting a wildcard certificate for the next level down of subdomains:</p>\n<pre>\n<span class=\"hljs-comment\"># Example of provisioning a wildcard certificate that protects BOTH test.example.com and mail.test.example.com,</span>\n<span class=\"hljs-comment\"># db.test.example.com, etc</span>\nacm_tls_certificates = {\n <span class=\"hljs-string\">\"test.example.com\"</span> = {\n subject_alternative_names = [<span class=\"hljs-string\">\"*.test.example.com\"</span>]\n tags = {\n Environment = <span class=\"hljs-string\">\"stage\"</span>\n run_destroy_check = true\n }\n create_verification_record = true\n verify_certificate = true\n }\n }\n</pre>\n<h2 class=\"preview__body--subtitle\" id=\"requesting-a-certificate-for-a-domain-that-doesnt-match-its-hosted-zone-name\">Requesting a certificate for a domain that doesn't match its hosted zone name</h2>\n<p>If you are requesting a a certificate for domain X, but you're attaching it to a hosted zone that is NOT named X you must specify the <code>hosted_zone_id</code> of the target hosted zone in the <code>var.acm_tls_certificates</code> input.</p>\n<p>For example, if you are requesting a certificate for <code>test-29283.example.com</code>, but you are attaching it to the public zone named <code>example.com</code>, then you MUST provide the <code>hosted_zone_id</code> for the example.com public zone in your <code>var.acm_tls_certificates</code> input map.</p>\n<p>Recall that, because of how the programmatic DNS validation scheme works <a href=\"#understanding-how-acm-certificates-are-programmatically-requested-and-verified\" class=\"preview__body--description--blue\">outlined above</a>, you need the DNS validation challenge records that will be generated for your requessted <code>test-29283.example.com</code> to resolve via public DNS queries, and therefore you need to write their records to the <code>example.com</code> hosted zone. Therefore you would provide the <code>hosted_zone_id</code> of <code>example.com</code> in your input map as in the following example:</p>\n<pre><span class=\"hljs-attr\">acm_tls_certificates</span> = {\n <span class=\"hljs-string\">\"test-29283.example.com\"</span> = {\n <span class=\"hljs-attr\">subject_alternative_names</span> = [<span class=\"hljs-string\">\"*.test.example.com\"</span>]\n <span class=\"hljs-attr\">tags</span> = {\n <span class=\"hljs-attr\">Environment</span> = <span class=\"hljs-string\">\"stage\"</span>\n <span class=\"hljs-attr\">run_destroy_check</span> = <span class=\"hljs-literal\">true</span>\n }\n <span class=\"hljs-attr\">create_verification_record</span> = <span class=\"hljs-literal\">true</span>\n <span class=\"hljs-attr\">verify_certificate</span> = <span class=\"hljs-literal\">true</span>\n <span class=\"hljs-comment\"># This is the ID of the public zone example.com</span>\n <span class=\"hljs-attr\">hosted_zone_id</span> = <span class=\"hljs-string\">\"Z04542822CKAS2ZFBUGT\"</span>\n }\n }\n</pre>\n<h2 class=\"preview__body--subtitle\" id=\"how-do-you-use-this-module\">How do you use this module?</h2>\n<ul>\n<li>See the <a href=\"/repos/v0.28.2/module-load-balancer/README.md\" class=\"preview__body--description--blue\">root README</a> for instructions on using Terraform modules.</li>\n<li>See the <a href=\"/repos/v0.28.2/module-load-balancer/examples\" class=\"preview__body--description--blue\">examples</a> folder for example usage.</li>\n<li>See <a href=\"/repos/v0.28.2/module-load-balancer/modules/acm-tls-certificate/vars.tf\" class=\"preview__body--description--blue\">vars.tf</a> for all the variables you can set on this module.</li>\n</ul>\n<h2 class=\"preview__body--subtitle\" id=\"special-handling-for-use-with-api-gateway\">Special handling for use with API Gateway</h2>\n<p>If you are using this module to create a TLS certificate that will be used with API Gateway, along with a custom\ndomain name, then you need to set a tag named exactly <code>run_destroy_check</code> with a value of <code>true</code>. Do this for every certificate you configure that will be used in this way:</p>\n<pre>module <span class=\"hljs-string\">\"cert\"</span> {\n <span class=\"hljs-attr\">source</span> = <span class=\"hljs-string\">\"git::git@github.com:gruntwork-io/terraform-aws-load-balancer.git//modules/acm-tls-certificate?ref=v0.13.2\"</span>\n \n <span class=\"hljs-comment\"># ... other params ommitted ...</span>\n \n <span class=\"hljs-attr\">acm_tls_certificates</span> = {\n <span class=\"hljs-string\">\"mail.example.com\"</span> = {\n <span class=\"hljs-attr\">subject_alternative_names</span> = [<span class=\"hljs-string\">\"mailme.example.com\"</span>]\n <span class=\"hljs-attr\">tags</span> = {\n <span class=\"hljs-attr\">Environment</span> = <span class=\"hljs-string\">\"stage\"</span>\n <span class=\"hljs-attr\">run_destroy_check</span> = <span class=\"hljs-literal\">true</span>\n }\n <span class=\"hljs-attr\">create_verification_record</span> = <span class=\"hljs-literal\">true</span>\n <span class=\"hljs-attr\">verify_certificate</span> = <span class=\"hljs-literal\">true</span>\n }\n <span class=\"hljs-string\">\"admin.example.com\"</span> = {\n <span class=\"hljs-attr\">subject_alternative_names</span> = [<span class=\"hljs-string\">\"restricted.example.com\"</span>]\n <span class=\"hljs-attr\">tags</span> = {\n <span class=\"hljs-attr\">Something</span> = <span class=\"hljs-string\">\"else\"</span>\n <span class=\"hljs-attr\">run_destroy_check</span> = <span class=\"hljs-literal\">true</span>\n }\n <span class=\"hljs-attr\">create_verification_record</span> = <span class=\"hljs-literal\">true</span>\n <span class=\"hljs-attr\">verify_certificate</span> = <span class=\"hljs-literal\">true</span>\n }\n }\n \n <span class=\"hljs-comment\"># ... other params ommitted ...</span>\n}\n</pre>\n<p>Without this, <code>terraform destroy</code> will fail. This is because you can't delete an ACM cert while it's in use,\ndeleting a custom domain name mapping for API Gateway takes 10 - 30 minutes, and Terraform only waits a max of 10\nminutes to delete the cert, so it times out and exits with an error every time. The <code>run_destroy_check</code> tells this\nmodule to run a script that waits until the cert is no longer in use.</p>\n<p>Two notes about this script:</p>\n<ol>\n<li>You must install the <a href=\"https://aws.amazon.com/cli/\" class=\"preview__body--description--blue\" target=\"_blank\">AWS CLI</a> to use it.</li>\n<li>The script is written in Bash, so it will not work on Windows versions earlier than Windows 10, which supports a Linux Bash shell.</li>\n</ol>\n<h2 class=\"preview__body--subtitle\" id=\"a-note-on-the-dependency-getter-pattern-implementing-module-depends\">A note on the dependency_getter pattern implementing module_depends</h2>\n<p>This module uses a <a href=\"/repos/terraform-aws-load-balancer/modules/acm-tls-certificate/main.tf#L15\" class=\"preview__body--description--blue\">null_resource</a> named <code>dependency_getter</code> to effectively implement <code>depends_on</code> at the module level. This is a temporary workaround as Terraform does not yet natively support <code>depends_on</code> at the module level. You can also see <a href=\"https://github.com/hashicorp/terraform/issues/1178\" class=\"preview__body--description--blue\" target=\"_blank\">this Terraform issue on GitHub</a> for more discussion.</p>\n<p>Here's how this works. First, we create <a href=\"/repos/terraform-aws-load-balancer/modules/acm-tls-certificate/vars.tf#L124\" class=\"preview__body--description--blue\">this optional dependencies variable</a> for the current module, which accepts a list of strings, but defaults to an empty list. The <code>null_resource</code> linked above does a simple join on the dependencies list, if present. Every resource within this module also <code>depends_on</code> this <code>null_resource.dependency_getter</code>(even though it's creating no resources).</p>\n<p>This has the desirable effect of causing the dependency graph that Terraform builds to look as you'd expect if there were in fact native support for <code>depends_on</code> at the module level. Let's say you had a separate Terraform module that needed to consume this current module in order to generate certificates, but you wanted this certificates module to wait to do any of its work until some of the outputs from the resources in your parent module were ready. In that case, you could pass those outputs into this module's <code>dependencies</code> variable, like so:</p>\n<pre><span class=\"hljs-comment\"># ---------------------------------------------------------------------------------------------------------------------</span>\n<span class=\"hljs-comment\"># CREATE PUBLIC HOSTED ZONE(S)</span>\n<span class=\"hljs-comment\"># ---------------------------------------------------------------------------------------------------------------------</span>\n\n<span class=\"hljs-keyword\">module</span> <span class=\"hljs-string\">\"acm-tls-certificates\"</span> {\n <span class=\"hljs-comment\"># When using these modules in your own repos, you will need to use a Git URL with a ref attribute that pins you</span>\n <span class=\"hljs-comment\"># to a specific version of the modules, such as the following example:</span>\n <span class=\"hljs-comment\"># source = \"git::git@github.com:gruntwork-io/terraform-aws-load-balancer.git//modules/acm-tls-certificate?ref=v0.19.0\"</span>\n\n source = <span class=\"hljs-string\">\"git::git@github.com:gruntwork-io/terraform-aws-load-balancer.git//modules/acm-tls-certificate?ref=v0.20.0\"</span>\n acm_tls_certificates = local.acm_tls_certificates\n\n <span class=\"hljs-comment\"># Workaround Terraform limitation where there is no module depends_on.</span>\n <span class=\"hljs-comment\"># See https://github.com/hashicorp/terraform/issues/1178 for more details.</span>\n <span class=\"hljs-comment\"># This effectively draws an explicit dependency between the public </span>\n <span class=\"hljs-comment\"># and private zones managed here and the ACM certificates that will be optionally </span>\n <span class=\"hljs-comment\"># provisioned for them </span>\n dependencies = flatten([values(aws_route53_zone.public_zones).*.name_servers])\n}\n</pre>\n<p>In this example, the <code>acm-tls-certificates</code> module will "wait" until your <code>aws_route53_zone.public_zones</code> resources have been successfully provisioned.</p>\n","repoName":"module-load-balancer","repoRef":"v0.26.0","serviceDescriptor":{"serviceName":"Elastic Load Balancer (ELB)","serviceRepoName":"module-load-balancer","serviceRepoOrg":"gruntwork-io","cloudProviders":["aws"],"description":"Deploy the Application Load Balancer (ALB) for load balancing HTTP and HTTPS, with support for routing rules and WebSockets.","imageUrl":"elastic.png","licenseType":"subscriber","technologies":["Terraform"],"compliance":[],"tags":[""]},"serviceCategoryName":"Networking","fileName":"README.md","filePath":"/modules/acm-tls-certificate","title":"Repo Browser: Elastic Load Balancer (ELB)","description":"Browse the repos in the Gruntwork Infrastructure as Code Library."}