diff --git a/.ci/cloudbuild.test.examples.yaml b/.ci/cloudbuild.test.examples.yaml new file mode 100644 index 000000000..52e27387a --- /dev/null +++ b/.ci/cloudbuild.test.examples.yaml @@ -0,0 +1,44 @@ +# 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 +# +# https://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. + +steps: + - name: python:3-alpine + id: prepare + entrypoint: sh + args: + - -c + - | + python -m pip install --user --no-warn-script-location -r tests/requirements.txt && + wget https://releases.hashicorp.com/terraform/${_TERRAFORM_VERSION}/terraform_${_TERRAFORM_VERSION}_linux_amd64.zip && + unzip terraform_${_TERRAFORM_VERSION}_linux_amd64.zip -d /builder/home/.local/bin && + rm terraform_${_TERRAFORM_VERSION}_linux_amd64.zip && + chmod 755 /builder/home/.local/bin/terraform && + mkdir -p /workspace/.terraform.d/plugin-cache + # TODO(ludoo): add a step that detects change files and sets tests to run + - name: python:3-alpine + id: test-examples + entrypoint: pytest + args: + - -vv + - tests/modules/examples + env: + - PATH=/usr/local/bin:/usr/bin:/bin:/builder/home/.local/bin + - TF_CLI_CONFIG_FILE=/workspace/.ci/.terraformrc + +substitutions: + _TERRAFORM_VERSION: 0.13.3 + +tags: + - "ci" + - "test" diff --git a/.ci/cloudbuild.test.yaml b/.ci/cloudbuild.test.yaml index d1945de56..26f6d865a 100644 --- a/.ci/cloudbuild.test.yaml +++ b/.ci/cloudbuild.test.yaml @@ -31,6 +31,8 @@ steps: entrypoint: pytest args: - -vv + - --ignore + - tests/modules/examples - tests/modules env: - PATH=/usr/local/bin:/usr/bin:/bin:/builder/home/.local/bin diff --git a/modules/artifact-registry/README.md b/modules/artifact-registry/README.md index 49fd84c24..82b601109 100644 --- a/modules/artifact-registry/README.md +++ b/modules/artifact-registry/README.md @@ -17,6 +17,7 @@ module "docker_artifact_registry" { "roles/artifactregistry.admin" = ["group:cicd@example.com"] } } +# tftest:modules=1:resources=2 ``` diff --git a/modules/bigquery-dataset/README.md b/modules/bigquery-dataset/README.md index 20f946c07..d227990b3 100644 --- a/modules/bigquery-dataset/README.md +++ b/modules/bigquery-dataset/README.md @@ -31,6 +31,7 @@ module "bigquery-dataset" { owner = "ludo@ludomagno.net" } } +# tftest:modules=1:resources=3 ``` ### Dataset options @@ -48,6 +49,7 @@ module "bigquery-dataset" { delete_contents_on_destroy = false } } +# tftest:modules=1:resources=1 ``` ### Tables and views @@ -69,6 +71,7 @@ module "bigquery-dataset" { } } } +# tftest:skip ``` If partitioning is needed, populate the `partitioning` variable using either the `time` or `range` attribute. @@ -92,6 +95,7 @@ module "bigquery-dataset" { } } } +# tftest:skip ``` To create views use the `view` variable. If you're querying a table created by the same module `terraform apply` will initially fail and eventually succeed once the underlying table has been created. You can probably also use the module's output in the view's query to create a dependency on the table. @@ -123,6 +127,7 @@ module "bigquery-dataset" { } } } +# tftest:skip ``` diff --git a/modules/bigtable-instance/README.md b/modules/bigtable-instance/README.md index 23aac694a..eb7c8f049 100644 --- a/modules/bigtable-instance/README.md +++ b/modules/bigtable-instance/README.md @@ -30,6 +30,7 @@ module "bigtable-instance" { "roles/bigtable.user" = ["user:viewer@testdomain.com"] } } +# tftest:modules=1:resources=4 ``` diff --git a/modules/cloud-function/README.md b/modules/cloud-function/README.md index 696154a14..d0e91d8d5 100644 --- a/modules/cloud-function/README.md +++ b/modules/cloud-function/README.md @@ -16,7 +16,7 @@ This deploys a Cloud Function with an HTTP endpoint, using a pre-existing GCS bu ```hcl module "cf-http" { - source = "../modules/cloud-function" + source = "./modules/cloud-function" project_id = "my-project" name = "test-cf-http" bucket_name = "test-cf-bundles" @@ -25,6 +25,7 @@ module "cf-http" { output_path = "bundle.zip" } } +# tftest:skip ``` ### PubSub and non-HTTP triggers @@ -33,7 +34,7 @@ Other trigger types other than HTTP are configured via the `trigger_config` vari ```hcl module "cf-http" { - source = "../modules/cloud-function" + source = "./modules/cloud-function" project_id = "my-project" name = "test-cf-http" bucket_name = "test-cf-bundles" @@ -47,6 +48,7 @@ module "cf-http" { retry = null } } +# tftest:skip ``` ### Controlling HTTP access @@ -55,7 +57,7 @@ To allow anonymous access to the function, grant the `roles/cloudfunctions.invok ```hcl module "cf-http" { - source = "../modules/cloud-function" + source = "./modules/cloud-function" project_id = "my-project" name = "test-cf-http" bucket_name = "test-cf-bundles" @@ -67,6 +69,7 @@ module "cf-http" { "roles/cloudfunctions.invoker" = ["allUsers"] } } +# tftest:skip ``` ### GCS bucket creation @@ -75,7 +78,7 @@ You can have the module auto-create the GCS bucket used for deployment via the ` ```hcl module "cf-http" { - source = "../modules/cloud-function" + source = "./modules/cloud-function" project_id = "my-project" name = "test-cf-http" bucket_name = "test-cf-bundles" @@ -88,6 +91,7 @@ module "cf-http" { output_path = "bundle.zip" } } +# tftest:skip ``` ### Service account management @@ -96,7 +100,7 @@ To use a custom service account managed by the module, set `service_account_crea ```hcl module "cf-http" { - source = "../modules/cloud-function" + source = "./modules/cloud-function" project_id = "my-project" name = "test-cf-http" bucket_name = "test-cf-bundles" @@ -106,13 +110,14 @@ module "cf-http" { } service_account_create = true } +# tftest:skip ``` To use an externally managed service account, pass its email in `service_account` and leave `service_account_create` to `false` (the default). ```hcl module "cf-http" { - source = "../modules/cloud-function" + source = "./modules/cloud-function" project_id = "my-project" name = "test-cf-http" bucket_name = "test-cf-bundles" @@ -122,6 +127,7 @@ module "cf-http" { } service_account = local.service_account_email } +# tftest:skip ``` diff --git a/modules/compute-mig/README.md b/modules/compute-mig/README.md index beada11f8..d8c401d4a 100644 --- a/modules/compute-mig/README.md +++ b/modules/compute-mig/README.md @@ -15,22 +15,126 @@ module "cos-nginx" { module "nginx-template" { source = "./modules/compute-vm" - project_id = "my-project" + project_id = var.project_id + name = "nginx-template" region = "europe-west1" - zone = "europe-west1-b" - name = "ilb-test" + zones = ["europe-west1-b", "europe-west1-d"] + tags = ["http-server", "ssh"] network_interfaces = [{ - network = local.network_self_link, - subnetwork = local.subnetwork_self_link, - nat = false, + network = var.vpc.self_link + subnetwork = var.subnet.self_link + nat = false addresses = null + alias_ips = null + }] + boot_disk = { + image = "projects/cos-cloud/global/images/family/cos-stable" + type = "pd-ssd" + size = 10 + } + 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" + } +} +# tftest:modules=2:resources=2 +``` + +### 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 "cos-nginx" { + source = "./modules/cloud-config-container/nginx" +} + +module "nginx-template" { + source = "./modules/compute-vm" + project_id = var.project_id + name = "nginx-template" + region = "europe-west1" + zones = ["europe-west1-b", "europe-west1-d"] + tags = ["http-server", "ssh"] + network_interfaces = [{ + network = var.vpc.self_link + subnetwork = var.subnet.self_link + nat = false + addresses = null + alias_ips = null + }] + boot_disk = { + image = "projects/cos-cloud/global/images/family/cos-stable" + type = "pd-ssd" + size = 10 + } + 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 = 3 + default_version = { + instance_template = module.nginx-template.template.self_link + name = "default" + } + versions = { + canary = { + instance_template = module.nginx-template.template.self_link + target_type = "fixed" + target_size = 1 + } + } +} +# tftest:modules=2:resources=2 +``` + +### Health check and autohealing policies + +Autohealing policies can use an externally defined health check, or have this module auto-create one: + +```hcl +module "cos-nginx" { + source = "./modules/cloud-config-container/nginx" +} + +module "nginx-template" { + source = "./modules/compute-vm" + project_id = var.project_id + name = "nginx-template" + region = "europe-west1" + zones = ["europe-west1-b", "europe-west1-d"] + tags = ["http-server", "ssh"] + network_interfaces = [{ + network = var.vpc.self_link, + subnetwork = var.subnet.self_link, + nat = false, + addresses = null + alias_ips = 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 @@ -42,54 +146,11 @@ module "nginx-mig" { project_id = "my-project" location = "europe-west1-b" name = "mig-test" - target_size = 2 + target_size = 3 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 @@ -101,6 +162,7 @@ module "nginx-mig" { logging = true } } +# tftest:modules=2:resources=3 ``` ### Autoscaling @@ -108,6 +170,35 @@ module "nginx-mig" { 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 "cos-nginx" { + source = "./modules/cloud-config-container/nginx" +} + +module "nginx-template" { + source = "./modules/compute-vm" + project_id = var.project_id + name = "nginx-template" + region = "europe-west1" + zones = ["europe-west1-b", "europe-west1-d"] + tags = ["http-server", "ssh"] + network_interfaces = [{ + network = var.vpc.self_link + subnetwork = var.subnet.self_link + nat = false + addresses = null + alias_ips = null + }] + boot_disk = { + image = "projects/cos-cloud/global/images/family/cos-stable" + type = "pd-ssd" + size = 10 + } + use_instance_template = true + metadata = { + user-data = module.cos-nginx.cloud_config + } +} + module "nginx-mig" { source = "./modules/compute-mig" project_id = "my-project" @@ -115,7 +206,7 @@ module "nginx-mig" { name = "mig-test" target_size = 3 default_version = { - instance_template = module.nginx-template-default.template.self_link + instance_template = module.nginx-template.template.self_link name = "default" } autoscaler_config = { @@ -127,11 +218,41 @@ module "nginx-mig" { metric = null } } +# tftest:modules=2:resources=3 ``` ### Update policy ```hcl +module "cos-nginx" { + source = "./modules/cloud-config-container/nginx" +} + +module "nginx-template" { + source = "./modules/compute-vm" + project_id = var.project_id + name = "nginx-template" + region = "europe-west1" + zones = ["europe-west1-b", "europe-west1-d"] + tags = ["http-server", "ssh"] + network_interfaces = [{ + network = var.vpc.self_link + subnetwork = var.subnet.self_link + nat = false + addresses = null + alias_ips = null + }] + boot_disk = { + image = "projects/cos-cloud/global/images/family/cos-stable" + type = "pd-ssd" + size = 10 + } + use_instance_template = true + metadata = { + user-data = module.cos-nginx.cloud_config + } +} + module "nginx-mig" { source = "./modules/compute-mig" project_id = "my-project" @@ -139,7 +260,7 @@ module "nginx-mig" { name = "mig-test" target_size = 3 default_version = { - instance_template = module.nginx-template-default.template.self_link + instance_template = module.nginx-template.template.self_link name = "default" } update_policy = { @@ -152,6 +273,7 @@ module "nginx-mig" { max_unavailable = null } } +# tftest:modules=2:resources=2 ``` diff --git a/modules/compute-vm/README.md b/modules/compute-vm/README.md index 3771452c4..8ad96af42 100644 --- a/modules/compute-vm/README.md +++ b/modules/compute-vm/README.md @@ -15,13 +15,13 @@ The simplest example leverages defaults for the boot disk image and size, and us ```hcl module "simple-vm-example" { - source = "../modules/compute-vm" - project_id = "my-project" - region = "europe-west1" + source = "./modules/compute-vm" + project_id = var.project_id + region = var.region name = "test" network_interfaces = [{ - network = local.network_self_link - subnetwork = local.subnet_self_link + network = var.vpc.self_link + subnetwork = var.subnet.self_link nat = false addresses = null alias_ips = null @@ -29,6 +29,8 @@ module "simple-vm-example" { service_account_create = true instance_count = 1 } +# tftest:modules=1:resources=2 + ``` ### Disk encryption with Cloud KMS @@ -37,13 +39,13 @@ This example shows how to control disk encryption via the the `encryption` varia ```hcl module "kms-vm-example" { - source = "../modules/compute-vm" - project_id = local.project_id - region = local.region + source = "./modules/compute-vm" + project_id = var.project_id + region = var.region name = "kms-test" network_interfaces = [{ - network = local.network_self_link - subnetwork = local.subnet_self_link + network = var.vpc.self_link + subnetwork = var.subnet.self_link nat = false addresses = null alias_ips = null @@ -71,9 +73,10 @@ module "kms-vm-example" { encryption = { encrypt_boot = true disk_encryption_key_raw = null - kms_key_self_link = local.kms_key.self_link + kms_key_self_link = var.kms_key.self_link } } +# tftest:modules=1:resources=3 ``` ### Using Alias IPs @@ -82,13 +85,13 @@ This example shows how add additional [Alias IPs](https://cloud.google.com/vpc/d ```hcl module "vm-with-alias-ips" { - source = "../modules/compute-vm" + source = "./modules/compute-vm" project_id = "my-project" region = "europe-west1" name = "test" network_interfaces = [{ - network = local.network_self_link - subnetwork = local.subnet_self_link + network = var.vpc.self_link + subnetwork = var.subnet.self_link nat = false addresses = null alias_ips = { @@ -102,6 +105,7 @@ module "vm-with-alias-ips" { service_account_create = true instance_count = 3 } +# tftest:modules=1:resources=4 ``` ### Instance template @@ -110,13 +114,13 @@ This example shows how to use the module to manage an instance template that def ```hcl module "cos-test" { - source = "../modules/compute-vm" + source = "./modules/compute-vm" project_id = "my-project" region = "europe-west1" name = "test" network_interfaces = [{ - network = local.network_self_link - subnetwork = local.subnet_self_link + network = var.vpc.self_link + subnetwork = var.subnet.self_link nat = false addresses = null alias_ips = null @@ -133,6 +137,7 @@ module "cos-test" { service_account = "vm-default@my-project.iam.gserviceaccount.com" use_instance_template = true } +# tftest:modules=1:resources=1 ``` ### Instance group @@ -140,14 +145,18 @@ module "cos-test" { 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 +locals { + cloud_config = "my cloud config" +} + module "instance-group" { - source = "../../cloud-foundation-fabric/modules/compute-vm" + source = "./modules/compute-vm" project_id = "my-project" region = "europe-west1" name = "ilb-test" network_interfaces = [{ - network = local.network_self_link - subnetwork = local.subnetwork_self_link + network = var.vpc.self_link + subnetwork = var.subnet.self_link nat = false addresses = null alias_ips = null @@ -157,14 +166,14 @@ module "instance-group" { type = "pd-ssd" size = 10 } - service_account = local.service_account_email + service_account = var.service_account.email service_account_scopes = ["https://www.googleapis.com/auth/cloud-platform"] metadata = { user-data = local.cloud_config } group = { named_ports = {} } } - +# tftest:modules=1:resources=2 ``` diff --git a/modules/container-registry/README.md b/modules/container-registry/README.md index ac382ccc9..92cdb8967 100644 --- a/modules/container-registry/README.md +++ b/modules/container-registry/README.md @@ -6,13 +6,14 @@ This module simplifies the creation of GCS buckets used by Google Container Regi ```hcl module "container_registry" { - source = "../../modules/container-registry" + source = "./modules/container-registry" project_id = "myproject" location = "EU" iam = { "roles/storage.admin" = ["group:cicd@example.com"] } } +# tftest:modules=1:resources=2 ``` diff --git a/modules/datafusion/README.md b/modules/datafusion/README.md index 75fddc654..f27a38d18 100644 --- a/modules/datafusion/README.md +++ b/modules/datafusion/README.md @@ -14,6 +14,7 @@ module "datafusion" { project_id = "my-project" network = "my-network-name" } +# tftest:modules=1:resources=4 ``` ### Externally managed IP allocation @@ -28,6 +29,7 @@ module "datafusion" { ip_allocation_create = false ip_allocation = "10.0.0.0/22" } +# tftest:modules=1:resources=3 ``` diff --git a/modules/dns/README.md b/modules/dns/README.md index 780e9f0b0..6a39e8911 100644 --- a/modules/dns/README.md +++ b/modules/dns/README.md @@ -13,11 +13,12 @@ module "private-dns" { type = "private" name = "test-example" domain = "test.example." - client_networks = [var.vpc_self_link] + client_networks = [var.vpc.self_link] recordsets = [ { name = "localhost", type = "A", ttl = 300, records = ["127.0.0.1"] } ] } +# tftest:modules=1:resources=2 ``` diff --git a/modules/endpoints/README.md b/modules/endpoints/README.md index 6706c9441..8e7bb44ff 100644 --- a/modules/endpoints/README.md +++ b/modules/endpoints/README.md @@ -8,14 +8,17 @@ This module allows simple management of ['Google Cloud Endpoints'](https://cloud ```hcl module "endpoint" { - source = "../../modules/endpoint" + source = "./modules/endpoints" project_id = "my-project" service_name = "YOUR-API.endpoints.YOUR-PROJECT-ID.cloud.goog" openapi_config = { "yaml_path" = "openapi.yaml" } - iam = { - "servicemanagement.serviceController" = ["serviceAccount:123456890-compute@developer.gserviceaccount.com"] + iam = { + "servicemanagement.serviceController" = [ + "serviceAccount:123456890-compute@developer.gserviceaccount.com" + ] } } +# tftest:skip ``` [Here](https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/endpoints/getting-started/openapi.yaml) you can find an example of an openapi.yaml file. Once created the endpoint, remember to activate the service at project level. diff --git a/modules/folder/README.md b/modules/folder/README.md index 5e85b49a0..e9c92f29e 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -15,6 +15,7 @@ module "folder" { "roles/owner" = ["group:users@example.com"] } } +# tftest:modules=1:resources=2 ``` ### Organization policies @@ -37,6 +38,7 @@ module "folder" { } } } +# tftest:modules=1:resources=4 ``` diff --git a/modules/folders-unit/README.md b/modules/folders-unit/README.md index 9ebad6447..e82142f36 100644 --- a/modules/folders-unit/README.md +++ b/modules/folders-unit/README.md @@ -21,6 +21,7 @@ module "folders-unit" { } service_account_keys = true } +# tftest:modules=1:resources=37 ``` diff --git a/modules/gcs/README.md b/modules/gcs/README.md index 33afea1f9..6130a85f9 100644 --- a/modules/gcs/README.md +++ b/modules/gcs/README.md @@ -16,6 +16,7 @@ module "bucket" { "roles/storage.admin" = ["group:storage@example.com"] } } +# tftest:modules=1:resources=2 ``` ### Example with Cloud KMS @@ -29,8 +30,9 @@ module "bucket" { iam = { "roles/storage.admin" = ["group:storage@example.com"] } - encryption_keys = local.kms_key.self_link + encryption_key = "my-encryption-key" } +# tftest:modules=1:resources=2 ``` ### Example with retention policy @@ -45,16 +47,17 @@ module "bucket" { "roles/storage.admin" = ["group:storage@example.com"] } - retention_policies = { + retention_policy = { retention_period = 100 is_locked = true } logging_config = { - log_bucket = bucket_name_for_logging + log_bucket = var.bucket log_object_prefix = null } } +# tftest:modules=1:resources=2 ``` diff --git a/modules/gke-cluster/README.md b/modules/gke-cluster/README.md index d0293f06c..13367dc06 100644 --- a/modules/gke-cluster/README.md +++ b/modules/gke-cluster/README.md @@ -6,12 +6,12 @@ This module allows simplified creation and management of GKE clusters and should ```hcl module "cluster-1" { - source = "./modules/gke-cluster" + source = "./modules/gke-cluster" project_id = "myproject" name = "cluster-1" location = "europe-west1-b" - network = var.network_self_link - subnetwork = var.subnet_self_link + network = var.vpc.self_link + subnetwork = var.subnet.self_link secondary_range_pods = "pods" secondary_range_services = "services" default_max_pods_per_node = 32 @@ -27,6 +27,7 @@ module "cluster-1" { environment = "dev" } } +# tftest:modules=1:resources=1 ``` diff --git a/modules/gke-nodepool/README.md b/modules/gke-nodepool/README.md index 17df73a88..20288767f 100644 --- a/modules/gke-nodepool/README.md +++ b/modules/gke-nodepool/README.md @@ -24,13 +24,14 @@ To have the module auto-create a service account for the nodes, set the `node_se ```hcl module "cluster-1-nodepool-1" { - source = "../modules/gke-nodepool" + source = "./modules/gke-nodepool" project_id = "myproject" cluster_name = "cluster-1" location = "europe-west1-b" name = "nodepool-1" node_service_account_create = true } +# tftest:modules=1:resources=1 ``` diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index 6389a0784..f12542edb 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -22,6 +22,7 @@ module "myproject-default-service-accounts" { ] } } +# tftest:modules=1:resources=5 ``` diff --git a/modules/kms/README.md b/modules/kms/README.md index 67b98c188..ba18449e1 100644 --- a/modules/kms/README.md +++ b/modules/kms/README.md @@ -14,7 +14,7 @@ In this module **no lifecycle blocks are set on resources to prevent destroy**, ```hcl module "kms" { - source = "../modules/kms" + source = "./modules/kms" project_id = "my-project" iam = { "roles/owner" = ["user:user1@example.com"] @@ -23,33 +23,35 @@ module "kms" { keyring_create = false keys = { key-a = null, key-b = null, key-c = null } } +# tftest:skip ``` ### Keyring creation and crypto key rotation and IAM roles ```hcl module "kms" { - source = "../modules/kms" - project_id = "my-project" + source = "./modules/kms" + project_id = "my-project" key_iam = { key-a = { "roles/owner" = ["user:user1@example.com"] } } - keyring = { location = "europe-west1", name = "test" } - keys = { + keyring = { location = "europe-west1", name = "test" } + keys = { key-a = null key-b = { rotation_period = "604800s", labels = null } key-c = { rotation_period = null, labels = { env = "test" } } } } +# tftest:modules=1:resources=5 ``` ### Crypto key purpose ```hcl module "kms" { - source = "../modules/kms" + source = "./modules/kms" project_id = "my-project" key_purpose = { key-c = { @@ -63,6 +65,7 @@ module "kms" { keyring = { location = "europe-west1", name = "test" } keys = { key-a = null, key-b = null, key-c = null } } +# tftest:modules=1:resources=4 ``` diff --git a/modules/logging-sinks/README.md b/modules/logging-sinks/README.md index c136e97d5..83f50165a 100644 --- a/modules/logging-sinks/README.md +++ b/modules/logging-sinks/README.md @@ -4,11 +4,6 @@ This module allows easy creation of one or more logging sinks. ## Example -```hcl -module "sinks" { -} -``` - ## Variables diff --git a/modules/net-address/README.md b/modules/net-address/README.md index eb3e1168a..f656c34f7 100644 --- a/modules/net-address/README.md +++ b/modules/net-address/README.md @@ -9,13 +9,14 @@ This module allows reserving Compute Engine external, global, and internal addre ```hcl module "addresses" { source = "./modules/net-address" - project_id = local.projects.host + project_id = var.project_id external_addresses = { nat-1 = var.region vpn-remote = var.region } global_addresses = ["app-1", "app-2"] } +# tftest:modules=1:resources=4 ``` ### Internal addresses @@ -23,15 +24,15 @@ module "addresses" { ```hcl module "addresses" { source = "./modules/net-address" - project_id = local.projects.host + project_id = var.project_id internal_addresses = { - ilb-1 = { - region = var.region - subnetwork = module.vpc.subnet_self_links["${var.region}-test"] + ilb-1 = { + region = var.region + subnetwork = var.subnet.self_link } - ilb-2 = { - region = var.region - subnetwork = module.vpc.subnet_self_links["${var.region}-test"] + ilb-2 = { + region = var.region + subnetwork = var.subnet.self_link } } # optional configuration @@ -43,6 +44,7 @@ module "addresses" { } } } +# tftest:modules=1:resources=2 ``` @@ -63,4 +65,4 @@ module "addresses" { | external_addresses | None | | | global_addresses | None | | | internal_addresses | None | | - \ No newline at end of file + diff --git a/modules/net-cloudnat/README.md b/modules/net-cloudnat/README.md index a8d65d9d1..f313c2e78 100644 --- a/modules/net-cloudnat/README.md +++ b/modules/net-cloudnat/README.md @@ -6,12 +6,13 @@ Simple Cloud NAT management, with optional router creation. ```hcl module "nat" { - source = "../modules/net-cloudnat" + source = "./modules/net-cloudnat" project_id = "my-project" region = "europe-west1" name = "default" router_network = "my-vpc" } +# tftest:modules=1:resources=2 ``` diff --git a/modules/net-ilb/README.md b/modules/net-ilb/README.md index 1d90c5aa3..2b7460f08 100644 --- a/modules/net-ilb/README.md +++ b/modules/net-ilb/README.md @@ -23,16 +23,18 @@ This examples shows how to create an ILB by combining externally managed instanc ```hcl module "ilb" { source = "./modules/net-ilb" - project_id = "my-project" + project_id = var.project_id region = "europe-west1" name = "ilb-test" service_label = "ilb-test" - network = local.network_self_link - subnetwork = local.subnetwork_self_link + network = var.vpc.self_link + subnetwork = var.subnet.self_link group_configs = { my-group = { - zone = europe-west1-b, named_ports = null, instances = [ - local.instance1_self_link, local.instance2_self_link + zone = "europe-west1-b", named_ports = null + instances = [ + "instance-1-self-link", + "instance-2-self-link" ] } } @@ -45,6 +47,7 @@ module "ilb" { type = "http", check = { port = 80 }, config = {}, logging = true } } +# tftest:modules=1:resources=4 ``` ### End to end example @@ -64,36 +67,37 @@ module "cos-nginx" { module "instance-group" { source = "./modules/compute-vm" - project_id = "my-project" + project_id = var.project_id region = "europe-west1" - zone = "europe-west1-b" + zones = ["europe-west1-b", "europe-west1-c"] name = "ilb-test" network_interfaces = [{ - network = local.network_self_link, - subnetwork = local.subnetwork_self_link, - nat = false, + network = var.vpc.self_link + subnetwork = var.subnet.self_link + nat = false addresses = null + alias_ips = null }] boot_disk = { image = "projects/cos-cloud/global/images/family/cos-stable" type = "pd-ssd" size = 10 } - tags = ["http-server", "ssh"] + tags = ["http-server", "ssh"] metadata = { user-data = module.cos-nginx.cloud_config } - group = {} + group = { named_ports = {} } } module "ilb" { source = "./modules/net-ilb" - project_id = "my-project" + project_id = var.project_id region = "europe-west1" name = "ilb-test" service_label = "ilb-test" - network = local.network_self_link - subnetwork = local.subnetwork_self_link + network = var.vpc.self_link + subnetwork = var.subnet.self_link ports = [80] backends = [{ failover = false @@ -104,7 +108,7 @@ module "ilb" { type = "http", check = { port = 80 }, config = {}, logging = true } } - +# tftest:modules=2:resources=5 ``` diff --git a/modules/net-vpc-firewall/README.md b/modules/net-vpc-firewall/README.md index 23b24eded..6b7800157 100644 --- a/modules/net-vpc-firewall/README.md +++ b/modules/net-vpc-firewall/README.md @@ -22,6 +22,7 @@ module "firewall" { admin_ranges_enabled = true admin_ranges = ["10.0.0.0/8"] } +# tftest:modules=1:resources=4 ``` ### Custom rules @@ -30,7 +31,7 @@ This is an example of how to define custom rules, with a sample rule allowing op ```hcl module "firewall" { - source = "../modules/net-vpc-firewall" + source = "./modules/net-vpc-firewall" project_id = "my-project" network = "my-network" admin_ranges_enabled = true @@ -49,6 +50,7 @@ module "firewall" { } } } +# tftest:modules=1:resources=5 ``` diff --git a/modules/net-vpc-peering/README.md b/modules/net-vpc-peering/README.md index df9170a81..66de5626d 100644 --- a/modules/net-vpc-peering/README.md +++ b/modules/net-vpc-peering/README.md @@ -13,34 +13,32 @@ Basic usage of this module is as follows: ```hcl module "peering" { - source = "modules/net-vpc-peering" - + source = "./modules/net-vpc-peering" prefix = "name-prefix" - local_network = "" - peer_network = "" + local_network = "projects/project-1/global/networks/vpc-1" + peer_network = "projects/project-1/global/networks/vpc-2" } +# tftest:modules=1:resources=2 ``` If you need to create more than one peering for the same VPC Network `(A -> B, A -> C)` you use a `depends_on` for second one to keep order of peering creation (It is not currently possible to create more than one peering connection for a VPC Network at the same time). ```hcl module "peering-a-b" { - source = "modules/net-vpc-peering" - + source = "./modules/net-vpc-peering" prefix = "name-prefix" - local_network = "" - peer_network = "" + local_network = "projects/project-a/global/networks/vpc-a" + peer_network = "projects/project-b/global/networks/vpc-b" } module "peering-a-c" { - source = "modules/net-vpc-peering" - + source = "./modules/net-vpc-peering" prefix = "name-prefix" - local_network = "" - peer_network = "" - - depends_on = [ module.peering-a-b ] + local_network = "projects/project-a/global/networks/vpc-a" + peer_network = "projects/project-c/global/networks/vpc-c" + depends_on = [module.peering-a-b] } +# tftest:modules=2:resources=4 ``` diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md index a000a474a..f15fa686f 100644 --- a/modules/net-vpc/README.md +++ b/modules/net-vpc/README.md @@ -10,7 +10,7 @@ The module allows for several different VPC configurations, some of the most com ```hcl module "vpc" { - source = "../modules/net-vpc" + source = "./modules/net-vpc" project_id = "my-project" name = "my-network" subnets = [ @@ -31,6 +31,7 @@ module "vpc" { } ] } +# tftest:modules=1:resources=3 ``` ### Peering @@ -40,34 +41,53 @@ A single peering can be configured for the VPC, so as to allow management of sim If you only want to create the "local" side of the peering, use `peering_create_remote_end` to `false`. This is useful if you don't have permissions on the remote project/VPC to create peerings. ```hcl +module "vpc-hub" { + source = "./modules/net-vpc" + project_id = "hub" + name = "vpc-hub" + subnets = [{ + ip_cidr_range = "10.0.0.0/24" + name = "subnet-1" + region = "europe-west1" + secondary_ip_range = null + }] +} + module "vpc-spoke-1" { - source = "../modules/net-vpc" - project_id = "my-project" - name = "my-network" - subnets = [ - { - ip_cidr_range = "10.0.0.0/24" - name = "subnet-1" - region = "europe-west1" - secondary_ip_range = { - pods = "172.16.0.0/20" - services = "192.168.0.0/24" - } - } - ] + source = "./modules/net-vpc" + project_id = "spoke1" + name = "vpc-spoke1" + subnets = [{ + ip_cidr_range = "10.0.1.0/24" + name = "subnet-2" + region = "europe-west1" + secondary_ip_range = null + }] peering_config = { peer_vpc_self_link = module.vpc-hub.self_link - export_routes = false - import_routes = true + export_routes = false + import_routes = true } } +# tftest:modules=2:resources=6 ``` ### Shared VPC ```hcl +locals { + service_project_1 = { + project_id = "project1" + gke_service_account = "gke" + cloud_services_service_account = "cloudsvc" + } + service_project_2 = { + project_id = "project2" + } +} + module "vpc-host" { - source = "../modules/net-vpc" + source = "./modules/net-vpc" project_id = "my-project" name = "my-host-network" subnets = [ @@ -89,15 +109,16 @@ module "vpc-host" { iam = { "europe-west1/subnet-1" = { "roles/compute.networkUser" = [ - local.service_project_1.cloudsvc_sa, - local.service_project_1.gke_sa + local.service_project_1.cloud_services_service_account, + local.service_project_1.gke_service_account ] "roles/compute.securityAdmin" = [ - local.service_project_1.gke_sa + local.service_project_1.gke_service_account ] } } } +# tftest:modules=1:resources=7 ``` diff --git a/modules/net-vpn-dynamic/README.md b/modules/net-vpn-dynamic/README.md index 218af8b14..8e15f40e6 100644 --- a/modules/net-vpn-dynamic/README.md +++ b/modules/net-vpn-dynamic/README.md @@ -22,7 +22,7 @@ module "vpn-dynamic" { } bgp_session_range = "169.254.139.133/30" ike_version = 2 - peer_ip = var.remote_vpn_gateway.address + peer_ip = "1.1.1.1" shared_secret = null bgp_peer_options = { advertise_groups = ["ALL_SUBNETS"] @@ -35,6 +35,7 @@ module "vpn-dynamic" { } } } +# tftest:modules=1:resources=10 ``` diff --git a/modules/net-vpn-ha/README.md b/modules/net-vpn-ha/README.md index 959f7fa79..9fe041766 100644 --- a/modules/net-vpn-ha/README.md +++ b/modules/net-vpn-ha/README.md @@ -6,13 +6,13 @@ This module makes it easy to deploy either GCP-to-GCP or GCP-to-On-prem [Cloud H ### GCP to GCP ```hcl module "vpn_ha-1" { - source = "../modules/net-vpn-ha" - project_id = "" - region = "europe-west4" - network = "https://www.googleapis.com/compute/v1/projects//global/networks/network-1" - name = "net1-to-net-2" + source = "./modules/net-vpn-ha" + project_id = "" + region = "europe-west4" + network = "https://www.googleapis.com/compute/v1/projects//global/networks/network-1" + name = "net1-to-net-2" peer_gcp_gateway = module.vpn_ha-2.self_link - router_asn = 64514 + router_asn = 64514 router_advertise_config = { groups = ["ALL_SUBNETS"] ip_ranges = { @@ -26,35 +26,35 @@ module "vpn_ha-1" { address = "169.254.1.1" asn = 64513 } - bgp_peer_options = null - bgp_session_range = "169.254.1.2/30" - ike_version = 2 - vpn_gateway_interface = 0 + bgp_peer_options = null + bgp_session_range = "169.254.1.2/30" + ike_version = 2 + vpn_gateway_interface = 0 peer_external_gateway_interface = null - shared_secret = "" + shared_secret = "" } remote-1 = { bgp_peer = { address = "169.254.2.1" asn = 64513 } - bgp_peer_options = null - bgp_session_range = "169.254.2.2/30" - ike_version = 2 - vpn_gateway_interface = 1 + bgp_peer_options = null + bgp_session_range = "169.254.2.2/30" + ike_version = 2 + vpn_gateway_interface = 1 peer_external_gateway_interface = null - shared_secret = "" + shared_secret = "" } } } module "vpn_ha-2" { - source = "../modules/net-vpn-ha" - project_id = "" - region = "europe-west4" - network = "https://www.googleapis.com/compute/v1/projects//global/networks/local-network" - name = "net2-to-net1" - router_asn = 64513 + source = "./modules/net-vpn-ha" + project_id = "" + region = "europe-west4" + network = "https://www.googleapis.com/compute/v1/projects//global/networks/local-network" + name = "net2-to-net1" + router_asn = 64513 peer_gcp_gateway = module.vpn_ha-1.self_link tunnels = { remote-0 = { @@ -62,44 +62,44 @@ module "vpn_ha-2" { address = "169.254.1.2" asn = 64514 } - bgp_peer_options = null - bgp_session_range = "169.254.1.1/30" - ike_version = 2 - vpn_gateway_interface = 0 + bgp_peer_options = null + bgp_session_range = "169.254.1.1/30" + ike_version = 2 + vpn_gateway_interface = 0 peer_external_gateway_interface = null - shared_secret = module.vpn_ha-1.random_secret + shared_secret = module.vpn_ha-1.random_secret } remote-1 = { bgp_peer = { address = "169.254.2.2" asn = 64514 } - bgp_peer_options = null - bgp_session_range = "169.254.2.1/30" - ike_version = 2 - vpn_gateway_interface = 1 + bgp_peer_options = null + bgp_session_range = "169.254.2.1/30" + ike_version = 2 + vpn_gateway_interface = 1 peer_external_gateway_interface = null - shared_secret = module.vpn_ha-1.random_secret + shared_secret = module.vpn_ha-1.random_secret } } } +# tftest:modules=2:resources=18 ``` ### GCP to on-prem -``` +```hcl module "vpn_ha" { - source = "../modules/net-vpn-ha" - project_id = "" - region = "europe-west4" - network = "https://www.googleapis.com/compute/v1/projects//global/networks/my-network" - name = "mynet-to-onprem" + source = "./modules/net-vpn-ha" + project_id = var.project_id + region = var.region + network = var.vpc.self_link + name = "mynet-to-onprem" peer_external_gateway = { - redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" - interfaces = [{ - id = 0 - ip_address = "8.8.8.8" # on-prem router ip address - - }] + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + interfaces = [{ + id = 0 + ip_address = "8.8.8.8" # on-prem router ip address + }] } router_asn = 64514 tunnels = { @@ -108,27 +108,28 @@ module "vpn_ha" { address = "169.254.1.1" asn = 64513 } - bgp_peer_options = null - bgp_session_range = "169.254.1.2/30" - ike_version = 2 - vpn_gateway_interface = 0 + bgp_peer_options = null + bgp_session_range = "169.254.1.2/30" + ike_version = 2 + vpn_gateway_interface = 0 peer_external_gateway_interface = 0 - shared_secret = "mySecret" + shared_secret = "mySecret" } remote-1 = { bgp_peer = { address = "169.254.2.1" asn = 64513 } - bgp_peer_options = null - bgp_session_range = "169.254.2.2/30" - ike_version = 2 - vpn_gateway_interface = 1 + bgp_peer_options = null + bgp_session_range = "169.254.2.2/30" + ike_version = 2 + vpn_gateway_interface = 1 peer_external_gateway_interface = 0 - shared_secret = "mySecret" + shared_secret = "mySecret" } } } +# tftest:modules=1:resources=10 ``` diff --git a/modules/net-vpn-static/README.md b/modules/net-vpn-static/README.md index 5961e8850..ef1b030dd 100644 --- a/modules/net-vpn-static/README.md +++ b/modules/net-vpn-static/README.md @@ -3,23 +3,33 @@ ## Example ```hcl +module "addresses" { + source = "./modules/net-address" + project_id = var.project_id + external_addresses = { + vpn = "europe-west1" + } +} + module "vpn" { source = "./modules/net-vpn-static" project_id = var.project_id region = var.region - network = var.network + network = var.vpc.self_link name = "remote" - # gateway_address = var.gateway_address - remote_ranges = [var.remote_ranges] + gateway_address_create = false + gateway_address = module.addresses.external_addresses["vpn"].address + remote_ranges = ["10.10.0.0/24"] tunnels = { remote-0 = { ike_version = 2 - peer_ip = var.remote_vpn_gateway_address - shared_secret = "" - traffic_selectors = { local = ["0.0.0.0/0"], remote = null } + peer_ip = "1.1.1.1" + shared_secret = "mysecret" + traffic_selectors = { local = ["0.0.0.0/0"], remote = ["0.0.0.0/0"] } } } } +# tftest:modules=2:resources=8 ``` diff --git a/modules/organization/README.md b/modules/organization/README.md index 0a090b128..9d12681c6 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -27,6 +27,7 @@ module "org" { } } } +# tftest:modules=1:resources=4 ``` diff --git a/modules/project/README.md b/modules/project/README.md index fcd8ca059..99755a0e3 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -5,6 +5,10 @@ ### Minimal example with IAM ```hcl +locals { + gke_service_account = "my_gke_service_account" +} + module "project" { source = "./modules/project" billing_account = "123456-123456-123456" @@ -17,10 +21,11 @@ module "project" { ] iam = { "roles/container.hostServiceAgentUser" = [ - "serviceAccount:${var.gke_service_account}" + "serviceAccount:${local.gke_service_account}" ] } } +# tftest:modules=1:resources=4 ``` ### Minimal example with IAM additive roles @@ -31,7 +36,7 @@ module "project" { name = "project-example" project_create = false - iam_additive = { + iam = { "group:usergroup_watermlon_experimentation@lemonadeinc.io" = [ "roles/viewer", "roles/storage.objectAdmin" @@ -48,6 +53,7 @@ module "project" { ], } } +# tftest:skip ``` ### Organization policies @@ -76,6 +82,7 @@ module "project" { } } } +# tftest:modules=1:resources=6 ``` diff --git a/modules/pubsub/README.md b/modules/pubsub/README.md index e708078eb..868f693f0 100644 --- a/modules/pubsub/README.md +++ b/modules/pubsub/README.md @@ -17,6 +17,7 @@ module "pubsub" { "roles/pubsub.subscriber" = ["user:user1@example.com"] } } +# tftest:modules=1:resources=3 ``` ### Subscriptions @@ -41,6 +42,7 @@ module "pubsub" { } } } +# tftest:modules=1:resources=3 ``` ### Push subscriptions @@ -63,6 +65,7 @@ module "pubsub" { } } } +# tftest:modules=1:resources=2 ``` ### Subscriptions with IAM @@ -82,6 +85,7 @@ module "pubsub" { } } } +# tftest:modules=1:resources=3 ``` diff --git a/modules/secret-manager/README.md b/modules/secret-manager/README.md index 7f237482c..7025634a4 100644 --- a/modules/secret-manager/README.md +++ b/modules/secret-manager/README.md @@ -21,6 +21,7 @@ module "secret-manager" { test-manual = ["europe-west1", "europe-west4"] } } +# tftest:modules=1:resources=2 ``` ### Secret IAM bindings @@ -44,6 +45,7 @@ module "secret-manager" { } } } +# tftest:modules=1:resources=4 ``` ### Secret versions @@ -68,6 +70,7 @@ module "secret-manager" { } } } +# tftest:modules=1:resources=5 ``` diff --git a/modules/service-directory/README.md b/modules/service-directory/README.md index 5bd32fd36..9bc881df6 100644 --- a/modules/service-directory/README.md +++ b/modules/service-directory/README.md @@ -21,6 +21,7 @@ module "service-directory" { ] } } +# tftest:modules=1:resources=2 ``` ### Services with IAM and endpoints @@ -49,6 +50,7 @@ module "service-directory" { "one/second" = { address = "127.0.0.2", port = 80, metadata = {} } } } +# tftest:modules=1:resources=5 ``` ### DNS based zone @@ -80,10 +82,10 @@ module "dns-sd" { type = "service-directory" name = "apps" domain = "apps.example.org." - client_networks = [local.vpc_self_link] + client_networks = [var.vpc.self_link] service_directory_namespace = module.service-directory.id } - +# tftest:modules=2:resources=5 ``` diff --git a/modules/source-repository/README.md b/modules/source-repository/README.md index 78a7e7d71..f5a92995d 100644 --- a/modules/source-repository/README.md +++ b/modules/source-repository/README.md @@ -9,13 +9,14 @@ This module allows managing a single Cloud Source Repository, including IAM bind ```hcl module "repo" { - source e = "./modules/source-repository" + source = "./modules/source-repository" project_id = "my-project" name = "my-repo" iam = { "roles/source.reader" = ["user:foo@example.com"] } } +# tftest:modules=1:resources=2 ``` diff --git a/modules/vpc-sc/README.md b/modules/vpc-sc/README.md index 18d35f039..77303155e 100644 --- a/modules/vpc-sc/README.md +++ b/modules/vpc-sc/README.md @@ -12,81 +12,85 @@ The Use of this module requires credentials with the [correct permissions](https ```hcl module "vpc-sc" { - source = "../../modules/vpc-sc" - org_id = 1234567890 + source = "./modules/vpc-sc" + org_id = 112233 access_policy_title = "My Access Policy" access_levels = { - my_trusted_proxy = { + my_trusted_proxy = { combining_function = "AND" - conditions = [{ - ip_subnetworks = ["85.85.85.52/32"] - members = [] - negate = false + conditions = [{ + ip_subnetworks = ["85.85.85.52/32"] + members = [] + negate = false }] } } access_level_perimeters = { - my_trusted_proxy = ["perimeter"] - } - perimeters = { + my_trusted_proxy = { + my_trusted_proxy = ["perimeter"] + } + } + perimeters = { perimeter = { - type = "PERIMETER_TYPE_REGULAR" - dry_run_config = null - enforced_config = { - restricted_services = ["storage.googleapis.com"] - vpc_accessible_services = ["storage.googleapis.com"] + type = "PERIMETER_TYPE_REGULAR" + dry_run_config = null + enforced_config = { + restricted_services = ["storage.googleapis.com"] + vpc_accessible_services = ["storage.googleapis.com"] } } } perimeter_projects = { perimeter = { - enforced = [111111111,222222222] + enforced = [111111111, 222222222] } } } +# tftest:modules=1:resources=3 ``` ## Example VCP-SC standard perimeter with one service and one project in dry run mode ```hcl module "vpc-sc" { - source = "../../modules/vpc-sc" - org_id = 1234567890 + source = "./modules/vpc-sc" + org_id = 112233 access_policy_title = "My Access Policy" access_levels = { - my_trusted_proxy = { + my_trusted_proxy = { combining_function = "AND" - conditions = [{ - ip_subnetworks = ["85.85.85.52/32"] - members = [] - negate = false + conditions = [{ + ip_subnetworks = ["85.85.85.52/32"] + members = [] + negate = false }] } } access_level_perimeters = { enforced = { - my_trusted_proxy = ["perimeter"] + my_trusted_proxy = ["perimeter"] } - } - perimeters = { + } + perimeters = { perimeter = { - type = "PERIMETER_TYPE_REGULAR" - dry_run_config = { + type = "PERIMETER_TYPE_REGULAR" + dry_run_config = { restricted_services = ["storage.googleapis.com", "bigquery.googleapis.com"] vpc_accessible_services = ["storage.googleapis.com", "bigquery.googleapis.com"] } - enforced_config = { - restricted_services = ["storage.googleapis.com"] - vpc_accessible_services = ["storage.googleapis.com"] + enforced_config = { + restricted_services = ["storage.googleapis.com"] + vpc_accessible_services = ["storage.googleapis.com"] } } } perimeter_projects = { perimeter = { - enforced = [111111111,222222222] + enforced = [111111111, 222222222] dry_run = [333333333] } } } +# tftest:modules=1:resources=3 ``` diff --git a/modules/vpc-sc/outputs.tf b/modules/vpc-sc/outputs.tf index e3e56c1c1..62512887c 100644 --- a/modules/vpc-sc/outputs.tf +++ b/modules/vpc-sc/outputs.tf @@ -17,14 +17,15 @@ output "org_id" { description = "Organization id dependent on module resources." value = var.org_id - depends_on = [ - google_organization_iam_audit_config, - google_organization_iam_binding.authoritative, - google_organization_iam_custom_role.roles, - google_organization_iam_member.additive, - google_organization_policy.boolean, - google_organization_policy.list - ] + # FIXME(jccb): these deps don't exist (??) + # depends_on = [ + # google_organization_iam_audit_config, + # google_organization_iam_binding.authoritative, + # google_organization_iam_custom_role.roles, + # google_organization_iam_member.additive, + # google_organization_policy.boolean, + # google_organization_policy.list + # ] } output "access_policy_name" { diff --git a/tests/conftest.py b/tests/conftest.py index a7acd58bc..2f34dee09 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -55,6 +55,26 @@ def plan_runner(): return run_plan +@pytest.fixture(scope='session') +def example_plan_runner(): + "Returns a function to run Terraform plan on an example." + + def run_plan(fixture_path, is_module=True, targets=None, **tf_vars): + "Runs Terraform plan and returns parsed output" + tf = tftest.TerraformTest(fixture_path, BASEDIR, + os.environ.get('TERRAFORM', 'terraform')) + tf.setup() + plan = tf.plan(output=True, tf_vars=tf_vars, targets=targets) + modules = plan.modules + resources = [] + for name, module in modules.items(): + for _, resource in module.resources.items(): + resources.append(resource) + return plan, modules, resources + + return run_plan + + @pytest.fixture(scope='session') def apply_runner(): "Returns a function to run Terraform apply on a fixture." diff --git a/tests/modules/examples/conftest.py b/tests/modules/examples/conftest.py new file mode 100644 index 000000000..eecbef0fa --- /dev/null +++ b/tests/modules/examples/conftest.py @@ -0,0 +1,46 @@ +# 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. + +from pathlib import Path + +import marko + + +MODULES_PATH = Path(__file__).parents[3] / 'modules/' + + +def pytest_generate_tests(metafunc): + if 'example' in metafunc.fixturenames: + modules = [ + x for x in MODULES_PATH.iterdir() + if x.is_dir() + ] + modules.sort() + examples = [] + ids = [] + for module in modules: + readme = module / 'README.md' + if not readme.exists(): continue + doc = marko.parse(readme.read_text()) + index = 0 + for child in doc.children: + if isinstance(child, marko.block.FencedCode) and child.lang == 'hcl': + index += 1 + code = child.children[0].children + if 'tftest:skip' in code: + continue + examples.append(code) + ids.append(f'{module.stem}:example{index}') + + metafunc.parametrize('example', examples, ids=ids) diff --git a/tests/modules/examples/test_plan.py b/tests/modules/examples/test_plan.py new file mode 100644 index 000000000..584fe6eee --- /dev/null +++ b/tests/modules/examples/test_plan.py @@ -0,0 +1,38 @@ +# 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 tftest +import re +import tempfile +from pathlib import Path + +import marko + +MODULES_PATH = Path(__file__, '../../../../modules/').resolve() +VARIABLES_PATH = Path(__file__, '../variables.tf').resolve() +EXPECTED_RESOURCES_RE = re.compile(r'# tftest:modules=(\d+):resources=(\d+)') + + +def test_example(example_plan_runner, tmp_path, example): + (tmp_path / 'modules').symlink_to(MODULES_PATH) + (tmp_path / 'variables.tf').symlink_to(VARIABLES_PATH) + (tmp_path / 'main.tf').write_text(example) + + match = EXPECTED_RESOURCES_RE.search(example) + expected_modules = int(match.group(1)) if match is not None else 1 + expected_resources = int(match.group(2)) if match is not None else 1 + + plan, modules, resources = example_plan_runner(str(tmp_path)) + assert expected_modules == len(modules) + assert expected_resources == len(resources) diff --git a/tests/modules/examples/variables.tf b/tests/modules/examples/variables.tf new file mode 100644 index 000000000..69b3cdec8 --- /dev/null +++ b/tests/modules/examples/variables.tf @@ -0,0 +1,69 @@ +# 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. + +# common variables used for examples + +variable "organization_id" { + default = "organization/organization" +} + +variable "project_id" { + default = "projects/project-id" +} + +variable "billing_account_id" { + default = "billing_account_id" +} + +variable "bucket" { + default = "bucket" +} + +variable "region" { + default = "region" +} + +variable "zone" { + default = "zone" +} + +variable "vpc" { + default = { + name = "vpc_name" + self_link = "vpc_self_link" + } +} + +variable "subnet" { + default = { + name = "subnet_name" + region = "subnet_region" + cidr = "subnet_cidr" + self_link = "subnet_self_link" + } +} + +variable "kms_key" { + default = { + self_link = "kms_key_self_link" + } +} + +variable "service_account" { + default = { + id = "service_account_id" + email = "service_account_email" + iam_email = "service_account_iam_email" + } +} diff --git a/tests/requirements.txt b/tests/requirements.txt index f233348b9..1b4d95451 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,4 @@ pytest>=4.6.0 PyYAML>=5.3 tftest>=1.5.2 +marko>=0.9.1