diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5780078569..4252d4a350 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+## [1.4.0] - 2020-05-01
+
+- fix DNS module internal zone lookup
+- fix Cloud NAT module internal router name lookup
+- re-enable and update outputs for the foundations environments example
+- add peering route configuration for private clusters to GKE cluster module
+- **incompatible changes** in the GKE nodepool module
+ - rename `node_config_workload_metadata_config` variable to `workload_metadata_config`
+ - new default for `workload_metadata_config` is `GKE_METADATA_SERVER`
+- **incompatible change** in the `compute-vm` module
+ - removed support for MIG and the `group_manager` variable
+- add `compute-mig` and `net-ilb` modules
+- **incompatible change** in `net-vpc`
+ - a new `name` attribute has been added to the `subnets` variable, allowing to directly set subnet name, to update to the new module add an extra `name = false` attribute to each subnet
+
## [1.3.0] - 2020-04-08
- add organization policy module
@@ -25,7 +40,8 @@ All notable changes to this project will be documented in this file.
- merge development branch with suite of new modules and end-to-end examples
-[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v1.3.0...HEAD
+[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v1.4.0...HEAD
+[1.4.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v1.3.0...v1.4.0
[1.3.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v1.2...v1.3.0
[1.2.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v1.1...v1.2
[1.1.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v1.0...v1.1
diff --git a/README.md b/README.md
index c34b5603f2..6561b33b38 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
This repository provides **end-to-end examples** and a **suite of Terraform modules** for Google Cloud, which support different use cases:
-- starter kits used to bootstrap real-word cloud foundations and infrastructure
+- starter kits used to bootstrap real-world cloud foundations and infrastructure
- reference examples used to deep dive on network patterns or product features
- composable modules that support quick prototyping and testing
- a comprehensive source of lean modules that lend themselves well to changes
diff --git a/foundations/environments/README.md b/foundations/environments/README.md
index 41f34c4595..8f5c361a4f 100644
--- a/foundations/environments/README.md
+++ b/foundations/environments/README.md
@@ -61,5 +61,6 @@ If no shared services are needed, the shared service project module can of cours
| environment_service_account_keys | Service account keys used to run each environment Terraform modules. | ✓ |
| environment_service_accounts | Service accounts used to run each environment Terraform modules. | |
| environment_tf_gcs_buckets | GCS buckets used for each environment Terraform state. | |
-| shared_resources_project | Project that holdes resources shared across environments. | |
+| shared_services_project | Project that holdes resources shared across environments. | |
+| terraform_project | Project that holds the base Terraform resources. | |
diff --git a/foundations/environments/outputs.tf b/foundations/environments/outputs.tf
index f083d1c8a0..733a574ce3 100644
--- a/foundations/environments/outputs.tf
+++ b/foundations/environments/outputs.tf
@@ -12,52 +12,51 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-/* output "terraform_project" {
+output "terraform_project" {
description = "Project that holds the base Terraform resources."
- value = module.project-tf.project_id
+ value = module.tf-project.project_id
}
output "bootstrap_tf_gcs_bucket" {
description = "GCS bucket used for the bootstrap Terraform state."
- value = module.gcs-tf-bootstrap.name
+ value = module.tf-gcs-bootstrap.name
}
output "environment_folders" {
description = "Top-level environment folders."
- value = module.folders-top-level.ids
+ value = module.environment-folders.ids
}
output "environment_tf_gcs_buckets" {
description = "GCS buckets used for each environment Terraform state."
- value = module.gcs-tf-environments.names
+ value = module.tf-gcs-environments.names
}
output "environment_service_account_keys" {
description = "Service account keys used to run each environment Terraform modules."
sensitive = true
- value = module.service-accounts-tf-environments.keys
+ value = module.tf-service-accounts.keys
}
output "environment_service_accounts" {
description = "Service accounts used to run each environment Terraform modules."
- value = module.service-accounts-tf-environments.emails
+ value = module.tf-service-accounts.emails
}
output "audit_logs_bq_dataset" {
description = "Bigquery dataset for the audit logs export."
- value = module.bq-audit-export.resource_name
+ value = module.audit-datasets.datasets[0].dataset_id
}
output "audit_logs_project" {
description = "Project that holds the audit logs export resources."
- value = module.project-audit.project_id
+ value = module.audit-project.project_id
}
-output "shared_resources_project" {
+output "shared_services_project" {
description = "Project that holdes resources shared across environments."
- value = module.project-shared-resources.project_id
+ value = module.sharedsvc-project.project_id
}
# Add further outputs here for the additional modules that manage shared
# resources, like GCR, GCS buckets, KMS, etc.
- */
diff --git a/infrastructure/hub-and-spoke-peering/README.md b/infrastructure/hub-and-spoke-peering/README.md
index 6e4cb87cdc..3a23ba9681 100644
--- a/infrastructure/hub-and-spoke-peering/README.md
+++ b/infrastructure/hub-and-spoke-peering/README.md
@@ -30,7 +30,9 @@ This sample creates several distinct groups of resources:
## Testing GKE access from spoke 1
-As mentioned above, a VPN tunnel is used as a workaround to avoid the peering transitivity issue that would prevent any VPC other than spoke 2 to connect to the GKE master.
+As mentioned above, a VPN tunnel is used as a workaround to avoid the peering transitivity issue that would prevent any VPC other than spoke 2 to connect to the GKE master. This diagram illustrates the solution
+
+
To test cluster access, first log on to the spoke 2 instance and confirm cluster and IAM roles are set up correctly:
@@ -39,7 +41,24 @@ gcloud container clusters get-credentials cluster-1 --zone europe-west1-b
kubectl get all
```
-The next step is to edit the peering towards the GKE master tenant VPC, and enable export routes. The peering has a name like `gke-xxxxxxxxxxxxxxxxxxxx-xxxx-xxxx-peer`, you can edit it in the Cloud Console from the *VPC network peering* page or using `gcloud`:
+The next step is to edit the peering towards the GKE master tenant VPC, and enable export routes. You can do it directly in Terraform with the GKE module `peering_config' variable, via gcloud, or on the cloud ccnsole. We're leaving it as an option, since one of the goals of this example is to allow testing both working and non-working configurations.
+
+### Export routes via Terraform
+
+Change the GKE cluster module and add a new variable after `private_cluster_config`:
+
+```hcl
+ peering_config = {
+ export_routes = true
+ import_routes = false
+ }
+```
+
+If you added the variable after applying, simply apply Terraform again.
+
+### Export routes via gcloud
+
+The peering has a name like `gke-xxxxxxxxxxxxxxxxxxxx-xxxx-xxxx-peer`, you can edit it in the Cloud Console from the *VPC network peering* page or using `gcloud`:
```
gcloud compute networks peerings list
@@ -52,7 +71,9 @@ Then connect via SSH to the spoke 1 instance and run the same commands you ran o
## Operational considerations
-A single pre-existing project is used in this example to keep variables and complexity to a minimum, in a real world scenarios each spoke would probably use a separate project.
+A single pre-existing project is used in this example to keep variables and complexity to a minimum, in a real world scenario each spoke would use a separate project (and Shared VPC).
+
+A few APIs need to be enabled in the project, if `apply` fails due to a service not being enabled just click on the link in the error message to enable it for the project, then resume `apply`.
The VPN used to connect the GKE masters VPC does not account for HA, upgrading to use HA VPN is reasonably simple by using the relevant [module](../../modules/net-vpn-ha).
diff --git a/infrastructure/hub-and-spoke-peering/diagram-network.png b/infrastructure/hub-and-spoke-peering/diagram-network.png
new file mode 100644
index 0000000000..2817fef526
Binary files /dev/null and b/infrastructure/hub-and-spoke-peering/diagram-network.png differ
diff --git a/infrastructure/hub-and-spoke-peering/diagram.png b/infrastructure/hub-and-spoke-peering/diagram.png
index 0c598fe56e..ad2b37ab1f 100644
Binary files a/infrastructure/hub-and-spoke-peering/diagram.png and b/infrastructure/hub-and-spoke-peering/diagram.png differ
diff --git a/infrastructure/hub-and-spoke-peering/main.tf b/infrastructure/hub-and-spoke-peering/main.tf
index 9f4b875734..08603e4dc7 100644
--- a/infrastructure/hub-and-spoke-peering/main.tf
+++ b/infrastructure/hub-and-spoke-peering/main.tf
@@ -34,6 +34,7 @@ module "vpc-hub" {
subnets = {
default = {
ip_cidr_range = var.ip_ranges.hub
+ name = null
region = var.region
secondary_ip_range = {}
}
@@ -59,6 +60,7 @@ module "vpc-spoke-1" {
subnets = {
default = {
ip_cidr_range = var.ip_ranges.spoke-1
+ name = null
region = var.region
secondary_ip_range = {}
}
@@ -101,6 +103,7 @@ module "vpc-spoke-2" {
subnets = {
default = {
ip_cidr_range = var.ip_ranges.spoke-2
+ name = null
region = var.region
secondary_ip_range = {
pods = var.ip_secondary_ranges.spoke-2-pods
diff --git a/infrastructure/hub-and-spoke-vpn/main.tf b/infrastructure/hub-and-spoke-vpn/main.tf
index bee66f7fb5..3211214f18 100644
--- a/infrastructure/hub-and-spoke-vpn/main.tf
+++ b/infrastructure/hub-and-spoke-vpn/main.tf
@@ -33,11 +33,13 @@ module "vpc-hub" {
subnets = {
a = {
ip_cidr_range = var.ip_ranges.hub-a
+ name = null
region = var.regions.a
secondary_ip_range = {}
}
b = {
ip_cidr_range = var.ip_ranges.hub-b
+ name = null
region = var.regions.b
secondary_ip_range = {}
}
@@ -121,11 +123,13 @@ module "vpc-spoke-1" {
subnets = {
a = {
ip_cidr_range = var.ip_ranges.spoke-1-a
+ name = null
region = var.regions.a
secondary_ip_range = {}
}
b = {
ip_cidr_range = var.ip_ranges.spoke-1-b
+ name = null
region = var.regions.a
secondary_ip_range = {}
}
@@ -182,11 +186,13 @@ module "vpc-spoke-2" {
subnets = {
a = {
ip_cidr_range = var.ip_ranges.spoke-2-a
+ name = null
region = var.regions.b
secondary_ip_range = {}
}
b = {
ip_cidr_range = var.ip_ranges.spoke-2-b
+ name = null
region = var.regions.b
secondary_ip_range = {}
}
diff --git a/infrastructure/onprem-google-access-dns/README.md b/infrastructure/onprem-google-access-dns/README.md
index 39f7a6d8de..2820c8f528 100644
--- a/infrastructure/onprem-google-access-dns/README.md
+++ b/infrastructure/onprem-google-access-dns/README.md
@@ -44,7 +44,7 @@ If the forwader address does not match the Terraform variable, add the correct v
```bash
tf apply
-tf taint module.on-prem.google_compute_instance.on_prem_in_a_box
+tf taint 'module.vm-onprem.google_compute_instance.default["onprem-1"]'
tf apply
```
@@ -89,7 +89,7 @@ google.internal {
```bash
# connect to the onprem instance
-gcloud compute ssh onprem
+gcloud compute ssh onprem-1
# check that the BGP session works and the advertised routes are set
sudo docker exec -it onprem_bird_1 ip route |grep bird
@@ -101,6 +101,12 @@ sudo docker exec -it onprem_bird_1 ip route |grep bird
# get a shell on the toolbox container
sudo docker exec -it onprem_toolbox_1 sh
+# test pinging the IP address of the test instance (check outputs for it)
+ping 10.0.0.3
+
+# note: if you are able to ping the IP but the DNS tests below do not work,
+# refer to the sections above on configuring the DNS inbound fwd IP
+
# test forwarding from CoreDNS via the Cloud DNS inbound policy
dig test-1.gcp.example.org +short
10.0.0.3
diff --git a/infrastructure/onprem-google-access-dns/main.tf b/infrastructure/onprem-google-access-dns/main.tf
index f5fa104dc8..5ebcbb1ede 100644
--- a/infrastructure/onprem-google-access-dns/main.tf
+++ b/infrastructure/onprem-google-access-dns/main.tf
@@ -55,6 +55,7 @@ module "vpc" {
subnets = {
default = {
ip_cidr_range = var.ip_ranges.gcp
+ name = null
region = var.region
secondary_ip_range = {}
}
diff --git a/infrastructure/shared-vpc-gke/main.tf b/infrastructure/shared-vpc-gke/main.tf
index 7c47ad812b..e55e54cd8e 100644
--- a/infrastructure/shared-vpc-gke/main.tf
+++ b/infrastructure/shared-vpc-gke/main.tf
@@ -91,7 +91,7 @@ module "project-svc-gke" {
module "vpc-shared" {
source = "../../modules/net-vpc"
- project_id = module.project-host.iam_project_id
+ project_id = module.project-host.project_id
name = "shared-vpc"
shared_vpc_host = true
shared_vpc_service_projects = [
@@ -101,11 +101,13 @@ module "vpc-shared" {
subnets = {
gce = {
ip_cidr_range = var.ip_ranges.gce
+ name = null
region = var.region
secondary_ip_range = {}
}
gke = {
ip_cidr_range = var.ip_ranges.gke
+ name = null
region = var.region
secondary_ip_range = {
pods = var.ip_secondary_ranges.gke-pods
diff --git a/modules/cloud-config-container/README.md b/modules/cloud-config-container/README.md
index 49083faa3d..7ee53bdc18 100644
--- a/modules/cloud-config-container/README.md
+++ b/modules/cloud-config-container/README.md
@@ -15,7 +15,7 @@ These modules are designed for several use cases:
- [MySQL](./mysql)
- [Nginx](./nginx)
- [On-prem in Docker](./onprem)
-- [ ] Squid forward proxy
+- [Squid forward proxy](./squid)
## Using the modules
@@ -27,4 +27,4 @@ For convenience when developing or prototyping infrastructure, an optional test
## TODO
-- [ ] convert all `xxx_config` variables to use file content instead of path
\ No newline at end of file
+- [ ] convert all `xxx_config` variables to use file content instead of path
diff --git a/modules/compute-mig/README.md b/modules/compute-mig/README.md
new file mode 100644
index 0000000000..918a2a3d97
--- /dev/null
+++ b/modules/compute-mig/README.md
@@ -0,0 +1,188 @@
+# GCE Managed Instance Group module
+
+This module allows creating a managed instance group supporting one or more application versions via instance templates. A health check and an autoscaler can also be optionally created.
+
+This module can be coupled with the [`compute-vm`](../compute-vm) module which can manage instance templates, and the [`net-ilb`](../net-ilb) module to assign the MIG to a backend wired to an Internal Load Balancer. The first use case is shown in the examples below.
+
+## Examples
+
+This example shows how to manage a simple MIG that leverages the `compute-vm` module to manage the underlying instance template. The following sub-examples will only show how to enable specific features of this module, and won't replicate the combined setup.
+
+```hcl
+module "cos-nginx" {
+ source = "./modules/cloud-config-container/nginx"
+}
+
+module "nginx-template" {
+ source = "./modules/compute-vm"
+ project_id = "my-project"
+ region = "europe-west1"
+ zone = "europe-west1-b"
+ name = "ilb-test"
+ network_interfaces = [{
+ network = local.network_self_link,
+ subnetwork = local.subnetwork_self_link,
+ nat = false,
+ addresses = null
+ }]
+ boot_disk = {
+ image = "projects/cos-cloud/global/images/family/cos-stable"
+ type = "pd-ssd"
+ size = 10
+ }
+ tags = ["http-server", "ssh"]
+ use_instance_template = true
+ metadata = {
+ user-data = module.cos-nginx.cloud_config
+ }
+}
+
+module "nginx-mig" {
+ source = "./modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 2
+ default_version = {
+ instance_template = module.nginx-template.template.self_link
+ name = "default"
+ }
+}
+```
+
+### Multiple versions
+
+If multiple versions are desired, use more `compute-vm` instances for the additional templates used in each version (not shown here), and reference them like this:
+
+```hcl
+module "nginx-mig" {
+ source = "./modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 3
+ default_version = {
+ instance_template = module.nginx-template-default.template.self_link
+ name = "default"
+ }
+ versions = {
+ canary = {
+ instance_template = module.nginx-template-default.template.self_link
+ target_type = "fixed"
+ target_size = 1
+ }
+ }
+}
+```
+
+### Health check and autohealing policies
+
+Autohealing policies can use an externally defined health check, or have this module auto-create one:
+
+```hcl
+module "nginx-mig" {
+ source = "./modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 3
+ default_version = {
+ instance_template = module.nginx-template-default.template.self_link
+ name = "default"
+ }
+ auto_healing_policies = {
+ health_check = module.nginx-mig.health_check.self_link
+ initial_delay_sec = 30
+ }
+ health_check_config = {
+ type = "http"
+ check = { port = 80 }
+ config = {}
+ logging = true
+ }
+}
+```
+
+### Autoscaling
+
+The module can create and manage an autoscaler associated with the MIG. When using autoscaling do not set the `target_size` variable or set it to `null`. Here we show a CPU utilization autoscaler, the other available modes are load balancing utilization and custom metric, like the underlying autoscaler resource.
+
+```hcl
+module "nginx-mig" {
+ source = "./modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 3
+ default_version = {
+ instance_template = module.nginx-template-default.template.self_link
+ name = "default"
+ }
+ autoscaler_config = {
+ max_replicas = 3
+ min_replicas = 1
+ cooldown_period = 30
+ cpu_utilization_target = 0.65
+ load_balancing_utilization_target = null
+ metric = null
+ }
+}
+```
+
+### Update policy
+
+```hcl
+module "nginx-mig" {
+ source = "./modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 3
+ default_version = {
+ instance_template = module.nginx-template-default.template.self_link
+ name = "default"
+ }
+ update_policy = {
+ type = "PROACTIVE"
+ minimal_action = "REPLACE"
+ min_ready_sec = 30
+ max_surge_type = "fixed"
+ max_surge = 1
+ max_unavailable_type = null
+ max_unavailable = null
+ }
+}
+```
+
+
+## Variables
+
+| name | description | type | required | default |
+|---|---|:---: |:---:|:---:|
+| default_version | Default application version template. Additional versions can be specified via the `versions` variable. | object({...})
| ✓ | |
+| location | Compute zone, or region if `regional` is set to true. | string
| ✓ | |
+| name | Managed group name. | string
| ✓ | |
+| project_id | Project id. | string
| ✓ | |
+| *auto_healing_policies* | Auto-healing policies for this group. | object({...})
| | null
|
+| *autoscaler_config* | Optional autoscaler configuration. Only one of 'cpu_utilization_target' 'load_balancing_utilization_target' or 'metric' can be not null. | object({...})
| | null
|
+| *health_check_config* | Optional auto-created helth check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | object({...})
| | null
|
+| *named_ports* | Named ports. | map(number)
| | null
|
+| *regional* | Use regional instance group. When set, `location` should be set to the region. | bool
| | false
|
+| *target_pools* | Optional list of URLs for target pools to which new instances in the group are added. | list(string)
| | []
|
+| *target_size* | Group target size, leave null when using an autoscaler. | number
| | null
|
+| *update_policy* | Update policy. Type can be 'OPPORTUNISTIC' or 'PROACTIVE', action 'REPLACE' or 'restart', surge type 'fixed' or 'percent'. | object({...})
| | null
|
+| *versions* | Additional application versions, target_type is either 'fixed' or 'percent'. | map(object({...}))
| | null
|
+| *wait_for_instances* | Wait for all instances to be created/updated before returning. | bool
| | null
|
+
+## Outputs
+
+| name | description | sensitive |
+|---|---|:---:|
+| autoscaler | Auto-created autoscaler resource. | |
+| group_manager | Instance group resource. | |
+| health_check | Auto-created health-check resource. | |
+
+
+## TODO
+
+- [ ] add support for instance groups
diff --git a/modules/compute-mig/main.tf b/modules/compute-mig/main.tf
new file mode 100644
index 0000000000..85c25b8686
--- /dev/null
+++ b/modules/compute-mig/main.tf
@@ -0,0 +1,367 @@
+/**
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+resource "google_compute_autoscaler" "default" {
+ provider = google-beta
+ count = var.regional || var.autoscaler_config == null ? 0 : 1
+ project = var.project_id
+ name = var.name
+ description = "Terraform managed."
+ zone = var.location
+ target = google_compute_instance_group_manager.default.0.id
+
+ autoscaling_policy {
+ max_replicas = var.autoscaler_config.max_replicas
+ min_replicas = var.autoscaler_config.min_replicas
+ cooldown_period = var.autoscaler_config.cooldown_period
+
+ dynamic cpu_utilization {
+ for_each = (
+ var.autoscaler_config.cpu_utilization_target == null ? [] : [""]
+ )
+ content {
+ target = var.autoscaler_config.cpu_utilization_target
+ }
+ }
+
+ dynamic load_balancing_utilization {
+ for_each = (
+ var.autoscaler_config.load_balancing_utilization_target == null ? [] : [""]
+ )
+ content {
+ target = var.autoscaler_config.load_balancing_utilization_target
+ }
+ }
+
+ dynamic metric {
+ for_each = (
+ var.autoscaler_config.metric == null
+ ? []
+ : [var.autoscaler_config.metric]
+ )
+ iterator = config
+ content {
+ name = config.value.name
+ single_instance_assignment = config.value.single_instance_assignment
+ target = config.value.target
+ type = config.value.type
+ filter = config.value.filter
+ }
+ }
+ }
+}
+
+
+resource "google_compute_instance_group_manager" "default" {
+ provider = google-beta
+ count = var.regional ? 0 : 1
+ project = var.project_id
+ zone = var.location
+ name = var.name
+ base_instance_name = var.name
+ description = "Terraform-managed."
+ target_size = var.target_size
+ target_pools = var.target_pools
+ wait_for_instances = var.wait_for_instances
+ dynamic auto_healing_policies {
+ for_each = var.auto_healing_policies == null ? [] : [var.auto_healing_policies]
+ iterator = config
+ content {
+ health_check = config.value.health_check
+ initial_delay_sec = config.value.initial_delay_sec
+ }
+ }
+ dynamic update_policy {
+ for_each = var.update_policy == null ? [] : [var.update_policy]
+ iterator = config
+ content {
+ type = config.value.type
+ minimal_action = config.value.minimal_action
+ min_ready_sec = config.value.min_ready_sec
+ max_surge_fixed = (
+ config.value.max_surge_type == "fixed" ? config.value.max_surge : null
+ )
+ max_surge_percent = (
+ config.value.max_surge_type == "percent" ? config.value.max_surge : null
+ )
+ max_unavailable_fixed = (
+ config.value.max_unavailable_type == "fixed" ? config.value.max_unavailable : null
+ )
+ max_unavailable_percent = (
+ config.value.max_unavailable_type == "percent" ? config.value.max_unavailable : null
+ )
+ }
+ }
+ dynamic named_port {
+ for_each = var.named_ports == null ? {} : var.named_ports
+ iterator = config
+ content {
+ name = config.key
+ port = config.value
+ }
+ }
+ version {
+ instance_template = var.default_version.instance_template
+ name = var.default_version.name
+ }
+ dynamic version {
+ for_each = var.versions == null ? {} : var.versions
+ iterator = version
+ content {
+ name = version.key
+ instance_template = version.value.instance_template
+ target_size {
+ fixed = (
+ version.value.target_type == "fixed" ? version.value.target_size : null
+ )
+ percent = (
+ version.value.target_type == "percent" ? version.value.target_size : null
+ )
+ }
+ }
+ }
+}
+
+
+resource "google_compute_region_autoscaler" "default" {
+ provider = google-beta
+ count = var.regional && var.autoscaler_config != null ? 1 : 0
+ project = var.project_id
+ name = var.name
+ description = "Terraform managed."
+ region = var.location
+ target = google_compute_region_instance_group_manager.default.0.id
+
+ autoscaling_policy {
+ max_replicas = var.autoscaler_config.max_replicas
+ min_replicas = var.autoscaler_config.min_replicas
+ cooldown_period = var.autoscaler_config.cooldown_period
+
+ dynamic cpu_utilization {
+ for_each = (
+ var.autoscaler_config.cpu_utilization_target == null ? [] : [""]
+ )
+ content {
+ target = var.autoscaler_config.cpu_utilization_target
+ }
+ }
+
+ dynamic load_balancing_utilization {
+ for_each = (
+ var.autoscaler_config.load_balancing_utilization_target == null ? [] : [""]
+ )
+ content {
+ target = var.autoscaler_config.load_balancing_utilization_target
+ }
+ }
+
+ dynamic metric {
+ for_each = (
+ var.autoscaler_config.metric == null
+ ? []
+ : [var.autoscaler_config.metric]
+ )
+ iterator = config
+ content {
+ name = config.value.name
+ single_instance_assignment = config.value.single_instance_assignment
+ target = config.value.target
+ type = config.value.type
+ filter = config.value.filter
+ }
+ }
+ }
+}
+
+
+resource "google_compute_region_instance_group_manager" "default" {
+ provider = google-beta
+ count = var.regional ? 1 : 0
+ project = var.project_id
+ region = var.location
+ name = var.name
+ base_instance_name = var.name
+ description = "Terraform-managed."
+ target_size = var.target_size
+ target_pools = var.target_pools
+ wait_for_instances = var.wait_for_instances
+ dynamic auto_healing_policies {
+ for_each = var.auto_healing_policies == null ? [] : [var.auto_healing_policies]
+ iterator = config
+ content {
+ health_check = config.value.health_check
+ initial_delay_sec = config.value.initial_delay_sec
+ }
+ }
+ dynamic update_policy {
+ for_each = var.update_policy == null ? [] : [var.update_policy]
+ iterator = config
+ content {
+ type = config.value.type
+ minimal_action = config.value.minimal_action
+ min_ready_sec = config.value.min_ready_sec
+ max_surge_fixed = (
+ config.value.max_surge_type == "fixed" ? config.value.max_surge : null
+ )
+ max_surge_percent = (
+ config.value.max_surge_type == "percent" ? config.value.max_surge : null
+ )
+ max_unavailable_fixed = (
+ config.value.max_unavailable_type == "fixed" ? config.value.max_unavailable : null
+ )
+ max_unavailable_percent = (
+ config.value.max_unavailable_type == "percent" ? config.value.max_unavailable : null
+ )
+ }
+ }
+ dynamic named_port {
+ for_each = var.named_ports == null ? {} : var.named_ports
+ iterator = config
+ content {
+ name = config.key
+ port = config.value
+ }
+ }
+ version {
+ instance_template = var.default_version.instance_template
+ name = var.default_version.name
+ }
+ dynamic version {
+ for_each = var.versions == null ? {} : var.versions
+ iterator = version
+ content {
+ name = version.key
+ instance_template = version.value.instance_template
+ target_size {
+ fixed = (
+ version.value.target_type == "fixed" ? version.value.target_size : null
+ )
+ percent = (
+ version.value.target_type == "percent" ? version.value.target_size : null
+ )
+ }
+ }
+ }
+}
+
+resource "google_compute_health_check" "default" {
+ provider = google-beta
+ count = var.health_check_config == null ? 0 : 1
+ project = var.project_id
+ name = var.name
+ description = "Terraform managed."
+
+ check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
+ healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
+ timeout_sec = try(var.health_check_config.config.timeout_sec, null)
+ unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
+
+ dynamic http_health_check {
+ for_each = (
+ try(var.health_check_config.type, null) == "http"
+ ? [var.health_check_config.check]
+ : []
+ )
+ iterator = check
+ content {
+ host = try(check.value.host, null)
+ port = try(check.value.port, null)
+ port_name = try(check.value.port_name, null)
+ port_specification = try(check.value.port_specification, null)
+ proxy_header = try(check.value.proxy_header, null)
+ request_path = try(check.value.request_path, null)
+ response = try(check.value.response, null)
+ }
+ }
+
+ dynamic https_health_check {
+ for_each = (
+ try(var.health_check_config.type, null) == "https"
+ ? [var.health_check_config.check]
+ : []
+ )
+ iterator = check
+ content {
+ host = try(check.value.host, null)
+ port = try(check.value.port, null)
+ port_name = try(check.value.port_name, null)
+ port_specification = try(check.value.port_specification, null)
+ proxy_header = try(check.value.proxy_header, null)
+ request_path = try(check.value.request_path, null)
+ response = try(check.value.response, null)
+ }
+ }
+
+ dynamic tcp_health_check {
+ for_each = (
+ try(var.health_check_config.type, null) == "tcp"
+ ? [var.health_check_config.check]
+ : []
+ )
+ iterator = check
+ content {
+ port = try(check.value.port, null)
+ port_name = try(check.value.port_name, null)
+ port_specification = try(check.value.port_specification, null)
+ proxy_header = try(check.value.proxy_header, null)
+ request = try(check.value.request, null)
+ response = try(check.value.response, null)
+ }
+ }
+
+ dynamic ssl_health_check {
+ for_each = (
+ try(var.health_check_config.type, null) == "ssl"
+ ? [var.health_check_config.check]
+ : []
+ )
+ iterator = check
+ content {
+ port = try(check.value.port, null)
+ port_name = try(check.value.port_name, null)
+ port_specification = try(check.value.port_specification, null)
+ proxy_header = try(check.value.proxy_header, null)
+ request = try(check.value.request, null)
+ response = try(check.value.response, null)
+ }
+ }
+
+ dynamic http2_health_check {
+ for_each = (
+ try(var.health_check_config.type, null) == "http2"
+ ? [var.health_check_config.check]
+ : []
+ )
+ iterator = check
+ content {
+ host = try(check.value.host, null)
+ port = try(check.value.port, null)
+ port_name = try(check.value.port_name, null)
+ port_specification = try(check.value.port_specification, null)
+ proxy_header = try(check.value.proxy_header, null)
+ request_path = try(check.value.request_path, null)
+ response = try(check.value.response, null)
+ }
+ }
+
+ dynamic log_config {
+ for_each = try(var.health_check_config.logging, false) ? [""] : []
+ content {
+ enable = true
+ }
+ }
+
+}
diff --git a/modules/compute-mig/outputs.tf b/modules/compute-mig/outputs.tf
new file mode 100644
index 0000000000..ca038a222f
--- /dev/null
+++ b/modules/compute-mig/outputs.tf
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+output "autoscaler" {
+ description = "Auto-created autoscaler resource."
+ value = var.autoscaler_config == null ? null : try(
+ google_compute_autoscaler.default.0,
+ google_compute_region_autoscaler.default.0,
+ {}
+ )
+}
+
+output "group_manager" {
+ description = "Instance group resource."
+ value = try(
+ google_compute_instance_group_manager.default.0,
+ google_compute_region_instance_group_manager.default.0,
+ {}
+ )
+}
+
+output "health_check" {
+ description = "Auto-created health-check resource."
+ value = var.health_check_config == null ? null : try(
+ google_compute_health_check.default.0, {}
+ )
+}
diff --git a/modules/compute-mig/variables.tf b/modules/compute-mig/variables.tf
new file mode 100644
index 0000000000..925ef7b703
--- /dev/null
+++ b/modules/compute-mig/variables.tf
@@ -0,0 +1,131 @@
+/**
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+variable "autoscaler_config" {
+ description = "Optional autoscaler configuration. Only one of 'cpu_utilization_target' 'load_balancing_utilization_target' or 'metric' can be not null."
+ type = object({
+ max_replicas = number
+ min_replicas = number
+ cooldown_period = number
+ cpu_utilization_target = number
+ load_balancing_utilization_target = number
+ metric = object({
+ name = string
+ single_instance_assignment = number
+ target = number
+ type = string # GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE
+ filter = string
+ })
+ })
+ default = null
+}
+
+variable "auto_healing_policies" {
+ description = "Auto-healing policies for this group."
+ type = object({
+ health_check = string
+ initial_delay_sec = number
+ })
+ default = null
+}
+
+variable "default_version" {
+ description = "Default application version template. Additional versions can be specified via the `versions` variable."
+ type = object({
+ instance_template = string
+ name = string
+ })
+}
+
+variable "health_check_config" {
+ description = "Optional auto-created helth check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage."
+ type = object({
+ type = string # http https tcp ssl http2
+ check = map(any) # actual health check block attributes
+ config = map(number) # interval, thresholds, timeout
+ logging = bool
+ })
+ default = null
+}
+
+variable "location" {
+ description = "Compute zone, or region if `regional` is set to true."
+ type = string
+}
+
+variable "name" {
+ description = "Managed group name."
+ type = string
+}
+
+variable "named_ports" {
+ description = "Named ports."
+ type = map(number)
+ default = null
+}
+
+variable "project_id" {
+ description = "Project id."
+ type = string
+}
+
+variable "regional" {
+ description = "Use regional instance group. When set, `location` should be set to the region."
+ type = bool
+ default = false
+}
+
+variable "target_pools" {
+ description = "Optional list of URLs for target pools to which new instances in the group are added."
+ type = list(string)
+ default = []
+}
+
+variable "target_size" {
+ description = "Group target size, leave null when using an autoscaler."
+ type = number
+ default = null
+}
+
+variable "update_policy" {
+ description = "Update policy. Type can be 'OPPORTUNISTIC' or 'PROACTIVE', action 'REPLACE' or 'restart', surge type 'fixed' or 'percent'."
+ type = object({
+ type = string # OPPORTUNISTIC | PROACTIVE
+ minimal_action = string # REPLACE | RESTART
+ min_ready_sec = number
+ max_surge_type = string # fixed | percent
+ max_surge = number
+ max_unavailable_type = string
+ max_unavailable = number
+ })
+ default = null
+}
+
+variable "versions" {
+ description = "Additional application versions, target_type is either 'fixed' or 'percent'."
+ type = map(object({
+ instance_template = string
+ target_type = string # fixed | percent
+ target_size = number
+ }))
+ default = null
+}
+
+variable "wait_for_instances" {
+ description = "Wait for all instances to be created/updated before returning."
+ type = bool
+ default = null
+}
diff --git a/modules/compute-vm/README.md b/modules/compute-vm/README.md
index e07680922c..14fb10dc7f 100644
--- a/modules/compute-vm/README.md
+++ b/modules/compute-vm/README.md
@@ -1,10 +1,11 @@
# Google Compute Engine VM module
-This module allows creating one or multiple instances or an instance template for a specific configuration. A service account is optionally created and assigned if not specified.
+This module can operate in two distinct modes:
-## TODO
+- instance creation, with optional unmanaged group
+- instance template creation
-- [ ] add examples for instance groups
+In both modes, an optional service account can be created and assigned to either instances or template. If you need a managed instance group when using the module in template mode, refer to the [`compute-mig`](../compute-mig) module.
## Examples
@@ -61,6 +62,39 @@ module "debian-test" {
}
```
+### Instance group
+
+If an instance group is needed when operating in instance mode, simply set the `group` variable to a non null map. The map can contain named port declarations, or be empty if named ports are not needed.
+
+```hcl
+module "instance-group" {
+ source = "../../cloud-foundation-fabric/modules/compute-vm"
+ project_id = "my-project"
+ region = "europe-west1"
+ zone = "europe-west1-b"
+ name = "ilb-test"
+ network_interfaces = [{
+ network = local.network_self_link,
+ subnetwork = local.subnetwork_self_link,
+ nat = false,
+ addresses = null
+ }]
+ boot_disk = {
+ image = "projects/cos-cloud/global/images/family/cos-stable"
+ type = "pd-ssd"
+ size = 10
+ }
+ service_account = local.service_account_email
+ service_account_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
+ use_instance_template = true
+ metadata = {
+ user-data = local.cloud_config
+ }
+ group = {}
+}
+
+```
+
## Variables
@@ -74,8 +108,7 @@ module "debian-test" {
| *attached_disk_defaults* | Defaults for attached disks options. | object({...})
| | ...
|
| *attached_disks* | Additional disks, if options is null defaults will be used in its place. | list(object({...}))
| | []
|
| *boot_disk* | Boot disk properties. | object({...})
| | ...
|
-| *group* | Instance group (for instance use). | object({...})
| | null
|
-| *group_manager* | Instance group manager (for template use). | object({...})
| | null
|
+| *group* | Define this variable to create an instance group for instances. Disabled for template use. | object({...})
| | null
|
| *hostname* | Instance FQDN name. | string
| | null
|
| *instance_count* | Number of instances to create (only for non-template usage). | number
| | 1
|
| *instance_type* | Instance type. | string
| | f1-micro
|
@@ -96,7 +129,6 @@ module "debian-test" {
|---|---|:---:|
| external_ips | Instance main interface external IP addresses. | |
| group | Instance group resource. | |
-| group_manager | Instance group resource. | |
| instances | Instance resources. | |
| internal_ips | Instance main interface internal IP addresses. | |
| names | Instance names. | |
diff --git a/modules/compute-vm/instance_group.tf b/modules/compute-vm/instance_group.tf
deleted file mode 100644
index 17dcd44dbe..0000000000
--- a/modules/compute-vm/instance_group.tf
+++ /dev/null
@@ -1,215 +0,0 @@
-/**
- * Copyright 2019 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-resource "google_compute_instance_group" "unmanaged" {
- count = (
- var.group != null && ! var.use_instance_template ? 1 : 0
- )
- project = var.project_id
- network = (
- length(var.network_interfaces) > 0
- ? var.network_interfaces.0.network
- : ""
- )
- zone = var.zone
- name = var.name
- description = "Terraform-managed."
- instances = [
- for name, instance in google_compute_instance.default : instance.self_link
- ]
- dynamic named_port {
- for_each = var.group.named_ports != null ? var.group.named_ports : {}
- iterator = config
- content {
- name = config.key
- port = config.value
- }
- }
-}
-
-resource "google_compute_instance_group_manager" "managed" {
- count = (
- var.group_manager != null && var.use_instance_template
- ? var.group_manager.regional ? 0 : 1
- : 0
- )
- project = var.project_id
- zone = var.zone
- name = var.name
- base_instance_name = var.name
- description = "Terraform-managed."
- target_size = var.group_manager.target_size
- target_pools = (
- var.group_manager.options == null
- ? null
- : var.group_manager.options.target_pools
- )
- wait_for_instances = (
- var.group_manager.options == null
- ? null
- : var.group_manager.options.wait_for_instances
- )
- dynamic auto_healing_policies {
- for_each = (
- var.group_manager.auto_healing_policies == null
- ? []
- : [var.group_manager.auto_healing_policies]
- )
- iterator = config
- content {
- health_check = config.value.health_check
- initial_delay_sec = config.value.initial_delay_sec
- }
- }
- dynamic update_policy {
- for_each = (
- var.group_manager.update_policy == null
- ? []
- : [var.group_manager.update_policy]
- )
- iterator = config
- content {
- type = config.value.type
- minimal_action = config.value.minimal_action
- min_ready_sec = config.value.min_ready_sec
- max_surge_fixed = (
- config.value.max_surge_type == "fixed" ? config.value.max_surge : null
- )
- max_surge_percent = (
- config.value.max_surge_type == "percent" ? config.value.max_surge : null
- )
- max_unavailable_fixed = (
- config.value.max_unavailable_type == "fixed" ? config.value.max_unavailable : null
- )
- max_unavailable_percent = (
- config.value.max_unavailable_type == "percent" ? config.value.max_unavailable : null
- )
- }
- }
- dynamic named_port {
- for_each = var.group_manager.named_ports != null ? var.group_manager.named_ports : {}
- iterator = config
- content {
- name = config.key
- port = config.value
- }
- }
- version {
- name = "${var.name}-default"
- instance_template = google_compute_instance_template.default.0.self_link
- }
- dynamic version {
- for_each = (
- var.group_manager.versions == null ? [] : [var.group_manager.versions]
- )
- iterator = config
- content {
- name = config.value.name
- instance_template = config.value.instance_template
- target_size {
- fixed = config.value.target_type == "fixed" ? config.value.target_size : null
- percent = config.value.target_type == "percent" ? config.value.target_size : null
- }
- }
- }
-}
-
-resource "google_compute_region_instance_group_manager" "managed" {
- count = (
- var.group_manager != null && var.use_instance_template
- ? var.group_manager.regional ? 1 : 0
- : 0
- )
- project = var.project_id
- region = var.region
- name = var.name
- base_instance_name = var.name
- description = "Terraform-managed."
- target_size = var.group_manager.target_size
- target_pools = (
- var.group_manager.options == null
- ? null
- : var.group_manager.options.target_pools
- )
- wait_for_instances = (
- var.group_manager.options == null
- ? null
- : var.group_manager.options.wait_for_instances
- )
- dynamic auto_healing_policies {
- for_each = (
- var.group_manager.auto_healing_policies == null
- ? []
- : [var.group_manager.auto_healing_policies]
- )
- iterator = config
- content {
- health_check = config.value.health_check
- initial_delay_sec = config.value.initial_delay_sec
- }
- }
- dynamic update_policy {
- for_each = (
- var.group_manager.update_policy == null
- ? []
- : [var.group_manager.update_policy]
- )
- iterator = config
- content {
- type = config.value.type
- minimal_action = config.value.minimal_action
- min_ready_sec = config.value.min_ready_sec
- max_surge_fixed = (
- config.value.max_surge_type == "fixed" ? config.value.max_surge : null
- )
- max_surge_percent = (
- config.value.max_surge_type == "percent" ? config.value.max_surge : null
- )
- max_unavailable_fixed = (
- config.value.max_unavailable_type == "fixed" ? config.value.max_unavailable : null
- )
- max_unavailable_percent = (
- config.value.max_unavailable_type == "percent" ? config.value.max_unavailable : null
- )
- }
- }
- dynamic named_port {
- for_each = var.group.named_ports
- iterator = config
- content {
- name = config.key
- port = config.value
- }
- }
- version {
- name = "${var.name}-default"
- instance_template = google_compute_instance_template.default.0.self_link
- }
- dynamic version {
- for_each = (
- var.group_manager.versions == null ? [] : [var.group_manager.versions]
- )
- iterator = config
- content {
- name = config.value.name
- instance_template = config.value.instance_template
- target_size {
- fixed = config.value.target_type == "fixed" ? config.value.target_size : null
- percent = config.value.target_type == "percent" ? config.value.target_size : null
- }
- }
- }
-}
diff --git a/modules/compute-vm/main.tf b/modules/compute-vm/main.tf
index 75f642dc8b..e97d576c21 100644
--- a/modules/compute-vm/main.tf
+++ b/modules/compute-vm/main.tf
@@ -218,6 +218,32 @@ resource "google_compute_instance_template" "default" {
}
}
+resource "google_compute_instance_group" "unmanaged" {
+ count = (
+ var.group != null && ! var.use_instance_template ? 1 : 0
+ )
+ project = var.project_id
+ network = (
+ length(var.network_interfaces) > 0
+ ? var.network_interfaces.0.network
+ : ""
+ )
+ zone = var.zone
+ name = var.name
+ description = "Terraform-managed."
+ instances = [
+ for name, instance in google_compute_instance.default : instance.self_link
+ ]
+ dynamic named_port {
+ for_each = var.group.named_ports != null ? var.group.named_ports : {}
+ iterator = config
+ content {
+ name = config.key
+ port = config.value
+ }
+ }
+}
+
resource "google_service_account" "service_account" {
count = var.service_account_create ? 1 : 0
project = var.project_id
diff --git a/modules/compute-vm/outputs.tf b/modules/compute-vm/outputs.tf
index 1fb31887c1..1291fdea10 100644
--- a/modules/compute-vm/outputs.tf
+++ b/modules/compute-vm/outputs.tf
@@ -28,24 +28,7 @@ output "external_ips" {
output "group" {
description = "Instance group resource."
- value = (
- length(google_compute_instance_group.unmanaged) > 0
- ? google_compute_instance_group.unmanaged.0
- : null
- )
-}
-
-output "group_manager" {
- description = "Instance group resource."
- value = (
- length(google_compute_instance_group_manager.managed) > 0
- ? google_compute_instance_group_manager.managed.0
- : (
- length(google_compute_region_instance_group_manager.managed) > 0
- ? google_compute_region_instance_group_manager.managed.0
- : null
- )
- )
+ value = try(google_compute_instance_group.unmanaged.0, null)
}
output "instances" {
diff --git a/modules/compute-vm/variables.tf b/modules/compute-vm/variables.tf
index 018e16d108..3593fef2c9 100644
--- a/modules/compute-vm/variables.tf
+++ b/modules/compute-vm/variables.tf
@@ -61,46 +61,13 @@ variable "boot_disk" {
}
variable "group" {
- description = "Instance group (for instance use)."
+ description = "Define this variable to create an instance group for instances. Disabled for template use."
type = object({
named_ports = map(number)
})
default = null
}
-variable "group_manager" {
- description = "Instance group manager (for template use)."
- type = object({
- auto_healing_policies = object({
- health_check = string
- initial_delay_sec = number
- })
- named_ports = map(number)
- options = object({
- target_pools = list(string)
- wait_for_instances = bool
- })
- regional = bool
- target_size = number
- update_policy = object({
- type = string # OPPORTUNISTIC | PROACTIVE
- minimal_action = string # REPLACE | RESTART
- min_ready_sec = number
- max_surge_type = string # fixed | percent
- max_surge = number
- max_unavailable_type = string
- max_unavailable = number
- })
- versions = list(object({
- name = string
- instance_template = string
- target_type = string # fixed | percent
- target_size = number
- }))
- })
- default = null
-}
-
variable "hostname" {
description = "Instance FQDN name."
type = string
diff --git a/modules/dns/main.tf b/modules/dns/main.tf
index 4c9dfae214..6e098b8d8d 100644
--- a/modules/dns/main.tf
+++ b/modules/dns/main.tf
@@ -20,9 +20,11 @@ locals {
for record in var.recordsets :
join("/", [record.name, record.type]) => record
}
- zone = element(concat(
- google_dns_managed_zone.non-public, google_dns_managed_zone.public
- ), 0)
+ zone = try(
+ google_dns_managed_zone.non-public.0, try(
+ google_dns_managed_zone.public.0, null
+ )
+ )
}
resource "google_dns_managed_zone" "non-public" {
diff --git a/modules/dns/outputs.tf b/modules/dns/outputs.tf
index df35628a21..87add74016 100644
--- a/modules/dns/outputs.tf
+++ b/modules/dns/outputs.tf
@@ -26,15 +26,15 @@ output "zone" {
output "name" {
description = "The DNS zone name."
- value = local.zone.name
+ value = try(local.zone.name, null)
}
output "domain" {
description = "The DNS zone domain."
- value = local.zone.dns_name
+ value = try(local.zone.dns_name, null)
}
output "name_servers" {
description = "The DNS zone name servers."
- value = local.zone.name_servers
+ value = try(local.zone.name_servers, null)
}
diff --git a/modules/gke-cluster/README.md b/modules/gke-cluster/README.md
index fb0c95afbe..fcbae58fa5 100644
--- a/modules/gke-cluster/README.md
+++ b/modules/gke-cluster/README.md
@@ -41,7 +41,7 @@ module "cluster-1" {
| secondary_range_pods | Subnet secondary range name used for pods. | string
| ✓ | |
| secondary_range_services | Subnet secondary range name used for services. | string
| ✓ | |
| subnetwork | VPC subnetwork name or self link. | string
| ✓ | |
-| *addons* | Addons enabled in the cluster (true means enabled). | object({...})
| | ...
|
+| *addons* | Addons enabled in the cluster (true means enabled). | object({...})
| | ...
|
| *authenticator_security_group* | RBAC security group for Google Groups for GKE, format is gke-security-groups@yourdomain.com. | string
| | null
|
| *cluster_autoscaling* | Enable and configure limits for Node Auto-Provisioning with Cluster Autoscaler. | object({...})
| | ...
|
| *database_encryption* | Enable and configure GKE application-layer secrets encryption. | object({...})
| | ...
|
@@ -58,8 +58,9 @@ module "cluster-1" {
| *min_master_version* | Minimum version of the master, defaults to the version of the most recent official release. | string
| | null
|
| *monitoring_service* | Monitoring service (disable with an empty string). | string
| | monitoring.googleapis.com/kubernetes
|
| *node_locations* | Zones in which the cluster's nodes are located. | list(string)
| | []
|
+| *peering_config* | Configure peering with the master VPC for private clusters. | object({...})
| | null
|
| *pod_security_policy* | Enable the PodSecurityPolicy feature. | bool
| | null
|
-| *private_cluster_config* | Enable and configure private cluster. | object({...})
| | null
|
+| *private_cluster_config* | Enable and configure private cluster, private nodes must be true if used. | object({...})
| | null
|
| *release_channel* | Release channel for GKE upgrades. | string
| | null
|
| *resource_usage_export_config* | Configure the ResourceUsageExportConfig feature. | object({...})
| | ...
|
| *vertical_pod_autoscaling* | Enable the Vertical Pod Autoscaling feature. | bool
| | null
|
diff --git a/modules/gke-cluster/main.tf b/modules/gke-cluster/main.tf
index 67e95dd3c7..4b98acd549 100644
--- a/modules/gke-cluster/main.tf
+++ b/modules/gke-cluster/main.tf
@@ -14,6 +14,16 @@
* limitations under the License.
*/
+locals {
+ # The Google provider is unable to validate certain configurations of
+ # private_cluster_config when enable_private_nodes is false (provider docs)
+ is_private = try(var.private_cluster_config.enable_private_nodes, false)
+ peering = try(
+ google_container_cluster.cluster.private_cluster_config.0.peering_name,
+ null
+ )
+}
+
resource "google_container_cluster" "cluster" {
provider = google-beta
project = var.project_id
@@ -36,8 +46,12 @@ resource "google_container_cluster" "cluster" {
remove_default_node_pool = true
# node_config
+ # TODO(ludomagno): compute addons map in locals and use a single dynamic block
addons_config {
+ dns_cache_config {
+ enabled = var.addons.dns_cache_config
+ }
http_load_balancing {
disabled = ! var.addons.http_load_balancing
}
@@ -106,7 +120,7 @@ resource "google_container_cluster" "cluster" {
}
dynamic private_cluster_config {
- for_each = var.private_cluster_config != null ? [var.private_cluster_config] : []
+ for_each = local.is_private ? [var.private_cluster_config] : []
iterator = config
content {
enable_private_nodes = config.value.enable_private_nodes
@@ -195,3 +209,12 @@ resource "google_container_cluster" "cluster" {
}
}
+
+resource "google_compute_network_peering_routes_config" "gke_master" {
+ count = local.is_private && var.peering_config != null ? 1 : 0
+ project = var.project_id
+ peering = local.peering
+ network = element(reverse(split("/", var.network)), 0)
+ import_custom_routes = var.peering_config.import_routes
+ export_custom_routes = var.peering_config.export_routes
+}
diff --git a/modules/gke-cluster/variables.tf b/modules/gke-cluster/variables.tf
index e39667711b..270a20f2aa 100644
--- a/modules/gke-cluster/variables.tf
+++ b/modules/gke-cluster/variables.tf
@@ -17,24 +17,26 @@
variable "addons" {
description = "Addons enabled in the cluster (true means enabled)."
type = object({
+ cloudrun_config = bool
+ dns_cache_config = bool
horizontal_pod_autoscaling = bool
http_load_balancing = bool
- network_policy_config = bool
- cloudrun_config = bool
istio_config = object({
enabled = bool
tls = bool
})
+ network_policy_config = bool
})
default = {
+ cloudrun_config = false
+ dns_cache_config = false
horizontal_pod_autoscaling = true
http_load_balancing = true
- network_policy_config = false
- cloudrun_config = false
istio_config = {
enabled = false
tls = false
}
+ network_policy_config = false
}
}
@@ -169,6 +171,15 @@ variable "node_locations" {
default = []
}
+variable "peering_config" {
+ description = "Configure peering with the master VPC for private clusters."
+ type = object({
+ export_routes = bool
+ import_routes = bool
+ })
+ default = null
+}
+
variable "pod_security_policy" {
description = "Enable the PodSecurityPolicy feature."
type = bool
@@ -176,7 +187,7 @@ variable "pod_security_policy" {
}
variable "private_cluster_config" {
- description = "Enable and configure private cluster."
+ description = "Enable and configure private cluster, private nodes must be true if used."
type = object({
enable_private_nodes = bool
enable_private_endpoint = bool
diff --git a/modules/gke-nodepool/README.md b/modules/gke-nodepool/README.md
index fcaa4bae59..0828190ebb 100644
--- a/modules/gke-nodepool/README.md
+++ b/modules/gke-nodepool/README.md
@@ -43,10 +43,10 @@ module "cluster-1-nodepool-1" {
| *node_config_service_account* | Service account used for nodes. | string
| | null
|
| *node_config_shielded_instance_config* | Shielded instance options. | object({...})
| | null
|
| *node_config_tags* | Network tags applied to nodes. | list(string)
| | null
|
-| *node_config_workload_metadata_config* | Metadata configuration to expose to workloads on the node pool. | string
| | SECURE
|
| *node_count* | Number of nodes per instance group, can be updated after creation. Ignored when autoscaling is set. | number
| | null
|
| *node_locations* | Optional list of zones in which nodes should be located. Uses cluster locations if unset. | list(string)
| | null
|
| *upgrade_config* | Optional node upgrade configuration. | object({...})
| | null
|
+| *workload_metadata_config* | Metadata configuration to expose to workloads on the node pool. | string
| | GKE_METADATA_SERVER
|
## Outputs
diff --git a/modules/gke-nodepool/main.tf b/modules/gke-nodepool/main.tf
index 40019e09c4..3fe022a741 100644
--- a/modules/gke-nodepool/main.tf
+++ b/modules/gke-nodepool/main.tf
@@ -77,7 +77,7 @@ resource "google_container_node_pool" "nodepool" {
}
workload_metadata_config {
- node_metadata = var.node_config_workload_metadata_config
+ node_metadata = var.workload_metadata_config
}
}
diff --git a/modules/gke-nodepool/variables.tf b/modules/gke-nodepool/variables.tf
index 049cfb3ede..96f05d074e 100644
--- a/modules/gke-nodepool/variables.tf
+++ b/modules/gke-nodepool/variables.tf
@@ -165,12 +165,6 @@ variable "node_config_tags" {
# default = null
# }
-variable "node_config_workload_metadata_config" {
- description = "Metadata configuration to expose to workloads on the node pool."
- type = string
- default = "SECURE"
-}
-
variable "node_count" {
description = "Number of nodes per instance group, can be updated after creation. Ignored when autoscaling is set."
type = number
@@ -196,3 +190,9 @@ variable "upgrade_config" {
})
default = null
}
+
+variable "workload_metadata_config" {
+ description = "Metadata configuration to expose to workloads on the node pool."
+ type = string
+ default = "GKE_METADATA_SERVER"
+}
diff --git a/modules/net-cloudnat/main.tf b/modules/net-cloudnat/main.tf
index fa652ff036..cd380d7255 100644
--- a/modules/net-cloudnat/main.tf
+++ b/modules/net-cloudnat/main.tf
@@ -16,13 +16,15 @@
locals {
router_name = (
- var.router_create ? google_compute_router.router[0].name : var.router_name
+ var.router_create
+ ? try(google_compute_router.router[0].name, null)
+ : var.router_name
)
}
resource "google_compute_router" "router" {
count = var.router_create ? 1 : 0
- name = var.router_name == "" ? "${var.name}-nat" : var.router_name
+ name = var.router_name == null ? "${var.name}-nat" : var.router_name
project = var.project_id
region = var.region
network = var.router_network
diff --git a/modules/net-cloudnat/outputs.tf b/modules/net-cloudnat/outputs.tf
index 8dad023498..1cf94a55f8 100644
--- a/modules/net-cloudnat/outputs.tf
+++ b/modules/net-cloudnat/outputs.tf
@@ -31,7 +31,11 @@ output "region" {
output "router" {
description = "Cloud NAT router resources (if auto created)."
- value = var.router_create ? google_compute_router.router[0] : null
+ value = (
+ var.router_create
+ ? try(google_compute_router.router[0], null)
+ : null
+ )
}
output "router_name" {
diff --git a/modules/net-cloudnat/variables.tf b/modules/net-cloudnat/variables.tf
index 5b987cf565..dbe0f46b5d 100644
--- a/modules/net-cloudnat/variables.tf
+++ b/modules/net-cloudnat/variables.tf
@@ -78,13 +78,13 @@ variable "router_create" {
variable "router_name" {
description = "Router name, leave blank if router will be created to use auto generated name."
type = string
- default = ""
+ default = null
}
variable "router_network" {
description = "Name of the VPC used for auto-created router."
type = string
- default = ""
+ default = null
}
variable "subnetworks" {
diff --git a/modules/net-ilb/README.md b/modules/net-ilb/README.md
new file mode 100644
index 0000000000..3669c54bb2
--- /dev/null
+++ b/modules/net-ilb/README.md
@@ -0,0 +1,110 @@
+# Internal Load Balancer Module
+
+This module allows managing a GCE Internal Load Balancer and integrates the forwarding rule, regional backend, and optional health check resources. It's designed to be a simple match for the [`compute-vm`](../compute-vm) module, which can be used to manage instance templates and instance groups.
+
+## Issues
+
+TODO(ludoo): check if this is still the case after splitting out MIG from compute-vm
+
+There are some corner cases (eg when switching the instance template from internal service account to an externally managed one) where Terraform raises a cycle error on apply. In these situations, run successive applies targeting resources used in the template first then the template itself, and the cycle should be fixed.
+
+One other issue is a `Provider produced inconsistent final plan` error which is sometimes raised when switching template version. This seems to be related to this [open provider issue](https://github.com/terraform-providers/terraform-provider-google/issues/3937), but it's relatively harmless since the resource is updated, and subsequent applies raise no errors.
+
+## Example
+
+This example spins up a simple HTTP server and combines four modules:
+
+- [`nginx`](../cloud-config-container/nginx) from the `cloud-config-container` collection, to manage instance configuration
+- [`compute-vm`](../compute-vm) to manage the instance template and unmanaged instance group
+- this module to create an Internal Load Balancer in front of the managed instance group
+
+Note that the example uses the GCE default service account. You might want to create an ad-hoc service account by combining the [`iam-service-accounts`](../iam-service-accounts) module, or by having the GCE VM module create one for you. In both cases, remember to set at least logging write permissions for the service account, or the container on the instances won't be able to start.
+
+```hcl
+module "cos-nginx" {
+ source = "./modules/cloud-config-container/nginx"
+}
+
+module "instance-group" {
+ source = "./modules/compute-vm"
+ project_id = "my-project"
+ region = "europe-west1"
+ zone = "europe-west1-b"
+ name = "ilb-test"
+ network_interfaces = [{
+ network = local.network_self_link,
+ subnetwork = local.subnetwork_self_link,
+ nat = false,
+ addresses = null
+ }]
+ boot_disk = {
+ image = "projects/cos-cloud/global/images/family/cos-stable"
+ type = "pd-ssd"
+ size = 10
+ }
+ tags = ["http-server", "ssh"]
+ metadata = {
+ user-data = module.cos-nginx.cloud_config
+ }
+ group = {}
+}
+
+module "ilb" {
+ source = "./modules/net-ilb"
+ project_id = "my-project"
+ region = "europe-west1"
+ name = "ilb-test"
+ service_label = "ilb-test"
+ network = local.network_self_link
+ subnetwork = local.subnetwork_self_link
+ ports = [80]
+ backends = [{
+ failover = false
+ group = module.instance-group.group.self_link
+ balancing_mode = "CONNECTION"
+ }]
+ health_check_config = {
+ type = "http", check = { port = 80 }, config = {}, logging = true
+ }
+}
+
+```
+
+
+## Variables
+
+| name | description | type | required | default |
+|---|---|:---: |:---:|:---:|
+| backends | Load balancer backends, balancing mode is one of 'CONNECTION' or 'UTILIZATION'. | list(object({...}))
| ✓ | |
+| name | Name used for all resources. | string
| ✓ | |
+| network | Network used for resources. | string
| ✓ | |
+| project_id | Project id where resources will be created. | string
| ✓ | |
+| region | GCP region. | string
| ✓ | |
+| subnetwork | Subnetwork used for the forwarding rule. | string
| ✓ | |
+| *address* | Optional IP address used for the forwarding rule. | string
| | null
|
+| *backend_config* | Optional backend configuration. | object({...})
| | null
|
+| *failover_config* | Optional failover configuration. | object({...})
| | null
|
+| *global_access* | Global access, defaults to false if not set. | bool
| | null
|
+| *health_check* | Name of existing health check to use, disables auto-created health check. | string
| | null
|
+| *health_check_config* | Configuration of the auto-created helth check. | object({...})
| | ...
|
+| *labels* | Labels set on resources. | map(string)
| | {}
|
+| *log_sample_rate* | Set a value between 0 and 1 to enable logging for resources, and set the sampling rate for backend logging. | number
| | null
|
+| *ports* | Comma-separated ports, leave null to use all ports. | list(string)
| | null
|
+| *protocol* | IP protocol used, defaults to TCP. | string
| | TCP
|
+| *service_label* | Optional prefix of the fully qualified forwarding rule name. | string
| | null
|
+
+## Outputs
+
+| name | description | sensitive |
+|---|---|:---:|
+| backend | Backend resource. | |
+| backend_id | Backend id. | |
+| backend_self_link | Backend self link. | |
+| forwarding_rule | Forwarding rule resource. | |
+| forwarding_rule_address | Forwarding rule address. | |
+| forwarding_rule_id | Forwarding rule id. | |
+| forwarding_rule_self_link | Forwarding rule self link. | |
+| health_check | Auto-created health-check resource. | |
+| health_check_self_id | Auto-created health-check self id. | |
+| health_check_self_link | Auto-created health-check self link. | |
+
diff --git a/modules/net-ilb/main.tf b/modules/net-ilb/main.tf
new file mode 100644
index 0000000000..d271402f2a
--- /dev/null
+++ b/modules/net-ilb/main.tf
@@ -0,0 +1,191 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+locals {
+ health_check = (
+ var.health_check == null
+ ? try(google_compute_health_check.default.0.self_link, null)
+ : var.health_check
+ )
+}
+
+resource "google_compute_forwarding_rule" "default" {
+ provider = google-beta
+ project = var.project_id
+ name = var.name
+ description = "Terraform managed."
+ load_balancing_scheme = "INTERNAL"
+ region = var.region
+ network = var.network
+ subnetwork = var.subnetwork
+ ip_address = var.address
+ ip_protocol = var.protocol # TCP | UDP
+ ports = var.ports # "nnnnn" or "nnnnn,nnnnn,nnnnn" max 5
+ service_label = var.service_label
+ all_ports = var.ports == null ? true : null
+ allow_global_access = var.global_access
+ backend_service = google_compute_region_backend_service.default.self_link
+ # is_mirroring_collector = false
+ labels = var.labels
+}
+
+resource "google_compute_region_backend_service" "default" {
+ provider = google-beta
+ project = var.project_id
+ name = var.name
+ description = "Terraform managed."
+ load_balancing_scheme = "INTERNAL"
+ region = var.region
+ network = var.network
+ health_checks = [local.health_check]
+ protocol = var.protocol
+
+ session_affinity = try(var.backend_config.session_affinity, null)
+ timeout_sec = try(var.backend_config.timeout_sec, null)
+ connection_draining_timeout_sec = try(var.backend_config.connection_draining_timeout_sec, null)
+
+ dynamic backend {
+ for_each = { for b in var.backends : b.group => b }
+ iterator = backend
+ content {
+ balancing_mode = backend.value.balancing_mode
+ description = "Terraform managed."
+ failover = backend.value.failover
+ group = backend.key
+ }
+ }
+
+ dynamic failover_policy {
+ for_each = var.failover_config == null ? [] : [var.failover_config]
+ iterator = config
+ content {
+ disable_connection_drain_on_failover = config.value.disable_connection_drain
+ drop_traffic_if_unhealthy = config.value.drop_traffic_if_unhealthy
+ failover_ratio = config.value.ratio
+ }
+ }
+
+}
+
+resource "google_compute_health_check" "default" {
+ provider = google-beta
+ count = var.health_check == null ? 1 : 0
+ project = var.project_id
+ name = var.name
+ description = "Terraform managed."
+
+ check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
+ healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
+ timeout_sec = try(var.health_check_config.config.timeout_sec, null)
+ unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
+
+ dynamic http_health_check {
+ for_each = (
+ try(var.health_check_config.type, null) == "http"
+ ? [var.health_check_config.check]
+ : []
+ )
+ iterator = check
+ content {
+ host = try(check.value.host, null)
+ port = try(check.value.port, null)
+ port_name = try(check.value.port_name, null)
+ port_specification = try(check.value.port_specification, null)
+ proxy_header = try(check.value.proxy_header, null)
+ request_path = try(check.value.request_path, null)
+ response = try(check.value.response, null)
+ }
+ }
+
+ dynamic https_health_check {
+ for_each = (
+ try(var.health_check_config.type, null) == "https"
+ ? [var.health_check_config.check]
+ : []
+ )
+ iterator = check
+ content {
+ host = try(check.value.host, null)
+ port = try(check.value.port, null)
+ port_name = try(check.value.port_name, null)
+ port_specification = try(check.value.port_specification, null)
+ proxy_header = try(check.value.proxy_header, null)
+ request_path = try(check.value.request_path, null)
+ response = try(check.value.response, null)
+ }
+ }
+
+ dynamic tcp_health_check {
+ for_each = (
+ try(var.health_check_config.type, null) == "tcp"
+ ? [var.health_check_config.check]
+ : []
+ )
+ iterator = check
+ content {
+ port = try(check.value.port, null)
+ port_name = try(check.value.port_name, null)
+ port_specification = try(check.value.port_specification, null)
+ proxy_header = try(check.value.proxy_header, null)
+ request = try(check.value.request, null)
+ response = try(check.value.response, null)
+ }
+ }
+
+ dynamic ssl_health_check {
+ for_each = (
+ try(var.health_check_config.type, null) == "ssl"
+ ? [var.health_check_config.check]
+ : []
+ )
+ iterator = check
+ content {
+ port = try(check.value.port, null)
+ port_name = try(check.value.port_name, null)
+ port_specification = try(check.value.port_specification, null)
+ proxy_header = try(check.value.proxy_header, null)
+ request = try(check.value.request, null)
+ response = try(check.value.response, null)
+ }
+ }
+
+ dynamic http2_health_check {
+ for_each = (
+ try(var.health_check_config.type, null) == "http2"
+ ? [var.health_check_config.check]
+ : []
+ )
+ iterator = check
+ content {
+ host = try(check.value.host, null)
+ port = try(check.value.port, null)
+ port_name = try(check.value.port_name, null)
+ port_specification = try(check.value.port_specification, null)
+ proxy_header = try(check.value.proxy_header, null)
+ request_path = try(check.value.request_path, null)
+ response = try(check.value.response, null)
+ }
+ }
+
+ dynamic log_config {
+ for_each = try(var.health_check_config.logging, false) ? [""] : []
+ content {
+ enable = true
+ }
+ }
+
+}
diff --git a/modules/net-ilb/outputs.tf b/modules/net-ilb/outputs.tf
new file mode 100644
index 0000000000..a3529eba85
--- /dev/null
+++ b/modules/net-ilb/outputs.tf
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+output "backend" {
+ description = "Backend resource."
+ value = google_compute_region_backend_service.default
+}
+
+output "backend_id" {
+ description = "Backend id."
+ value = google_compute_region_backend_service.default.id
+}
+
+output "backend_self_link" {
+ description = "Backend self link."
+ value = google_compute_region_backend_service.default.self_link
+}
+
+output "forwarding_rule" {
+ description = "Forwarding rule resource."
+ value = google_compute_forwarding_rule.default
+}
+
+output "forwarding_rule_address" {
+ description = "Forwarding rule address."
+ value = google_compute_forwarding_rule.default.ip_address
+}
+
+output "forwarding_rule_id" {
+ description = "Forwarding rule id."
+ value = google_compute_forwarding_rule.default.id
+}
+
+output "forwarding_rule_self_link" {
+ description = "Forwarding rule self link."
+ value = google_compute_forwarding_rule.default.self_link
+}
+
+output "health_check" {
+ description = "Auto-created health-check resource."
+ value = try(google_compute_health_check.default.0, {})
+}
+
+output "health_check_self_id" {
+ description = "Auto-created health-check self id."
+ value = try(google_compute_health_check.default.0.id, null)
+}
+
+output "health_check_self_link" {
+ description = "Auto-created health-check self link."
+ value = try(google_compute_health_check.default.0.self_link, null)
+}
diff --git a/modules/net-ilb/variables.tf b/modules/net-ilb/variables.tf
new file mode 100644
index 0000000000..df7e7ee8b8
--- /dev/null
+++ b/modules/net-ilb/variables.tf
@@ -0,0 +1,129 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+variable "address" {
+ description = "Optional IP address used for the forwarding rule."
+ type = string
+ default = null
+}
+
+variable "backends" {
+ description = "Load balancer backends, balancing mode is one of 'CONNECTION' or 'UTILIZATION'."
+ type = list(object({
+ failover = bool
+ group = string
+ balancing_mode = string
+ }))
+}
+
+variable "backend_config" {
+ description = "Optional backend configuration."
+ type = object({
+ session_affinity = string
+ timeout_sec = number
+ connection_draining_timeout_sec = number
+ })
+ default = null
+}
+
+variable "failover_config" {
+ description = "Optional failover configuration."
+ type = object({
+ disable_connection_drain = bool
+ drop_traffic_if_unhealthy = bool
+ ratio = number
+ })
+ default = null
+}
+
+variable "global_access" {
+ description = "Global access, defaults to false if not set."
+ type = bool
+ default = null
+}
+
+variable "health_check" {
+ description = "Name of existing health check to use, disables auto-created health check."
+ type = string
+ default = null
+}
+
+variable "health_check_config" {
+ description = "Configuration of the auto-created helth check."
+ type = object({
+ type = string # http https tcp ssl http2
+ check = map(any) # actual health check block attributes
+ config = map(number) # interval, thresholds, timeout
+ logging = bool
+ })
+ default = {
+ type = "http"
+ check = {
+ port_specification = "USE_SERVING_PORT"
+ }
+ config = {}
+ logging = false
+ }
+}
+
+variable "labels" {
+ description = "Labels set on resources."
+ type = map(string)
+ default = {}
+}
+
+variable "name" {
+ description = "Name used for all resources."
+ type = string
+}
+
+variable "network" {
+ description = "Network used for resources."
+ type = string
+}
+
+variable "project_id" {
+ description = "Project id where resources will be created."
+ type = string
+}
+
+variable "ports" {
+ description = "Comma-separated ports, leave null to use all ports."
+ type = list(string)
+ default = null
+}
+
+variable "protocol" {
+ description = "IP protocol used, defaults to TCP."
+ type = string
+ default = "TCP"
+}
+
+variable "region" {
+ description = "GCP region."
+ type = string
+}
+
+variable "service_label" {
+ description = "Optional prefix of the fully qualified forwarding rule name."
+ type = string
+ default = null
+}
+
+variable "subnetwork" {
+ description = "Subnetwork used for the forwarding rule."
+ type = string
+}
diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md
index 0826502527..80f7545fe2 100644
--- a/modules/net-vpc/README.md
+++ b/modules/net-vpc/README.md
@@ -16,6 +16,7 @@ module "vpc" {
subnets = {
subnet-1 = {
ip_cidr_range = "10.0.0.0/24"
+ name = "production"
region = "europe-west1"
secondary_ip_range = {
pods = "172.16.0.0/20"
@@ -24,7 +25,8 @@ module "vpc" {
}
subnet-2 = {
ip_cidr_range = "10.0.16.0/24"
- region = "europe-west1"
+ name = "production"
+ region = "europe-west2"
secondary_ip_range = {}
}
}
@@ -43,6 +45,7 @@ module "vpc-spoke-1" {
subnets = {
subnet-1 = {
ip_cidr_range = "10.0.0.0/24"
+ name = null
region = "europe-west1"
secondary_ip_range = {
pods = "172.16.0.0/20"
@@ -68,6 +71,7 @@ module "vpc-host" {
subnets = {
subnet-1 = {
ip_cidr_range = "10.0.0.0/24"
+ name = null
region = "europe-west1"
secondary_ip_range = {
pods = "172.16.0.0/20"
@@ -121,7 +125,7 @@ module "vpc-host" {
| *subnet_descriptions* | Optional map of subnet descriptions, keyed by subnet name. | map(string)
| | {}
|
| *subnet_flow_logs* | Optional map of boolean to control flow logs (default is disabled), keyed by subnet name. | map(bool)
| | {}
|
| *subnet_private_access* | Optional map of boolean to control private Google access (default is enabled), keyed by subnet name. | map(bool)
| | {}
|
-| *subnets* | The list of subnets being created | map(object({...}))
| | null
|
+| *subnets* | Subnets being created. If name is set to null, a default will be used combining network name and this map key. | map(object({...}))
| | null
|
## Outputs
diff --git a/modules/net-vpc/main.tf b/modules/net-vpc/main.tf
index f94cc59546..0d854f2eb4 100644
--- a/modules/net-vpc/main.tf
+++ b/modules/net-vpc/main.tf
@@ -116,7 +116,7 @@ resource "google_compute_subnetwork" "subnetwork" {
project = var.project_id
network = google_compute_network.network.name
region = each.value.region
- name = "${var.name}-${each.key}"
+ name = each.value.name != null ? each.value.name : "${var.name}-${each.key}"
ip_cidr_range = each.value.ip_cidr_range
secondary_ip_range = each.value.secondary_ip_range == null ? [] : [
for name, range in each.value.secondary_ip_range :
diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf
index 1f45132118..0d37637e2e 100644
--- a/modules/net-vpc/variables.tf
+++ b/modules/net-vpc/variables.tf
@@ -109,9 +109,10 @@ variable "shared_vpc_service_projects" {
}
variable "subnets" {
- description = "The list of subnets being created"
+ description = "Subnets being created. If name is set to null, a default will be used combining network name and this map key."
type = map(object({
ip_cidr_range = string
+ name = string
region = string
secondary_ip_range = map(string)
}))
diff --git a/modules/net-vpn-ha/main.tf b/modules/net-vpn-ha/main.tf
index 49055b1b5f..751081ee23 100644
--- a/modules/net-vpn-ha/main.tf
+++ b/modules/net-vpn-ha/main.tf
@@ -24,7 +24,7 @@ locals {
)
router = (
var.router_create
- ? google_compute_router.router[0].name
+ ? try(google_compute_router.router[0].name, null)
: var.router_name
)
secret = random_id.secret.b64_url
diff --git a/modules/net-vpn-ha/outputs.tf b/modules/net-vpn-ha/outputs.tf
index 2a5388900e..9426937725 100644
--- a/modules/net-vpn-ha/outputs.tf
+++ b/modules/net-vpn-ha/outputs.tf
@@ -53,7 +53,7 @@ output "tunnels" {
description = "VPN tunnel resources."
value = {
for name in keys(var.tunnels) :
- name => google_compute_vpn_tunnel.tunnels[name]
+ name => try(google_compute_vpn_tunnel.tunnels[name], null)
}
}
@@ -61,7 +61,7 @@ output "tunnel_names" {
description = "VPN tunnel names."
value = {
for name in keys(var.tunnels) :
- name => google_compute_vpn_tunnel.tunnels[name].name
+ name => try(google_compute_vpn_tunnel.tunnels[name].name, null)
}
}
@@ -69,7 +69,7 @@ output "tunnel_self_links" {
description = "VPN tunnel self links."
value = {
for name in keys(var.tunnels) :
- name => google_compute_vpn_tunnel.tunnels[name].self_link
+ name => try(google_compute_vpn_tunnel.tunnels[name].self_link, null)
}
}
diff --git a/tests/modules/compute_vm/fixture/main.tf b/tests/modules/compute_vm/fixture/main.tf
index 4f4b3b36d2..a134883467 100644
--- a/tests/modules/compute_vm/fixture/main.tf
+++ b/tests/modules/compute_vm/fixture/main.tf
@@ -30,5 +30,4 @@ module "test" {
instance_count = var.instance_count
use_instance_template = var.use_instance_template
group = var.group
- group_manager = var.group_manager
}
diff --git a/tests/modules/compute_vm/fixture/variables.tf b/tests/modules/compute_vm/fixture/variables.tf
index 7b2b9aad23..7191d315ca 100644
--- a/tests/modules/compute_vm/fixture/variables.tf
+++ b/tests/modules/compute_vm/fixture/variables.tf
@@ -19,11 +19,6 @@ variable "group" {
default = null
}
-variable "group_manager" {
- type = any
- default = null
-}
-
variable "instance_count" {
type = number
default = 1
diff --git a/tests/modules/compute_vm/test_plan.py b/tests/modules/compute_vm/test_plan.py
index 009309984c..733ee1eec0 100644
--- a/tests/modules/compute_vm/test_plan.py
+++ b/tests/modules/compute_vm/test_plan.py
@@ -44,3 +44,12 @@ def test_template(plan_runner):
assert len(resources) == 1
assert resources[0]['type'] == 'google_compute_instance_template'
assert resources[0]['values']['name_prefix'] == 'test-'
+
+
+def test_group(plan_runner):
+ plan, resources = plan_runner(FIXTURES_DIR, instance_count=2,
+ group='{named_ports={}}')
+ assert len(resources) == 3
+ assert set(r['type'] for r in resources) == set([
+ 'google_compute_instance_group', 'google_compute_instance'
+ ])
diff --git a/tests/modules/compute_vm/test_plan_group.py b/tests/modules/compute_vm/test_plan_group.py
deleted file mode 100644
index de198d731c..0000000000
--- a/tests/modules/compute_vm/test_plan_group.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-import os
-import pytest
-
-
-FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture')
-
-
-def test_unmanaged(plan_runner):
- plan, resources = plan_runner(FIXTURES_DIR, instance_count=2,
- group='{named_ports={}}')
- assert len(resources) == 3
- assert set(r['type'] for r in resources) == set([
- 'google_compute_instance_group', 'google_compute_instance'
- ])
-
-
-def test_managed(plan_runner):
- plan, resources = plan_runner(
- FIXTURES_DIR, use_instance_template='true', group_manager=(
- '{ '
- 'auto_healing_policies=null, named_ports={}, options=null, '
- 'regional=false, target_size=1, update_policy=null, versions=null'
- ' }'
- )
- )
- assert len(resources) == 2
- assert set(r['type'] for r in resources) == set([
- 'google_compute_instance_group_manager', 'google_compute_instance_template'
- ])
diff --git a/tests/modules/net-vpc/__init__.py b/tests/modules/net_ilb/__init__.py
similarity index 100%
rename from tests/modules/net-vpc/__init__.py
rename to tests/modules/net_ilb/__init__.py
diff --git a/tests/modules/net_ilb/fixture/main.tf b/tests/modules/net_ilb/fixture/main.tf
new file mode 100644
index 0000000000..ff7ad5aeaa
--- /dev/null
+++ b/tests/modules/net_ilb/fixture/main.tf
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+module "test" {
+ source = "../../../../modules/net-ilb"
+ project_id = "my-project"
+ region = "europe-west1"
+ network = "default"
+ subnetwork = "default"
+ name = "ilb-test"
+ labels = {}
+ address = var.address
+ backends = var.backends
+ backend_config = var.backend_config
+ failover_config = var.failover_config
+ global_access = var.global_access
+ health_check = var.health_check
+ health_check_config = var.health_check_config
+ ports = var.ports
+ protocol = var.protocol
+ service_label = var.service_label
+}
diff --git a/tests/modules/net_ilb/fixture/variables.tf b/tests/modules/net_ilb/fixture/variables.tf
new file mode 100644
index 0000000000..5955e69e01
--- /dev/null
+++ b/tests/modules/net_ilb/fixture/variables.tf
@@ -0,0 +1,88 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+variable "address" {
+ type = string
+ default = null
+}
+
+variable "backends" {
+ type = list(object({
+ failover = bool
+ group = string
+ balancing_mode = string
+ }))
+}
+
+variable "backend_config" {
+ type = object({
+ session_affinity = string
+ timeout_sec = number
+ connection_draining_timeout_sec = number
+ })
+ default = null
+}
+
+variable "failover_config" {
+ type = object({
+ disable_connection_drain = bool
+ drop_traffic_if_unhealthy = bool
+ ratio = number
+ })
+ default = null
+}
+
+variable "global_access" {
+ type = bool
+ default = null
+}
+
+variable "health_check" {
+ type = string
+ default = null
+}
+
+variable "health_check_config" {
+ type = object({
+ type = string # http https tcp ssl http2
+ check = map(any) # actual health check block attributes
+ config = map(number) # interval, thresholds, timeout
+ logging = bool
+ })
+ default = {
+ type = "http"
+ check = {
+ port_specification = "USE_SERVING_PORT"
+ }
+ config = {}
+ logging = false
+ }
+}
+
+variable "ports" {
+ type = list(string)
+ default = null
+}
+
+variable "protocol" {
+ type = string
+ default = "TCP"
+}
+
+variable "service_label" {
+ type = string
+ default = null
+}
diff --git a/tests/modules/net_ilb/test_plan.py b/tests/modules/net_ilb/test_plan.py
new file mode 100644
index 0000000000..e528dc5cf1
--- /dev/null
+++ b/tests/modules/net_ilb/test_plan.py
@@ -0,0 +1,55 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import os
+import pytest
+
+
+FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture')
+
+_BACKENDS = '[{balancing_mode="CONNECTION", group="foo", failover=false}]'
+
+
+def test_defaults(plan_runner):
+ "Test variable defaults."
+ _, resources = plan_runner(FIXTURES_DIR, backends=_BACKENDS)
+ assert len(resources) == 3
+ resources = dict((r['type'], r['values']) for r in resources)
+ fwd_rule = resources['google_compute_forwarding_rule']
+ assert fwd_rule['load_balancing_scheme'] == 'INTERNAL'
+ assert fwd_rule['all_ports']
+ assert fwd_rule['allow_global_access'] is None
+ backend = resources['google_compute_region_backend_service']
+ assert len(backend['backend']) == 1
+ assert backend['backend'][0]['group'] == 'foo'
+ health_check = resources['google_compute_health_check']
+ for k, v in health_check.items():
+ if k == 'http_health_check':
+ assert len(v) == 1
+ assert v[0]['port_specification'] == 'USE_SERVING_PORT'
+ elif k.endswith('_health_check'):
+ assert len(v) == 0
+
+
+def test_forwarding_rule(plan_runner):
+ "Test forwarding rule variables."
+ _, resources = plan_runner(
+ FIXTURES_DIR, backends=_BACKENDS, global_access='true', ports="[80]")
+ assert len(resources) == 3
+ values = [r['values'] for r in resources if r['type']
+ == 'google_compute_forwarding_rule'][0]
+ assert not values['all_ports']
+ assert values['ports'] == ['80']
+ assert values['allow_global_access']
diff --git a/tests/modules/net_vpc/__init__.py b/tests/modules/net_vpc/__init__.py
new file mode 100644
index 0000000000..6913f02e36
--- /dev/null
+++ b/tests/modules/net_vpc/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tests/modules/net-vpc/fixture/main.tf b/tests/modules/net_vpc/fixture/main.tf
similarity index 100%
rename from tests/modules/net-vpc/fixture/main.tf
rename to tests/modules/net_vpc/fixture/main.tf
diff --git a/tests/modules/net-vpc/fixture/variables.tf b/tests/modules/net_vpc/fixture/variables.tf
similarity index 98%
rename from tests/modules/net-vpc/fixture/variables.tf
rename to tests/modules/net_vpc/fixture/variables.tf
index 56f190a207..c34750adba 100644
--- a/tests/modules/net-vpc/fixture/variables.tf
+++ b/tests/modules/net_vpc/fixture/variables.tf
@@ -99,6 +99,7 @@ variable "subnets" {
description = "The list of subnets being created"
type = map(object({
ip_cidr_range = string
+ name = string
region = string
secondary_ip_range = map(string)
}))
diff --git a/tests/modules/net-vpc/test_plan.py b/tests/modules/net_vpc/test_plan.py
similarity index 100%
rename from tests/modules/net-vpc/test_plan.py
rename to tests/modules/net_vpc/test_plan.py
diff --git a/tests/modules/net-vpc/test_plan_subnets.py b/tests/modules/net_vpc/test_plan_subnets.py
similarity index 91%
rename from tests/modules/net-vpc/test_plan_subnets.py
rename to tests/modules/net_vpc/test_plan_subnets.py
index f08f425203..47dcd67a6a 100644
--- a/tests/modules/net-vpc/test_plan_subnets.py
+++ b/tests/modules/net_vpc/test_plan_subnets.py
@@ -21,11 +21,11 @@
_VAR_SUBNETS = (
'{ '
'a={region = "europe-west1", ip_cidr_range = "10.0.0.0/24",'
- ' secondary_ip_range=null},'
+ ' name=null, secondary_ip_range=null},'
'b={region = "europe-west1", ip_cidr_range = "10.0.1.0/24",'
- ' secondary_ip_range=null},'
+ ' name=null, secondary_ip_range=null},'
'c={region = "europe-west1", ip_cidr_range = "10.0.2.0/24",'
- ' secondary_ip_range={a="192.168.0.0/24", b="192.168.1.0/24"}},'
+ ' name="c", secondary_ip_range={a="192.168.0.0/24", b="192.168.1.0/24"}},'
'}'
)
_VAR_LOG_CONFIG = '{a = { flow_sampling = 0.1 }}'
@@ -45,7 +45,7 @@ def test_subnets_simple(plan_runner):
subnets = [r['values']
for r in resources if r['type'] == 'google_compute_subnetwork']
assert set(s['name'] for s in subnets) == set(
- ['my-vpc-a', 'my-vpc-b', 'my-vpc-c'])
+ ['my-vpc-a', 'my-vpc-b', 'c'])
assert set(len(s['secondary_ip_range']) for s in subnets) == set([0, 0, 2])
@@ -75,5 +75,5 @@ def test_subnet_log_configs(plan_runner):
'metadata': 'INCLUDE_ALL_METADATA'
}],
# don't enable
- 'my-vpc-c': []
+ 'c': []
}