This is the base Terraform configuration to be used for the broad-redirect-* projects. If you need to run Terraform for an environment locally, you first need to deal with Terraform state, which will be stored in a Google bucket (GCS).
This terraform config is designed to be run in a Atlantis CI/CD pipeline.
Atlantis is a tool that runs Terraform in response to GitHub pull requests.
Atlantis is configured to run this terraform config in the atlantis.yaml
file.
You will need to create a feature branch and open a pull request. Atlantis will
run the terraform plan and apply operations in response to the pull request.
Once the changes are approved, you can leave a comment of atlantis apply
, and
Atlantis will apply the changes to the prod
environment, and merge the branch
into main
.
The process for making changes to this terraform config:
- Create a feature branch off this repo for your changes
- Make your changes to the terraform config
- Create a PR into the
main
branch with your changes - Verify your changes look correct in the Terraform plans shown in the PR
- Wait for approval from one of the CODEOWNERS
- Once approved, run
atlantis apply
to apply the changes to theprod
environment - Verify that the changes are working as expected in the
prod
environment - Verify that Atlantis merged the PR into the
main
branch - Delete your feature branch
When adding a certificate to an existing http_redirects
section, or creating a
new section under http_redirects
editing the file is relatively simple. The
key to each map is what the name of the certificate will be. The value of each
is a list of strings that represent the hostnames that will be created inside
that certificate. What happens behind the scenes is that each hostname gets a
DNS authorization created (if use_dns_authorizations
is true
for that
section).
If you need the certificate to be set up and working before you change DNS, you will need to do some targetted Terraform applies before doing the main apply. To do this you would run the following:
terraform plan -out=planfile -target google_certificate_manager_dns_authorization.domains
This will create a plan that just create the DNS authorization resources in the project. You can apply this as you normally would to create the resources.
terraform apply planfile
You can then retrieve the authorization information you need using the following
gcloud
command:
gcloud certificate-manager --project broad-redirect-prod dns-authorizations list --format json
You then need to add CNAME records to your DNS system to match the hostnames you
just added to the certificate(s). This will provide the DNS authorization needed
when you apply the rest of the change so that the certificates all get created,
moved to active
state, and linked to your redirect correctly so they can be
tested before you change any DNS records to have the hostnames point at the
redirect system.
If you add, remove, or change a hostname in an existing certificate, the plan will succeed, but the apply will fail with an error from Google similar to:
Error: Error when reading or editing Certificate: googleapi: Error 400: can't delete certificate that is referenced by a CertificateMapEntry or other resources
This happens because if you change one or more hostnames in a certificate, Google must destroy the first certificate and recreate it. Because that certificate is attached to a map entry, you have a conflict.
To fix this, the easiest thing to do is taint the map entry for all entries in Terraform that your certificate is attached to. That means tainting all map entries for all hostnames except for the ones you are adding, removing, or updating. If you do this, the next plan will remove the map entry first, then remove the certificate, then it will create the new certificate and finally recreate the map entry.
Note: Removing the map entries will make all the hostnames in that certificate stop responding until the entries are recreated, so note that this means downtime for those hostnames. Use the instructions in Adding Certificates to pre-create the resources.
As an example, suppose you had a redirect entry as follows:
"broadinstitute-test" = {
certificates = {
"example.org" = ["example.org", "www.example.org"],
}
redirects = []
}
Now, we want to add a new hostname test.example.org
to this list:
"broadinstitute-test" = {
certificates = {
"example.org" = ["example.org", "test.example.org", "www.example.org"],
}
redirects = []
}
This would cause the apply error.
Note: Make sure all DNS authorizations are done prior to performing the next steps so that the DNS entries are already in place for any new hostnames so that the certificates get created as quickly as possible.
To fix this, you can taing the two pre-existing map entries:
terraform taint module.http_redirects["broadinstitute-test"].module.certificates.google_certificate_manager_certificate_map_entry.certificates["example.org"]
terraform taint module.http_redirects["broadinstitute-test"].module.certificates.google_certificate_manager_certificate_map_entry.certificates["www.example.org"]
Now, when you run another plan and apply, you should see that the map entries will be removed, then recreated along with the new entry you just added.
Name | Version |
---|---|
terraform | >= 1.12 |
6.44.0 |
Name | Version |
---|---|
6.44.0 |
Name | Source | Version |
---|---|---|
domain_redirects | github.com/broadinstitute/terraform-google-domain-redirect | multihost |
path_redirects | ../modules/terraform-google-url-redirects | n/a |
Name | Type |
---|---|
google_project.redirects | resource |
google_project_iam_policy.broad_redirect | resource |
google_project_service.api_services | resource |
google_resource_manager_lien.redirects | resource |
google_iam_policy.admin | data source |
Name | Description | Type | Default | Required |
---|---|---|---|---|
billing_account | The billing account used by the project | string |
n/a | yes |
cost_object | The cost object that pays for the project | string |
n/a | yes |
folder_id | The ID for the folder in which the project should reside | string |
n/a | yes |
path_redirects | The map of redirects to apply to the path under the hostname. | map(object({ |
n/a | yes |
project | The project ID used for this project | string |
n/a | yes |
project_name | The descriptive name for the project | string |
n/a | yes |
region | The deployment region | string |
n/a | yes |
api_services | The billing account used by the project | list(any) |
[ |
no |
domain_redirects | The data used to create all the domain-level redirects | map(object({ |
{} |
no |
No outputs.
Terraform Docs created by running:
podman run --rm -u $(id -u) \
--volume "$(pwd):/terraform-docs" \
-w /terraform-docs \
quay.io/terraform-docs/terraform-docs:latest \
--output-file ../docs/README.md \
--output-mode inject /terraform-docs/prod
Remember update the dependency lock file for different architectures:
terraform providers lock \
-platform=linux_amd64 \
-platform=linux_arm64 \
-platform=darwin_amd64 \
-platform=darwin_arm64 \
-platform=windows_amd64