https://github.com/ackeecz/terraform-gcp-lb
Terraform module for provisioning of GCP LB on top of precreated named NEG passed as parameter to this module.
https://github.com/ackeecz/terraform-gcp-lb
Last synced: 8 months ago
JSON representation
Terraform module for provisioning of GCP LB on top of precreated named NEG passed as parameter to this module.
- Host: GitHub
- URL: https://github.com/ackeecz/terraform-gcp-lb
- Owner: AckeeCZ
- Created: 2022-08-01T09:06:11.000Z (about 3 years ago)
- Default Branch: master
- Last Pushed: 2024-09-14T02:01:38.000Z (about 1 year ago)
- Last Synced: 2024-09-15T15:23:00.138Z (about 1 year ago)
- Language: HCL
- Size: 103 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# Terraform GCP HTTP(S) Load Balancing
Terraform module for provisioning of GCP LB on top of precreated named NEGs, Cloud Run services and GCS buckets passed as parameter to this module.
## Usage
### HTTPS Load-balancer with self-signed certificate and Cloudflare DNS record creation:
```hcl
data "cloudflare_zones" "ackee_cz" {
filter {
name = "ackee.cz"
}
}resource "google_storage_bucket" "test" {
name = "test-randompostfix-98582341"
location = var.region
storage_class = "STANDARD"
uniform_bucket_level_access = true
website {
main_page_suffix = "index.html"
}
}module "api_unicorn" {
source = "git::ssh://git@gitlab.ack.ee/Infra/tf-module/terraform-gcp-lb.git?ref=master"
name = "main-${var.project}-${var.namespace}"
project = var.project
region = var.region
self_signed_tls = trueservices = [
{
type = "neg"
name = "ackee-api-unicorn"
zone = var.zone
},
{
type = "bucket"
bucket_name = "${google_storage_bucket.test.name}"
},
{
type = "cloudrun"
service_name = cloud-run-service
}
]url_map = {
matcher1 = {
hostnames = ["api-unicorn.ackee.cz", "api-unicorn2.ackee.cz"]
path_rules = [
{
paths = ["/api/v1/*"]
service = {
type = "neg"
name = "ackee-api-unicorn"
zone = var.zone
}
},
]
}
matcher2 = {
hostnames = ["api-unicorn.ackee.cz", "api-unicorn2.ackee.cz"]
path_rules = [
{
paths = ["/*"]
service = "${google_storage_bucket.test.name}"
},
]
}
matcher3 = {
hostnames = ["cloud-run-service.ackee.cz"]
path_rules = [
{
paths = ["/*"]
service = "cloud-run-service"
},
]
}
}
}resource "cloudflare_record" "api" {
zone_id = data.cloudflare_zones.ackee_cz.zones[0].id
name = "api-unicorn"
value = module.api_unicorn.ip_address
type = "A"
ttl = 1
proxied = true
}
```
If NEG named `ackee-api-unicorn` exists and CF is set to "SSL:Full" you should have working app now on https://api-unicorn.ackee.cz and https://api-unicorn2.ackee.cz### HTTPS Load-balancer with Google-managed certificate and Cloudflare DNS record creation:
```hcl
data "cloudflare_zones" "ackee_cz" {
filter {
name = "ackee.cz"
}
}module "api_unicorn" {
source = "git::ssh://git@gitlab.ack.ee/Infra/tf-module/terraform-gcp-lb.git?ref=master"
name = "main-${var.project}-${var.namespace}"
project = var.project
region = var.region
google_managed_tls = trueservices = [
{
type = "neg"
name = "ackee-api-unicorn"
zone = var.zone
},
]url_map = {
matcher1 = {
hostnames = ["api-unicorn.ackee.cz", "api-unicorn2.ackee.cz"]
path_rules = [
{
paths = ["/api/v1/*"]
service = "ackee-api-unicorn"
},
]
}
}
}resource "cloudflare_record" "api" {
zone_id = data.cloudflare_zones.ackee_cz.zones[0].id
name = "api-unicorn"
value = module.api_unicorn.ip_address
type = "A"
ttl = 1
proxied = false
}
```
If NEG named `ackee-api-unicorn` exists you should have working app now on https://api-unicorn.ackee.cz
**Beware**: If you use more then one hostname with Google-managed certificate, only one certificate, with first hostname in list, will be created.### HTTPS Load-balancer with preexisting NEG, Google-managed certificate and Cloudflare DNS record creation:
```hcl
data "google_compute_network_endpoint_group" "old_neg" {
name = "k8s1-aab5af95-production-ackee-unicorn-80-bafd3c69"
zone = "europe-west3-c"
count = 1
}data "cloudflare_zones" "ackee_cz" {
filter {
name = "ackee.cz"
}
}module "api_unicorn" {
source = "git::ssh://git@gitlab.ack.ee/Infra/tf-module/terraform-gcp-lb.git?ref=master"
name = "main-${var.project}-${var.namespace}"
project = var.project
region = var.region
google_managed_tls = trueservices = [
{
type = "neg"
name = "ackee-api-unicorn"
zone = var.zone
additional_negs = [data.google_compute_network_endpoint_group.old_neg]
http_backend_protocol = "HTTP"
},
]url_map = {
matcher1 = {
hostnames = ["api-unicorn.ackee.cz""]
path_rules = [
{
paths = ["/*"]
service = "ackee-api-unicorn"
},
]
}
}
}resource "cloudflare_record" "api" {
zone_id = data.cloudflare_zones.ackee_cz.zones[0].id
name = "api-unicorn"
value = module.api_unicorn.ip_address
type = "A"
ttl = 1
proxied = false
}
```If we pass `data.google_compute_network_endpoint_group` resource as value for `additional_negs` parameter, then our new load-balancer
gets created from new named NEG's auto discovered by name in `neg_name` parameter and from NEG's from `additional_negs` parameter -
this should be used when migrating from old setup, so we balance to both new and old application.### HTTPS Load-balancer with pre-existing certificate (signed by external CA):
```hcl
module "api_unicorn" {
source = "git::ssh://git@gitlab.ack.ee/Infra/tf-module/terraform-gcp-lb.git?ref=master"
name = "main-${var.project}-${var.namespace}"
project = var.project
region = var.regionservices = [
{
type = "neg"
name = "ackee-api-unicorn"
zone = var.zone
additional_negs = [data.google_compute_network_endpoint_group.old_neg]
http_backend_protocol = "HTTP"
},
]url_map = {
matcher1 = {
hostnames = ["api-unicorn.ackee.cz""]
path_rules = [
{
paths = ["/*"]
service = "ackee-api-unicorn"
},
]
}
}certificate = file("${path.root}/tls/certificate_chain.crt")
private_key = file("${path.root}/tls/private.key")
}
```
It is recommended to use some secure storage (eg. Vault) and pass value from here, rather then saving plaintext private key into git repo# Pitfalls
## Error: Error creating SslCertificate: googleapi: Error 409: The resource ... already exists, alreadyExists
This might show once you are adding new hostname to the load balancer and SSL certificate `web_lb_cert` needs to add the hostname into `dns_names`. Terraform is trying to update the certificate in-place or creates a certificate with the same name. For that, you might want to do these few steps manually:
Get certificates from the state file:
```bash
CERT=`mktemp`
CERT_KEY=`mktemp`terraform show -json | jq -r --arg MODULE "$MODULE" '.values.root_module.child_modules[] | select (.address=="module.lb_72541") | .resources[] | select(.address=="module.lb_72541.google_compute_ssl_certificate.gcs_certs[0]") | .values.private_key' > $CERT_KEY
terraform show -json | jq -r --arg MODULE "$MODULE" '.values.root_module.child_modules[] | select (.address=="module.lb_72541") | .resources[] | select(.address=="module.lb_72541.google_compute_ssl_certificate.gcs_certs[0]") | .values.certificate' > $CERT
```where `module.lb_72541` is the name of the module used in your Terraform.
Create new temporary certificate:
```bash
gcloud compute ssl-certificates create tmp --certificate=$CERT --private-key=$CERT_KEY
gcloud compute target-https-proxies update "NAME_OF_PROXY" --ssl-certificates "tmp"
```The name of the https proxy can be found in the state file:
```bash
terraform state show 'module.lb_72541.google_compute_target_https_proxy.self_signed[0]'
```Remove the old certificate:
Get the name from the error output. Let's say you have this error:
```
Error: Error creating SslCertificate: googleapi: Error 409: The resource 'projects/awesome-project/global/sslCertificates/main-awesome-project-development-72541-cert-self-signed' already exists, alreadyExists
```Then command will look like this:
```bash
gcloud compute ssl-certificates delete main-awesome-project-development-72541-cert-self-signed
```Run `terraform apply` to create a certificate from Terraform and once done delete the temporary certificate:
```bash
gcloud compute ssl-certificates delete tmp
```## Creation of NEG's is not automatic!
**BEWARE: Network Endpoint Groups REFERENCED BY THIS MODULE MUST EXIST BEFORE YOU USE THIS MODULE, OTHERWISE IT WILL FAIL WITH ERROR SIMILIAR TO:**
```
Error: Required attribute is not seton ../load-balancer.tf line 68, in resource "google_compute_backend_service" "cn_lb":
68: resource "google_compute_backend_service" "cn_lb" {
```
Also note, that purging app (typically with `helm delete`) does not automatically cleanup existing NEGs## Using example
Because of chicken-egg mentioned in previous section, it is also not so easy to use provider example code. Running just `terraform apply` will fail
on similiar error. Workaround is to create testing NEG first and load balancer above it in next step.
```
terraform apply -target=google_compute_network_endpoint_group.neg_one -target=google_compute_network_endpoint_group.neg_two
terraform apply
```## Before you do anything in this module
Install pre-commit hooks by running following commands:
```shell script
brew install pre-commit
pre-commit install
```## Requirements
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.0 |## Providers
| Name | Version |
|------|---------|
| [google](#provider\_google) | 5.8.0 |
| [google-beta](#provider\_google-beta) | 5.8.0 |
| [random](#provider\_random) | 3.6.0 |
| [tls](#provider\_tls) | 4.0.5 |## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [google-beta_google_compute_backend_service.app_backend](https://registry.terraform.io/providers/hashicorp/google-beta/latest/docs/resources/google_compute_backend_service) | resource |
| [google-beta_google_compute_backend_service.cloudrun](https://registry.terraform.io/providers/hashicorp/google-beta/latest/docs/resources/google_compute_backend_service) | resource |
| [google-beta_google_compute_global_forwarding_rule.external_signed](https://registry.terraform.io/providers/hashicorp/google-beta/latest/docs/resources/google_compute_global_forwarding_rule) | resource |
| [google-beta_google_compute_global_forwarding_rule.google_managed](https://registry.terraform.io/providers/hashicorp/google-beta/latest/docs/resources/google_compute_global_forwarding_rule) | resource |
| [google-beta_google_compute_global_forwarding_rule.self_signed](https://registry.terraform.io/providers/hashicorp/google-beta/latest/docs/resources/google_compute_global_forwarding_rule) | resource |
| [google_compute_backend_bucket.bucket](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_backend_bucket) | resource |
| [google_compute_backend_bucket.cn_lb](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_backend_bucket) | resource |
| [google_compute_firewall.gcp_hc_ip_allow](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource |
| [google_compute_global_address.gca](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_global_address) | resource |
| [google_compute_global_forwarding_rule.non_tls](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_global_forwarding_rule) | resource |
| [google_compute_health_check.cn_lb](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_health_check) | resource |
| [google_compute_managed_ssl_certificate.gcs_certs](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_managed_ssl_certificate) | resource |
| [google_compute_region_network_endpoint_group.cloudrun_neg](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_region_network_endpoint_group) | resource |
| [google_compute_ssl_certificate.external_certs](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_ssl_certificate) | resource |
| [google_compute_ssl_certificate.gcs_certs](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_ssl_certificate) | resource |
| [google_compute_target_http_proxy.non_tls](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_target_http_proxy) | resource |
| [google_compute_target_https_proxy.external_signed](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_target_https_proxy) | resource |
| [google_compute_target_https_proxy.google_managed](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_target_https_proxy) | resource |
| [google_compute_target_https_proxy.self_signed](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_target_https_proxy) | resource |
| [google_compute_url_map.cn_lb](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map) | resource |
| [google_logging_project_sink.log_archive_sink](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/logging_project_sink) | resource |
| [google_storage_bucket.cn_lb](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket) | resource |
| [google_storage_bucket.log_archive_sink](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket) | resource |
| [google_storage_bucket_iam_binding.log_archive_sink_writer](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_iam_binding) | resource |
| [random_id.external_certificate](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |
| [random_string.random_suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |
| [tls_private_key.web_lb_key](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource |
| [tls_self_signed_cert.web_lb_cert](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/self_signed_cert) | resource |
| [google_cloud_run_service.cloud_run_service](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/cloud_run_service) | data source |
| [google_compute_network_endpoint_group.cn_lb](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_network_endpoint_group) | data source |
| [google_compute_zones.available](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_zones) | data source |## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| [allow\_non\_tls\_frontend](#input\_allow\_non\_tls\_frontend) | If true, enables port 80 frontend - creates non-TLS (http://) variant of LB | `string` | `false` | no |
| [backend\_bucket\_location](#input\_backend\_bucket\_location) | GCS location(https://cloud.google.com/storage/docs/locations) of bucket where invalid requests are routed. | `string` | `"EUROPE-WEST3"` | no |
| [certificate](#input\_certificate) | The certificate in PEM format. The certificate chain must be no greater than 5 certs long. The chain must include at least one intermediate cert. Note: This property is sensitive and will not be displayed in the plan. | `string` | `null` | no |
| [check\_interval\_sec](#input\_check\_interval\_sec) | How often (in seconds) to send a health check. The default value is 5 seconds. | `number` | `5` | no |
| [create\_logging\_sink\_bucket](#input\_create\_logging\_sink\_bucket) | If true, creates bucket and set up logging sink | `bool` | `false` | no |
| [custom\_health\_check\_ports](#input\_custom\_health\_check\_ports) | Custom ports for GCE health checks, not needed unless your services are not in 30000-32767 or 3000, 5000 | `list(string)` | `[]` | no |
| [custom\_target\_http\_proxy\_name](#input\_custom\_target\_http\_proxy\_name) | Custom name for HTTP proxy name used instead of non-tls-proxy- | `string` | `""` | no |
| [custom\_url\_map\_name](#input\_custom\_url\_map\_name) | Custom name for URL map name used instead of lb-var.name | `string` | `""` | no |
| [default\_iap\_setup](#input\_default\_iap\_setup) | In case you use the same IAP setup for all backends |object({| `null` | no |
oauth2_client_id = string
oauth2_client_secret = string
})
| [default\_network\_name](#input\_default\_network\_name) | Default firewall network name, used to place a default fw allowing google's default health checks. Leave blank if you use GKE ingress-provisioned LB (now deprecated) | `string` | `"default"` | no |
| [dont\_use\_dns\_names\_in\_certificate](#input\_dont\_use\_dns\_names\_in\_certificate) | Due to backward compatibility, TLS setup can omit setup of dns\_names in self signed certificate | `bool` | `false` | no |
| [google\_managed\_tls](#input\_google\_managed\_tls) | If true, creates Google-managed TLS cert | `bool` | `false` | no |
| [health\_check\_request\_path](#input\_health\_check\_request\_path) | Health checked path (URN) | `string` | `"/healthz"` | no |
| [healthy\_threshold](#input\_healthy\_threshold) | A so-far unhealthy instance will be marked healthy after this many consecutive successes. The default value is 2. | `number` | `2` | no |
| [http\_backend\_protocol](#input\_http\_backend\_protocol) | HTTP backend protocol, one of: HTTP/HTTP2 | `string` | `"HTTP"` | no |
| [http\_backend\_timeout](#input\_http\_backend\_timeout) | Time of http request timeout (in seconds) | `string` | `"30"` | no |
| [iap\_setup](#input\_iap\_setup) | Service setup for IAP, overwrites default\_iap\_setup if used |map(object({| `{}` | no |
oauth2_client_id = string
oauth2_client_secret = string
}))
| [keys\_alg](#input\_keys\_alg) | Algorithm used for private keys | `string` | `"RSA"` | no |
| [keys\_valid\_period](#input\_keys\_valid\_period) | Validation period of the self signed key | `number` | `29200` | no |
| [log\_config\_sample\_rate](#input\_log\_config\_sample\_rate) | The value of the field must be in [0, 1]. This configures the sampling rate of requests to the load balancer where 1.0 means all logged requests are reported and 0.0 means no logged requests are reported. The default value is 1.0. | `string` | `"1.0"` | no |
| [logging\_sink\_bucket\_retency](#input\_logging\_sink\_bucket\_retency) | Number of days after which log files are deleted from bucket | `number` | `730` | no |
| [managed\_certificate\_name](#input\_managed\_certificate\_name) | Name of Google-managed certificate. Useful when migrating from Ingress-provisioned load balancer | `string` | `null` | no |
| [mask\_metrics\_endpoint](#input\_mask\_metrics\_endpoint) | If set, requests /metrics will be sent to default backend | `bool` | `false` | no |
| [name](#input\_name) | Instance name | `string` | `"default_value"` | no |
| [non\_tls\_global\_forwarding\_rule\_name](#input\_non\_tls\_global\_forwarding\_rule\_name) | Global non tls forwarding rule name, if set, changes name of non-tls forwarding rule | `string` | `""` | no |
| [private\_key](#input\_private\_key) | The write-only private key in PEM format. Note: This property is sensitive and will not be displayed in the plan. | `string` | `null` | no |
| [project](#input\_project) | Project ID | `string` | n/a | yes |
| [random\_suffix\_size](#input\_random\_suffix\_size) | Size of random suffix | `number` | `8` | no |
| [region](#input\_region) | GCP region where we will look for NEGs | `string` | n/a | yes |
| [self\_signed\_tls](#input\_self\_signed\_tls) | If true, creates self-signed TLS cert | `bool` | `false` | no |
| [services](#input\_services) | List of services: cloudrun, neg, bucket, ... to be used in the map |list(object({| n/a | yes |
name = string
type = string
bucket_name = optional(string)
location = optional(string)
zone = optional(string)
additional_negs = optional(list(string))
timeout_sec = optional(number)
check_interval_sec = optional(number)
healthy_threshold = optional(number)
unhealthy_threshold = optional(number)
http_backend_protocol = optional(string)
http_backend_timeout = optional(string)
health_check_request_path = optional(string)
enable_cdn = optional(bool)
}))
| [timeout\_sec](#input\_timeout\_sec) | How long (in seconds) to wait before claiming failure. The default value is 5 seconds. It is invalid for timeout\_sec to have greater value than check\_interval\_sec. | `number` | `5` | no |
| [unhealthy\_threshold](#input\_unhealthy\_threshold) | A so-far healthy instance will be marked unhealthy after this many consecutive failures. The default value is 2. | `number` | `2` | no |
| [url\_map](#input\_url\_map) | Url map setup |map(object({| n/a | yes |
hostnames = list(string)
default_service = string
path_rules = optional(list(object({
paths = list(string)
service = string
})))
}))
| [use\_random\_suffix\_for\_network\_endpoint\_group](#input\_use\_random\_suffix\_for\_network\_endpoint\_group) | If true, uses random suffix for NEG name | `bool` | `true` | no |
| [zone](#input\_zone) | GCP zone where we will look for NEGs - optional parameter, if not set, the we will automatically search in all zones in region | `string` | `null` | no |## Outputs
| Name | Description |
|------|-------------|
| [ip\_address](#output\_ip\_address) | IP address |