From 205975ff39179717402c2b769b326a777649b896 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Mon, 6 Dec 2021 17:02:56 +0100 Subject: [PATCH 01/52] SA key uploading and credentials json generation with terraform. --- .../onprem-sa-key-management/README.md | 54 +++++++++++++++++++ .../backend.tf.sample | 23 ++++++++ .../cloud-shell-readme.txt | 5 ++ .../onprem-sa-key-management/main.tf | 48 +++++++++++++++++ .../onprem-sa-key-management/outputs.tf | 25 +++++++++ .../public-keys/data-uploader/public_key.pem | 17 ++++++ .../prisma-security/public_key.pem | 17 ++++++ .../onprem-sa-key-management/variables.tf | 26 +++++++++ .../onprem-sa-key-management/versions.tf | 29 ++++++++++ modules/iam-service-account/README.md | 4 +- modules/iam-service-account/main.tf | 29 ++++++++++ modules/iam-service-account/outputs.tf | 5 ++ modules/iam-service-account/variables.tf | 6 +++ 13 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 cloud-operations/onprem-sa-key-management/README.md create mode 100644 cloud-operations/onprem-sa-key-management/backend.tf.sample create mode 100644 cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt create mode 100644 cloud-operations/onprem-sa-key-management/main.tf create mode 100644 cloud-operations/onprem-sa-key-management/outputs.tf create mode 100644 cloud-operations/onprem-sa-key-management/public-keys/data-uploader/public_key.pem create mode 100644 cloud-operations/onprem-sa-key-management/public-keys/prisma-security/public_key.pem create mode 100644 cloud-operations/onprem-sa-key-management/variables.tf create mode 100644 cloud-operations/onprem-sa-key-management/versions.tf diff --git a/cloud-operations/onprem-sa-key-management/README.md b/cloud-operations/onprem-sa-key-management/README.md new file mode 100644 index 00000000..6661133d --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/README.md @@ -0,0 +1,54 @@ +# Generationg and uploading public keys for a service accounts + +This example shows how to manage IAM Service Account Keys by generating a key pair and uploading public keys to GCP. + +By generating a key inside a `box` where the key is intended to be used we AVOID: + - [passing keys between users](https://cloud.google.com/iam/docs/best-practices-for-managing-service-account-keys#pass-between-users) or systems + - having SA key stored in the terraform state (only public part in the state) + - having SA key with no expiration period + +TODO (averbukh) +## Running the example +# cleaning up example keys +- rm -f /public-keys/data-uploader/ +- rm -f /public-keys/prisma-security/ + +# generate your keys +- mkdir keys && cd keys +- openssl req -x509 -nodes -newkey rsa:2048 -days 3650 \ + -keyout data_uploader_private_key.pem \ + -out ../public-keys/data-uploader/public_key.pem \ + -subj "/CN=unused" +- openssl req -x509 -nodes -newkey rsa:2048 -days 3650 \ + -keyout prisma_security_private_key.pem \ + -out ../public-keys/prisma-security/public_key.pem \ + -subj "/CN=unused" + +- cd .. +- terraform init +- terraform apply -var project_id=$GOOGLE_CLOUD_PROJECT + +- terraform show -json | jq '.values.outputs."data-uploader-credentials".value."public_key.pem" | fromjson' > data-uploader.json +- terraform show -json | jq '.values.outputs."prisma-security-credentials".value."public_key.pem" | fromjson' > prisma-security.json + +- contents=$(jq --arg key "$(cat keys/data_uploader_private_key.pem)" '.private_key=$key' data-uploader.json) && echo "$contents" > data-uploader.json +- contents=$(jq --arg key "$(cat keys/prisma_security_private_key.pem)" '.private_key=$key' prisma-security.json) && echo "$contents" > prisma-security.json + +- gcloud auth activate-service-account --key-file prisma-security.json +- gcloud auth activate-service-account --key-file data-uploader.json + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| project_id | Project id. | string | ✓ | | +| *project_create* | Create project instead ofusing an existing one. | bool | | false | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| data-uploader-credentials | Data Uploader SA json key templates. | | +| prisma-security-credentials | Prisma Security SA json key templates. | | + diff --git a/cloud-operations/onprem-sa-key-management/backend.tf.sample b/cloud-operations/onprem-sa-key-management/backend.tf.sample new file mode 100644 index 00000000..9eebc1ba --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/backend.tf.sample @@ -0,0 +1,23 @@ +# Copyright 2021 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. + +# set a valid bucket below and rename this file to backend.tf + +terraform { + backend "gcs" { + bucket = "" + prefix = "fabric/operations/onprem-sa-key-management" + } +} + diff --git a/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt b/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt new file mode 100644 index 00000000..da5b32f0 --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt @@ -0,0 +1,5 @@ + + +################################# Quickstart ################################# +TODO(averbukh) +Refer to the README.md file for more info and testing flow. diff --git a/cloud-operations/onprem-sa-key-management/main.tf b/cloud-operations/onprem-sa-key-management/main.tf new file mode 100644 index 00000000..302574d8 --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/main.tf @@ -0,0 +1,48 @@ +/** + * Copyright 2021 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 "project" { + source = "../../modules/project" + name = var.project_id + project_create = var.project_create +} + +module "onprem-data-uploader" { + source = "../../modules/iam-service-account" + project_id = module.project.project_id + name = "onprem-data-uploader" + iam_project_roles = { + (module.project.project_id) = [ + "roles/bigquery.dataOwner", + "roles/bigquery.jobUser", + "roles/storage.objectAdmin" + ] + } + public_keys_directory = "public-keys/data-uploader/" +} + +module "onprem-prisma-security" { + source = "../../modules/iam-service-account" + project_id = module.project.project_id + name = "onprem-prisma-security" + iam_project_roles = { + (module.project.project_id) = [ + "roles/iam.securityReviewer" + ] + } + public_keys_directory = "public-keys/prisma-security/" +} diff --git a/cloud-operations/onprem-sa-key-management/outputs.tf b/cloud-operations/onprem-sa-key-management/outputs.tf new file mode 100644 index 00000000..539f27fd --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/outputs.tf @@ -0,0 +1,25 @@ +/** + * Copyright 2021 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 "data-uploader-credentials" { + description = "Data Uploader SA json key templates." + value = module.onprem-data-uploader.service_account_credentials +} + +output "prisma-security-credentials" { + description = "Prisma Security SA json key templates." + value = module.onprem-prisma-security.service_account_credentials +} diff --git a/cloud-operations/onprem-sa-key-management/public-keys/data-uploader/public_key.pem b/cloud-operations/onprem-sa-key-management/public-keys/data-uploader/public_key.pem new file mode 100644 index 00000000..ad5bc9fe --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/public-keys/data-uploader/public_key.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICnjCCAYYCCQDhgw8htVCGmTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZ1 +bnVzZWQwHhcNMjExMjA2MDgwNTAyWhcNMzExMjA0MDgwNTAyWjARMQ8wDQYDVQQD +DAZ1bnVzZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0xlwdjkBS +1ovANJ1RXKpFdbPQWYlqKUUo+/KLClNYC9KxRqrc+u5FtPIdCPv5WRH5sz+z8gcf +3zJMht0dO7fOwJ9wSDKzvHkMUdXTGBPbm2i9PNA6f+YEwJQjWJlAHFH4Lp3x6ddT +4KO4FRQEkN/5V1+sfmyGGFaSXaoi+PcDcQHvfUUlp5iyX4I+8tqwh1kdg1M5orkE +7iBG0wHWzfOSmZq5in6t9+lWzOZeYapi8bVBm7Vz+dmHZPKS6EGmAXS1wpLCSKHB +uv23KXY4gAXOPHiDI70JpeNiSJBE9WgXs+nL78vNjLTvDhpC10b9nOxLjRc6wA5b +3q2Am0dW1DPRAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAJqyTIibNZM/q30Fn+vR +V9q++19CIervZig1uCarH1M86cpPYRfKcYHOi6tnoCTL9VG8Ky8pbmkZNkET7vnN +OQirpsPmqu3d+FBoqXUt8w1mT1JVr0YiTo3i07zTH8rvQKHjEfPxR73IAyYNvJ3D +k3SdUvU3xXOa+otOQcBKIxX6mJPLhzXgZd144KCfD95qOvpoQOsNW4UWXZ3sPC0k +VcMlN5O8/+D65y63nNtyECXvLicLdn/cdpA2H7Tqhz2ZZR+6tLcDW1kSsA8b6+rQ +1IaKpF+TYo0jMD+WLatRrOHXOWije8871zooAXq9MLVJrT889TdsmEIYT7YPWIeJ +Jcg= +-----END CERTIFICATE----- diff --git a/cloud-operations/onprem-sa-key-management/public-keys/prisma-security/public_key.pem b/cloud-operations/onprem-sa-key-management/public-keys/prisma-security/public_key.pem new file mode 100644 index 00000000..5da20f0b --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/public-keys/prisma-security/public_key.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICnjCCAYYCCQDXMv59IiZqfTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZ1 +bnVzZWQwHhcNMjExMjA2MDgwNjE3WhcNMzExMjA0MDgwNjE3WjARMQ8wDQYDVQQD +DAZ1bnVzZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOK6XwgTzL +icSITBrQBmhnYNOuggDhQr40j8/pIuTOiFZbd+ne3MhcFxpE58T9cOXgR0i/S4ok ++kcGE74H2U7RsRpNi7fJhi62T9e2CXpibURQNJD6y0lXBQkfx6kCrhyvXqHbTxm5 +J0f5mpLlze+w7ATikmYI0mrU9XjtnRJOdxtGfiIaQ/suGTaZ0z4tZgAXy9RnwUAb +LPXn0BD1+GYpCs82+1q7HpMIf343VRH0AdsQJteQSj5LKfaZZTNUF9NIgKtMylck +z0Pt8TmBU0GtJX/XkSWCwMUdqdedXkvhY1XoAZPjaEBSSwq6P15PmdpDq9q2TYjD +8U3kuCX0AlWjAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADAmjI1sg150MK97DCSl +d5OpEShCypaEZSLb/mFONW6mTX2OSdF9ipd9B07BQ2DrL8Xou2/V1aDtQZOWPIGu +Hlm1LKw8sZY2rWX0Rq/v/NxY5iGRlwPMh7Rn9fnpHgaC1PktoDJEcvNMpzBjtfKn +beKP9MNSChAFTTbJVWO5xT/ljE/yoPL3jyJKzKHH7y7AfbonrbQjAENbX/WCRYh3 +zOEWZG/fusRcKkZ/cO7wFFP1gzJFE9wFRu7LOA/FntCixtVSnclsOnunQfqQEVmp +Y0IjfceIerJysCTo0I5HfRw0DOFfZimallOa4Mv5BDmzMWWyX9TvppHCnmqvM2El +ISY= +-----END CERTIFICATE----- diff --git a/cloud-operations/onprem-sa-key-management/variables.tf b/cloud-operations/onprem-sa-key-management/variables.tf new file mode 100644 index 00000000..3740814b --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/variables.tf @@ -0,0 +1,26 @@ +/** + * Copyright 2021 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 "project_create" { + description = "Create project instead ofusing an existing one." + type = bool + default = false +} + +variable "project_id" { + description = "Project id." + type = string +} diff --git a/cloud-operations/onprem-sa-key-management/versions.tf b/cloud-operations/onprem-sa-key-management/versions.tf new file mode 100644 index 00000000..1cc6bf89 --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/versions.tf @@ -0,0 +1,29 @@ +# Copyright 2021 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. + +terraform { + required_version = ">= 1.0.0" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.0.0" + } + google-beta = { + source = "hashicorp/google-beta" + version = ">= 4.0.0" + } + } +} + + diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index 12030e19..0a3b57d9 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -1,6 +1,6 @@ # Google Service Account Module -This module allows simplified creation and management of one a service account and its IAM bindings. A key can optionally be generated and will be stored in Terraform state. To use it create a sensitive output in your root modules referencing the `key` output, then extract the private key from the JSON formatted outputs. +This module allows simplified creation and management of one a service account and its IAM bindings. A key can optionally be generated and will be stored in Terraform state. To use it create a sensitive output in your root modules referencing the `key` output, then extract the private key from the JSON formatted outputs. Alternatively, the `key` can be generated with `openssl` library and only public part uploaded to the Service Account, for more refere to the [Onprem SA Key Management](../../cloud-operations/onprem-sa-key-management/README.md)) example. ## Example @@ -42,6 +42,7 @@ module "myproject-default-service-accounts" { | *iam_project_roles* | Project roles granted to the service account, by project id. | map(list(string)) | | {} | | *iam_storage_roles* | Storage roles granted to the service account, by bucket name. | map(list(string)) | | {} | | *prefix* | Prefix applied to service account names. | string | | null | +| *public_keys_directory* | Path to public keys data files to upload to the service account (should have `.crt` extension). | string | | | | *service_account_create* | Create service account. When set to false, uses a data source to reference an existing service account. | bool | | true | ## Outputs @@ -52,4 +53,5 @@ module "myproject-default-service-accounts" { | iam_email | IAM-format service account email. | | | key | Service account key. | ✓ | | service_account | Service account resource. | | +| service_account_credentials | Service account json credential templates for uploaded public keys data. | | diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf index 244f182e..005ee12c 100644 --- a/modules/iam-service-account/main.tf +++ b/modules/iam-service-account/main.tf @@ -65,6 +65,29 @@ locals { ? try(google_service_account.service_account.0, null) : try(data.google_service_account.service_account.0, null) ) + service_account_credential_templates = { + for file, _ in local.public_keys_data : file => jsonencode( + { + type: "service_account", + project_id: var.project_id, + private_key_id: split("/",google_service_account_key.upload_key[file].id)[5] + private_key: "REPLASE_ME_WITH_PRIVATE_KEY_DATA" + client_email: local.resource_email_static + client_id: local.service_account.unique_id, + auth_uri: "https://accounts.google.com/o/oauth2/auth", + token_uri: "https://oauth2.googleapis.com/token", + auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs", + client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/${urlencode(local.resource_email_static)}" + } + ) + } + public_keys_data = ( + var.public_keys_directory != "" + ? { + for file in fileset("${path.root}/${var.public_keys_directory}", "*.pem") + : file => filebase64("${path.root}/${var.public_keys_directory}/${file}")} + : {} + ) } @@ -87,6 +110,12 @@ resource "google_service_account_key" "key" { service_account_id = local.service_account.email } +resource "google_service_account_key" "upload_key" { + for_each = local.public_keys_data + service_account_id = local.service_account.email + public_key_data = each.value +} + resource "google_service_account_iam_binding" "roles" { for_each = var.iam service_account_id = local.service_account.name diff --git a/modules/iam-service-account/outputs.tf b/modules/iam-service-account/outputs.tf index 45faa5f3..cf12f530 100644 --- a/modules/iam-service-account/outputs.tf +++ b/modules/iam-service-account/outputs.tf @@ -40,3 +40,8 @@ output "service_account" { description = "Service account resource." value = local.service_account } + +output "service_account_credentials" { + description = "Service account json credential templates for uploaded public keys data." + value = local.service_account_credential_templates +} diff --git a/modules/iam-service-account/variables.tf b/modules/iam-service-account/variables.tf index 59579c7a..d1f5d3e3 100644 --- a/modules/iam-service-account/variables.tf +++ b/modules/iam-service-account/variables.tf @@ -84,6 +84,12 @@ variable "project_id" { type = string } +variable "public_keys_directory" { + description = "Path to public keys data files to upload to the service account (should have `.pem` extension)." + type = string + default = "" +} + variable "service_account_create" { description = "Create service account. When set to false, uses a data source to reference an existing service account." type = bool From 2d9c2fe77483a6b64bafa991022c5d640a58d0a7 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Mon, 6 Dec 2021 17:09:48 +0100 Subject: [PATCH 02/52] Fix typo in the SA module readme --- modules/iam-service-account/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index 0a3b57d9..a7c0cb0f 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -1,6 +1,6 @@ # Google Service Account Module -This module allows simplified creation and management of one a service account and its IAM bindings. A key can optionally be generated and will be stored in Terraform state. To use it create a sensitive output in your root modules referencing the `key` output, then extract the private key from the JSON formatted outputs. Alternatively, the `key` can be generated with `openssl` library and only public part uploaded to the Service Account, for more refere to the [Onprem SA Key Management](../../cloud-operations/onprem-sa-key-management/README.md)) example. +This module allows simplified creation and management of one a service account and its IAM bindings. A key can optionally be generated and will be stored in Terraform state. To use it create a sensitive output in your root modules referencing the `key` output, then extract the private key from the JSON formatted outputs. Alternatively, the `key` can be generated with `openssl` library and only public part uploaded to the Service Account, for more refer to the [Onprem SA Key Management](../../cloud-operations/onprem-sa-key-management/) example. ## Example From 0e5fdda107e448053da1c671e09ee3f58c94d1f2 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Mon, 6 Dec 2021 17:23:53 +0100 Subject: [PATCH 03/52] TF fmt --- .../onprem-sa-key-management/outputs.tf | 4 +-- modules/iam-service-account/main.tf | 30 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cloud-operations/onprem-sa-key-management/outputs.tf b/cloud-operations/onprem-sa-key-management/outputs.tf index 539f27fd..314bb2e2 100644 --- a/cloud-operations/onprem-sa-key-management/outputs.tf +++ b/cloud-operations/onprem-sa-key-management/outputs.tf @@ -16,10 +16,10 @@ output "data-uploader-credentials" { description = "Data Uploader SA json key templates." - value = module.onprem-data-uploader.service_account_credentials + value = module.onprem-data-uploader.service_account_credentials } output "prisma-security-credentials" { description = "Prisma Security SA json key templates." - value = module.onprem-prisma-security.service_account_credentials + value = module.onprem-prisma-security.service_account_credentials } diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf index 005ee12c..dc0ee3b4 100644 --- a/modules/iam-service-account/main.tf +++ b/modules/iam-service-account/main.tf @@ -66,26 +66,26 @@ locals { : try(data.google_service_account.service_account.0, null) ) service_account_credential_templates = { - for file, _ in local.public_keys_data : file => jsonencode( + for file, _ in local.public_keys_data : file => jsonencode( { - type: "service_account", - project_id: var.project_id, - private_key_id: split("/",google_service_account_key.upload_key[file].id)[5] - private_key: "REPLASE_ME_WITH_PRIVATE_KEY_DATA" - client_email: local.resource_email_static - client_id: local.service_account.unique_id, - auth_uri: "https://accounts.google.com/o/oauth2/auth", - token_uri: "https://oauth2.googleapis.com/token", - auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs", - client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/${urlencode(local.resource_email_static)}" + type : "service_account", + project_id : var.project_id, + private_key_id : split("/", google_service_account_key.upload_key[file].id)[5] + private_key : "REPLASE_ME_WITH_PRIVATE_KEY_DATA" + client_email : local.resource_email_static + client_id : local.service_account.unique_id, + auth_uri : "https://accounts.google.com/o/oauth2/auth", + token_uri : "https://oauth2.googleapis.com/token", + auth_provider_x509_cert_url : "https://www.googleapis.com/oauth2/v1/certs", + client_x509_cert_url : "https://www.googleapis.com/robot/v1/metadata/x509/${urlencode(local.resource_email_static)}" } ) } public_keys_data = ( - var.public_keys_directory != "" - ? { - for file in fileset("${path.root}/${var.public_keys_directory}", "*.pem") - : file => filebase64("${path.root}/${var.public_keys_directory}/${file}")} + var.public_keys_directory != "" + ? { + for file in fileset("${path.root}/${var.public_keys_directory}", "*.pem") + : file => filebase64("${path.root}/${var.public_keys_directory}/${file}") } : {} ) } From 4fd1ccb982de33de2a5e5f52832586bba1237a5a Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Mon, 6 Dec 2021 17:30:56 +0100 Subject: [PATCH 04/52] Update iam-sa docs --- modules/iam-service-account/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index a7c0cb0f..a1b1ed9c 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -42,7 +42,7 @@ module "myproject-default-service-accounts" { | *iam_project_roles* | Project roles granted to the service account, by project id. | map(list(string)) | | {} | | *iam_storage_roles* | Storage roles granted to the service account, by bucket name. | map(list(string)) | | {} | | *prefix* | Prefix applied to service account names. | string | | null | -| *public_keys_directory* | Path to public keys data files to upload to the service account (should have `.crt` extension). | string | | | +| *public_keys_directory* | Path to public keys data files to upload to the service account (should have `.pem` extension). | string | | | | *service_account_create* | Create service account. When set to false, uses a data source to reference an existing service account. | bool | | true | ## Outputs From 50b88fe2885f556d73b16771a998f69f7a43a8f8 Mon Sep 17 00:00:00 2001 From: averbukh Date: Mon, 13 Dec 2021 22:36:42 +0100 Subject: [PATCH 05/52] Finalize onprem-sa-ket-mgmt example --- README.md | 2 +- cloud-operations/README.md | 7 ++ .../onprem-sa-key-management/README.md | 72 ++++++++++++------- .../cloud-shell-readme.txt | 45 +++++++++++- .../onprem-sa-key-management/main.tf | 1 - .../onprem-sa-key-management/variables.tf | 2 +- 6 files changed, 100 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 6ff7f999..4e5a0055 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Currently available examples: - **foundations** - [single level hierarchy](./foundations/environments/) (environments), [multiple level hierarchy](./foundations/business-units/) (business units + environments) - **networking** - [hub and spoke via peering](./networking/hub-and-spoke-peering/), [hub and spoke via VPN](./networking/hub-and-spoke-vpn/), [DNS and Google Private Access for on-premises](./networking/onprem-google-access-dns/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [ILB as next hop](./networking/ilb-next-hop), [PSC for on-premises Cloud Function invocation](./networking/private-cloud-function-from-onprem/), [decentralized firewall](./networking/decentralized-firewall) - **data solutions** - [GCE/GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms/), [Cloud Storage to Bigquery with Cloud Dataflow](./data-solutions/gcs-to-bq-with-dataflow/) -- **cloud operations** - [Resource tracking and remediation via Cloud Asset feeds](.//cloud-operations/asset-inventory-feed-remediation), [Granular Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Granular Cloud DNS IAM for Shared VPC](./cloud-operations/dns-shared-vpc), [Compute Engine quota monitoring](./cloud-operations/quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Packer image builder](./cloud-operations/packer-image-builder) +- **cloud operations** - [Resource tracking and remediation via Cloud Asset feeds](.//cloud-operations/asset-inventory-feed-remediation), [Granular Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Granular Cloud DNS IAM for Shared VPC](./cloud-operations/dns-shared-vpc), [Compute Engine quota monitoring](./cloud-operations/quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Packer image builder](./cloud-operations/packer-image-builder), [On-prem SA key management](./cloud-operations/onprem-sa-key-management) - **third party solutions** - [OpenShift cluster on Shared VPC](./third-party-solutions/openshift) - **factories** - [Example environments](./factories/example-environments), [Hierarchical Firewall Policies](./factories/firewall-hierarchical-policies), [VPC Firewall Rules](./factories/firewall-vpc-rules), [Subnets](./factories/subnets) diff --git a/cloud-operations/README.md b/cloud-operations/README.md index 57cad5e1..fb8fe9e9 100644 --- a/cloud-operations/README.md +++ b/cloud-operations/README.md @@ -45,3 +45,10 @@ The example's feed tracks changes to Google Compute instances, and the Cloud Fun This [example](./packer-image-builder) shows how to deploy infrastructure for a Compute Engine image builder based on [Hashicorp's Packer tool](https://www.packer.io).
+ +## On-prem Service Account key management + + +This [example](./onprem-sa-key-management) shows how to manage IAM Service Account Keys by generating a key pair and uploading the public part of the key to GCP. + +
\ No newline at end of file diff --git a/cloud-operations/onprem-sa-key-management/README.md b/cloud-operations/onprem-sa-key-management/README.md index 6661133d..ec26e80b 100644 --- a/cloud-operations/onprem-sa-key-management/README.md +++ b/cloud-operations/onprem-sa-key-management/README.md @@ -1,41 +1,65 @@ -# Generationg and uploading public keys for a service accounts +# Managing on-prem service account keys by uploading public keys -This example shows how to manage IAM Service Account Keys by generating a key pair and uploading public keys to GCP. +When managing GCP Service Accounts with terraform, it's often a question on **how to avoid Service Account Key in the terraform state?** + +This example shows how to manage IAM Service Account Keys by generating a key pair and uploading the public part of the key to GCP, it has the following benefits: + + - no [passing keys between users](https://cloud.google.com/iam/docs/best-practices-for-managing-service-account-keys#pass-between-users) or systems + - no SA key stored in the terraform state (only public part of the key in the state) + - let keys [expire automatically](https://cloud.google.com/iam/docs/best-practices-for-managing-service-account-keys#key-expiryhaving) -By generating a key inside a `box` where the key is intended to be used we AVOID: - - [passing keys between users](https://cloud.google.com/iam/docs/best-practices-for-managing-service-account-keys#pass-between-users) or systems - - having SA key stored in the terraform state (only public part in the state) - - having SA key with no expiration period -TODO (averbukh) ## Running the example -# cleaning up example keys -- rm -f /public-keys/data-uploader/ -- rm -f /public-keys/prisma-security/ -# generate your keys -- mkdir keys && cd keys -- openssl req -x509 -nodes -newkey rsa:2048 -days 3650 \ +Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2Fterraform-google-modules%2Fcloud-foundation-fabric&cloudshell_print=cloud-shell-readme.txt&cloudshell_working_dir=cloud-operations%2Fonprem-sa-key-management&cloudshell_open_in_editor=cloudshell_open%2Fcloud-foundation-fabric%2Fcloud-operations%2Fonprem-sa-key-management%2Fvariables.tf), then go through the following steps to create resources: + +Cleaning up example keys +```bash +rm -f /public-keys/data-uploader/ +rm -f /public-keys/prisma-security/ +``` + +Generate keys for service accounts +```bash +mkdir keys && cd keys +openssl req -x509 -nodes -newkey rsa:2048 -days 30 \ -keyout data_uploader_private_key.pem \ -out ../public-keys/data-uploader/public_key.pem \ -subj "/CN=unused" -- openssl req -x509 -nodes -newkey rsa:2048 -days 3650 \ +openssl req -x509 -nodes -newkey rsa:2048 -days 30 \ -keyout prisma_security_private_key.pem \ -out ../public-keys/prisma-security/public_key.pem \ -subj "/CN=unused" +``` -- cd .. -- terraform init -- terraform apply -var project_id=$GOOGLE_CLOUD_PROJECT +Deploy service accounts and keys +```bash +cd .. +terraform init +terraform apply -var project_id=$GOOGLE_CLOUD_PROJECT -- terraform show -json | jq '.values.outputs."data-uploader-credentials".value."public_key.pem" | fromjson' > data-uploader.json -- terraform show -json | jq '.values.outputs."prisma-security-credentials".value."public_key.pem" | fromjson' > prisma-security.json +``` -- contents=$(jq --arg key "$(cat keys/data_uploader_private_key.pem)" '.private_key=$key' data-uploader.json) && echo "$contents" > data-uploader.json -- contents=$(jq --arg key "$(cat keys/prisma_security_private_key.pem)" '.private_key=$key' prisma-security.json) && echo "$contents" > prisma-security.json +Extract JSON credentials templates from terraform output and put the private part of the keys into templates +```bash +terraform show -json | jq '.values.outputs."data-uploader-credentials".value."public_key.pem" | fromjson' > data-uploader.json +terraform show -json | jq '.values.outputs."prisma-security-credentials".value."public_key.pem" | fromjson' > prisma-security.json -- gcloud auth activate-service-account --key-file prisma-security.json -- gcloud auth activate-service-account --key-file data-uploader.json +contents=$(jq --arg key "$(cat keys/data_uploader_private_key.pem)" '.private_key=$key' data-uploader.json) && echo "$contents" > data-uploader.json +contents=$(jq --arg key "$(cat keys/prisma_security_private_key.pem)" '.private_key=$key' prisma-security.json) && echo "$contents" > prisma-security.json +``` + +## Testing the example +Validate that service accounts json credentials are valid +```bash +gcloud auth activate-service-account --key-file prisma-security.json +gcloud auth activate-service-account --key-file data-uploader.json +``` + +## Cleaning up +```bash +terraform destroy -var project_id=$GOOGLE_CLOUD_PROJECT +``` ## Variables @@ -43,7 +67,7 @@ TODO (averbukh) | name | description | type | required | default | |---|---|:---: |:---:|:---:| | project_id | Project id. | string | ✓ | | -| *project_create* | Create project instead ofusing an existing one. | bool | | false | +| *project_create* | Create project instead of using an existing one. | bool | | false | ## Outputs diff --git a/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt b/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt index da5b32f0..167df24a 100644 --- a/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt +++ b/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt @@ -1,5 +1,46 @@ ################################# Quickstart ################################# -TODO(averbukh) -Refer to the README.md file for more info and testing flow. + +# cleaning up example keys + +- rm -f /public-keys/data-uploader/ +- rm -f /public-keys/prisma-security/ + +# generate keys for service accounts + +- mkdir keys && cd keys +- openssl req -x509 -nodes -newkey rsa:2048 -days 30 \ + -keyout data_uploader_private_key.pem \ + -out ../public-keys/data-uploader/public_key.pem \ + -subj "/CN=unused" +- openssl req -x509 -nodes -newkey rsa:2048 -days 30 \ + -keyout prisma_security_private_key.pem \ + -out ../public-keys/prisma-security/public_key.pem \ + -subj "/CN=unused" + +# deploy service accounts and keys + +- cd .. +- terraform init +- terraform apply -var project_id=$GOOGLE_CLOUD_PROJECT + + +# extract JSON credentials templates from terraform output and put the private part of the keys into templates + +- terraform show -json | jq '.values.outputs."data-uploader-credentials".value."public_key.pem" | fromjson' > data-uploader.json +- terraform show -json | jq '.values.outputs."prisma-security-credentials".value."public_key.pem" | fromjson' > prisma-security.json + +- contents=$(jq --arg key "$(cat keys/data_uploader_private_key.pem)" '.private_key=$key' data-uploader.json) && echo "$contents" > data-uploader.json +- contents=$(jq --arg key "$(cat keys/prisma_security_private_key.pem)" '.private_key=$key' prisma-security.json) && echo "$contents" > prisma-security.json + + + +# validate that service accounts json credentials are valid + +- gcloud auth activate-service-account --key-file prisma-security.json +- gcloud auth activate-service-account --key-file data-uploader.json + + +# cleaning up +- terraform destroy -var project_id=$GOOGLE_CLOUD_PROJECT diff --git a/cloud-operations/onprem-sa-key-management/main.tf b/cloud-operations/onprem-sa-key-management/main.tf index 302574d8..b925cf19 100644 --- a/cloud-operations/onprem-sa-key-management/main.tf +++ b/cloud-operations/onprem-sa-key-management/main.tf @@ -14,7 +14,6 @@ * limitations under the License. */ - module "project" { source = "../../modules/project" name = var.project_id diff --git a/cloud-operations/onprem-sa-key-management/variables.tf b/cloud-operations/onprem-sa-key-management/variables.tf index 3740814b..e80fbd0c 100644 --- a/cloud-operations/onprem-sa-key-management/variables.tf +++ b/cloud-operations/onprem-sa-key-management/variables.tf @@ -15,7 +15,7 @@ */ variable "project_create" { - description = "Create project instead ofusing an existing one." + description = "Create project instead of using an existing one." type = bool default = false } From ae4d8e0611e6531ca20481c3a683a079bc7116d9 Mon Sep 17 00:00:00 2001 From: averbukh Date: Mon, 13 Dec 2021 22:48:18 +0100 Subject: [PATCH 06/52] Add basic test for onprem-sa-mgmt example --- .../onprem_sa_key_management/__init__.py | 13 +++++++++ .../onprem_sa_key_management/fixture/main.tf | 21 +++++++++++++++ .../fixture/variables.tf | 23 ++++++++++++++++ .../onprem_sa_key_management/test_plan.py | 27 +++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 tests/cloud_operations/onprem_sa_key_management/__init__.py create mode 100644 tests/cloud_operations/onprem_sa_key_management/fixture/main.tf create mode 100644 tests/cloud_operations/onprem_sa_key_management/fixture/variables.tf create mode 100644 tests/cloud_operations/onprem_sa_key_management/test_plan.py diff --git a/tests/cloud_operations/onprem_sa_key_management/__init__.py b/tests/cloud_operations/onprem_sa_key_management/__init__.py new file mode 100644 index 00000000..d46dbae5 --- /dev/null +++ b/tests/cloud_operations/onprem_sa_key_management/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2021 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/cloud_operations/onprem_sa_key_management/fixture/main.tf b/tests/cloud_operations/onprem_sa_key_management/fixture/main.tf new file mode 100644 index 00000000..1f609552 --- /dev/null +++ b/tests/cloud_operations/onprem_sa_key_management/fixture/main.tf @@ -0,0 +1,21 @@ +/** + * Copyright 2021 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 = "../../../../cloud-operations/onprem-sa-key-management" + project_create = var.project_create + project_id = var.project_id +} diff --git a/tests/cloud_operations/onprem_sa_key_management/fixture/variables.tf b/tests/cloud_operations/onprem_sa_key_management/fixture/variables.tf new file mode 100644 index 00000000..7014ccb5 --- /dev/null +++ b/tests/cloud_operations/onprem_sa_key_management/fixture/variables.tf @@ -0,0 +1,23 @@ +# Copyright 2021 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. + +variable "project_create" { + type = bool + default = true +} + +variable "project_id" { + type = string + default = "test" +} diff --git a/tests/cloud_operations/onprem_sa_key_management/test_plan.py b/tests/cloud_operations/onprem_sa_key_management/test_plan.py new file mode 100644 index 00000000..7de84db5 --- /dev/null +++ b/tests/cloud_operations/onprem_sa_key_management/test_plan.py @@ -0,0 +1,27 @@ +# Copyright 2021 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_resources(e2e_plan_runner): + "Test that plan works and the numbers of resources is as expected." + modules, resources = e2e_plan_runner(FIXTURES_DIR) + assert len(modules) == 3 + assert len(resources) == 7 From 6bd4b8021a0fbeca0fb3397f44359a34fb2a267e Mon Sep 17 00:00:00 2001 From: averbukh Date: Wed, 15 Dec 2021 11:07:22 +0100 Subject: [PATCH 07/52] Refactoring --- .../onprem-sa-key-management/README.md | 9 ++--- .../cloud-shell-readme.txt | 4 +-- .../onprem-sa-key-management/main.tf | 30 ++++++---------- .../onprem-sa-key-management/outputs.tf | 11 ++---- .../onprem-sa-key-management/variables.tf | 34 +++++++++++++++++++ 5 files changed, 54 insertions(+), 34 deletions(-) diff --git a/cloud-operations/onprem-sa-key-management/README.md b/cloud-operations/onprem-sa-key-management/README.md index ec26e80b..c16c56db 100644 --- a/cloud-operations/onprem-sa-key-management/README.md +++ b/cloud-operations/onprem-sa-key-management/README.md @@ -42,8 +42,8 @@ terraform apply -var project_id=$GOOGLE_CLOUD_PROJECT Extract JSON credentials templates from terraform output and put the private part of the keys into templates ```bash -terraform show -json | jq '.values.outputs."data-uploader-credentials".value."public_key.pem" | fromjson' > data-uploader.json -terraform show -json | jq '.values.outputs."prisma-security-credentials".value."public_key.pem" | fromjson' > prisma-security.json +terraform show -json | jq '.values.outputs."sa-credentials".value."data-uploader"."public_key.pem" | fromjson' > data-uploader.json +terraform show -json | jq '.values.outputs."sa-credentials".value."prisma-security"."public_key.pem" | fromjson' > prisma-security.json contents=$(jq --arg key "$(cat keys/data_uploader_private_key.pem)" '.private_key=$key' data-uploader.json) && echo "$contents" > data-uploader.json contents=$(jq --arg key "$(cat keys/prisma_security_private_key.pem)" '.private_key=$key' prisma-security.json) && echo "$contents" > prisma-security.json @@ -68,11 +68,12 @@ terraform destroy -var project_id=$GOOGLE_CLOUD_PROJECT |---|---|:---: |:---:|:---:| | project_id | Project id. | string | ✓ | | | *project_create* | Create project instead of using an existing one. | bool | | false | +| *service_accounts* | List of service accounts. | list(object({...})) | | ... | +| *services* | Service APIs to enable. | list(string) | | [] | ## Outputs | name | description | sensitive | |---|---|:---:| -| data-uploader-credentials | Data Uploader SA json key templates. | | -| prisma-security-credentials | Prisma Security SA json key templates. | | +| sa-credentials | SA json key templates. | | diff --git a/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt b/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt index 167df24a..ff75626a 100644 --- a/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt +++ b/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt @@ -28,8 +28,8 @@ # extract JSON credentials templates from terraform output and put the private part of the keys into templates -- terraform show -json | jq '.values.outputs."data-uploader-credentials".value."public_key.pem" | fromjson' > data-uploader.json -- terraform show -json | jq '.values.outputs."prisma-security-credentials".value."public_key.pem" | fromjson' > prisma-security.json +- terraform show -json | jq '.values.outputs."sa-credentials".value."data-uploader"."public_key.pem" | fromjson' > data-uploader.json +- terraform show -json | jq '.values.outputs."sa-credentials".value."prisma-security"."public_key.pem" | fromjson' > prisma-security.json - contents=$(jq --arg key "$(cat keys/data_uploader_private_key.pem)" '.private_key=$key' data-uploader.json) && echo "$contents" > data-uploader.json - contents=$(jq --arg key "$(cat keys/prisma_security_private_key.pem)" '.private_key=$key' prisma-security.json) && echo "$contents" > prisma-security.json diff --git a/cloud-operations/onprem-sa-key-management/main.tf b/cloud-operations/onprem-sa-key-management/main.tf index b925cf19..e4ffe168 100644 --- a/cloud-operations/onprem-sa-key-management/main.tf +++ b/cloud-operations/onprem-sa-key-management/main.tf @@ -14,34 +14,24 @@ * limitations under the License. */ +locals { + service_accounts = { for sa in var.service_accounts : sa.name => sa } +} + module "project" { source = "../../modules/project" name = var.project_id project_create = var.project_create + services = var.services } -module "onprem-data-uploader" { +module "integration-sa" { source = "../../modules/iam-service-account" + for_each = local.service_accounts project_id = module.project.project_id - name = "onprem-data-uploader" + name = each.value.name iam_project_roles = { - (module.project.project_id) = [ - "roles/bigquery.dataOwner", - "roles/bigquery.jobUser", - "roles/storage.objectAdmin" - ] + (module.project.project_id) = each.value.iam_project_roles } - public_keys_directory = "public-keys/data-uploader/" -} - -module "onprem-prisma-security" { - source = "../../modules/iam-service-account" - project_id = module.project.project_id - name = "onprem-prisma-security" - iam_project_roles = { - (module.project.project_id) = [ - "roles/iam.securityReviewer" - ] - } - public_keys_directory = "public-keys/prisma-security/" + public_keys_directory = each.value.public_keys_path } diff --git a/cloud-operations/onprem-sa-key-management/outputs.tf b/cloud-operations/onprem-sa-key-management/outputs.tf index 314bb2e2..a8f9c16a 100644 --- a/cloud-operations/onprem-sa-key-management/outputs.tf +++ b/cloud-operations/onprem-sa-key-management/outputs.tf @@ -14,12 +14,7 @@ * limitations under the License. */ -output "data-uploader-credentials" { - description = "Data Uploader SA json key templates." - value = module.onprem-data-uploader.service_account_credentials -} - -output "prisma-security-credentials" { - description = "Prisma Security SA json key templates." - value = module.onprem-prisma-security.service_account_credentials +output "sa-credentials" { + description = "SA json key templates." + value = { for key, value in module.integration-sa : key => value.service_account_credentials } } diff --git a/cloud-operations/onprem-sa-key-management/variables.tf b/cloud-operations/onprem-sa-key-management/variables.tf index e80fbd0c..5ad2390b 100644 --- a/cloud-operations/onprem-sa-key-management/variables.tf +++ b/cloud-operations/onprem-sa-key-management/variables.tf @@ -24,3 +24,37 @@ variable "project_id" { description = "Project id." type = string } + +variable "services" { + description = "Service APIs to enable." + type = list(string) + default = [] +} + +variable "service_accounts" { + description = "List of service accounts." + type = list(object({ + name = string + iam_project_roles = list(string) + public_keys_path = string + })) + default = [ + { + name = "data-uploader" + iam_project_roles = [ + "roles/bigquery.dataOwner", + "roles/bigquery.jobUser", + "roles/storage.objectAdmin" + ] + public_keys_path = "public-keys/data-uploader/" + }, + { + name = "prisma-security" + iam_project_roles = [ + "roles/iam.securityReviewer" + ] + public_keys_path = "public-keys/prisma-security/" + }, + ] + +} From 52878c15642f83de1818223f0f6d19456d7fa02d Mon Sep 17 00:00:00 2001 From: averbukh Date: Wed, 15 Dec 2021 11:13:18 +0100 Subject: [PATCH 08/52] Reorder variables --- .../onprem-sa-key-management/variables.tf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cloud-operations/onprem-sa-key-management/variables.tf b/cloud-operations/onprem-sa-key-management/variables.tf index 5ad2390b..60f893c5 100644 --- a/cloud-operations/onprem-sa-key-management/variables.tf +++ b/cloud-operations/onprem-sa-key-management/variables.tf @@ -25,12 +25,6 @@ variable "project_id" { type = string } -variable "services" { - description = "Service APIs to enable." - type = list(string) - default = [] -} - variable "service_accounts" { description = "List of service accounts." type = list(object({ @@ -58,3 +52,9 @@ variable "service_accounts" { ] } + +variable "services" { + description = "Service APIs to enable." + type = list(string) + default = [] +} From 36b277222db25b89eab86d7638bb0d8af03b5e59 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Wed, 15 Dec 2021 15:12:43 +0100 Subject: [PATCH 09/52] Bump tftest version Bump tftest version to 1.6.1 and ensure test runners can update providers if needed and available. --- tests/conftest.py | 4 ++-- tests/requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 2a90f925..33c63596 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -30,7 +30,7 @@ def _plan_runner(): "Runs Terraform plan and returns parsed output." tf = tftest.TerraformTest(fixture_path, BASEDIR, os.environ.get('TERRAFORM', 'terraform')) - tf.setup() + tf.setup(upgrade=True) return tf.plan(output=True, refresh=refresh, tf_vars=tf_vars, targets=targets) return run_plan @@ -94,7 +94,7 @@ def apply_runner(): "Runs Terraform apply and returns parsed output" tf = tftest.TerraformTest(fixture_path, BASEDIR, os.environ.get('TERRAFORM', 'terraform')) - tf.setup() + tf.setup(upgrade=True) apply = tf.apply(tf_vars=tf_vars) output = tf.output(json_format=True) return apply, output diff --git a/tests/requirements.txt b/tests/requirements.txt index 1b4d9545..480dbeb5 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,4 +1,4 @@ pytest>=4.6.0 PyYAML>=5.3 -tftest>=1.5.2 +tftest>=1.6.1 marko>=0.9.1 From 39b77569587b46ba4a3156392add96f0db4d48fb Mon Sep 17 00:00:00 2001 From: averbukh Date: Wed, 15 Dec 2021 18:26:44 +0100 Subject: [PATCH 10/52] Rewording, fix typos --- cloud-operations/README.md | 4 ++-- cloud-operations/onprem-sa-key-management/README.md | 4 ++-- modules/iam-service-account/main.tf | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cloud-operations/README.md b/cloud-operations/README.md index fb8fe9e9..1a491e94 100644 --- a/cloud-operations/README.md +++ b/cloud-operations/README.md @@ -49,6 +49,6 @@ The example's feed tracks changes to Google Compute instances, and the Cloud Fun ## On-prem Service Account key management -This [example](./onprem-sa-key-management) shows how to manage IAM Service Account Keys by generating a key pair and uploading the public part of the key to GCP. - +This [example](./onprem-sa-key-management) shows how to manage IAM Service Account Keys by manually generating a key pair and uploading the public part of the key to GCP. +s
\ No newline at end of file diff --git a/cloud-operations/onprem-sa-key-management/README.md b/cloud-operations/onprem-sa-key-management/README.md index c16c56db..1d8d8f87 100644 --- a/cloud-operations/onprem-sa-key-management/README.md +++ b/cloud-operations/onprem-sa-key-management/README.md @@ -2,10 +2,10 @@ When managing GCP Service Accounts with terraform, it's often a question on **how to avoid Service Account Key in the terraform state?** -This example shows how to manage IAM Service Account Keys by generating a key pair and uploading the public part of the key to GCP, it has the following benefits: +This example shows how to manage IAM Service Account Keys by manually generating a key pair and uploading the public part of the key to GCP. It has the following benefits: - no [passing keys between users](https://cloud.google.com/iam/docs/best-practices-for-managing-service-account-keys#pass-between-users) or systems - - no SA key stored in the terraform state (only public part of the key in the state) + - no private keys stored in the terraform state (only public part of the key is in the state) - let keys [expire automatically](https://cloud.google.com/iam/docs/best-practices-for-managing-service-account-keys#key-expiryhaving) diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf index dc0ee3b4..71955908 100644 --- a/modules/iam-service-account/main.tf +++ b/modules/iam-service-account/main.tf @@ -71,7 +71,7 @@ locals { type : "service_account", project_id : var.project_id, private_key_id : split("/", google_service_account_key.upload_key[file].id)[5] - private_key : "REPLASE_ME_WITH_PRIVATE_KEY_DATA" + private_key : "REPLACE_ME_WITH_PRIVATE_KEY_DATA" client_email : local.resource_email_static client_id : local.service_account.unique_id, auth_uri : "https://accounts.google.com/o/oauth2/auth", From 976fabdf86163cf958fe0ee9cb3b010bd5df61c8 Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 16 Dec 2021 15:39:49 +0800 Subject: [PATCH 11/52] Add bq_table_overwrite handling --- .../cf/main.py | 22 +++++++++++-------- .../main.tf | 11 +++++----- .../variables.tf | 7 +++--- .../fixture/variables.tf | 15 ++++++++----- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py b/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py index ad97c326..9f9cfb3f 100755 --- a/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py @@ -50,18 +50,19 @@ def _configure_logging(verbose=True): @click.option('--bq-project', required=True, help='Bigquery project to use.') @click.option('--bq-dataset', required=True, help='Bigquery dataset to use.') @click.option('--bq-table', required=True, help='Bigquery table name to use.') +@click.option('--bq-table-overwrite', required=True, help='Overwrite existing BQ table or create new datetime() one.') @click.option('--target-node', required=True, help='Node in Google Cloud resource hierarchy.') @click.option('--read-time', required=False, help=( 'Day to take an asset snapshot in \'YYYYMMDD\' format, uses current day ' ' as default. Export will run at midnight of the specified day.')) @click.option('--verbose', is_flag=True, help='Verbose output') -def main_cli(project=None, bq_project=None, bq_dataset=None, bq_table=None, target_node=None, +def main_cli(project=None, bq_project=None, bq_dataset=None, bq_table=None, bq_table_overwrite=None, target_node=None, read_time=None, verbose=False): '''Trigger Cloud Asset inventory export to Bigquery. Data will be stored in the dataset specified on a dated table with the name specified. ''' try: - _main(project, bq_project, bq_dataset, bq_table, target_node, read_time, verbose) + _main(project, bq_project, bq_dataset, bq_table, bq_table_overwrite, target_node, read_time, verbose) except RuntimeError: logging.exception('exception raised') @@ -79,19 +80,22 @@ def main(event, context): logging.exception('exception in cloud function entry point') -def _main(project=None, bq_project=None, bq_dataset=None, bq_table=None, target_node=None, read_time=None, verbose=False): +def _main(project=None, bq_project=None, bq_dataset=None, bq_table=None, bq_table_overwrite=None, target_node=None, read_time=None, verbose=False): 'Module entry point used by cli and cloud function wrappers.' _configure_logging(verbose) - if not read_time: - read_time = datetime.datetime.now() - client = asset_v1.AssetServiceClient() - content_type = asset_v1.ContentType.RESOURCE output_config = asset_v1.OutputConfig() + client = asset_v1.AssetServiceClient() + if bq_table_overwrite == False: + read_time = datetime.datetime.now() + output_config.bigquery_destination.table = '%s_%s' % ( + bq_table, read_time.strftime('%Y%m%d')) + else: + output_config.bigquery_destination.table = '%s_latest' % ( + bq_table) + content_type = asset_v1.ContentType.RESOURCE output_config.bigquery_destination.dataset = 'projects/%s/datasets/%s' % ( bq_project, bq_dataset) - output_config.bigquery_destination.table = '%s_%s' % ( - bq_table, read_time.strftime('%Y%m%d')) output_config.bigquery_destination.separate_tables_per_asset_type = True output_config.bigquery_destination.force = True try: diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index 0052401d..6fb31afc 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -118,11 +118,12 @@ resource "google_cloud_scheduler_job" "job" { attributes = {} topic_name = module.pubsub.topic.id data = base64encode(jsonencode({ - project = module.project.project_id - bq_project = module.project.project_id - bq_dataset = var.cai_config.bq_dataset - bq_table = var.cai_config.bq_table - target_node = var.cai_config.target_node + project = module.project.project_id + bq_project = module.project.project_id + bq_dataset = var.cai_config.bq_dataset + bq_table = var.cai_config.bq_table + bq_table_overwrite = var.cai_config.bq_table_overwrite + target_node = var.cai_config.target_node })) } } diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf index 5bb62166..988674f3 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf @@ -29,9 +29,10 @@ variable "bundle_path" { variable "cai_config" { description = "Cloud Asset inventory export config." type = object({ - bq_dataset = string - bq_table = string - target_node = string + bq_dataset = string + bq_table = string + bq_table_overwrite = bool + target_node = string }) } diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf index 1d70f827..5c1e0ac5 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf @@ -19,17 +19,20 @@ variable "billing_account" { variable "cai_config" { type = object({ - bq_dataset = string - bq_table = string - target_node = string + bq_dataset = string + bq_table = string + bq_table_overwrite = bool + target_node = string }) default = { - bq_dataset = "my-dataset" - bq_table = "my_table" - target_node = "organization/1234567890" + bq_dataset = "my-dataset" + bq_table = "my_table" + bq_table_overwrite = "true" + target_node = "organization/1234567890" } } + variable "project_create" { type = bool default = true From b36688ec78226b4a3692d23977cf33b4928c5b96 Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 16 Dec 2021 17:54:38 +0800 Subject: [PATCH 12/52] Add optional BQ table as a file export config --- .../cffile/main.py | 96 ++++++++++++++++++ .../cffile/requirements.txt | 3 + .../main.tf | 55 ++++++++++ .../variables.tf | 26 ++++- .../fixture/bundle_cffile.zip | Bin 0 -> 131 bytes .../fixture/cffile/README | 0 .../fixture/main.tf | 1 + .../fixture/variables.tf | 17 ++++ .../test_plan.py | 4 +- 9 files changed, 199 insertions(+), 3 deletions(-) create mode 100755 cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/cffile/requirements.txt create mode 100644 tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/bundle_cffile.zip create mode 100644 tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/cffile/README diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py b/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py new file mode 100755 index 00000000..ec8572ce --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py @@ -0,0 +1,96 @@ +# Copyright 2021 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. + +'''Cloud Function module to export BQ table as JSON. + +This module is designed to be plugged in a Cloud Function, attached to Cloud +Scheduler trigger to create a JSON of IP to hostname mappings from BigQuery. + +''' + +import base64 +import datetime +import json +import logging +import os +import warnings + +from google.api_core.exceptions import GoogleAPIError +from google.cloud import bigquery + +import googleapiclient.discovery +import googleapiclient.errors + + +def _configure_logging(verbose=True): + '''Basic logging configuration. + Args: + verbose: enable verbose logging + ''' + level = logging.DEBUG if verbose else logging.INFO + logging.basicConfig(level=level) + warnings.filterwarnings('ignore', r'.*end user credentials.*', UserWarning) + + + +@click.command() +@click.option('--bucket', required=True, help='GCS bucket for export') +@click.option('--filename', required=True, help='Path and filename with extension to export e.g. folder/export.json .') +@click.option('--format', required=True, help='The exported file format, e.g. NEWLINE_DELIMITED_JSON or CSV.') +@click.option('--bq-dataset', required=True, help='Bigquery dataset where table for export is located.') +@click.option('--bq-table', required=True, help='Bigquery table to export.') +@click.option('--bq-table-overwrite', required=True, help='Overwrite existing BQ table or create new datetime() one.') +@click.option('--verbose', is_flag=True, help='Verbose output') +def main_cli(bucket=None, filename=None, format=None, bq_dataset=None, bq_table=None, verbose=False): + '''Trigger Cloud Asset inventory export from Bigquery to file. Data will be stored in + the dataset specified on a dated table with the name specified. + ''' + try: + _main(bucket, filename, format, bq_dataset, bq_table, verbose) + except RuntimeError: + logging.exception('exception raised') + +def main(event, context): + 'Cloud Function entry point.' + try: + data = json.loads(base64.b64decode(event['data']).decode('utf-8')) + print(data) + _main(**data) + # uncomment once https://issuetracker.google.com/issues/155215191 is fixed + # except RuntimeError: + # raise + except Exception: + logging.exception('exception in cloud function entry point') + + +def _main(bucket=None, filename=None, format=None, bq_dataset=None, bq_table=None, verbose=False): + 'Module entry point used by cli and cloud function wrappers.' + + _configure_logging(verbose) + client = bigquery.Client() + destination_uri = 'gs://{}/{}'.format(bucket, filename) + dataset_ref = client.dataset(bq_dataset) + table_ref = dataset_ref.table(bq_table) + job_config = bigquery.job.ExtractJobConfig() + job_config.destination_format = ( + "bigquery.DestinationFormat." + format) + extract_job = client.extract_table( + table_ref, destination_uri, job_config=job_config + ) + try: + extract_job.result() + except (GoogleAPIError, googleapiclient.errors.HttpError) as e: + logging.debug('API Error: %s', e, exc_info=True) + raise RuntimeError( + 'Error exporting BQ table %s as a file' % bq_table, e) \ No newline at end of file diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cffile/requirements.txt b/cloud-operations/scheduled-asset-inventory-export-bq/cffile/requirements.txt new file mode 100644 index 00000000..d48ebb54 --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cffile/requirements.txt @@ -0,0 +1,3 @@ +google-api-python-client>=1.10.1 +google-cloud-monitoring>=1.1.0 +google-cloud-bigquery \ No newline at end of file diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index 6fb31afc..e3752362 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -66,6 +66,17 @@ module "pubsub" { # at the project level via roles/cloudscheduler.serviceAgent } +module "pubsub_file" { + source = "../../modules/pubsub" + project_id = module.project.project_id + name = var.name_cffile + subscriptions = { + "${var.name_cffile}-default" = null + } + # the Cloud Scheduler robot service account already has pubsub.topics.publish + # at the project level via roles/cloudscheduler.serviceAgent +} + ############################################################################### # Cloud Function # ############################################################################### @@ -93,6 +104,29 @@ module "cf" { } } +module "cffile" { + source = "../../modules/cloud-function" + project_id = module.project.project_id + region = var.region + name = var.name_cffile + bucket_name = "${var.name_cffile}-${random_pet.random.id}" + bucket_config = { + location = var.region + lifecycle_delete_age = null + } + bundle_config = { + source_dir = "cffile" + output_path = var.bundle_path_cffile + excludes = null + } + service_account = module.service-account.email + trigger_config = { + event = "google.pubsub.topic.publish" + resource = module.pubsub_file.topic.id + retry = null + } +} + resource "random_pet" "random" { length = 1 } @@ -128,6 +162,27 @@ resource "google_cloud_scheduler_job" "job" { } } +resource "google_cloud_scheduler_job" "job_file" { + project = google_app_engine_application.app.project + region = var.region + name = "file-export-job" + description = "File export from BQ Job" + schedule = "* 9 * * 1" + time_zone = "Etc/UTC" + + pubsub_target { + attributes = {} + topic_name = module.pubsub_file.topic.id + data = base64encode(jsonencode({ + bucket = var.file_config.bucket + filename = var.file_config.filename + format = var.file_config.format + bq_dataset = var.file_config.bq_dataset + bq_table = var.file_config.bq_table + })) + } +} + ############################################################################### # Bigquery # ############################################################################### diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf index 988674f3..83823b90 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf @@ -26,8 +26,14 @@ variable "bundle_path" { default = "./bundle.zip" } +variable "bundle_path_cffile" { + description = "Path used to write the intermediate Cloud Function code bundle." + type = string + default = "./bundle_cffile.zip" +} + variable "cai_config" { - description = "Cloud Asset inventory export config." + description = "Cloud Asset Inventory export config." type = object({ bq_dataset = string bq_table = string @@ -36,6 +42,17 @@ variable "cai_config" { }) } +variable "file_config" { + description = "Optional BQ table as a file export function config." + type = object({ + bucket = string + filename = string + format = string + bq_dataset = string + bq_table = string + }) +} + variable "location" { description = "Appe Engine location used in the example." type = string @@ -49,6 +66,13 @@ variable "name" { default = "asset-inventory" } + +variable "name_cffile" { + description = "Arbitrary string used to name created resources." + type = string + default = "cffile-exporter" +} + variable "project_create" { description = "Create project instead ofusing an existing one." type = bool diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/bundle_cffile.zip b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/bundle_cffile.zip new file mode 100644 index 0000000000000000000000000000000000000000..454bc1f7c7b1d7756e78f008aa5a098485a9de00 GIT binary patch literal 131 zcmWIWW@Zs#-~d7f2E{HQ0S9bAR*t<8 literal 0 HcmV?d00001 diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/cffile/README b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/cffile/README new file mode 100644 index 00000000..e69de29b diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf index b892dadb..fd5e22d2 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf @@ -18,6 +18,7 @@ module "test" { source = "../../../../cloud-operations/scheduled-asset-inventory-export-bq" billing_account = var.billing_account cai_config = var.cai_config + file_config = var.file_config project_create = var.project_create project_id = var.project_id } diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf index 5c1e0ac5..8b5212f7 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf @@ -32,6 +32,23 @@ variable "cai_config" { } } +variable "file_config" { + type = object({ + bucket = string + filename = string + format = string + bq_dataset = string + bq_table = string + }) + default = { + bucket = "my-bucket" + filename = "my-folder/myfile.json" + format = "NEWLINE_DELIMITED_JSON" + bq_dataset = "my-dataset" + bq_table = "my_table" + } +} + variable "project_create" { type = bool diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py b/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py index de94c82d..f3d07509 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py @@ -23,5 +23,5 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') def test_resources(e2e_plan_runner): "Test that plan works and the numbers of resources is as expected." modules, resources = e2e_plan_runner(FIXTURES_DIR) - assert len(modules) == 5 - assert len(resources) == 23 + assert len(modules) == 7 + assert len(resources) == 28 From e428504d7f68d015616361f839945766becfcbe5 Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 16 Dec 2021 20:10:22 +0800 Subject: [PATCH 13/52] Add getattr() to retrieve attribute from format --- .../cffile/main.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py b/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py index ec8572ce..cb54b0bc 100755 --- a/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py @@ -29,6 +29,8 @@ import warnings from google.api_core.exceptions import GoogleAPIError from google.cloud import bigquery +import click + import googleapiclient.discovery import googleapiclient.errors @@ -42,15 +44,12 @@ def _configure_logging(verbose=True): logging.basicConfig(level=level) warnings.filterwarnings('ignore', r'.*end user credentials.*', UserWarning) - - @click.command() @click.option('--bucket', required=True, help='GCS bucket for export') @click.option('--filename', required=True, help='Path and filename with extension to export e.g. folder/export.json .') @click.option('--format', required=True, help='The exported file format, e.g. NEWLINE_DELIMITED_JSON or CSV.') @click.option('--bq-dataset', required=True, help='Bigquery dataset where table for export is located.') @click.option('--bq-table', required=True, help='Bigquery table to export.') -@click.option('--bq-table-overwrite', required=True, help='Overwrite existing BQ table or create new datetime() one.') @click.option('--verbose', is_flag=True, help='Verbose output') def main_cli(bucket=None, filename=None, format=None, bq_dataset=None, bq_table=None, verbose=False): '''Trigger Cloud Asset inventory export from Bigquery to file. Data will be stored in @@ -84,7 +83,7 @@ def _main(bucket=None, filename=None, format=None, bq_dataset=None, bq_table=Non table_ref = dataset_ref.table(bq_table) job_config = bigquery.job.ExtractJobConfig() job_config.destination_format = ( - "bigquery.DestinationFormat." + format) + getattr(bigquery.DestinationFormat, format) ) extract_job = client.extract_table( table_ref, destination_uri, job_config=job_config ) @@ -93,4 +92,8 @@ def _main(bucket=None, filename=None, format=None, bq_dataset=None, bq_table=Non except (GoogleAPIError, googleapiclient.errors.HttpError) as e: logging.debug('API Error: %s', e, exc_info=True) raise RuntimeError( - 'Error exporting BQ table %s as a file' % bq_table, e) \ No newline at end of file + 'Error exporting BQ table %s as a file' % bq_table, e) + + +if __name__ == '__main__': + main_cli() \ No newline at end of file From f23e314bea546f2aecc50fa28bbd65a7ac488408 Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 16 Dec 2021 20:27:26 +0800 Subject: [PATCH 14/52] Add roles/bigquery.jobUser for CF --- cloud-operations/scheduled-asset-inventory-export-bq/main.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index e3752362..ef27264f 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -47,6 +47,7 @@ module "service-account" { iam_project_roles = { (var.project_id) = [ "roles/cloudasset.owner", + "roles/bigquery.jobUser" ] } } From 6a42929df31ebdfcce1fae80411b332af2fb8b35 Mon Sep 17 00:00:00 2001 From: sergiotejon Date: Thu, 16 Dec 2021 13:32:17 +0100 Subject: [PATCH 15/52] Linux sysctls configuration and Kubelet config (#388) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Linux sysctls configuration and Kubelet config * Fix terraform linting issues * Updated README.md * Updated Kubelet config object type * Update readme Co-authored-by: Sergio Tejón Co-authored-by: Julio Castillo --- modules/gke-nodepool/README.md | 2 ++ modules/gke-nodepool/main.tf | 17 +++++++++++++++++ modules/gke-nodepool/variables.tf | 16 ++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/modules/gke-nodepool/README.md b/modules/gke-nodepool/README.md index 7757ddde..f5ea3a6e 100644 --- a/modules/gke-nodepool/README.md +++ b/modules/gke-nodepool/README.md @@ -45,6 +45,8 @@ module "cluster-1-nodepool-1" { | *autoscaling_config* | Optional autoscaling configuration. | object({...}) | | null | | *gke_version* | Kubernetes nodes version. Ignored if auto_upgrade is set in management_config. | string | | null | | *initial_node_count* | Initial number of nodes for the pool. | number | | 1 | +| *kubelet_config* | Kubelet configuration. | object({...}) | | null | +| *linux_node_config_sysctls* | Linux node configuration. | map(string) | | null | | *management_config* | Optional node management configuration. | object({...}) | | null | | *max_pods_per_node* | Maximum number of pods per node. | number | | null | | *name* | Optional nodepool name. | string | | null | diff --git a/modules/gke-nodepool/main.tf b/modules/gke-nodepool/main.tf index 585cbd23..536b31ae 100644 --- a/modules/gke-nodepool/main.tf +++ b/modules/gke-nodepool/main.tf @@ -144,6 +144,23 @@ resource "google_container_node_pool" "nodepool" { mode = var.workload_metadata_config } + dynamic "kubelet_config" { + for_each = var.kubelet_config != null ? [var.kubelet_config] : [] + iterator = config + content { + cpu_manager_policy = config.value.cpu_manager_policy + cpu_cfs_quota = config.value.cpu_cfs_quota + cpu_cfs_quota_period = config.value.cpu_cfs_quota_period + } + } + + dynamic "linux_node_config" { + for_each = var.linux_node_config_sysctls != null ? [var.linux_node_config_sysctls] : [] + iterator = config + content { + sysctls = config.value + } + } } dynamic "autoscaling" { diff --git a/modules/gke-nodepool/variables.tf b/modules/gke-nodepool/variables.tf index a162f2fd..0053d7aa 100644 --- a/modules/gke-nodepool/variables.tf +++ b/modules/gke-nodepool/variables.tf @@ -40,6 +40,22 @@ variable "initial_node_count" { default = 1 } +variable "kubelet_config" { + description = "Kubelet configuration." + type = object({ + cpu_cfs_quota = string + cpu_cfs_quota_period = string + cpu_manager_policy = string + }) + default = null +} + +variable "linux_node_config_sysctls" { + description = "Linux node configuration." + type = map(string) + default = null +} + variable "location" { description = "Cluster location." type = string From 9bbae283ba0bd48cbd3d5a6904a288b104fbbd8f Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 16 Dec 2021 21:27:51 +0800 Subject: [PATCH 16/52] Update test_plan and README --- .../scheduled-asset-inventory-export-bq/README.md | 14 +++++++++++++- .../variables.tf | 7 +++++++ .../test_plan.py | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index ef8bca88..69c6e589 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -38,17 +38,29 @@ Once resources are created, you can run queries on the data you exported on Bigq You can also create a dashboard connecting [Datalab](https://datastudio.google.com/) or any other BI tools of your choice to your Bigquery datase. +## File exporter for JSON, CSV. + +Regular file-based exports of Cloud Asset Inventory may be useful for scale-out network dependency discovery like [Planet Exporter](https://github.com/williamchanrico/planet-exporter) or to update asset tracking or configuration management workflows. Bigquery supports multiple [export formats](https://cloud.google.com/bigquery/docs/exporting-data#export_formats_and_compression_types) and one may upload objects to Storage Bucket using provided Cloud Function. Specify `job.DestinationFormat` as defined in [documentation](https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.job.DestinationFormat.html), e.g. `NEWLINE_DELIMITED_JSON`. + +It helps to create custom [scheduled query](https://cloud.google.com/bigquery/docs/scheduling-queries#console) for to comply with downstream systems' fields, and to time it with CAI export into BQ for freshness. See [sample queries](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery-sample-queries). + +Note: Cloud Function's Service Account needs write-capable IAM role on `bucket`. + + ## Variables | name | description | type | required | default | |---|---|:---: |:---:|:---:| -| cai_config | Cloud Asset inventory export config. | object({...}) | ✓ | | +| cai_config | Cloud Asset Inventory export config. | object({...}) | ✓ | | | project_id | Project id that references existing project. | string | ✓ | | | *billing_account* | Billing account id used as default for new projects. | string | | null | | *bundle_path* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle.zip | +| *bundle_path_cffile* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle_cffile.zip | +| *file_config* | Optional BQ table as a file export function config. | object({...}) | | ... | | *location* | Appe Engine location used in the example. | string | | europe-west | | *name* | Arbitrary string used to name created resources. | string | | asset-inventory | +| *name_cffile* | Arbitrary string used to name created resources. | string | | cffile-exporter | | *project_create* | Create project instead ofusing an existing one. | bool | | true | | *region* | Compute region used in the example. | string | | europe-west1 | | *root_node* | The resource name of the parent folder or organization for project creation, in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf index 83823b90..c2137d89 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf @@ -51,6 +51,13 @@ variable "file_config" { bq_dataset = string bq_table = string }) + default = { + bucket = null + filename = null + format = null + bq_dataset = null + bq_table = null + } } variable "location" { diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py b/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py index f3d07509..484496a5 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py @@ -24,4 +24,4 @@ def test_resources(e2e_plan_runner): "Test that plan works and the numbers of resources is as expected." modules, resources = e2e_plan_runner(FIXTURES_DIR) assert len(modules) == 7 - assert len(resources) == 28 + assert len(resources) == 29 From eeaca4023d8101cd70fccada6c42b376d18a705b Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Thu, 16 Dec 2021 17:47:25 +0100 Subject: [PATCH 17/52] net-address: Added support for PSA ranges --- modules/net-address/README.md | 19 +++++++++++++++++++ modules/net-address/main.tf | 15 ++++++++++++++- modules/net-address/outputs.tf | 12 ++++++++++++ modules/net-address/variables.tf | 10 ++++++++++ tests/modules/net_address/fixture/main.tf | 1 + .../modules/net_address/fixture/variables.tf | 9 +++++++++ tests/modules/net_address/test_plan.py | 10 ++++++++++ 7 files changed, 75 insertions(+), 1 deletion(-) diff --git a/modules/net-address/README.md b/modules/net-address/README.md index 01442bcd..f9f11fe0 100644 --- a/modules/net-address/README.md +++ b/modules/net-address/README.md @@ -47,6 +47,23 @@ module "addresses" { # tftest:modules=1:resources=2 ``` +### PSA addresses + +```hcl +module "addresses" { + source = "./modules/net-address" + project_id = var.project_id + psa_addresses = { + cloudsql-mysql = { + address = "10.10.10.0" + network = var.vpc.self_link + prefix_length = 24 + } + } +} +# tftest:modules=1:resources=1 +``` + ### PSC addresses ```hcl @@ -77,6 +94,7 @@ module "addresses" { | *global_addresses* | List of global addresses to create. | list(string) | | [] | | *internal_addresses* | Map of internal addresses to create, keyed by name. | map(object({...})) | | {} | | *internal_addresses_config* | Optional configuration for internal addresses, keyed by name. Unused options can be set to null. | map(object({...})) | | {} | +| *psa_addresses* | Map of internal addresses used for Private Service Access. | map(object({...})) | | {} | | *psc_addresses* | Map of internal addresses used for Private Service Connect. | map(object({...})) | | {} | ## Outputs @@ -86,5 +104,6 @@ module "addresses" { | external_addresses | Allocated external addresses. | | | global_addresses | Allocated global external addresses. | | | internal_addresses | Allocated internal addresses. | | +| psa_addresses | Allocated internal addresses for PSC endpoints. | | | psc_addresses | Allocated internal addresses for PSC endpoints. | | diff --git a/modules/net-address/main.tf b/modules/net-address/main.tf index 3d438628..972afa0b 100644 --- a/modules/net-address/main.tf +++ b/modules/net-address/main.tf @@ -50,9 +50,22 @@ resource "google_compute_global_address" "psc" { project = var.project_id name = each.key description = "Terraform managed." + address = try(each.value.address, null) address_type = "INTERNAL" network = each.value.network - address = try(each.value.address, null) purpose = "PRIVATE_SERVICE_CONNECT" # labels = lookup(var.internal_address_labels, each.key, {}) } + +resource "google_compute_global_address" "psa" { + for_each = var.psa_addresses + project = var.project_id + name = each.key + description = "Terraform managed." + address = each.value.address + address_type = "INTERNAL" + network = each.value.network + prefix_length = each.value.prefix_length + purpose = "VPC_PEERING" + # labels = lookup(var.internal_address_labels, each.key, {}) +} diff --git a/modules/net-address/outputs.tf b/modules/net-address/outputs.tf index 1f662b3a..54b7a556 100644 --- a/modules/net-address/outputs.tf +++ b/modules/net-address/outputs.tf @@ -47,6 +47,18 @@ output "internal_addresses" { } } +output "psa_addresses" { + description = "Allocated internal addresses for PSC endpoints." + value = { + for address in google_compute_global_address.psa : + address.name => { + address = address.address + prefix_length = address.prefix_length + self_link = address.self_link + } + } +} + output "psc_addresses" { description = "Allocated internal addresses for PSC endpoints." value = { diff --git a/modules/net-address/variables.tf b/modules/net-address/variables.tf index 01b2c38a..823d79a6 100644 --- a/modules/net-address/variables.tf +++ b/modules/net-address/variables.tf @@ -62,6 +62,16 @@ variable "project_id" { type = string } +variable "psa_addresses" { + description = "Map of internal addresses used for Private Service Access." + type = map(object({ + address = string + network = string + prefix_length = number + })) + default = {} +} + variable "psc_addresses" { description = "Map of internal addresses used for Private Service Connect." type = map(object({ diff --git a/tests/modules/net_address/fixture/main.tf b/tests/modules/net_address/fixture/main.tf index a2118749..5e62fe13 100644 --- a/tests/modules/net_address/fixture/main.tf +++ b/tests/modules/net_address/fixture/main.tf @@ -20,5 +20,6 @@ module "test" { global_addresses = var.global_addresses internal_addresses = var.internal_addresses internal_addresses_config = var.internal_addresses_config + psa_addresses = var.psa_addresses project_id = var.project_id } diff --git a/tests/modules/net_address/fixture/variables.tf b/tests/modules/net_address/fixture/variables.tf index 93393f55..32813859 100644 --- a/tests/modules/net_address/fixture/variables.tf +++ b/tests/modules/net_address/fixture/variables.tf @@ -45,3 +45,12 @@ variable "project_id" { type = string default = "my-project" } + +variable "psa_addresses" { + type = map(object({ + address = string + network = string + prefix_length = number + })) + default = {} +} diff --git a/tests/modules/net_address/test_plan.py b/tests/modules/net_address/test_plan.py index 02e9c765..6c6e413e 100644 --- a/tests/modules/net_address/test_plan.py +++ b/tests/modules/net_address/test_plan.py @@ -68,3 +68,13 @@ def test_internal_addresses_config(plan_runner): for r in resources] == ['10.0.0.2', None] assert [r['values'].get('purpose') for r in resources] == ['SHARED_LOADBALANCER_VIP', None] + + +def test_psa_config(plan_runner): + psa_addresses = '{cloudsql-mysql={address="10.199.0.0", network="foobar", prefix_length = 24}}' + _, resources = plan_runner(FIXTURES_DIR, + psa_addresses=psa_addresses) + assert set(r['values']['purpose'] + for r in resources) == set(['VPC_PEERING']) + assert set(r['values']['address'] + for r in resources) == set(['10.199.0.0']) From 794ca6cb6c94161133f5d9d7c9aacec654ea5101 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Thu, 16 Dec 2021 17:50:41 +0100 Subject: [PATCH 18/52] Update outputs.tf --- modules/net-address/outputs.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/net-address/outputs.tf b/modules/net-address/outputs.tf index 54b7a556..3c6a26b8 100644 --- a/modules/net-address/outputs.tf +++ b/modules/net-address/outputs.tf @@ -48,7 +48,7 @@ output "internal_addresses" { } output "psa_addresses" { - description = "Allocated internal addresses for PSC endpoints." + description = "Allocated internal addresses for PSA endpoints." value = { for address in google_compute_global_address.psa : address.name => { From d2cbf800fcc71ef17ee1012febd43bf409ad6d55 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Thu, 16 Dec 2021 17:52:08 +0100 Subject: [PATCH 19/52] Fixes typo --- modules/net-address/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/net-address/README.md b/modules/net-address/README.md index f9f11fe0..d844137c 100644 --- a/modules/net-address/README.md +++ b/modules/net-address/README.md @@ -104,6 +104,6 @@ module "addresses" { | external_addresses | Allocated external addresses. | | | global_addresses | Allocated global external addresses. | | | internal_addresses | Allocated internal addresses. | | -| psa_addresses | Allocated internal addresses for PSC endpoints. | | +| psa_addresses | Allocated internal addresses for PSA endpoints. | | | psc_addresses | Allocated internal addresses for PSC endpoints. | | From 1ac3fe44606976e1d7e9f6034235b8abb1f673db Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 21 Dec 2021 08:51:51 +0100 Subject: [PATCH 20/52] New tfdoc version (#396) * update tfdoc * rewrite check docs, refactor tfdoc replace, regenerate modules READMEs * remove dead code from check docs * do not fail on missing variable files in check docs * fix typos --- .github/workflows/linting.yml | 14 +- .../README.md | 15 +- .../dns-fine-grained-iam/README.md | 15 +- cloud-operations/dns-shared-vpc/README.md | 19 +- .../iam-delegated-role-grants/README.md | 16 +- .../onprem-sa-key-management/README.md | 13 +- .../packer-image-builder/README.md | 25 +- cloud-operations/quota-monitoring/README.md | 18 +- .../README.md | 23 +- .../cmek-via-centralized-kms/README.md | 23 +- .../01-environment/README.md | 21 +- .../02-resources/README.md | 29 +- .../gcs-to-bq-with-dataflow/README.md | 25 +- .../firewall-hierarchical-policies/README.md | 9 +- factories/firewall-vpc-rules/flat/README.md | 13 +- factories/firewall-vpc-rules/nested/README.md | 9 +- factories/subnets/README.md | 7 +- foundations/business-units/README.md | 27 +- foundations/environments/README.md | 35 +- modules/__experimental/net-neg/README.md | 17 +- modules/apigee-organization/README.md | 23 +- modules/apigee-x-instance/README.md | 19 +- modules/artifact-registry/README.md | 19 +- modules/artifact-registry/variables.tf | 2 +- modules/bigquery-dataset/README.md | 33 +- modules/bigtable-instance/README.md | 29 +- modules/billing-budget/README.md | 27 +- .../cloud-config-container/coredns/README.md | 18 +- .../cos-generic-metadata/README.md | 33 +- .../envoy-traffic-director/README.md | 9 +- .../cloud-config-container/mysql/README.md | 22 +- .../cloud-config-container/nginx/README.md | 20 +- .../cloud-config-container/onprem/README.md | 20 +- .../cloud-config-container/squid/README.md | 26 +- modules/cloud-function/README.md | 41 +- modules/cloud-identity-group/README.md | 15 +- modules/cloud-run/README.md | 39 +- modules/cloudsql-instance/README.md | 41 +- modules/compute-mig/README.md | 35 +- modules/compute-vm/README.md | 63 ++- modules/container-registry/README.md | 11 +- modules/datafusion/README.md | 33 +- modules/dns/README.md | 33 +- modules/endpoints/README.md | 15 +- modules/folder/README.md | 33 +- modules/folders-unit/README.md | 33 +- modules/gcs/README.md | 39 +- modules/gke-cluster/README.md | 81 +-- modules/gke-nodepool/README.md | 71 +-- modules/iam-service-account/README.md | 33 +- modules/kms/README.md | 21 +- modules/logging-bucket/README.md | 17 +- modules/naming-convention/README.md | 21 +- modules/net-address/README.md | 19 +- modules/net-cloudnat/README.md | 31 +- modules/net-ilb/README.md | 39 +- .../README.md | 27 +- modules/net-vpc-firewall/README.md | 28 +- modules/net-vpc-peering/README.md | 17 +- modules/net-vpc/README.md | 53 +- modules/net-vpn-dynamic/README.md | 29 +- modules/net-vpn-ha/README.md | 33 +- modules/net-vpn-static/README.md | 23 +- modules/organization/README.md | 39 +- modules/project/README.md | 65 +-- modules/pubsub/README.md | 27 +- modules/secret-manager/README.md | 15 +- modules/service-directory/README.md | 21 +- modules/source-repository/README.md | 11 +- modules/vpc-sc/README.md | 29 +- networking/decentralized-firewall/README.md | 17 +- networking/filtering-proxy/README.md | 21 +- networking/hub-and-spoke-peering/README.md | 19 +- networking/hub-and-spoke-vpn/README.md | 17 +- networking/ilb-next-hop/README.md | 21 +- networking/onprem-google-access-dns/README.md | 21 +- .../README.md | 17 +- networking/shared-vpc-gke/README.md | 29 +- third-party-solutions/openshift/tf/README.md | 31 +- tools/check_documentation.py | 93 +--- tools/tfdoc.py | 522 ++++++++++-------- 81 files changed, 1456 insertions(+), 1206 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index d250cef2..aa042740 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -name: 'Linting' -on: +name: "Linting" +on: pull_request: branches: - master @@ -30,7 +30,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: '3.9' + python-version: "3.9" - name: Set up Terraform uses: hashicorp/setup-terraform@v1 @@ -54,4 +54,10 @@ jobs: - name: Check documentation id: documentation run: | - python3 tools/check_documentation.py cloud-operations/ data-solutions/ data-solutions/data-platform-foundations/ factories/ factories/firewall-vpc-rules/ foundations/ modules/ networking/ + python3 tools/check_documentation.py \ + cloud-operations \ + data-solutions \ + factories \ + foundations \ + modules \ + networking diff --git a/cloud-operations/asset-inventory-feed-remediation/README.md b/cloud-operations/asset-inventory-feed-remediation/README.md index 9f624884..0668a62a 100644 --- a/cloud-operations/asset-inventory-feed-remediation/README.md +++ b/cloud-operations/asset-inventory-feed-remediation/README.md @@ -53,15 +53,16 @@ Run the `subscription_pull` command until it returns nothing, then run the follo + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project id that references existing project. | string | ✓ | | -| *bundle_path* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle.zip | -| *name* | Arbitrary string used to name created resources. | string | | asset-feed | -| *project_create* | Create project instead of using an existing one. | bool | | false | -| *region* | Compute region used in the example. | string | | europe-west1 | +|---|---|:---:|:---:|:---:| +| project_id | Project id that references existing project. | string | ✓ | | +| bundle_path | Path used to write the intermediate Cloud Function code bundle. | string | | "./bundle.zip" | +| name | Arbitrary string used to name created resources. | string | | "asset-feed" | +| project_create | Create project instead of using an existing one. | bool | | false | +| region | Compute region used in the example. | string | | "europe-west1" | ## Outputs @@ -71,5 +72,7 @@ Run the `subscription_pull` command until it returns nothing, then run the follo | subscription_pull | Subscription pull command. | | | tag_add | Instance add tag command. | | | tag_show | Instance add tag command. | | + + diff --git a/cloud-operations/dns-fine-grained-iam/README.md b/cloud-operations/dns-fine-grained-iam/README.md index f514dbbd..36781137 100644 --- a/cloud-operations/dns-fine-grained-iam/README.md +++ b/cloud-operations/dns-fine-grained-iam/README.md @@ -100,15 +100,16 @@ dig app1.svc.example.org +short ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Existing project id. | string | ✓ | | -| *name* | Arbitrary string used to name created resources. | string | | dns-sd-test | -| *project_create* | Create project instead ofusing an existing one. | bool | | false | -| *region* | Compute region used in the example. | string | | europe-west1 | -| *zone_domain* | Domain name used for the DNS zone. | string | | svc.example.org. | +|---|---|:---:|:---:|:---:| +| project_id | Existing project id. | string | ✓ | | +| name | Arbitrary string used to name created resources. | string | | "dns-sd-test" | +| project_create | Create project instead ofusing an existing one. | bool | | false | +| region | Compute region used in the example. | string | | "europe-west1" | +| zone_domain | Domain name used for the DNS zone. | string | | "svc.example.org." | ## Outputs @@ -116,4 +117,6 @@ dig app1.svc.example.org +short |---|---|:---:| | gcloud_commands | Commands used to SSH to the VMs. | | | vms | VM names. | | + + diff --git a/cloud-operations/dns-shared-vpc/README.md b/cloud-operations/dns-shared-vpc/README.md index 3358440c..267b39ef 100644 --- a/cloud-operations/dns-shared-vpc/README.md +++ b/cloud-operations/dns-shared-vpc/README.md @@ -20,21 +20,24 @@ The resources created in this example are shown in the high level diagram below: Note that Terraform 0.13 at least is required due to the use of `for_each` with modules. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account_id | Billing account associated with the GCP Projects that will be created for each team. | string | ✓ | | -| folder_id | Folder ID in which DNS projects will be created. | string | ✓ | | -| shared_vpc_link | Shared VPC self link, used for DNS peering. | string | ✓ | | -| *dns_domain* | DNS domain under which each application team DNS domain will be created. | string | | example.org | -| *prefix* | Customer name to use as prefix for resources' naming. | string | | test-dns | -| *project_services* | Service APIs enabled by default. | list(string) | | ... | -| *teams* | List of application teams requiring their own Cloud DNS instance. | list(string) | | ... | +|---|---|:---:|:---:|:---:| +| billing_account_id | Billing account associated with the GCP Projects that will be created for each team. | string | ✓ | | +| folder_id | Folder ID in which DNS projects will be created. | string | ✓ | | +| shared_vpc_link | Shared VPC self link, used for DNS peering. | string | ✓ | | +| dns_domain | DNS domain under which each application team DNS domain will be created. | string | | "example.org" | +| prefix | Customer name to use as prefix for resources' naming. | string | | "test-dns" | +| project_services | Service APIs enabled by default. | list(string) | | […] | +| teams | List of application teams requiring their own Cloud DNS instance. | list(string) | | […] | ## Outputs | name | description | sensitive | |---|---|:---:| | teams | Team resources | | + + diff --git a/cloud-operations/iam-delegated-role-grants/README.md b/cloud-operations/iam-delegated-role-grants/README.md index 7896673c..f2dfd04c 100644 --- a/cloud-operations/iam-delegated-role-grants/README.md +++ b/cloud-operations/iam-delegated-role-grants/README.md @@ -65,17 +65,17 @@ If you get any warnings, check the roles and remove any of them granting any of + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_administrators | List identities granted administrator permissions. | list(string) | ✓ | | -| project_id | GCP project id where to grant direct and delegated roles to the users listed in project_administrators. | string | ✓ | | -| *delegated_role_grants* | List of roles that project administrators will be allowed to grant/revoke. | list(string) | | ... | -| *direct_role_grants* | List of roles granted directly to project administrators. | list(string) | | ... | -| *project_create* | Create project instead of using an existing one. | bool | | false | -| *restricted_role_grant* | Role grant to which the restrictions will apply. | string | | roles/resourcemanager.projectIamAdmin | +|---|---|:---:|:---:|:---:| +| project_administrators | List identities granted administrator permissions. | list(string) | ✓ | | +| project_id | GCP project id where to grant direct and delegated roles to the users listed in project_administrators. | string | ✓ | | +| delegated_role_grants | List of roles that project administrators will be allowed to grant/revoke. | list(string) | | […] | +| direct_role_grants | List of roles granted directly to project administrators. | list(string) | | […] | +| project_create | Create project instead of using an existing one. | bool | | false | +| restricted_role_grant | Role grant to which the restrictions will apply. | string | | "roles/resourcemanager.projectIamAdmin" | -## Outputs diff --git a/cloud-operations/onprem-sa-key-management/README.md b/cloud-operations/onprem-sa-key-management/README.md index 1d8d8f87..c1939136 100644 --- a/cloud-operations/onprem-sa-key-management/README.md +++ b/cloud-operations/onprem-sa-key-management/README.md @@ -62,18 +62,21 @@ terraform destroy -var project_id=$GOOGLE_CLOUD_PROJECT ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project id. | string | ✓ | | -| *project_create* | Create project instead of using an existing one. | bool | | false | -| *service_accounts* | List of service accounts. | list(object({...})) | | ... | -| *services* | Service APIs to enable. | list(string) | | [] | +|---|---|:---:|:---:|:---:| +| project_id | Project id. | string | ✓ | | +| project_create | Create project instead of using an existing one. | bool | | false | +| service_accounts | List of service accounts. | list(object({…})) | | […] | +| services | Service APIs to enable. | list(string) | | [] | ## Outputs | name | description | sensitive | |---|---|:---:| | sa-credentials | SA json key templates. | | + + diff --git a/cloud-operations/packer-image-builder/README.md b/cloud-operations/packer-image-builder/README.md index 0f8ec5ee..6339c72f 100644 --- a/cloud-operations/packer-image-builder/README.md +++ b/cloud-operations/packer-image-builder/README.md @@ -68,20 +68,21 @@ the resources over the Internet (i.e. to install OS packages). Since Compute VM address for security reasons, Internet connectivity is done with [Cloud NAT](https://cloud.google.com/nat/docs/overview). + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project id that references existing project. | string | ✓ | | -| *billing_account* | Billing account id used as default for new projects. | string | | null | -| *cidrs* | CIDR ranges for subnets | map(string) | | ... | -| *create_packer_vars* | Create packer variables file using template file and terraform output. | bool | | false | -| *packer_account_users* | List of members that will be allowed to impersonate Packer image builder service account in IAM format, i.e. 'user:{emailid}'. | list(string) | | [] | -| *packer_source_cidrs* | List of CIDR ranges allowed to connect to the temporary VM for provisioning. | list(string) | | ["0.0.0.0/0"] | -| *project_create* | Create project instead of using an existing one. | bool | | true | -| *region* | Default region for resources | string | | europe-west1 | -| *root_node* | The resource name of the parent folder or organization for project creation, in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | -| *use_iap* | Use IAP tunnel to connect to Compute Engine instance for provisioning. | bool | | true | +|---|---|:---:|:---:|:---:| +| project_id | Project id that references existing project. | string | ✓ | | +| billing_account | Billing account id used as default for new projects. | string | | null | +| cidrs | CIDR ranges for subnets | map(string) | | {…} | +| create_packer_vars | Create packer variables file using template file and terraform output. | bool | | false | +| packer_account_users | List of members that will be allowed to impersonate Packer image builder service account in IAM format, i.e. 'user:{emailid}'. | list(string) | | [] | +| packer_source_cidrs | List of CIDR ranges allowed to connect to the temporary VM for provisioning. | list(string) | | ["0.0.0.0/0"] | +| project_create | Create project instead of using an existing one. | bool | | true | +| region | Default region for resources | string | | "europe-west1" | +| root_node | The resource name of the parent folder or organization for project creation, in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +| use_iap | Use IAP tunnel to connect to Compute Engine instance for provisioning. | bool | | true | ## Outputs @@ -91,4 +92,6 @@ address for security reasons, Internet connectivity is done with [Cloud NAT](htt | compute_sa | Packer's temporary VM service account email. | | | compute_subnetwork | Name of a subnetwork for Packer's temporary VM. | | | compute_zone | Name of a compute engine zone for Packer's temporary VM. | | + + diff --git a/cloud-operations/quota-monitoring/README.md b/cloud-operations/quota-monitoring/README.md index a92abf68..47611aa6 100644 --- a/cloud-operations/quota-monitoring/README.md +++ b/cloud-operations/quota-monitoring/README.md @@ -24,19 +24,19 @@ Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/c - `terraform apply -var project_id=my-project-id` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project id that references existing project. | string | ✓ | | -| *bundle_path* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle.zip | -| *name* | Arbitrary string used to name created resources. | string | | quota-monitor | -| *project_create* | Create project instead ofusing an existing one. | bool | | false | -| *quota_config* | Cloud function configuration. | object({...}) | | ... | -| *region* | Compute region used in the example. | string | | europe-west1 | -| *schedule_config* | Schedule timer configuration in crontab format | string | | 0 * * * * | +|---|---|:---:|:---:|:---:| +| project_id | Project id that references existing project. | string | ✓ | | +| bundle_path | Path used to write the intermediate Cloud Function code bundle. | string | | "./bundle.zip" | +| name | Arbitrary string used to name created resources. | string | | "quota-monitor" | +| project_create | Create project instead ofusing an existing one. | bool | | false | +| quota_config | Cloud function configuration. | object({…}) | | {…} | +| region | Compute region used in the example. | string | | "europe-west1" | +| schedule_config | Schedule timer configuration in crontab format | string | | "0 * * * *" | -## Outputs diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index ef8bca88..4c9d2ae7 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -39,19 +39,20 @@ Once resources are created, you can run queries on the data you exported on Bigq You can also create a dashboard connecting [Datalab](https://datastudio.google.com/) or any other BI tools of your choice to your Bigquery datase. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| cai_config | Cloud Asset inventory export config. | object({...}) | ✓ | | -| project_id | Project id that references existing project. | string | ✓ | | -| *billing_account* | Billing account id used as default for new projects. | string | | null | -| *bundle_path* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle.zip | -| *location* | Appe Engine location used in the example. | string | | europe-west | -| *name* | Arbitrary string used to name created resources. | string | | asset-inventory | -| *project_create* | Create project instead ofusing an existing one. | bool | | true | -| *region* | Compute region used in the example. | string | | europe-west1 | -| *root_node* | The resource name of the parent folder or organization for project creation, in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +|---|---|:---:|:---:|:---:| +| cai_config | Cloud Asset inventory export config. | object({…}) | ✓ | | +| project_id | Project id that references existing project. | string | ✓ | | +| billing_account | Billing account id used as default for new projects. | string | | null | +| bundle_path | Path used to write the intermediate Cloud Function code bundle. | string | | "./bundle.zip" | +| location | Appe Engine location used in the example. | string | | "europe-west" | +| name | Arbitrary string used to name created resources. | string | | "asset-inventory" | +| project_create | Create project instead ofusing an existing one. | bool | | true | +| region | Compute region used in the example. | string | | "europe-west1" | +| root_node | The resource name of the parent folder or organization for project creation, in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | ## Outputs @@ -59,4 +60,6 @@ You can also create a dashboard connecting [Datalab](https://datastudio.google.c |---|---|:---:| | bq-dataset | Bigquery instance details. | | | cloud-function | Cloud Function instance details. | | + + diff --git a/data-solutions/cmek-via-centralized-kms/README.md b/data-solutions/cmek-via-centralized-kms/README.md index d63b780c..a476b70a 100644 --- a/data-solutions/cmek-via-centralized-kms/README.md +++ b/data-solutions/cmek-via-centralized-kms/README.md @@ -31,19 +31,20 @@ This sample creates several distinct groups of resources: - One bucket encrypted with a CMEK Cryptokey hosted in Cloud KMS + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account | Billing account id used as default for new projects. | string | ✓ | | -| root_node | The resource name of the parent Folder or Organization. Must be of the form folders/folder_id or organizations/org_id. | string | ✓ | | -| *location* | The location where resources will be deployed. | string | | europe | -| *project_kms_name* | Name for the new KMS Project. | string | | my-project-kms-001 | -| *project_service_name* | Name for the new Service Project. | string | | my-project-service-001 | -| *region* | The region where resources will be deployed. | string | | europe-west1 | -| *vpc_ip_cidr_range* | Ip range used in the subnet deployef in the Service Project. | string | | 10.0.0.0/20 | -| *vpc_name* | Name of the VPC created in the Service Project. | string | | local | -| *vpc_subnet_name* | Name of the subnet created in the Service Project. | string | | subnet | +|---|---|:---:|:---:|:---:| +| billing_account | Billing account id used as default for new projects. | string | ✓ | | +| root_node | The resource name of the parent Folder or Organization. Must be of the form folders/folder_id or organizations/org_id. | string | ✓ | | +| location | The location where resources will be deployed. | string | | "europe" | +| project_kms_name | Name for the new KMS Project. | string | | "my-project-kms-001" | +| project_service_name | Name for the new Service Project. | string | | "my-project-service-001" | +| region | The region where resources will be deployed. | string | | "europe-west1" | +| vpc_ip_cidr_range | Ip range used in the subnet deployef in the Service Project. | string | | "10.0.0.0/20" | +| vpc_name | Name of the VPC created in the Service Project. | string | | "local" | +| vpc_subnet_name | Name of the subnet created in the Service Project. | string | | "subnet" | ## Outputs @@ -54,4 +55,6 @@ This sample creates several distinct groups of resources: | projects | Project ids. | | | vm | GCE VM. | | | vm_keys | GCE VM Cloud KMS crypto keys. | | + + diff --git a/data-solutions/data-platform-foundations/01-environment/README.md b/data-solutions/data-platform-foundations/01-environment/README.md index 318a4e1f..d7bca97d 100644 --- a/data-solutions/data-platform-foundations/01-environment/README.md +++ b/data-solutions/data-platform-foundations/01-environment/README.md @@ -48,18 +48,19 @@ gcloud access-context-manager perimeters list --format="json" | grep name The script use 'google_access_context_manager_service_perimeter_resource' terraform resource. If this resource is used alongside the 'vpc-sc' module, remember to uncomment the lifecycle block in the 'vpc-sc' module so they don't fight over which resources should be in the perimeter. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account_id | Billing account id. | string | ✓ | | -| root_node | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | ✓ | | -| *admins* | List of users allowed to impersonate the service account | list(string) | | null | -| *prefix* | Prefix used to generate project id and name. | string | | null | -| *project_names* | Override this variable if you need non-standard names. | object({...}) | | ... | -| *service_account_names* | Override this variable if you need non-standard names. | object({...}) | | ... | -| *service_encryption_key_ids* | Cloud KMS encryption key in {LOCATION => [KEY_URL]} format. Keys belong to existing project. | object({...}) | | ... | -| *service_perimeter_standard* | VPC Service control standard perimeter name in the form of 'accessPolicies/ACCESS_POLICY_NAME/servicePerimeters/PERIMETER_NAME'. All projects will be added to the perimeter in enforced mode. | string | | null | +|---|---|:---:|:---:|:---:| +| billing_account_id | Billing account id. | string | ✓ | | +| root_node | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | ✓ | | +| admins | List of users allowed to impersonate the service account | list(string) | | null | +| prefix | Prefix used to generate project id and name. | string | | null | +| project_names | Override this variable if you need non-standard names. | object({…}) | | {…} | +| service_account_names | Override this variable if you need non-standard names. | object({…}) | | {…} | +| service_encryption_key_ids | Cloud KMS encryption key in {LOCATION => [KEY_URL]} format. Keys belong to existing project. | object({…}) | | {…} | +| service_perimeter_standard | VPC Service control standard perimeter name in the form of 'accessPolicies/ACCESS_POLICY_NAME/servicePerimeters/PERIMETER_NAME'. All projects will be added to the perimeter in enforced mode. | string | | null | ## Outputs @@ -68,4 +69,6 @@ The script use 'google_access_context_manager_service_perimeter_resource' terraf | project_ids | Project ids for created projects. | | | service_account | Main service account. | | | service_encryption_key_ids | Cloud KMS encryption keys in {LOCATION => [KEY_URL]} format. | | + + diff --git a/data-solutions/data-platform-foundations/02-resources/README.md b/data-solutions/data-platform-foundations/02-resources/README.md index 5d9a0da9..e34cdef4 100644 --- a/data-solutions/data-platform-foundations/02-resources/README.md +++ b/data-solutions/data-platform-foundations/02-resources/README.md @@ -52,22 +52,23 @@ Once done testing, you can clean up resources by running `terraform destroy`. You can configure GCP resources to use existing CMEK keys configuring the 'service_encryption_key_ids' variable. You need to specify a 'global' and a 'multiregional' key. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_ids | Project IDs. | object({...}) | ✓ | | -| *admins* | List of users allowed to impersonate the service account | list(string) | | null | -| *datamart_bq_datasets* | Datamart Bigquery datasets | map(object({...})) | | ... | -| *dwh_bq_datasets* | DWH Bigquery datasets | map(object({...})) | | ... | -| *landing_buckets* | List of landing buckets to create | map(object({...})) | | ... | -| *landing_pubsub* | List of landing pubsub topics and subscriptions to create | map(map(object({...}))) | | ... | -| *landing_service_account* | landing service accounts list. | string | | sa-landing | -| *service_account_names* | Project service accounts list. | object({...}) | | ... | -| *service_encryption_key_ids* | Cloud KMS encryption key in {LOCATION => [KEY_URL]} format. Keys belong to existing project. | object({...}) | | ... | -| *transformation_buckets* | List of transformation buckets to create | map(object({...})) | | ... | -| *transformation_subnets* | List of subnets to create in the transformation Project. | list(object({...})) | | ... | -| *transformation_vpc_name* | Name of the VPC created in the transformation Project. | string | | transformation-vpc | +|---|---|:---:|:---:|:---:| +| project_ids | Project IDs. | object({…}) | ✓ | | +| admins | List of users allowed to impersonate the service account | list(string) | | null | +| datamart_bq_datasets | Datamart Bigquery datasets | map(object({…})) | | {…} | +| dwh_bq_datasets | DWH Bigquery datasets | map(object({…})) | | {…} | +| landing_buckets | List of landing buckets to create | map(object({…})) | | {…} | +| landing_pubsub | List of landing pubsub topics and subscriptions to create | map(map(object({…}))) | | {…} | +| landing_service_account | landing service accounts list. | string | | "sa-landing" | +| service_account_names | Project service accounts list. | object({…}) | | {…} | +| service_encryption_key_ids | Cloud KMS encryption key in {LOCATION => [KEY_URL]} format. Keys belong to existing project. | object({…}) | | {…} | +| transformation_buckets | List of transformation buckets to create | map(object({…})) | | {…} | +| transformation_subnets | List of subnets to create in the transformation Project. | list(object({…})) | | […] | +| transformation_vpc_name | Name of the VPC created in the transformation Project. | string | | "transformation-vpc" | ## Outputs @@ -79,4 +80,6 @@ You can configure GCP resources to use existing CMEK keys configuring the 'servi | landing-pubsub | List of pubsub topics and subscriptions created for the landing project. | | | transformation-buckets | List of buckets created for the transformation project. | | | transformation-vpc | Transformation VPC details | | + + diff --git a/data-solutions/gcs-to-bq-with-dataflow/README.md b/data-solutions/gcs-to-bq-with-dataflow/README.md index b3314161..5ab92d73 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/README.md +++ b/data-solutions/gcs-to-bq-with-dataflow/README.md @@ -111,20 +111,21 @@ schema_bq_import.json You can check data imported into Google BigQuery from the Google Cloud Console UI. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account | Billing account id used as default for new projects. | string | ✓ | | -| project_kms_name | Name for the new KMS Project. | string | ✓ | | -| project_service_name | Name for the new Service Project. | string | ✓ | | -| root_node | The resource name of the parent Folder or Organization. Must be of the form folders/folder_id or organizations/org_id. | string | ✓ | | -| *location* | The location where resources will be deployed. | string | | europe | -| *region* | The region where resources will be deployed. | string | | europe-west1 | -| *ssh_source_ranges* | IP CIDR ranges that will be allowed to connect via SSH to the onprem instance. | list(string) | | ["0.0.0.0/0"] | -| *vpc_ip_cidr_range* | Ip range used in the subnet deployef in the Service Project. | string | | 10.0.0.0/20 | -| *vpc_name* | Name of the VPC created in the Service Project. | string | | local | -| *vpc_subnet_name* | Name of the subnet created in the Service Project. | string | | subnet | +|---|---|:---:|:---:|:---:| +| billing_account | Billing account id used as default for new projects. | string | ✓ | | +| project_kms_name | Name for the new KMS Project. | string | ✓ | | +| project_service_name | Name for the new Service Project. | string | ✓ | | +| root_node | The resource name of the parent Folder or Organization. Must be of the form folders/folder_id or organizations/org_id. | string | ✓ | | +| location | The location where resources will be deployed. | string | | "europe" | +| region | The region where resources will be deployed. | string | | "europe-west1" | +| ssh_source_ranges | IP CIDR ranges that will be allowed to connect via SSH to the onprem instance. | list(string) | | ["0.0.0.0/0"] | +| vpc_ip_cidr_range | Ip range used in the subnet deployef in the Service Project. | string | | "10.0.0.0/20" | +| vpc_name | Name of the VPC created in the Service Project. | string | | "local" | +| vpc_subnet_name | Name of the subnet created in the Service Project. | string | | "subnet" | ## Outputs @@ -134,4 +135,6 @@ You can check data imported into Google BigQuery from the Google Cloud Console U | buckets | GCS Bucket Cloud KMS crypto keys. | | | projects | Project ids. | | | vm | GCE VM. | | + + diff --git a/factories/firewall-hierarchical-policies/README.md b/factories/firewall-hierarchical-policies/README.md index db374ebf..cf7cee30 100644 --- a/factories/firewall-hierarchical-policies/README.md +++ b/factories/firewall-hierarchical-policies/README.md @@ -149,16 +149,19 @@ web_frontends: ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| config_folder | Relative path of the folder containing the hierarchical firewall configuration | string | ✓ | | -| templates_folder | Relative path of the folder containing the cidr/service account templates | string | ✓ | | +|---|---|:---:|:---:|:---:| +| config_folder | Relative path of the folder containing the hierarchical firewall configuration | string | ✓ | | +| templates_folder | Relative path of the folder containing the cidr/service account templates | string | ✓ | | ## Outputs | name | description | sensitive | |---|---|:---:| | hierarchical-firewall-rules | Generated Hierarchical Firewall Rules | | + + diff --git a/factories/firewall-vpc-rules/flat/README.md b/factories/firewall-vpc-rules/flat/README.md index c13c5f86..96f3bc41 100644 --- a/factories/firewall-vpc-rules/flat/README.md +++ b/factories/firewall-vpc-rules/flat/README.md @@ -136,14 +136,15 @@ web-app-a-ingress: ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| config_directories | List of paths to folders where firewall configs are stored in yaml format. Folder may include subfolders with configuration files. Files suffix must be `.yaml` | list(string) | ✓ | | -| network | Name of the network this set of firewall rules applies to. | string | ✓ | | -| project_id | Project Id. | string | ✓ | | -| *log_config* | Log configuration. Possible values for `metadata` are `EXCLUDE_ALL_METADATA` and `INCLUDE_ALL_METADATA`. Set to `null` for disabling firewall logging. | object({...}) | | null | +|---|---|:---:|:---:|:---:| +| config_directories | List of paths to folders where firewall configs are stored in yaml format. Folder may include subfolders with configuration files. Files suffix must be `.yaml` | list(string) | ✓ | | +| network | Name of the network this set of firewall rules applies to. | string | ✓ | | +| project_id | Project Id. | string | ✓ | | +| log_config | Log configuration. Possible values for `metadata` are `EXCLUDE_ALL_METADATA` and `INCLUDE_ALL_METADATA`. Set to `null` for disabling firewall logging. | object({…}) | | null | ## Outputs @@ -153,4 +154,6 @@ web-app-a-ingress: | egress_deny_rules | Egress rules with allow blocks. | | | ingress_allow_rules | Ingress rules with allow blocks. | | | ingress_deny_rules | Ingress rules with deny blocks. | | + + diff --git a/factories/firewall-vpc-rules/nested/README.md b/factories/firewall-vpc-rules/nested/README.md index 65468afd..35bdf418 100644 --- a/factories/firewall-vpc-rules/nested/README.md +++ b/factories/firewall-vpc-rules/nested/README.md @@ -125,16 +125,19 @@ web_frontends: ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| config_folder | Relative path of the folder containing the hierarchical firewall configuration | string | ✓ | | -| templates_folder | Relative path of the folder containing the cidr/service account templates | string | ✓ | | +|---|---|:---:|:---:|:---:| +| config_folder | Relative path of the folder containing the hierarchical firewall configuration | string | ✓ | | +| templates_folder | Relative path of the folder containing the cidr/service account templates | string | ✓ | | ## Outputs | name | description | sensitive | |---|---|:---:| | vpc-firewall-rules | Generated VPC Firewall Rules | | + + diff --git a/factories/subnets/README.md b/factories/subnets/README.md index 13df44d4..0c9a54e0 100644 --- a/factories/subnets/README.md +++ b/factories/subnets/README.md @@ -53,15 +53,18 @@ secondary_ip_ranges: # Opt- List of secondary IP ranges ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| config_folder | Relative path of the folder containing the subnet configuration | string | ✓ | | +|---|---|:---:|:---:|:---:| +| config_folder | Relative path of the folder containing the subnet configuration | string | ✓ | | ## Outputs | name | description | sensitive | |---|---|:---:| | subnet | Generated subnets | | + + diff --git a/foundations/business-units/README.md b/foundations/business-units/README.md index 36d80ecc..12d0fa75 100644 --- a/foundations/business-units/README.md +++ b/foundations/business-units/README.md @@ -26,21 +26,22 @@ The number of resources in this sample is kept to a minimum so as to make it gen This sample uses a top-level folder to encapsulate projects that host resources that are not specific to a single environment. If no shared services are needed,the Terraform and audit modules can be easily attached to the root node, and the shared services folder and project removed from `main.tf`. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | -| organization_id | Organization id in organizations/nnnnnnn format. | string | ✓ | | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | -| root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| *audit_filter* | Audit log filter used for the log sink. | string | | ... | -| *environments* | Environment short names. | map(string) | | ... | -| *gcs_defaults* | Defaults use for the state GCS buckets. | map(string) | | ... | -| *iam_audit_viewers* | Audit project viewers, in IAM format. | list(string) | | [] | -| *iam_shared_owners* | Shared services project owners, in IAM format. | list(string) | | [] | -| *iam_terraform_owners* | Terraform project owners, in IAM format. | list(string) | | [] | -| *project_services* | Service APIs enabled by default in new projects. | list(string) | | ... | +|---|---|:---:|:---:|:---:| +| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | +| organization_id | Organization id in organizations/nnnnnnn format. | string | ✓ | | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | +| root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | +| audit_filter | Audit log filter used for the log sink. | string | | | +| environments | Environment short names. | map(string) | | {…} | +| gcs_defaults | Defaults use for the state GCS buckets. | map(string) | | {…} | +| iam_audit_viewers | Audit project viewers, in IAM format. | list(string) | | [] | +| iam_shared_owners | Shared services project owners, in IAM format. | list(string) | | [] | +| iam_terraform_owners | Terraform project owners, in IAM format. | list(string) | | [] | +| project_services | Service APIs enabled by default in new projects. | list(string) | | […] | ## Outputs @@ -55,4 +56,6 @@ This sample uses a top-level folder to encapsulate projects that host resources | shared_folder_id | Shared folder id. | | | shared_resources_project | Project that holdes resources shared across business units. | | | terraform_project | Project that holds the base Terraform resources. | | + + diff --git a/foundations/environments/README.md b/foundations/environments/README.md index 8993e599..67b41f0f 100644 --- a/foundations/environments/README.md +++ b/foundations/environments/README.md @@ -28,25 +28,26 @@ For more complex setups where multiple shared services projects are needed to en If no shared services are needed, the shared service project module can of course be removed from `main.tf`. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account_id | Billing account id used as to create projects. | string | ✓ | | -| environments | Environment short names. | set(string) | ✓ | | -| organization_id | Organization id in organizations/nnnnnnnn format. | string | ✓ | | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | -| root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| *audit_filter* | Audit log filter used for the log sink. | string | | ... | -| *gcs_location* | GCS bucket location. | string | | EU | -| *iam_audit_viewers* | Audit project viewers, in IAM format. | list(string) | | [] | -| *iam_billing_config* | Control granting billing user role to service accounts. Target the billing account by default. | object({...}) | | ... | -| *iam_folder_roles* | List of roles granted to each service account on its respective folder (excluding XPN roles). | list(string) | | ... | -| *iam_shared_owners* | Shared services project owners, in IAM format. | list(string) | | [] | -| *iam_terraform_owners* | Terraform project owners, in IAM format. | list(string) | | [] | -| *iam_xpn_config* | Control granting Shared VPC creation roles to service accounts. Target the root node by default. | object({...}) | | ... | -| *project_services* | Service APIs enabled by default in new projects. | list(string) | | ... | -| *service_account_keys* | Generate and store service account keys in the state file. | bool | | true | +|---|---|:---:|:---:|:---:| +| billing_account_id | Billing account id used as to create projects. | string | ✓ | | +| environments | Environment short names. | set(string) | ✓ | | +| organization_id | Organization id in organizations/nnnnnnnn format. | string | ✓ | | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | +| root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | +| audit_filter | Audit log filter used for the log sink. | string | | | +| gcs_location | GCS bucket location. | string | | "EU" | +| iam_audit_viewers | Audit project viewers, in IAM format. | list(string) | | [] | +| iam_billing_config | Control granting billing user role to service accounts. Target the billing account by default. | object({…}) | | {…} | +| iam_folder_roles | List of roles granted to each service account on its respective folder (excluding XPN roles). | list(string) | | […] | +| iam_shared_owners | Shared services project owners, in IAM format. | list(string) | | [] | +| iam_terraform_owners | Terraform project owners, in IAM format. | list(string) | | [] | +| iam_xpn_config | Control granting Shared VPC creation roles to service accounts. Target the root node by default. | object({…}) | | {…} | +| project_services | Service APIs enabled by default in new projects. | list(string) | | […] | +| service_account_keys | Generate and store service account keys in the state file. | bool | | true | ## Outputs @@ -61,4 +62,6 @@ If no shared services are needed, the shared service project module can of cours | environment_tf_gcs_buckets | GCS buckets used for each environment Terraform state. | | | shared_services_project | Project that holdes resources shared across environments. | | | terraform_project | Project that holds the base Terraform resources. | | + + diff --git a/modules/__experimental/net-neg/README.md b/modules/__experimental/net-neg/README.md index 5a1e6a33..8915c344 100644 --- a/modules/__experimental/net-neg/README.md +++ b/modules/__experimental/net-neg/README.md @@ -25,16 +25,17 @@ module "neg" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| endpoints | List of (instance, port, address) of the NEG | list(object({...})) | ✓ | | -| name | NEG name | string | ✓ | | -| network | Name or self link of the VPC used for the NEG. Use the self link for Shared VPC. | string | ✓ | | -| project_id | NEG project id. | string | ✓ | | -| subnetwork | VPC subnetwork name or self link. | string | ✓ | | -| zone | NEG zone | string | ✓ | | +|---|---|:---:|:---:|:---:| +| endpoints | List of (instance, port, address) of the NEG | list(object({…})) | ✓ | | +| name | NEG name | string | ✓ | | +| network | Name or self link of the VPC used for the NEG. Use the self link for Shared VPC. | string | ✓ | | +| project_id | NEG project id. | string | ✓ | | +| subnetwork | VPC subnetwork name or self link. | string | ✓ | | +| zone | NEG zone | string | ✓ | | ## Outputs @@ -43,4 +44,6 @@ module "neg" { | id | Network endpoint group ID | | | self_lnk | Network endpoint group self link | | | size | Size of the network endpoint group | | + + diff --git a/modules/apigee-organization/README.md b/modules/apigee-organization/README.md index 7d46e061..d2325e2f 100644 --- a/modules/apigee-organization/README.md +++ b/modules/apigee-organization/README.md @@ -100,19 +100,20 @@ module "apigee-organization" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| analytics_region | Analytics Region for the Apigee Organization (immutable). See https://cloud.google.com/apigee/docs/api-platform/get-started/install-cli. | string | ✓ | | -| project_id | Project ID to host this Apigee organization (will also become the Apigee Org name). | string | ✓ | | -| runtime_type | Apigee runtime type. Must be `CLOUD` or `HYBRID`. | string | ✓ | | -| *apigee_envgroups* | Apigee Environment Groups. | map(object({...})) | | {} | -| *apigee_environments* | Apigee Environment Names. | list(string) | | [] | -| *authorized_network* | VPC network self link (requires service network peering enabled (Used in Apigee X only). | string | | null | -| *database_encryption_key* | Cloud KMS key self link (e.g. `projects/foo/locations/us/keyRings/bar/cryptoKeys/baz`) used for encrypting the data that is stored and replicated across runtime instances (immutable, used in Apigee X only). | string | | null | -| *description* | Description of the Apigee Organization. | string | | Apigee Organization created by tf module | -| *display_name* | Display Name of the Apigee Organization. | string | | null | +|---|---|:---:|:---:|:---:| +| analytics_region | Analytics Region for the Apigee Organization (immutable). See https://cloud.google.com/apigee/docs/api-platform/get-started/install-cli. | string | ✓ | | +| project_id | Project ID to host this Apigee organization (will also become the Apigee Org name). | string | ✓ | | +| runtime_type | Apigee runtime type. Must be `CLOUD` or `HYBRID`. | string | ✓ | | +| apigee_envgroups | Apigee Environment Groups. | map(object({…})) | | {} | +| apigee_environments | Apigee Environment Names. | list(string) | | [] | +| authorized_network | VPC network self link (requires service network peering enabled (Used in Apigee X only). | string | | null | +| database_encryption_key | Cloud KMS key self link (e.g. `projects/foo/locations/us/keyRings/bar/cryptoKeys/baz`) used for encrypting the data that is stored and replicated across runtime instances (immutable, used in Apigee X only). | string | | null | +| description | Description of the Apigee Organization. | string | | "Apigee Organization created by tf module" | +| display_name | Display Name of the Apigee Organization. | string | | null | ## Outputs @@ -123,4 +124,6 @@ module "apigee-organization" { | org_ca_certificate | Apigee organization CA certificate. | | | org_id | Apigee Organization ID. | | | subscription_type | Apigee subscription type. | | + + diff --git a/modules/apigee-x-instance/README.md b/modules/apigee-x-instance/README.md index 371f8f0b..d63dcfaf 100644 --- a/modules/apigee-x-instance/README.md +++ b/modules/apigee-x-instance/README.md @@ -44,17 +44,18 @@ module "apigee-x-instance" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| apigee_org_id | Apigee Organization ID | string | ✓ | | -| cidr_mask | CIDR mask for the Apigee instance | number | ✓ | | -| name | Apigee instance name. | string | ✓ | | -| region | Compute region. | string | ✓ | | -| *apigee_envgroups* | Apigee Environment Groups. | map(object({...})) | | {} | -| *apigee_environments* | Apigee Environment Names. | list(string) | | [] | -| *disk_encryption_key* | Customer Managed Encryption Key (CMEK) self link (e.g. `projects/foo/locations/us/keyRings/bar/cryptoKeys/baz`) used for disk and volume encryption (required for PAID Apigee Orgs only). | string | | null | +|---|---|:---:|:---:|:---:| +| apigee_org_id | Apigee Organization ID | string | ✓ | | +| cidr_mask | CIDR mask for the Apigee instance | number | ✓ | | +| name | Apigee instance name. | string | ✓ | | +| region | Compute region. | string | ✓ | | +| apigee_envgroups | Apigee Environment Groups. | map(object({…})) | | {} | +| apigee_environments | Apigee Environment Names. | list(string) | | [] | +| disk_encryption_key | Customer Managed Encryption Key (CMEK) self link (e.g. `projects/foo/locations/us/keyRings/bar/cryptoKeys/baz`) used for disk and volume encryption (required for PAID Apigee Orgs only). | string | | null | ## Outputs @@ -64,4 +65,6 @@ module "apigee-x-instance" { | id | Apigee instance ID. | | | instance | Apigee instance. | | | port | Port number of the internal endpoint of the Apigee instance. | | + + diff --git a/modules/artifact-registry/README.md b/modules/artifact-registry/README.md index 82b60110..507a6e8e 100644 --- a/modules/artifact-registry/README.md +++ b/modules/artifact-registry/README.md @@ -21,17 +21,18 @@ module "docker_artifact_registry" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| id | Repository id | string | ✓ | | -| project_id | Registry project id. | string | ✓ | | -| *description* | An optional description for the repository | string | | Terraform-managed registry | -| *format* | Repository format. One of DOCKER or UNSPECIFIED | string | | DOCKER | -| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *labels* | Labels to be attached to the registry. | map(string) | | {} | -| *location* | Registry location. Use `gcloud beta artifacts locations list' to get valid values | string | | | +|---|---|:---:|:---:|:---:| +| id | Repository id | string | ✓ | | +| project_id | Registry project id. | string | ✓ | | +| description | An optional description for the repository | string | | "Terraform-managed registry" | +| format | Repository format. One of DOCKER or UNSPECIFIED | string | | "DOCKER" | +| iam | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| labels | Labels to be attached to the registry. | map(string) | | {} | +| location | Registry location. Use `gcloud beta artifacts locations list' to get valid values | string | | null | ## Outputs @@ -39,4 +40,6 @@ module "docker_artifact_registry" { |---|---|:---:| | id | Repository id | | | name | Repository name | | + + diff --git a/modules/artifact-registry/variables.tf b/modules/artifact-registry/variables.tf index 57ef2082..b350e824 100644 --- a/modules/artifact-registry/variables.tf +++ b/modules/artifact-registry/variables.tf @@ -46,7 +46,7 @@ variable "labels" { variable "location" { description = "Registry location. Use `gcloud beta artifacts locations list' to get valid values" type = string - default = "" + default = null } variable "project_id" { diff --git a/modules/bigquery-dataset/README.md b/modules/bigquery-dataset/README.md index 492dddf5..e9ff2d5c 100644 --- a/modules/bigquery-dataset/README.md +++ b/modules/bigquery-dataset/README.md @@ -175,24 +175,25 @@ module "bigquery-dataset" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| id | Dataset id. | string | ✓ | | -| project_id | Id of the project where datasets will be created. | string | ✓ | | -| *access* | Map of access rules with role and identity type. Keys are arbitrary and must match those in the `access_identities` variable, types are `domain`, `group`, `special_group`, `user`, `view`. | map(object({...})) | | ... | -| *access_identities* | Map of access identities used for basic access roles. View identities have the format 'project_id|dataset_id|table_id'. | map(string) | | {} | -| *dataset_access* | Set access in the dataset resource instead of using separate resources. | bool | | false | -| *description* | Optional description. | string | | Terraform managed. | -| *encryption_key* | Self link of the KMS key that will be used to protect destination table. | string | | null | -| *friendly_name* | Dataset friendly name. | string | | null | -| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. Mutually exclusive with the access_* variables used for basic roles. | map(list(string)) | | {} | -| *labels* | Dataset labels. | map(string) | | {} | -| *location* | Dataset location. | string | | EU | -| *options* | Dataset options. | object({...}) | | ... | -| *tables* | Table definitions. Options and partitioning default to null. Partitioning can only use `range` or `time`, set the unused one to null. | map(object({...})) | | {} | -| *views* | View definitions. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| id | Dataset id. | string | ✓ | | +| project_id | Id of the project where datasets will be created. | string | ✓ | | +| access | Map of access rules with role and identity type. Keys are arbitrary and must match those in the `access_identities` variable, types are `domain`, `group`, `special_group`, `user`, `view`. | map(object({…})) | | {} | +| access_identities | Map of access identities used for basic access roles. View identities have the format 'project_id|dataset_id|table_id'. | map(string) | | {} | +| dataset_access | Set access in the dataset resource instead of using separate resources. | bool | | false | +| description | Optional description. | string | | "Terraform managed." | +| encryption_key | Self link of the KMS key that will be used to protect destination table. | string | | null | +| friendly_name | Dataset friendly name. | string | | null | +| iam | IAM bindings in {ROLE => [MEMBERS]} format. Mutually exclusive with the access_* variables used for basic roles. | map(list(string)) | | {} | +| labels | Dataset labels. | map(string) | | {} | +| location | Dataset location. | string | | "EU" | +| options | Dataset options. | object({…}) | | {…} | +| tables | Table definitions. Options and partitioning default to null. Partitioning can only use `range` or `time`, set the unused one to null. | map(object({…})) | | {} | +| views | View definitions. | map(object({…})) | | {} | ## Outputs @@ -206,5 +207,7 @@ module "bigquery-dataset" { | tables | Table resources. | | | view_ids | Map of fully qualified view ids keyed by view ids. | | | views | View resources. | | + + diff --git a/modules/bigtable-instance/README.md b/modules/bigtable-instance/README.md index eb7c8f04..cbbae24f 100644 --- a/modules/bigtable-instance/README.md +++ b/modules/bigtable-instance/README.md @@ -34,22 +34,23 @@ module "bigtable-instance" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | The name of the Cloud Bigtable instance. | string | ✓ | | -| project_id | Id of the project where datasets will be created. | string | ✓ | | -| zone | The zone to create the Cloud Bigtable cluster in. | string | ✓ | | -| *cluster_id* | The ID of the Cloud Bigtable cluster. | string | | europe-west1 | -| *deletion_protection* | Whether or not to allow Terraform to destroy the instance. Unless this field is set to false in Terraform state, a terraform destroy or terraform apply that would delete the instance will fail. | | | true | -| *display_name* | The human-readable display name of the Bigtable instance. | | | null | -| *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *instance_type* | (deprecated) The instance type to create. One of 'DEVELOPMENT' or 'PRODUCTION'. | string | | null | -| *num_nodes* | The number of nodes in your Cloud Bigtable cluster. | number | | 1 | -| *storage_type* | The storage type to use. | string | | SSD | -| *table_options_defaults* | Default option of tables created in the BigTable instance. | object({...}) | | ... | -| *tables* | Tables to be created in the BigTable instance, options can be null. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| name | The name of the Cloud Bigtable instance. | string | ✓ | | +| project_id | Id of the project where datasets will be created. | string | ✓ | | +| zone | The zone to create the Cloud Bigtable cluster in. | string | ✓ | | +| cluster_id | The ID of the Cloud Bigtable cluster. | string | | "europe-west1" | +| deletion_protection | Whether or not to allow Terraform to destroy the instance. Unless this field is set to false in Terraform state, a terraform destroy or terraform apply that would delete the instance will fail. | | | true | +| display_name | The human-readable display name of the Bigtable instance. | | | null | +| iam | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| instance_type | (deprecated) The instance type to create. One of 'DEVELOPMENT' or 'PRODUCTION'. | string | | null | +| num_nodes | The number of nodes in your Cloud Bigtable cluster. | number | | 1 | +| storage_type | The storage type to use. | string | | "SSD" | +| table_options_defaults | Default option of tables created in the BigTable instance. | object({…}) | | {…} | +| tables | Tables to be created in the BigTable instance, options can be null. | map(object({…})) | | {} | ## Outputs @@ -59,5 +60,7 @@ module "bigtable-instance" { | instance | BigTable intance. | | | table_ids | Map of fully qualified table ids keyed by table name. | | | tables | Table resources. | | + + diff --git a/modules/billing-budget/README.md b/modules/billing-budget/README.md index 253ef071..54f2face 100644 --- a/modules/billing-budget/README.md +++ b/modules/billing-budget/README.md @@ -63,21 +63,22 @@ module "pubsub" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account | Billing account id. | string | ✓ | | -| name | Budget name. | string | ✓ | | -| thresholds | Thresholds percentages at which alerts are sent. Must be a value between 0 and 1. | object({...}) | ✓ | | -| *amount* | Amount in the billing account's currency for the budget. Use 0 to set budget to 100% of last period's spend. | number | | 0 | -| *credit_treatment* | How credits should be treated when determining spend for threshold calculations. Only INCLUDE_ALL_CREDITS or EXCLUDE_ALL_CREDITS are supported | string | | ... | -| *email_recipients* | Emails where budget notifications will be sent. Setting this will create a notification channel for each email in the specified project. | object({...}) | | null | -| *notification_channels* | Monitoring notification channels where to send updates. | list(string) | | null | -| *notify_default_recipients* | Notify Billing Account Administrators and Billing Account Users IAM roles for the target account. | bool | | false | -| *projects* | List of projects of the form projects/{project_number}, specifying that usage from only this set of projects should be included in the budget. Set to null to include all projects linked to the billing account. | list(string) | | null | -| *pubsub_topic* | The ID of the Cloud Pub/Sub topic where budget related messages will be published. | string | | null | -| *services* | List of services of the form services/{service_id}, specifying that usage from only this set of services should be included in the budget. Set to null to include usage for all services. | list(string) | | null | +|---|---|:---:|:---:|:---:| +| billing_account | Billing account id. | string | ✓ | | +| name | Budget name. | string | ✓ | | +| thresholds | Thresholds percentages at which alerts are sent. Must be a value between 0 and 1. | object({…}) | ✓ | | +| amount | Amount in the billing account's currency for the budget. Use 0 to set budget to 100% of last period's spend. | number | | 0 | +| credit_treatment | How credits should be treated when determining spend for threshold calculations. Only INCLUDE_ALL_CREDITS or EXCLUDE_ALL_CREDITS are supported | string | | "INCLUDE_ALL_CREDITS" | +| email_recipients | Emails where budget notifications will be sent. Setting this will create a notification channel for each email in the specified project. | object({…}) | | null | +| notification_channels | Monitoring notification channels where to send updates. | list(string) | | null | +| notify_default_recipients | Notify Billing Account Administrators and Billing Account Users IAM roles for the target account. | bool | | false | +| projects | List of projects of the form projects/{project_number}, specifying that usage from only this set of projects should be included in the budget. Set to null to include all projects linked to the billing account. | list(string) | | null | +| pubsub_topic | The ID of the Cloud Pub/Sub topic where budget related messages will be published. | string | | null | +| services | List of services of the form services/{service_id}, specifying that usage from only this set of services should be included in the budget. Set to null to include usage for all services. | list(string) | | null | ## Outputs @@ -85,4 +86,6 @@ module "pubsub" { |---|---|:---:| | budget | Budget resource. | | | id | Budget ID. | | + + diff --git a/modules/cloud-config-container/coredns/README.md b/modules/cloud-config-container/coredns/README.md index 82ba51d8..03c8d72b 100644 --- a/modules/cloud-config-container/coredns/README.md +++ b/modules/cloud-config-container/coredns/README.md @@ -70,22 +70,22 @@ module "cos-coredns" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| *cloud_config* | Cloud config template path. If null default will be used. | string | | null | -| *config_variables* | Additional variables used to render the cloud-config and CoreDNS templates. | map(any) | | {} | -| *coredns_config* | CoreDNS configuration path, if null default will be used. | string | | null | -| *file_defaults* | Default owner and permissions for files. | object({...}) | | ... | -| *files* | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | map(object({...})) | | {} | -| *test_instance* | Test/development instance attributes, leave null to skip creation. | object({...}) | | null | -| *test_instance_defaults* | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | object({...}) | | ... | +|---|---|:---:|:---:|:---:| +| cloud_config | Cloud config template path. If null default will be used. | string | | null | +| config_variables | Additional variables used to render the cloud-config and CoreDNS templates. | map(any) | | {} | +| coredns_config | CoreDNS configuration path, if null default will be used. | string | | null | +| file_defaults | Default owner and permissions for files. | object({…}) | | {…} | +| files | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | map(object({…})) | | {} | ## Outputs | name | description | sensitive | |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | -| test_instance | Optional test instance name and address | | + + diff --git a/modules/cloud-config-container/cos-generic-metadata/README.md b/modules/cloud-config-container/cos-generic-metadata/README.md index ad5ada41..70192670 100644 --- a/modules/cloud-config-container/cos-generic-metadata/README.md +++ b/modules/cloud-config-container/cos-generic-metadata/README.md @@ -59,28 +59,31 @@ module "cos-envoy" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| container_image | Container image. | string | ✓ | | -| *authenticate_gcr* | Setup docker to pull images from private GCR. Requires at least one user since the token is stored in the home of the first user defined. | bool | | false | -| *boot_commands* | List of cloud-init `bootcmd`s | list(string) | | [] | -| *cloud_config* | Cloud config template path. If provided, takes precedence over all other arguments. | string | | null | -| *config_variables* | Additional variables used to render the template passed via `cloud_config` | map(any) | | {} | -| *container_args* | Arguments for container | string | | | -| *container_name* | Name of the container to be run | string | | container | -| *container_volumes* | List of volumes | list(object({...})) | | [] | -| *docker_args* | Extra arguments to be passed for docker | string | | null | -| *file_defaults* | Default owner and permissions for files. | object({...}) | | ... | -| *files* | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | map(object({...})) | | {} | -| *gcp_logging* | Should container logs be sent to Google Cloud Logging | bool | | true | -| *run_commands* | List of cloud-init `runcmd`s | list(string) | | [] | -| *users* | List of usernames to be created. If provided, first user will be used to run the container. | list(object({...})) | | ... | +|---|---|:---:|:---:|:---:| +| container_image | Container image. | string | ✓ | | +| authenticate_gcr | Setup docker to pull images from private GCR. Requires at least one user since the token is stored in the home of the first user defined. | bool | | false | +| boot_commands | List of cloud-init `bootcmd`s | list(string) | | [] | +| cloud_config | Cloud config template path. If provided, takes precedence over all other arguments. | string | | null | +| config_variables | Additional variables used to render the template passed via `cloud_config` | map(any) | | {} | +| container_args | Arguments for container | string | | "" | +| container_name | Name of the container to be run | string | | "container" | +| container_volumes | List of volumes | list(object({…})) | | [] | +| docker_args | Extra arguments to be passed for docker | string | | null | +| file_defaults | Default owner and permissions for files. | object({…}) | | {…} | +| files | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | map(object({…})) | | {} | +| gcp_logging | Should container logs be sent to Google Cloud Logging | bool | | true | +| run_commands | List of cloud-init `runcmd`s | list(string) | | [] | +| users | List of usernames to be created. If provided, first user will be used to run the container. | list(object({…})) | | […] | ## Outputs | name | description | sensitive | |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | + + diff --git a/modules/cloud-config-container/envoy-traffic-director/README.md b/modules/cloud-config-container/envoy-traffic-director/README.md index 97d5f6e0..99615428 100644 --- a/modules/cloud-config-container/envoy-traffic-director/README.md +++ b/modules/cloud-config-container/envoy-traffic-director/README.md @@ -47,16 +47,19 @@ module "vm-cos" { + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| *envoy_image* | Envoy Proxy container image to use. | string | | envoyproxy/envoy:v1.14.1 | -| *gcp_logging* | Should container logs be sent to Google Cloud Logging | bool | | true | +|---|---|:---:|:---:|:---:| +| envoy_image | Envoy Proxy container image to use. | string | | "envoyproxy/envoy:v1.14.1" | +| gcp_logging | Should container logs be sent to Google Cloud Logging | bool | | true | ## Outputs | name | description | sensitive | |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | + + diff --git a/modules/cloud-config-container/mysql/README.md b/modules/cloud-config-container/mysql/README.md index 1ba160b0..0e894b5b 100644 --- a/modules/cloud-config-container/mysql/README.md +++ b/modules/cloud-config-container/mysql/README.md @@ -75,24 +75,24 @@ module "cos-mysql" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| mysql_password | MySQL root password. If an encrypted password is set, use the kms_config variable to specify KMS configuration. | string | ✓ | | -| *cloud_config* | Cloud config template path. If null default will be used. | string | | null | -| *config_variables* | Additional variables used to render the cloud-config template. | map(any) | | {} | -| *image* | MySQL container image. | string | | mysql:5.7 | -| *kms_config* | Optional KMS configuration to decrypt passed-in password. Leave null if a plaintext password is used. | object({...}) | | null | -| *mysql_config* | MySQL configuration file content, if null container default will be used. | string | | null | -| *mysql_data_disk* | MySQL data disk name in /dev/disk/by-id/ including the google- prefix. If null the boot disk will be used for data. | string | | null | -| *test_instance* | Test/development instance attributes, leave null to skip creation. | object({...}) | | null | -| *test_instance_defaults* | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | object({...}) | | ... | +|---|---|:---:|:---:|:---:| +| mysql_password | MySQL root password. If an encrypted password is set, use the kms_config variable to specify KMS configuration. | string | ✓ | | +| cloud_config | Cloud config template path. If null default will be used. | string | | null | +| config_variables | Additional variables used to render the cloud-config template. | map(any) | | {} | +| image | MySQL container image. | string | | "mysql:5.7" | +| kms_config | Optional KMS configuration to decrypt passed-in password. Leave null if a plaintext password is used. | object({…}) | | null | +| mysql_config | MySQL configuration file content, if null container default will be used. | string | | null | +| mysql_data_disk | MySQL data disk name in /dev/disk/by-id/ including the google- prefix. If null the boot disk will be used for data. | string | | null | ## Outputs | name | description | sensitive | |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | -| test_instance | Optional test instance name and address | | + + diff --git a/modules/cloud-config-container/nginx/README.md b/modules/cloud-config-container/nginx/README.md index 0192527e..1db8ece9 100644 --- a/modules/cloud-config-container/nginx/README.md +++ b/modules/cloud-config-container/nginx/README.md @@ -53,23 +53,23 @@ module "cos-nginx" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| *cloud_config* | Cloud config template path. If null default will be used. | string | | null | -| *config_variables* | Additional variables used to render the cloud-config and Nginx templates. | map(any) | | {} | -| *file_defaults* | Default owner and permissions for files. | object({...}) | | ... | -| *files* | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | map(object({...})) | | {} | -| *image* | Nginx container image. | string | | nginxdemos/hello:plain-text | -| *nginx_config* | Nginx configuration path, if null container default will be used. | string | | null | -| *test_instance* | Test/development instance attributes, leave null to skip creation. | object({...}) | | null | -| *test_instance_defaults* | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | object({...}) | | ... | +|---|---|:---:|:---:|:---:| +| cloud_config | Cloud config template path. If null default will be used. | string | | null | +| config_variables | Additional variables used to render the cloud-config and Nginx templates. | map(any) | | {} | +| file_defaults | Default owner and permissions for files. | object({…}) | | {…} | +| files | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | map(object({…})) | | {} | +| image | Nginx container image. | string | | "nginxdemos/hello:plain-text" | +| nginx_config | Nginx configuration path, if null container default will be used. | string | | null | ## Outputs | name | description | sensitive | |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | -| test_instance | Optional test instance name and address | | + + diff --git a/modules/cloud-config-container/onprem/README.md b/modules/cloud-config-container/onprem/README.md index e735c4bd..560226fb 100644 --- a/modules/cloud-config-container/onprem/README.md +++ b/modules/cloud-config-container/onprem/README.md @@ -60,23 +60,23 @@ module "on-prem" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| vpn_config | VPN configuration, type must be one of 'dynamic' or 'static'. | object({...}) | ✓ | | -| *config_variables* | Additional variables used to render the cloud-config and CoreDNS templates. | map(any) | | {} | -| *coredns_config* | CoreDNS configuration path, if null default will be used. | string | | null | -| *local_ip_cidr_range* | IP CIDR range used for the Docker onprem network. | string | | 192.168.192.0/24 | -| *test_instance* | Test/development instance attributes, leave null to skip creation. | object({...}) | | null | -| *test_instance_defaults* | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | object({...}) | | ... | -| *vpn_dynamic_config* | BGP configuration for dynamic VPN, ignored if VPN type is 'static'. | object({...}) | | ... | -| *vpn_static_ranges* | Remote CIDR ranges for static VPN, ignored if VPN type is 'dynamic'. | list(string) | | ["10.0.0.0/8"] | +|---|---|:---:|:---:|:---:| +| vpn_config | VPN configuration, type must be one of 'dynamic' or 'static'. | object({…}) | ✓ | | +| config_variables | Additional variables used to render the cloud-config and CoreDNS templates. | map(any) | | {} | +| coredns_config | CoreDNS configuration path, if null default will be used. | string | | null | +| local_ip_cidr_range | IP CIDR range used for the Docker onprem network. | string | | "192.168.192.0/24" | +| vpn_dynamic_config | BGP configuration for dynamic VPN, ignored if VPN type is 'static'. | object({…}) | | {…} | +| vpn_static_ranges | Remote CIDR ranges for static VPN, ignored if VPN type is 'dynamic'. | list(string) | | ["10.0.0.0/8"] | ## Outputs | name | description | sensitive | |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | -| test_instance | Optional test instance name and address | | + + diff --git a/modules/cloud-config-container/squid/README.md b/modules/cloud-config-container/squid/README.md index eb9c705d..368ece78 100644 --- a/modules/cloud-config-container/squid/README.md +++ b/modules/cloud-config-container/squid/README.md @@ -57,26 +57,26 @@ module "cos-squid" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| *allow* | List of domains Squid will allow connections to. | list(string) | | [] | -| *clients* | List of CIDR ranges from which Squid will allow connections. | list(string) | | [] | -| *cloud_config* | Cloud config template path. If null default will be used. | string | | null | -| *config_variables* | Additional variables used to render the cloud-config and Squid templates. | map(any) | | {} | -| *default_action* | Default action for domains not matching neither the allow or deny lists | string | | ... | -| *deny* | List of domains Squid will deny connections to. | list(string) | | [] | -| *file_defaults* | Default owner and permissions for files. | object({...}) | | ... | -| *files* | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | map(object({...})) | | {} | -| *squid_config* | Squid configuration path, if null default will be used. | string | | null | -| *test_instance* | Test/development instance attributes, leave null to skip creation. | object({...}) | | null | -| *test_instance_defaults* | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | object({...}) | | ... | +|---|---|:---:|:---:|:---:| +| allow | List of domains Squid will allow connections to. | list(string) | | [] | +| clients | List of CIDR ranges from which Squid will allow connections. | list(string) | | [] | +| cloud_config | Cloud config template path. If null default will be used. | string | | null | +| config_variables | Additional variables used to render the cloud-config and Squid templates. | map(any) | | {} | +| default_action | Default action for domains not matching neither the allow or deny lists | string | | "deny" | +| deny | List of domains Squid will deny connections to. | list(string) | | [] | +| file_defaults | Default owner and permissions for files. | object({…}) | | {…} | +| files | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | map(object({…})) | | {} | +| squid_config | Squid configuration path, if null default will be used. | string | | null | ## Outputs | name | description | sensitive | |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | -| test_instance | Optional test instance name and address | | + + diff --git a/modules/cloud-function/README.md b/modules/cloud-function/README.md index 3cba80bc..77516b90 100644 --- a/modules/cloud-function/README.md +++ b/modules/cloud-function/README.md @@ -156,28 +156,29 @@ module "cf-http" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| bucket_name | Name of the bucket that will be used for the function code. It will be created with prefix prepended if bucket_config is not null. | string | ✓ | | -| bundle_config | Cloud function source folder and generated zip bundle paths. Output path defaults to '/tmp/bundle.zip' if null. | object({...}) | ✓ | | -| name | Name used for cloud function and associated resources. | string | ✓ | | -| project_id | Project id used for all resources. | string | ✓ | | -| *bucket_config* | Enable and configure auto-created bucket. Set fields to null to use defaults. | object({...}) | | null | -| *description* | Optional description. | string | | Terraform managed. | -| *environment_variables* | Cloud function environment variables. | map(string) | | {} | -| *function_config* | Cloud function configuration. | object({...}) | | ... | -| *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *ingress_settings* | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL and ALLOW_INTERNAL_ONLY. | string | | null | -| *labels* | Resource labels | map(string) | | {} | -| *prefix* | Optional prefix used for resource names. | string | | null | -| *region* | Region used for all resources. | string | | europe-west1 | -| *service_account* | Service account email. Unused if service account is auto-created. | string | | null | -| *service_account_create* | Auto-create service account. | bool | | false | -| *trigger_config* | Function trigger configuration. Leave null for HTTP trigger. | object({...}) | | null | -| *vpc_connector* | VPC connector configuration. Set create to 'true' if a new connector needs to be created | object({...}) | | null | -| *vpc_connector_config* | VPC connector network configuration. Must be provided if new VPC connector is being created | object({...}) | | null | +|---|---|:---:|:---:|:---:| +| bucket_name | Name of the bucket that will be used for the function code. It will be created with prefix prepended if bucket_config is not null. | string | ✓ | | +| bundle_config | Cloud function source folder and generated zip bundle paths. Output path defaults to '/tmp/bundle.zip' if null. | object({…}) | ✓ | | +| name | Name used for cloud function and associated resources. | string | ✓ | | +| project_id | Project id used for all resources. | string | ✓ | | +| bucket_config | Enable and configure auto-created bucket. Set fields to null to use defaults. | object({…}) | | null | +| description | Optional description. | string | | "Terraform managed." | +| environment_variables | Cloud function environment variables. | map(string) | | {} | +| function_config | Cloud function configuration. | object({…}) | | {…} | +| iam | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| ingress_settings | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL and ALLOW_INTERNAL_ONLY. | string | | null | +| labels | Resource labels | map(string) | | {} | +| prefix | Optional prefix used for resource names. | string | | null | +| region | Region used for all resources. | string | | "europe-west1" | +| service_account | Service account email. Unused if service account is auto-created. | string | | null | +| service_account_create | Auto-create service account. | bool | | false | +| trigger_config | Function trigger configuration. Leave null for HTTP trigger. | object({…}) | | null | +| vpc_connector | VPC connector configuration. Set create to 'true' if a new connector needs to be created | object({…}) | | null | +| vpc_connector_config | VPC connector network configuration. Must be provided if new VPC connector is being created | object({…}) | | null | ## Outputs @@ -191,4 +192,6 @@ module "cf-http" { | service_account_email | Service account email. | | | service_account_iam_email | Service account email. | | | vpc_connector | VPC connector resource if created. | | + + diff --git a/modules/cloud-identity-group/README.md b/modules/cloud-identity-group/README.md index 25f5e808..c3ff7864 100644 --- a/modules/cloud-identity-group/README.md +++ b/modules/cloud-identity-group/README.md @@ -33,15 +33,16 @@ module "group" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| customer_id | Directory customer ID in the form customers/C0xxxxxxx. | string | ✓ | | -| display_name | Group display name. | string | ✓ | | -| name | Group ID (usually an email). | string | ✓ | | -| *description* | Group description | string | | null | -| *members* | List of group members. | list(string) | | [] | +|---|---|:---:|:---:|:---:| +| customer_id | Directory customer ID in the form customers/C0xxxxxxx. | string | ✓ | | +| display_name | Group display name. | string | ✓ | | +| name | Group ID (usually an email). | string | ✓ | | +| description | Group description | string | | null | +| members | List of group members. | list(string) | | [] | ## Outputs @@ -49,4 +50,6 @@ module "group" { |---|---|:---:| | id | Group ID. | | | name | Group name. | | + + diff --git a/modules/cloud-run/README.md b/modules/cloud-run/README.md index cab35e19..cdc1e0a4 100644 --- a/modules/cloud-run/README.md +++ b/modules/cloud-run/README.md @@ -208,27 +208,28 @@ module "cloud_run" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| containers | Containers | list(object({...})) | ✓ | | -| name | Name used for cloud run service | string | ✓ | | -| project_id | Project id used for all resources. | string | ✓ | | -| *audit_log_triggers* | Event arc triggers (Audit log) | list(object({...})) | | null | -| *iam* | IAM bindings for Cloud Run service in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *ingress_settings* | Ingress settings | string | | null | -| *labels* | Resource labels | map(string) | | {} | -| *prefix* | Optional prefix used for resource names. | string | | null | -| *pubsub_triggers* | Eventarc triggers (Pub/Sub) | list(string) | | null | -| *region* | Region used for all resources. | string | | europe-west1 | -| *revision_name* | Revision name | string | | null | -| *service_account* | Service account email. Unused if service account is auto-created. | string | | null | -| *service_account_create* | Auto-create service account. | bool | | false | -| *traffic* | Traffic | map(number) | | null | -| *volumes* | Volumes | list(object({...})) | | null | -| *vpc_connector* | VPC connector configuration. Set create to 'true' if a new connecto needs to be created | object({...}) | | null | -| *vpc_connector_config* | VPC connector network configuration. Must be provided if new VPC connector is being created | object({...}) | | null | +|---|---|:---:|:---:|:---:| +| containers | Containers | list(object({…})) | ✓ | | +| name | Name used for cloud run service | string | ✓ | | +| project_id | Project id used for all resources. | string | ✓ | | +| audit_log_triggers | Event arc triggers (Audit log) | list(object({…})) | | null | +| iam | IAM bindings for Cloud Run service in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| ingress_settings | Ingress settings | string | | null | +| labels | Resource labels | map(string) | | {} | +| prefix | Optional prefix used for resource names. | string | | null | +| pubsub_triggers | Eventarc triggers (Pub/Sub) | list(string) | | null | +| region | Region used for all resources. | string | | "europe-west1" | +| revision_name | Revision name | string | | null | +| service_account | Service account email. Unused if service account is auto-created. | string | | null | +| service_account_create | Auto-create service account. | bool | | false | +| traffic | Traffic | map(number) | | null | +| volumes | Volumes | list(object({…})) | | null | +| vpc_connector | VPC connector configuration. Set create to 'true' if a new connecto needs to be created | object({…}) | | null | +| vpc_connector_config | VPC connector network configuration. Must be provided if new VPC connector is being created | object({…}) | | null | ## Outputs @@ -240,4 +241,6 @@ module "cloud_run" { | service_account_iam_email | Service account email. | | | service_name | Cloud Run service name | | | vpc_connector | VPC connector resource if created. | | + + diff --git a/modules/cloudsql-instance/README.md b/modules/cloudsql-instance/README.md index 34290eeb..217fcd45 100644 --- a/modules/cloudsql-instance/README.md +++ b/modules/cloudsql-instance/README.md @@ -92,28 +92,29 @@ module "db" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| database_version | Database type and version to create. | string | ✓ | | -| name | Name of primary replica. | string | ✓ | | -| network | VPC self link where the instances will be deployed. Private Service Networking must be enabled and configured in this VPC. | string | ✓ | | -| project_id | The ID of the project where this instances will be created. | string | ✓ | | -| region | Region of the primary replica. | string | ✓ | | -| tier | The machine type to use for the instances. | string | ✓ | | -| *authorized_networks* | Map of NAME=>CIDR_RANGE to allow to connect to the database(s). | map(string) | | null | -| *availability_type* | Availability type for the primary replica. Either `ZONAL` or `REGIONAL` | string | | ZONAL | -| *backup_configuration* | Backup settings for primary instance. Will be automatically enabled if using MySQL with one or more replicas | object({...}) | | ... | -| *databases* | Databases to create once the primary instance is created. | list(string) | | null | -| *deletion_protection* | Allow terraform to delete instances. | bool | | false | -| *disk_size* | Disk size in GB. Set to null to enable autoresize. | number | | null | -| *disk_type* | The type of data disk: `PD_SSD` or `PD_HDD`. | string | | PD_SSD | -| *flags* | Map FLAG_NAME=>VALUE for database-specific tuning. | map(string) | | null | -| *labels* | Labels to be attached to all instances. | map(string) | | null | -| *prefix* | Prefix used to generate instance names. | string | | null | -| *replicas* | Map of NAME=>REGION for additional read replicas. Set to null to disable replica creation. | map(any) | | null | -| *users* | Map of users to create in the primary instance (and replicated to other replicas) in the format USER=>PASSWORD. For MySQL, anything afterr the first `@` (if persent) will be used as the user's host. Set PASSWORD to null if you want to get an autogenerated password | map(string) | | null | +|---|---|:---:|:---:|:---:| +| database_version | Database type and version to create. | string | ✓ | | +| name | Name of primary replica. | string | ✓ | | +| network | VPC self link where the instances will be deployed. Private Service Networking must be enabled and configured in this VPC. | string | ✓ | | +| project_id | The ID of the project where this instances will be created. | string | ✓ | | +| region | Region of the primary replica. | string | ✓ | | +| tier | The machine type to use for the instances. | string | ✓ | | +| authorized_networks | Map of NAME=>CIDR_RANGE to allow to connect to the database(s). | map(string) | | null | +| availability_type | Availability type for the primary replica. Either `ZONAL` or `REGIONAL` | string | | "ZONAL" | +| backup_configuration | Backup settings for primary instance. Will be automatically enabled if using MySQL with one or more replicas | object({…}) | | {…} | +| databases | Databases to create once the primary instance is created. | list(string) | | null | +| deletion_protection | Allow terraform to delete instances. | bool | | false | +| disk_size | Disk size in GB. Set to null to enable autoresize. | number | | null | +| disk_type | The type of data disk: `PD_SSD` or `PD_HDD`. | string | | "PD_SSD" | +| flags | Map FLAG_NAME=>VALUE for database-specific tuning. | map(string) | | null | +| labels | Labels to be attached to all instances. | map(string) | | null | +| prefix | Prefix used to generate instance names. | string | | null | +| replicas | Map of NAME=>REGION for additional read replicas. Set to null to disable replica creation. | map(any) | | null | +| users | Map of users to create in the primary instance (and replicated to other replicas) in the format USER=>PASSWORD. For MySQL, anything afterr the first `@` (if persent) will be used as the user's host. Set PASSWORD to null if you want to get an autogenerated password | map(string) | | null | ## Outputs @@ -129,4 +130,6 @@ module "db" { | self_link | Self link of the primary instance | | | self_links | Self links of all instances | | | user_passwords | Map of containing the password of all users created through terraform. | ✓ | + + diff --git a/modules/compute-mig/README.md b/modules/compute-mig/README.md index f1651bc0..841ff57a 100644 --- a/modules/compute-mig/README.md +++ b/modules/compute-mig/README.md @@ -445,25 +445,26 @@ module "nginx-mig" { ``` + ## 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 health 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 | -| *stateful_config* | Stateful configuration can be done by individual instances or for all instances in the MIG. They key in per_instance_config is the name of the specific instance. The key of the stateful_disks is the 'device_name' field of the resource. Please note that device_name is defined at the OS mount level, unlike the disk name. | object({...}) | | null | -| *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 | +|---|---|:---:|:---:|:---:| +| 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 health 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 | +| stateful_config | Stateful configuration can be done by individual instances or for all instances in the MIG. They key in per_instance_config is the name of the specific instance. The key of the stateful_disks is the 'device_name' field of the resource. Please note that device_name is defined at the OS mount level, unlike the disk name. | object({…}) | | null | +| 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 @@ -472,6 +473,8 @@ module "nginx-mig" { | autoscaler | Auto-created autoscaler resource. | | | group_manager | Instance group resource. | | | health_check | Auto-created health-check resource. | | + + ## TODO diff --git a/modules/compute-vm/README.md b/modules/compute-vm/README.md index c85ca71f..b29ba0a4 100644 --- a/modules/compute-vm/README.md +++ b/modules/compute-vm/README.md @@ -294,39 +294,40 @@ module "instance-group" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | Instance name. | string | ✓ | | -| network_interfaces | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | list(object({...})) | ✓ | | -| project_id | Project id. | string | ✓ | | -| zone | Compute zone. | string | ✓ | | -| *attached_disk_defaults* | Defaults for attached disks options. | object({...}) | | ... | -| *attached_disks* | Additional disks, if options is null defaults will be used in its place. Source type is one of 'image' (zonal disks in vms and template), 'snapshot' (vm), 'existing', and null. | list(object({...})) | | ... | -| *boot_disk* | Boot disk properties. | object({...}) | | ... | -| *boot_disk_delete* | Auto delete boot disk. | bool | | true | -| *can_ip_forward* | Enable IP forwarding. | bool | | false | -| *confidential_compute* | Enable Confidential Compute for these instances. | bool | | false | -| *create_template* | Create instance template instead of instances. | bool | | false | -| *description* | Description of a Compute Instance. | string | | Managed by the compute-vm Terraform module. | -| *enable_display* | Enable virtual display on the instances | bool | | false | -| *encryption* | Encryption options. Only one of kms_key_self_link and disk_encryption_key_raw may be set. If needed, you can specify to encrypt or not the boot disk. | 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 | -| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *instance_type* | Instance type. | string | | f1-micro | -| *labels* | Instance labels. | map(string) | | {} | -| *metadata* | Instance metadata. | map(string) | | {} | -| *min_cpu_platform* | Minimum CPU platform. | string | | null | -| *network_interface_options* | Network interfaces extended options. The key is the index of the inteface to configure. The value is an object with alias_ips and nic_type. Set alias_ips or nic_type to null if you need only one of them. | map(object({...})) | | {} | -| *options* | Instance options. | object({...}) | | ... | -| *scratch_disks* | Scratch disks configuration. | object({...}) | | ... | -| *service_account* | Service account email. Unused if service account is auto-created. | string | | null | -| *service_account_create* | Auto-create service account. | bool | | false | -| *service_account_scopes* | Scopes applied to service account. | list(string) | | [] | -| *shielded_config* | Shielded VM configuration of the instances. | object({...}) | | null | -| *tags* | Instance tags. | list(string) | | [] | +|---|---|:---:|:---:|:---:| +| name | Instance name. | string | ✓ | | +| network_interfaces | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | list(object({…})) | ✓ | | +| project_id | Project id. | string | ✓ | | +| zone | Compute zone. | string | ✓ | | +| attached_disk_defaults | Defaults for attached disks options. | object({…}) | | {…} | +| attached_disks | Additional disks, if options is null defaults will be used in its place. Source type is one of 'image' (zonal disks in vms and template), 'snapshot' (vm), 'existing', and null. | list(object({…})) | | [] | +| boot_disk | Boot disk properties. | object({…}) | | {…} | +| boot_disk_delete | Auto delete boot disk. | bool | | true | +| can_ip_forward | Enable IP forwarding. | bool | | false | +| confidential_compute | Enable Confidential Compute for these instances. | bool | | false | +| create_template | Create instance template instead of instances. | bool | | false | +| description | Description of a Compute Instance. | string | | "Managed by the compute-vm Terraform module." | +| enable_display | Enable virtual display on the instances | bool | | false | +| encryption | Encryption options. Only one of kms_key_self_link and disk_encryption_key_raw may be set. If needed, you can specify to encrypt or not the boot disk. | 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 | +| iam | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| instance_type | Instance type. | string | | "f1-micro" | +| labels | Instance labels. | map(string) | | {} | +| metadata | Instance metadata. | map(string) | | {} | +| min_cpu_platform | Minimum CPU platform. | string | | null | +| network_interface_options | Network interfaces extended options. The key is the index of the inteface to configure. The value is an object with alias_ips and nic_type. Set alias_ips or nic_type to null if you need only one of them. | map(object({…})) | | {} | +| options | Instance options. | object({…}) | | {…} | +| scratch_disks | Scratch disks configuration. | object({…}) | | {…} | +| service_account | Service account email. Unused if service account is auto-created. | string | | null | +| service_account_create | Auto-create service account. | bool | | false | +| service_account_scopes | Scopes applied to service account. | list(string) | | [] | +| shielded_config | Shielded VM configuration of the instances. | object({…}) | | null | +| tags | Instance tags. | list(string) | | [] | ## Outputs @@ -342,6 +343,8 @@ module "instance-group" { | service_account_iam_email | Service account email. | | | template | Template resource. | | | template_name | Template name. | | + + ## TODO diff --git a/modules/container-registry/README.md b/modules/container-registry/README.md index 92cdb896..04723dc9 100644 --- a/modules/container-registry/README.md +++ b/modules/container-registry/README.md @@ -17,17 +17,20 @@ module "container_registry" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Registry project id. | string | ✓ | | -| *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *location* | Registry location. Can be US, EU, ASIA or empty | string | | | +|---|---|:---:|:---:|:---:| +| project_id | Registry project id. | string | ✓ | | +| iam | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| location | Registry location. Can be US, EU, ASIA or empty | string | | "" | ## Outputs | name | description | sensitive | |---|---|:---:| | bucket_id | ID of the GCS bucket created | | + + diff --git a/modules/datafusion/README.md b/modules/datafusion/README.md index 417d4de4..c8e6013e 100644 --- a/modules/datafusion/README.md +++ b/modules/datafusion/README.md @@ -35,24 +35,25 @@ module "datafusion" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | Name of the DataFusion instance. | string | ✓ | | -| network | Name of the network in the project with which the tenant project will be peered for executing pipelines in the form of projects/{project-id}/global/networks/{network} | string | ✓ | | -| project_id | Project ID. | string | ✓ | | -| region | DataFusion region. | string | ✓ | | -| *description* | DataFuzion instance description. | string | | Terraform managed. | -| *enable_stackdriver_logging* | Option to enable Stackdriver Logging. | bool | | false | -| *enable_stackdriver_monitoring* | Option to enable Stackdriver Monitorig. | bool | | false | -| *firewall_create* | Create Network firewall rules to enable SSH. | bool | | true | -| *ip_allocation* | Ip allocated for datafusion instance when not using the auto created one and created outside of the module. | string | | null | -| *ip_allocation_create* | Create Ip range for datafusion instance. | bool | | true | -| *labels* | The resource labels for instance to use to annotate any related underlying resources, such as Compute Engine VMs. | map(string) | | {} | -| *network_peering* | Create Network peering between project and DataFusion tenant project. | bool | | true | -| *private_instance* | Create private instance. | bool | | true | -| *type* | Datafusion Instance type. It can be BASIC or ENTERPRISE (default value). | string | | ENTERPRISE | +|---|---|:---:|:---:|:---:| +| name | Name of the DataFusion instance. | string | ✓ | | +| network | Name of the network in the project with which the tenant project will be peered for executing pipelines in the form of projects/{project-id}/global/networks/{network} | string | ✓ | | +| project_id | Project ID. | string | ✓ | | +| region | DataFusion region. | string | ✓ | | +| description | DataFuzion instance description. | string | | "Terraform managed." | +| enable_stackdriver_logging | Option to enable Stackdriver Logging. | bool | | false | +| enable_stackdriver_monitoring | Option to enable Stackdriver Monitorig. | bool | | false | +| firewall_create | Create Network firewall rules to enable SSH. | bool | | true | +| ip_allocation | Ip allocated for datafusion instance when not using the auto created one and created outside of the module. | string | | null | +| ip_allocation_create | Create Ip range for datafusion instance. | bool | | true | +| labels | The resource labels for instance to use to annotate any related underlying resources, such as Compute Engine VMs. | map(string) | | {} | +| network_peering | Create Network peering between project and DataFusion tenant project. | bool | | true | +| private_instance | Create private instance. | bool | | true | +| type | Datafusion Instance type. It can be BASIC or ENTERPRISE (default value). | string | | "ENTERPRISE" | ## Outputs @@ -64,4 +65,6 @@ module "datafusion" { | service_account | DataFusion Service Account. | | | service_endpoint | DataFusion Service Endpoint. | | | version | DataFusion version. | | + + diff --git a/modules/dns/README.md b/modules/dns/README.md index d2623089..95d908d7 100644 --- a/modules/dns/README.md +++ b/modules/dns/README.md @@ -54,24 +54,25 @@ module "private-dns" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| domain | Zone domain, must end with a period. | string | ✓ | | -| name | Zone name, must be unique within the project. | string | ✓ | | -| project_id | Project id for the zone. | string | ✓ | | -| *client_networks* | List of VPC self links that can see this zone. | list(string) | | [] | -| *default_key_specs_key* | DNSSEC default key signing specifications: algorithm, key_length, key_type, kind. | any | | {} | -| *default_key_specs_zone* | DNSSEC default zone signing specifications: algorithm, key_length, key_type, kind. | any | | {} | -| *description* | Domain description. | string | | Terraform managed. | -| *dnssec_config* | DNSSEC configuration: kind, non_existence, state. | any | | {} | -| *forwarders* | Map of {IPV4_ADDRESS => FORWARDING_PATH} for 'forwarding' zone types. Path can be 'default', 'private', or null for provider default. | map(string) | | {} | -| *peer_network* | Peering network self link, only valid for 'peering' zone types. | string | | null | -| *recordsets* | Map of DNS recordsets in \"type name\" => {ttl, [records]} format. | map(object({...})) | | ... | -| *service_directory_namespace* | Service directory namespace id (URL), only valid for 'service-directory' zone types. | string | | null | -| *type* | Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'. | string | | ... | -| *zone_create* | Create zone. When set to false, uses a data source to reference existing zone. | bool | | true | +|---|---|:---:|:---:|:---:| +| domain | Zone domain, must end with a period. | string | ✓ | | +| name | Zone name, must be unique within the project. | string | ✓ | | +| project_id | Project id for the zone. | string | ✓ | | +| client_networks | List of VPC self links that can see this zone. | list(string) | | [] | +| default_key_specs_key | DNSSEC default key signing specifications: algorithm, key_length, key_type, kind. | any | | {} | +| default_key_specs_zone | DNSSEC default zone signing specifications: algorithm, key_length, key_type, kind. | any | | {} | +| description | Domain description. | string | | "Terraform managed." | +| dnssec_config | DNSSEC configuration: kind, non_existence, state. | any | | {} | +| forwarders | Map of {IPV4_ADDRESS => FORWARDING_PATH} for 'forwarding' zone types. Path can be 'default', 'private', or null for provider default. | map(string) | | {} | +| peer_network | Peering network self link, only valid for 'peering' zone types. | string | | null | +| recordsets | Map of DNS recordsets in \"type name\" => {ttl, [records]} format. | map(object({…})) | | {} | +| service_directory_namespace | Service directory namespace id (URL), only valid for 'service-directory' zone types. | string | | null | +| type | Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'. | string | | "private" | +| zone_create | Create zone. When set to false, uses a data source to reference existing zone. | bool | | true | ## Outputs @@ -83,4 +84,6 @@ module "private-dns" { | name_servers | The DNS zone name servers. | | | type | The DNS zone type. | | | zone | DNS zone resource. | | + + diff --git a/modules/endpoints/README.md b/modules/endpoints/README.md index 8e7bb44f..8d5636f3 100644 --- a/modules/endpoints/README.md +++ b/modules/endpoints/README.md @@ -24,15 +24,16 @@ module "endpoint" { [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. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| openapi_config | The configuration for an OpenAPI endopoint. Either this or grpc_config must be specified. | object({...}) | ✓ | | -| service_name | The name of the service. Usually of the form '$apiname.endpoints.$projectid.cloud.goog'. | string | ✓ | | -| *grpc_config* | The configuration for a gRPC enpoint. Either this or openapi_config must be specified. | object({...}) | | null | -| *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *project_id* | The project ID that the service belongs to. | string | | null | +|---|---|:---:|:---:|:---:| +| openapi_config | The configuration for an OpenAPI endopoint. Either this or grpc_config must be specified. | object({…}) | ✓ | | +| service_name | The name of the service. Usually of the form '$apiname.endpoints.$projectid.cloud.goog'. | string | ✓ | | +| grpc_config | The configuration for a gRPC enpoint. Either this or openapi_config must be specified. | object({…}) | | null | +| iam | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| project_id | The project ID that the service belongs to. | string | | null | ## Outputs @@ -41,4 +42,6 @@ module "endpoint" { | endpoints | A list of Endpoint objects. | | | endpoints_service | The Endpoint service resource. | | | service_name | The name of the service.. | | + + diff --git a/modules/folder/README.md b/modules/folder/README.md index 612d092b..3b0ae8b7 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -165,24 +165,25 @@ module "folder2" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| *contacts* | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES | map(list(string)) | | {} | -| *firewall_policies* | Hierarchical firewall policies created in this folder. | map(map(object({...}))) | | {} | -| *firewall_policy_attachments* | List of hierarchical firewall policy IDs to attached to this folder. | map(string) | | {} | -| *firewall_policy_factory* | Configuration for the firewall policy factory. | object({...}) | | null | -| *folder_create* | Create folder. When set to false, uses id to reference an existing folder. | bool | | true | -| *group_iam* | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string)) | | {} | -| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *id* | Folder ID in case you use folder_create=false | string | | null | -| *logging_exclusions* | Logging exclusions for this folder in the form {NAME -> FILTER}. | map(string) | | {} | -| *logging_sinks* | Logging sinks to create for this folder. | map(object({...})) | | {} | -| *name* | Folder name. | string | | null | -| *parent* | Parent in folders/folder_id or organizations/org_id format. | string | | ... | -| *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | -| *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| contacts | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES | map(list(string)) | | {} | +| firewall_policies | Hierarchical firewall policies created in this folder. | map(map(object({…}))) | | {} | +| firewall_policy_attachments | List of hierarchical firewall policy IDs to attached to this folder. | map(string) | | {} | +| firewall_policy_factory | Configuration for the firewall policy factory. | object({…}) | | null | +| folder_create | Create folder. When set to false, uses id to reference an existing folder. | bool | | true | +| group_iam | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string)) | | {} | +| iam | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| id | Folder ID in case you use folder_create=false | string | | null | +| logging_exclusions | Logging exclusions for this folder in the form {NAME -> FILTER}. | map(string) | | {} | +| logging_sinks | Logging sinks to create for this folder. | map(object({…})) | | {} | +| name | Folder name. | string | | null | +| parent | Parent in folders/folder_id or organizations/org_id format. | string | | null | +| policy_boolean | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | +| policy_list | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | map(object({…})) | | {} | ## Outputs @@ -194,4 +195,6 @@ module "folder2" { | id | Folder id. | | | name | Folder name. | | | sink_writer_identities | Writer identities created for each sink. | | + + diff --git a/modules/folders-unit/README.md b/modules/folders-unit/README.md index e82142f3..a3b6d2a2 100644 --- a/modules/folders-unit/README.md +++ b/modules/folders-unit/README.md @@ -25,24 +25,25 @@ module "folders-unit" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| automation_project_id | Project id used for automation service accounts. | string | ✓ | | -| billing_account_id | Country billing account account. | string | ✓ | | -| name | Top folder name. | string | ✓ | | -| organization_id | Organization id in organizations/nnnnnn format. | string | ✓ | | -| root_node | Root node in folders/folder_id or organizations/org_id format. | string | ✓ | | -| short_name | Short name used as GCS bucket and service account prefixes, do not use capital letters or spaces. | string | ✓ | | -| *environments* | Unit environments short names. | map(string) | | ... | -| *gcs_defaults* | Defaults use for the state GCS buckets. | map(string) | | ... | -| *iam* | IAM bindings for the top-level folder in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *iam_billing_config* | Grant billing user role to service accounts, defaults to granting on the billing account. | object({...}) | | ... | -| *iam_enviroment_roles* | IAM roles granted to the environment service account on the environment sub-folder. | list(string) | | ... | -| *iam_xpn_config* | Grant Shared VPC creation roles to service accounts, defaults to granting at folder level. | object({...}) | | ... | -| *prefix* | Optional prefix used for GCS bucket names to ensure uniqueness. | string | | null | -| *service_account_keys* | Generate and store service account keys in the state file. | bool | | false | +|---|---|:---:|:---:|:---:| +| automation_project_id | Project id used for automation service accounts. | string | ✓ | | +| billing_account_id | Country billing account account. | string | ✓ | | +| name | Top folder name. | string | ✓ | | +| organization_id | Organization id in organizations/nnnnnn format. | string | ✓ | | +| root_node | Root node in folders/folder_id or organizations/org_id format. | string | ✓ | | +| short_name | Short name used as GCS bucket and service account prefixes, do not use capital letters or spaces. | string | ✓ | | +| environments | Unit environments short names. | map(string) | | {…} | +| gcs_defaults | Defaults use for the state GCS buckets. | map(string) | | {…} | +| iam | IAM bindings for the top-level folder in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| iam_billing_config | Grant billing user role to service accounts, defaults to granting on the billing account. | object({…}) | | {…} | +| iam_enviroment_roles | IAM roles granted to the environment service account on the environment sub-folder. | list(string) | | […] | +| iam_xpn_config | Grant Shared VPC creation roles to service accounts, defaults to granting at folder level. | object({…}) | | {…} | +| prefix | Optional prefix used for GCS bucket names to ensure uniqueness. | string | | null | +| service_account_keys | Generate and store service account keys in the state file. | bool | | false | ## Outputs @@ -53,4 +54,6 @@ module "folders-unit" { | env_sa_keys | Unit environments service account keys. | ✓ | | env_service_accounts | Unit environments service accounts. | | | unit_folder | Unit top level folder. | | + + diff --git a/modules/gcs/README.md b/modules/gcs/README.md index 60de84d1..a7f8297e 100644 --- a/modules/gcs/README.md +++ b/modules/gcs/README.md @@ -108,27 +108,28 @@ module "bucket-gcs-notification" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | Bucket name suffix. | string | ✓ | | -| project_id | Bucket project id. | string | ✓ | | -| *cors* | CORS configuration for the bucket. Defaults to null. | object({...}) | | null | -| *encryption_key* | KMS key that will be used for encryption. | string | | null | -| *force_destroy* | Optional map to set force destroy keyed by name, defaults to false. | bool | | false | -| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *labels* | Labels to be attached to all buckets. | map(string) | | {} | -| *lifecycle_rule* | Bucket lifecycle rule | object({...}) | | null | -| *location* | Bucket location. | string | | EU | -| *logging_config* | Bucket logging configuration. | object({...}) | | null | -| *notification_config* | GCS Notification configuration. | object({...}) | | null | -| *prefix* | Prefix used to generate the bucket name. | string | | null | -| *retention_policy* | Bucket retention policy. | object({...}) | | null | -| *storage_class* | Bucket storage class. | string | | ... | -| *uniform_bucket_level_access* | Allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API). | bool | | true | -| *versioning* | Enable versioning, defaults to false. | bool | | false | -| *website* | Bucket website. | object({...}) | | null | +|---|---|:---:|:---:|:---:| +| name | Bucket name suffix. | string | ✓ | | +| project_id | Bucket project id. | string | ✓ | | +| cors | CORS configuration for the bucket. Defaults to null. | object({…}) | | null | +| encryption_key | KMS key that will be used for encryption. | string | | null | +| force_destroy | Optional map to set force destroy keyed by name, defaults to false. | bool | | false | +| iam | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| labels | Labels to be attached to all buckets. | map(string) | | {} | +| lifecycle_rule | Bucket lifecycle rule | object({…}) | | null | +| location | Bucket location. | string | | "EU" | +| logging_config | Bucket logging configuration. | object({…}) | | null | +| notification_config | GCS Notification configuration. | object({…}) | | null | +| prefix | Prefix used to generate the bucket name. | string | | null | +| retention_policy | Bucket retention policy. | object({…}) | | null | +| storage_class | Bucket storage class. | string | | "MULTI_REGIONAL" | +| uniform_bucket_level_access | Allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API). | bool | | true | +| versioning | Enable versioning, defaults to false. | bool | | false | +| website | Bucket website. | object({…}) | | null | ## Outputs @@ -139,4 +140,6 @@ module "bucket-gcs-notification" { | notification | GCS Notification self link. | | | topic | Topic ID used by GCS. | | | url | Bucket URL. | | + + diff --git a/modules/gke-cluster/README.md b/modules/gke-cluster/README.md index dc6d18d7..20c98797 100644 --- a/modules/gke-cluster/README.md +++ b/modules/gke-cluster/README.md @@ -64,48 +64,49 @@ module "cluster-1" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| location | Cluster zone or region. | string | ✓ | | -| name | Cluster name. | string | ✓ | | -| network | Name or self link of the VPC used for the cluster. Use the self link for Shared VPC. | string | ✓ | | -| project_id | Cluster project id. | string | ✓ | | -| 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({...}) | | ... | -| *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({...}) | | ... | -| *default_max_pods_per_node* | Maximum number of pods per node in this cluster. | number | | 110 | -| *description* | Cluster description. | string | | null | -| *dns_config* | Configuration for Using Cloud DNS for GKE. | object({...}) | | ... | -| *enable_autopilot* | Create cluster in autopilot mode. With autopilot there's no need to create node-pools and some features are not supported (e.g. setting default_max_pods_per_node) | bool | | false | -| *enable_binary_authorization* | Enable Google Binary Authorization. | bool | | null | -| *enable_dataplane_v2* | Enable Dataplane V2 on the cluster, will disable network_policy addons config | bool | | false | -| *enable_intranode_visibility* | Enable intra-node visibility to make same node pod to pod traffic visible. | bool | | null | -| *enable_l4_ilb_subsetting* | Enable L4ILB Subsetting. | bool | | null | -| *enable_shielded_nodes* | Enable Shielded Nodes features on all nodes in this cluster. | bool | | null | -| *enable_tpu* | Enable Cloud TPU resources in this cluster. | bool | | null | -| *labels* | Cluster resource labels. | map(string) | | null | -| *logging_config* | Logging configuration (enabled components). | list(string) | | null | -| *logging_service* | Logging service (disable with an empty string). | string | | logging.googleapis.com/kubernetes | -| *maintenance_config* | Maintenance window configuration | object({...}) | | ... | -| *master_authorized_ranges* | External Ip address ranges that can access the Kubernetes cluster master through HTTPS. | map(string) | | {} | -| *min_master_version* | Minimum version of the master, defaults to the version of the most recent official release. | string | | null | -| *monitoring_config* | Monitoring configuration (enabled components). | list(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) | | [] | -| *notification_config* | GKE Cluster upgrade notifications via PubSub. | bool | | false | -| *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, 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 | -| *workload_identity* | Enable the Workload Identity feature. | bool | | true | +|---|---|:---:|:---:|:---:| +| location | Cluster zone or region. | string | ✓ | | +| name | Cluster name. | string | ✓ | | +| network | Name or self link of the VPC used for the cluster. Use the self link for Shared VPC. | string | ✓ | | +| project_id | Cluster project id. | string | ✓ | | +| 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({…}) | | {…} | +| 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({…}) | | {…} | +| default_max_pods_per_node | Maximum number of pods per node in this cluster. | number | | 110 | +| description | Cluster description. | string | | null | +| dns_config | Configuration for Using Cloud DNS for GKE. | object({…}) | | {…} | +| enable_autopilot | Create cluster in autopilot mode. With autopilot there's no need to create node-pools and some features are not supported (e.g. setting default_max_pods_per_node) | bool | | false | +| enable_binary_authorization | Enable Google Binary Authorization. | bool | | null | +| enable_dataplane_v2 | Enable Dataplane V2 on the cluster, will disable network_policy addons config | bool | | false | +| enable_intranode_visibility | Enable intra-node visibility to make same node pod to pod traffic visible. | bool | | null | +| enable_l4_ilb_subsetting | Enable L4ILB Subsetting. | bool | | null | +| enable_shielded_nodes | Enable Shielded Nodes features on all nodes in this cluster. | bool | | null | +| enable_tpu | Enable Cloud TPU resources in this cluster. | bool | | null | +| labels | Cluster resource labels. | map(string) | | null | +| logging_config | Logging configuration (enabled components). | list(string) | | null | +| logging_service | Logging service (disable with an empty string). | string | | "logging.googleapis.com/kubernetes" | +| maintenance_config | Maintenance window configuration | object({…}) | | {…} | +| master_authorized_ranges | External Ip address ranges that can access the Kubernetes cluster master through HTTPS. | map(string) | | {} | +| min_master_version | Minimum version of the master, defaults to the version of the most recent official release. | string | | null | +| monitoring_config | Monitoring configuration (enabled components). | list(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) | | [] | +| notification_config | GKE Cluster upgrade notifications via PubSub. | bool | | false | +| 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, 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 | +| workload_identity | Enable the Workload Identity feature. | bool | | true | ## Outputs @@ -118,4 +119,6 @@ module "cluster-1" { | master_version | Master version. | | | name | Cluster name. | | | notifications | GKE PubSub notifications topic. | | + + diff --git a/modules/gke-nodepool/README.md b/modules/gke-nodepool/README.md index f5ea3a6e..12a22da1 100644 --- a/modules/gke-nodepool/README.md +++ b/modules/gke-nodepool/README.md @@ -35,43 +35,44 @@ module "cluster-1-nodepool-1" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| cluster_name | Cluster name. | string | ✓ | | -| location | Cluster location. | string | ✓ | | -| project_id | Cluster project id. | string | ✓ | | -| *autoscaling_config* | Optional autoscaling configuration. | object({...}) | | null | -| *gke_version* | Kubernetes nodes version. Ignored if auto_upgrade is set in management_config. | string | | null | -| *initial_node_count* | Initial number of nodes for the pool. | number | | 1 | -| *kubelet_config* | Kubelet configuration. | object({...}) | | null | -| *linux_node_config_sysctls* | Linux node configuration. | map(string) | | null | -| *management_config* | Optional node management configuration. | object({...}) | | null | -| *max_pods_per_node* | Maximum number of pods per node. | number | | null | -| *name* | Optional nodepool name. | string | | null | -| *node_boot_disk_kms_key* | Customer Managed Encryption Key used to encrypt the boot disk attached to each node | string | | null | -| *node_count* | Number of nodes per instance group, can be updated after creation. Ignored when autoscaling is set. | number | | null | -| *node_disk_size* | Node disk size, defaults to 100GB. | number | | 100 | -| *node_disk_type* | Node disk type, defaults to pd-standard. | string | | pd-standard | -| *node_guest_accelerator* | Map of type and count of attached accelerator cards. | map(number) | | {} | -| *node_image_type* | Nodes image type. | string | | null | -| *node_labels* | Kubernetes labels attached to nodes. | map(string) | | {} | -| *node_local_ssd_count* | Number of local SSDs attached to nodes. | number | | 0 | -| *node_locations* | Optional list of zones in which nodes should be located. Uses cluster locations if unset. | list(string) | | null | -| *node_machine_type* | Nodes machine type. | string | | n1-standard-1 | -| *node_metadata* | Metadata key/value pairs assigned to nodes. Set disable-legacy-endpoints to true when using this variable. | map(string) | | null | -| *node_min_cpu_platform* | Minimum CPU platform for nodes. | string | | null | -| *node_preemptible* | Use preemptible VMs for nodes. | bool | | null | -| *node_sandbox_config* | GKE Sandbox configuration. Needs image_type set to COS_CONTAINERD and node_version set to 1.12.7-gke.17 when using this variable. | string | | null | -| *node_service_account* | Service account email. Unused if service account is auto-created. | string | | null | -| *node_service_account_create* | Auto-create service account. | bool | | false | -| *node_service_account_scopes* | Scopes applied to service account. Default to: 'cloud-platform' when creating a service account; 'devstorage.read_only', 'logging.write', 'monitoring.write' otherwise. | list(string) | | [] | -| *node_shielded_instance_config* | Shielded instance options. | object({...}) | | null | -| *node_tags* | Network tags applied to nodes. | list(string) | | null | -| *node_taints* | Kubernetes taints applied to nodes. E.g. type=blue:NoSchedule | list(string) | | [] | -| *upgrade_config* | Optional node upgrade configuration. | object({...}) | | null | -| *workload_metadata_config* | Metadata configuration to expose to workloads on the node pool. | string | | GKE_METADATA | +|---|---|:---:|:---:|:---:| +| cluster_name | Cluster name. | string | ✓ | | +| location | Cluster location. | string | ✓ | | +| project_id | Cluster project id. | string | ✓ | | +| autoscaling_config | Optional autoscaling configuration. | object({…}) | | null | +| gke_version | Kubernetes nodes version. Ignored if auto_upgrade is set in management_config. | string | | null | +| initial_node_count | Initial number of nodes for the pool. | number | | 1 | +| kubelet_config | Kubelet configuration. | object({…}) | | null | +| linux_node_config_sysctls | Linux node configuration. | map(string) | | null | +| management_config | Optional node management configuration. | object({…}) | | null | +| max_pods_per_node | Maximum number of pods per node. | number | | null | +| name | Optional nodepool name. | string | | null | +| node_boot_disk_kms_key | Customer Managed Encryption Key used to encrypt the boot disk attached to each node | string | | null | +| node_count | Number of nodes per instance group, can be updated after creation. Ignored when autoscaling is set. | number | | null | +| node_disk_size | Node disk size, defaults to 100GB. | number | | 100 | +| node_disk_type | Node disk type, defaults to pd-standard. | string | | "pd-standard" | +| node_guest_accelerator | Map of type and count of attached accelerator cards. | map(number) | | {} | +| node_image_type | Nodes image type. | string | | null | +| node_labels | Kubernetes labels attached to nodes. | map(string) | | {} | +| node_local_ssd_count | Number of local SSDs attached to nodes. | number | | 0 | +| node_locations | Optional list of zones in which nodes should be located. Uses cluster locations if unset. | list(string) | | null | +| node_machine_type | Nodes machine type. | string | | "n1-standard-1" | +| node_metadata | Metadata key/value pairs assigned to nodes. Set disable-legacy-endpoints to true when using this variable. | map(string) | | null | +| node_min_cpu_platform | Minimum CPU platform for nodes. | string | | null | +| node_preemptible | Use preemptible VMs for nodes. | bool | | null | +| node_sandbox_config | GKE Sandbox configuration. Needs image_type set to COS_CONTAINERD and node_version set to 1.12.7-gke.17 when using this variable. | string | | null | +| node_service_account | Service account email. Unused if service account is auto-created. | string | | null | +| node_service_account_create | Auto-create service account. | bool | | false | +| node_service_account_scopes | Scopes applied to service account. Default to: 'cloud-platform' when creating a service account; 'devstorage.read_only', 'logging.write', 'monitoring.write' otherwise. | list(string) | | [] | +| node_shielded_instance_config | Shielded instance options. | object({…}) | | null | +| node_tags | Network tags applied to nodes. | list(string) | | null | +| node_taints | Kubernetes taints applied to nodes. E.g. type=blue:NoSchedule | list(string) | | [] | +| upgrade_config | Optional node upgrade configuration. | object({…}) | | null | +| workload_metadata_config | Metadata configuration to expose to workloads on the node pool. | string | | "GKE_METADATA" | ## Outputs @@ -81,4 +82,6 @@ module "cluster-1-nodepool-1" { | service_account | Service account resource. | | | service_account_email | Service account email. | | | service_account_iam_email | Service account email. | | + + diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index a1b1ed9c..ee443ebb 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -26,24 +26,25 @@ module "myproject-default-service-accounts" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | Name of the service account to create. | string | ✓ | | -| project_id | Project id where service account will be created. | string | ✓ | | -| *description* | Optional description. | string | | null | -| *display_name* | Display name of the service account to create. | string | | Terraform-managed. | -| *generate_key* | Generate a key for service account. | bool | | false | -| *iam* | IAM bindings on the service account in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *iam_billing_roles* | Project roles granted to the service account, by billing account id. | map(list(string)) | | {} | -| *iam_folder_roles* | Project roles granted to the service account, by folder id. | map(list(string)) | | {} | -| *iam_organization_roles* | Project roles granted to the service account, by organization id. | map(list(string)) | | {} | -| *iam_project_roles* | Project roles granted to the service account, by project id. | map(list(string)) | | {} | -| *iam_storage_roles* | Storage roles granted to the service account, by bucket name. | map(list(string)) | | {} | -| *prefix* | Prefix applied to service account names. | string | | null | -| *public_keys_directory* | Path to public keys data files to upload to the service account (should have `.pem` extension). | string | | | -| *service_account_create* | Create service account. When set to false, uses a data source to reference an existing service account. | bool | | true | +|---|---|:---:|:---:|:---:| +| name | Name of the service account to create. | string | ✓ | | +| project_id | Project id where service account will be created. | string | ✓ | | +| description | Optional description. | string | | null | +| display_name | Display name of the service account to create. | string | | "Terraform-managed." | +| generate_key | Generate a key for service account. | bool | | false | +| iam | IAM bindings on the service account in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| iam_billing_roles | Project roles granted to the service account, by billing account id. | map(list(string)) | | {} | +| iam_folder_roles | Project roles granted to the service account, by folder id. | map(list(string)) | | {} | +| iam_organization_roles | Project roles granted to the service account, by organization id. | map(list(string)) | | {} | +| iam_project_roles | Project roles granted to the service account, by project id. | map(list(string)) | | {} | +| iam_storage_roles | Storage roles granted to the service account, by bucket name. | map(list(string)) | | {} | +| prefix | Prefix applied to service account names. | string | | null | +| public_keys_directory | Path to public keys data files to upload to the service account (should have `.pem` extension). | string | | "" | +| service_account_create | Create service account. When set to false, uses a data source to reference an existing service account. | bool | | true | ## Outputs @@ -54,4 +55,6 @@ module "myproject-default-service-accounts" { | key | Service account key. | ✓ | | service_account | Service account resource. | | | service_account_credentials | Service account json credential templates for uploaded public keys data. | | + + diff --git a/modules/kms/README.md b/modules/kms/README.md index 43618e32..9b2b55db 100644 --- a/modules/kms/README.md +++ b/modules/kms/README.md @@ -69,18 +69,19 @@ module "kms" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| keyring | Keyring attributes. | object({...}) | ✓ | | -| project_id | Project id where the keyring will be created. | string | ✓ | | -| *iam* | Keyring IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *key_iam* | Key IAM bindings for topic in {KEY => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | -| *key_purpose* | Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | map(object({...})) | | {} | -| *key_purpose_defaults* | Defaults used for key purpose when not defined at the key level. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | object({...}) | | ... | -| *keyring_create* | Set to false to manage keys and IAM bindings in an existing keyring. | bool | | true | -| *keys* | Key names and base attributes. Set attributes to null if not needed. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| keyring | Keyring attributes. | object({…}) | ✓ | | +| project_id | Project id where the keyring will be created. | string | ✓ | | +| iam | Keyring IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| key_iam | Key IAM bindings for topic in {KEY => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | +| key_purpose | Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | map(object({…})) | | {} | +| key_purpose_defaults | Defaults used for key purpose when not defined at the key level. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | object({…}) | | {…} | +| keyring_create | Set to false to manage keys and IAM bindings in an existing keyring. | bool | | true | +| keys | Key names and base attributes. Set attributes to null if not needed. | map(object({…})) | | {} | ## Outputs @@ -92,4 +93,6 @@ module "kms" { | keys | Key resources. | | | location | Keyring location. | | | name | Keyring name. | | + + diff --git a/modules/logging-bucket/README.md b/modules/logging-bucket/README.md index 257fafdc..08c811c4 100644 --- a/modules/logging-bucket/README.md +++ b/modules/logging-bucket/README.md @@ -42,20 +42,23 @@ module "bucket-default" { + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| id | Name of the logging bucket. | string | ✓ | | -| parent | ID of the parentresource containing the bucket in the format 'project_id' 'folders/folder_id', 'organizations/organization_id' or 'billing_account_id'. | string | ✓ | | -| parent_type | Parent object type for the bucket (project, folder, organization, billing_account). | string | ✓ | | -| *description* | Human-readable description for the logging bucket. | string | | null | -| *location* | Location of the bucket. | string | | global | -| *retention* | Retention time in days for the logging bucket. | number | | 30 | +|---|---|:---:|:---:|:---:| +| id | Name of the logging bucket. | string | ✓ | | +| parent | ID of the parentresource containing the bucket in the format 'project_id' 'folders/folder_id', 'organizations/organization_id' or 'billing_account_id'. | string | ✓ | | +| parent_type | Parent object type for the bucket (project, folder, organization, billing_account). | string | ✓ | | +| description | Human-readable description for the logging bucket. | string | | null | +| location | Location of the bucket. | string | | "global" | +| retention | Retention time in days for the logging bucket. | number | | 30 | ## Outputs | name | description | sensitive | |---|---|:---:| | id | ID of the created bucket. | | + + diff --git a/modules/naming-convention/README.md b/modules/naming-convention/README.md index 54bba4eb..d56629dc 100644 --- a/modules/naming-convention/README.md +++ b/modules/naming-convention/README.md @@ -66,18 +66,19 @@ module "project-tf" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| environment | Environment abbreviation used in names and labels. | string | ✓ | | -| resources | Short resource names by type. | map(list(string)) | ✓ | | -| team | Team name. | string | ✓ | | -| *labels* | Per-resource labels. | map(map(map(string))) | | {} | -| *prefix* | Optional name prefix. | string | | null | -| *separator_override* | Optional separator override for specific resource types. | map(string) | | {} | -| *suffix* | Optional name suffix. | string | | null | -| *use_resource_prefixes* | Prefix names with the resource type. | bool | | false | +|---|---|:---:|:---:|:---:| +| environment | Environment abbreviation used in names and labels. | string | ✓ | | +| resources | Short resource names by type. | map(list(string)) | ✓ | | +| team | Team name. | string | ✓ | | +| labels | Per-resource labels. | map(map(map(string))) | | {} | +| prefix | Optional name prefix. | string | | null | +| separator_override | Optional separator override for specific resource types. | map(string) | | {} | +| suffix | Optional name suffix. | string | | null | +| use_resource_prefixes | Prefix names with the resource type. | bool | | false | ## Outputs @@ -85,4 +86,6 @@ module "project-tf" { |---|---|:---:| | labels | Per resource labels. | | | names | Per resource names. | | + + diff --git a/modules/net-address/README.md b/modules/net-address/README.md index d844137c..c3f596d5 100644 --- a/modules/net-address/README.md +++ b/modules/net-address/README.md @@ -85,17 +85,18 @@ module "addresses" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project where the addresses will be created. | string | ✓ | | -| *external_addresses* | Map of external address regions, keyed by name. | map(string) | | {} | -| *global_addresses* | List of global addresses to create. | list(string) | | [] | -| *internal_addresses* | Map of internal addresses to create, keyed by name. | map(object({...})) | | {} | -| *internal_addresses_config* | Optional configuration for internal addresses, keyed by name. Unused options can be set to null. | map(object({...})) | | {} | -| *psa_addresses* | Map of internal addresses used for Private Service Access. | map(object({...})) | | {} | -| *psc_addresses* | Map of internal addresses used for Private Service Connect. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| project_id | Project where the addresses will be created. | string | ✓ | | +| external_addresses | Map of external address regions, keyed by name. | map(string) | | {} | +| global_addresses | List of global addresses to create. | list(string) | | [] | +| internal_addresses | Map of internal addresses to create, keyed by name. | map(object({…})) | | {} | +| internal_addresses_config | Optional configuration for internal addresses, keyed by name. Unused options can be set to null. | map(object({…})) | | {} | +| psa_addresses | Map of internal addresses used for Private Service Access. | map(object({…})) | | {} | +| psc_addresses | Map of internal addresses used for Private Service Connect. | map(object({…})) | | {} | ## Outputs @@ -106,4 +107,6 @@ module "addresses" { | internal_addresses | Allocated internal addresses. | | | psa_addresses | Allocated internal addresses for PSA endpoints. | | | psc_addresses | Allocated internal addresses for PSC endpoints. | | + + diff --git a/modules/net-cloudnat/README.md b/modules/net-cloudnat/README.md index f313c2e7..29b8cfd0 100644 --- a/modules/net-cloudnat/README.md +++ b/modules/net-cloudnat/README.md @@ -16,23 +16,24 @@ module "nat" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | Name of the Cloud NAT resource. | string | ✓ | | -| project_id | Project where resources will be created. | string | ✓ | | -| region | Region where resources will be created. | string | ✓ | | -| *addresses* | Optional list of external address self links. | list(string) | | [] | -| *config_min_ports_per_vm* | Minimum number of ports allocated to a VM from this NAT config. | number | | 64 | -| *config_source_subnets* | Subnetwork configuration (ALL_SUBNETWORKS_ALL_IP_RANGES, ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, LIST_OF_SUBNETWORKS). | string | | ALL_SUBNETWORKS_ALL_IP_RANGES | -| *config_timeouts* | Timeout configurations. | object({...}) | | ... | -| *logging_filter* | Enables logging if not null, value is one of 'ERRORS_ONLY', 'TRANSLATIONS_ONLY', 'ALL'. | string | | null | -| *router_asn* | Router ASN used for auto-created router. | number | | 64514 | -| *router_create* | Create router. | bool | | true | -| *router_name* | Router name, leave blank if router will be created to use auto generated name. | string | | null | -| *router_network* | Name of the VPC used for auto-created router. | string | | null | -| *subnetworks* | Subnetworks to NAT, only used when config_source_subnets equals LIST_OF_SUBNETWORKS. | list(object({...})) | | [] | +|---|---|:---:|:---:|:---:| +| name | Name of the Cloud NAT resource. | string | ✓ | | +| project_id | Project where resources will be created. | string | ✓ | | +| region | Region where resources will be created. | string | ✓ | | +| addresses | Optional list of external address self links. | list(string) | | [] | +| config_min_ports_per_vm | Minimum number of ports allocated to a VM from this NAT config. | number | | 64 | +| config_source_subnets | Subnetwork configuration (ALL_SUBNETWORKS_ALL_IP_RANGES, ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, LIST_OF_SUBNETWORKS). | string | | "ALL_SUBNETWORKS_ALL_IP_RANGES" | +| config_timeouts | Timeout configurations. | object({…}) | | {…} | +| logging_filter | Enables logging if not null, value is one of 'ERRORS_ONLY', 'TRANSLATIONS_ONLY', 'ALL'. | string | | null | +| router_asn | Router ASN used for auto-created router. | number | | 64514 | +| router_create | Create router. | bool | | true | +| router_name | Router name, leave blank if router will be created to use auto generated name. | string | | null | +| router_network | Name of the VPC used for auto-created router. | string | | null | +| subnetworks | Subnetworks to NAT, only used when config_source_subnets equals LIST_OF_SUBNETWORKS. | list(object({…})) | | [] | ## Outputs @@ -43,4 +44,6 @@ module "nat" { | region | Cloud NAT region. | | | router | Cloud NAT router resources (if auto created). | | | router_name | Cloud NAT router name. | | + + diff --git a/modules/net-ilb/README.md b/modules/net-ilb/README.md index 95ef9254..6a242d36 100644 --- a/modules/net-ilb/README.md +++ b/modules/net-ilb/README.md @@ -109,27 +109,28 @@ module "ilb" { ``` + ## 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 | -| *group_configs* | Optional unmanaged groups to create. Can be referenced in backends via outputs. | map(object({...})) | | {} | -| *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) | | {} | -| *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 | +|---|---|:---:|:---:|:---:| +| 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 | +| group_configs | Optional unmanaged groups to create. Can be referenced in backends via outputs. | map(object({…})) | | {} | +| 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) | | {} | +| 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 @@ -147,4 +148,6 @@ module "ilb" { | 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-interconnect-attachment-direct/README.md b/modules/net-interconnect-attachment-direct/README.md index e9e4c15c..eff2e556 100644 --- a/modules/net-interconnect-attachment-direct/README.md +++ b/modules/net-interconnect-attachment-direct/README.md @@ -105,21 +105,22 @@ module "vlan-attachment-2" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| interconnect | URL of the underlying Interconnect object that this attachment's traffic will traverse through. | string | ✓ | | -| peer | Peer Ip address and asn. Only IPv4 supported | object({...}) | ✓ | | -| project_id | The project containing the resources | string | ✓ | | -| *bgp* | Bgp session parameters | object({...}) | | null | -| *config* | VLAN attachment parameters: description, vlan_id, bandwidth, admin_enabled, interconnect | object({...}) | | ... | -| *name* | The name of the vlan attachment | string | | vlan-attachment | -| *region* | Region where the router resides | string | | europe-west1-b | -| *router_config* | Router asn and custom advertisement configuration, ip_ranges is a map of address ranges and descriptions.. | object({...}) | | ... | -| *router_create* | Create router. | bool | | true | -| *router_name* | Router name used for auto created router, or to specify an existing router to use if `router_create` is set to `true`. Leave blank to use vlan attachment name for auto created router. | string | | router-vlan-attachment | -| *router_network* | A reference to the network to which this router belongs | string | | null | +|---|---|:---:|:---:|:---:| +| interconnect | URL of the underlying Interconnect object that this attachment's traffic will traverse through. | string | ✓ | | +| peer | Peer Ip address and asn. Only IPv4 supported | object({…}) | ✓ | | +| project_id | The project containing the resources | string | ✓ | | +| router_config | Router asn and custom advertisement configuration, ip_ranges is a map of address ranges and descriptions.. | object({…} | ✓ | | +| bgp | Bgp session parameters | object({…}) | | null | +| config | VLAN attachment parameters: description, vlan_id, bandwidth, admin_enabled, interconnect | object({…}) | | {…} | +| name | The name of the vlan attachment | string | | "vlan-attachment" | +| region | Region where the router resides | string | | "europe-west1-b" | +| router_create | Create router. | bool | | true | +| router_name | Router name used for auto created router, or to specify an existing router to use if `router_create` is set to `true`. Leave blank to use vlan attachment name for auto created router. | string | | "router-vlan-attachment" | +| router_network | A reference to the network to which this router belongs | string | | null | ## Outputs @@ -128,4 +129,6 @@ module "vlan-attachment-2" { | bgpsession | bgp session | | | interconnect_attachment | interconnect attachment | | | router | Router resource (only if auto-created). | | + + diff --git a/modules/net-vpc-firewall/README.md b/modules/net-vpc-firewall/README.md index a3ad750f..8288c6ac 100644 --- a/modules/net-vpc-firewall/README.md +++ b/modules/net-vpc-firewall/README.md @@ -125,29 +125,33 @@ healthchecks: ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| network | Name of the network this set of firewall rules applies to. | string | ✓ | | -| project_id | Project id of the project that holds the network. | string | ✓ | | -| *admin_ranges* | IP CIDR ranges that have complete access to all subnets. | list(string) | | [] | -| *cidr_template_file* | Path for optional file containing name->cidr_list map to be used by the rules factory. | string | | null | -| *custom_rules* | List of custom rule definitions (refer to variables file for syntax). | map(object({...})) | | {} | -| *data_folder* | Path for optional folder containing firewall rules defined as YaML objects used by the rules factory. | string | | null | -| *http_source_ranges* | List of IP CIDR ranges for tag-based HTTP rule, defaults to the health checkers ranges. | list(string) | | ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] | -| *https_source_ranges* | List of IP CIDR ranges for tag-based HTTPS rule, defaults to the health checkers ranges. | list(string) | | ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] | -| *named_ranges* | Names that can be used of valid values for the `ranges` field of `custom_rules` | map(list(string)) | | ... | -| *ssh_source_ranges* | List of IP CIDR ranges for tag-based SSH rule, defaults to the IAP forwarders range. | list(string) | | ["35.235.240.0/20"] | +|---|---|:---:|:---:|:---:| +| network | Name of the network this set of firewall rules applies to. | string | ✓ | | +| project_id | Project id of the project that holds the network. | string | ✓ | | +| admin_ranges | IP CIDR ranges that have complete access to all subnets. | list(string) | | [] | +| cidr_template_file | Path for optional file containing name->cidr_list map to be used by the rules factory. | string | | null | +| custom_rules | List of custom rule definitions (refer to variables file for syntax). | map(object({…})) | | {} | +| data_folder | Path for optional folder containing firewall rules defined as YaML objects used by the rules factory. | string | | null | +| http_source_ranges | List of IP CIDR ranges for tag-based HTTP rule, defaults to the health checkers ranges. | list(string) | | ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] | +| https_source_ranges | List of IP CIDR ranges for tag-based HTTPS rule, defaults to the health checkers ranges. | list(string) | | ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] | +| named_ranges | Names that can be used of valid values for the `ranges` field of `custom_rules` | map(list(string)) | | {…} | +| ssh_source_ranges | List of IP CIDR ranges for tag-based SSH rule, defaults to the IAP forwarders range. | list(string) | | ["35.235.240.0/20"] | ## Outputs | name | description | sensitive | |---|---|:---:| -| admin_ranges | Admin ranges data. | | +| admin_ranges | Admin ranges data. + value = { enabled = length(var.admin_ranges) > 0 ranges = join(",", var.admin_ranges) } | | | custom_egress_allow_rules | Custom egress rules with allow blocks. | | | custom_egress_deny_rules | Custom egress rules with allow blocks. | | | custom_ingress_allow_rules | Custom ingress rules with allow blocks. | | | custom_ingress_deny_rules | Custom ingress rules with deny blocks. | | | rules | All google_compute_firewall resources created. | | + + diff --git a/modules/net-vpc-peering/README.md b/modules/net-vpc-peering/README.md index 66de5626..5cf83210 100644 --- a/modules/net-vpc-peering/README.md +++ b/modules/net-vpc-peering/README.md @@ -42,16 +42,17 @@ module "peering-a-c" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| local_network | Resource link of the network to add a peering to. | string | ✓ | | -| peer_network | Resource link of the peer network. | string | ✓ | | -| *export_local_custom_routes* | Export custom routes to peer network from local network. | bool | | false | -| *export_peer_custom_routes* | Export custom routes to local network from peer network. | bool | | false | -| *peer_create_peering* | Create the peering on the remote side. If false, only the peering from this network to the remote network is created. | bool | | true | -| *prefix* | Name prefix for the network peerings. | string | | network-peering | +|---|---|:---:|:---:|:---:| +| local_network | Resource link of the network to add a peering to. | string | ✓ | | +| peer_network | Resource link of the peer network. | string | ✓ | | +| export_local_custom_routes | Export custom routes to peer network from local network. | bool | | false | +| export_peer_custom_routes | Export custom routes to local network from peer network. | bool | | false | +| peer_create_peering | Create the peering on the remote side. If false, only the peering from this network to the remote network is created. | bool | | true | +| prefix | Name prefix for the network peerings. | string | | "network-peering" | ## Outputs @@ -59,4 +60,6 @@ module "peering-a-c" { |---|---|:---:| | local_network_peering | Network peering resource. | | | peer_network_peering | Peer network peering resource. | | + + diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md index 41fc347d..ee5da703 100644 --- a/modules/net-vpc/README.md +++ b/modules/net-vpc/README.md @@ -203,34 +203,35 @@ flow_logs: # enable, set to empty map to use defaults ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | The name of the network being created | string | ✓ | | -| project_id | The ID of the project where this VPC will be created | string | ✓ | | -| *auto_create_subnetworks* | Set to true to create an auto mode subnet, defaults to custom mode. | bool | | false | -| *data_folder* | An optional folder containing the subnet configurations in YaML format. | string | | null | -| *delete_default_routes_on_create* | Set to true to delete the default routes at creation time. | bool | | false | -| *description* | An optional description of this resource (triggers recreation on change). | string | | Terraform-managed. | -| *dns_policy* | DNS policy setup for the VPC. | object({...}) | | null | -| *iam* | Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format. | map(map(list(string))) | | {} | -| *log_config_defaults* | Default configuration for flow logs when enabled. | object({...}) | | ... | -| *log_configs* | Map keyed by subnet 'region/name' of optional configurations for flow logs when enabled. | map(map(string)) | | {} | -| *mtu* | Maximum Transmission Unit in bytes. The minimum value for this field is 1460 and the maximum value is 1500 bytes. | | | null | -| *peering_config* | VPC peering configuration. | object({...}) | | null | -| *peering_create_remote_end* | Skip creation of peering on the remote end when using peering_config | bool | | true | -| *psn_ranges* | CIDR ranges used for Google services that support Private Service Networking. | list(string) | | ... | -| *routes* | Network routes, keyed by name. | map(object({...})) | | {} | -| *routing_mode* | The network routing mode (default 'GLOBAL') | string | | ... | -| *shared_vpc_host* | Enable shared VPC for this project. | bool | | false | -| *shared_vpc_service_projects* | Shared VPC service projects to register with this host | list(string) | | [] | -| *subnet_descriptions* | Optional map of subnet descriptions, keyed by subnet 'region/name'. | map(string) | | {} | -| *subnet_flow_logs* | Optional map of boolean to control flow logs (default is disabled), keyed by subnet 'region/name'. | map(bool) | | {} | -| *subnet_private_access* | Optional map of boolean to control private Google access (default is enabled), keyed by subnet 'region/name'. | map(bool) | | {} | -| *subnets* | List of subnets being created. | list(object({...})) | | [] | -| *subnets_l7ilb* | List of subnets for private HTTPS load balancer. | list(object({...})) | | [] | -| *vpc_create* | Create VPC. When set to false, uses a data source to reference existing VPC. | bool | | true | +|---|---|:---:|:---:|:---:| +| name | The name of the network being created | string | ✓ | | +| project_id | The ID of the project where this VPC will be created | string | ✓ | | +| auto_create_subnetworks | Set to true to create an auto mode subnet, defaults to custom mode. | bool | | false | +| data_folder | An optional folder containing the subnet configurations in YaML format. | string | | null | +| delete_default_routes_on_create | Set to true to delete the default routes at creation time. | bool | | false | +| description | An optional description of this resource (triggers recreation on change). | string | | "Terraform-managed." | +| dns_policy | DNS policy setup for the VPC. | object({…}) | | null | +| iam | Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format. | map(map(list(string))) | | {} | +| log_config_defaults | Default configuration for flow logs when enabled. | object({…}) | | {…} | +| log_configs | Map keyed by subnet 'region/name' of optional configurations for flow logs when enabled. | map(map(string)) | | {} | +| mtu | Maximum Transmission Unit in bytes. The minimum value for this field is 1460 and the maximum value is 1500 bytes. | | | null | +| peering_config | VPC peering configuration. | object({…}) | | null | +| peering_create_remote_end | Skip creation of peering on the remote end when using peering_config | bool | | true | +| psn_ranges | CIDR ranges used for Google services that support Private Service Networking. | list(string) | | null | +| routes | Network routes, keyed by name. | map(object({…})) | | {} | +| routing_mode | The network routing mode (default 'GLOBAL') | string | | "GLOBAL" | +| shared_vpc_host | Enable shared VPC for this project. | bool | | false | +| shared_vpc_service_projects | Shared VPC service projects to register with this host | list(string) | | [] | +| subnet_descriptions | Optional map of subnet descriptions, keyed by subnet 'region/name'. | map(string) | | {} | +| subnet_flow_logs | Optional map of boolean to control flow logs (default is disabled), keyed by subnet 'region/name'. | map(bool) | | {} | +| subnet_private_access | Optional map of boolean to control private Google access (default is enabled), keyed by subnet 'region/name'. | map(bool) | | {} | +| subnets | List of subnets being created. | list(object({…})) | | [] | +| subnets_l7ilb | List of subnets for private HTTPS load balancer. | list(object({…})) | | [] | +| vpc_create | Create VPC. When set to false, uses a data source to reference existing VPC. | bool | | true | ## Outputs @@ -247,6 +248,8 @@ flow_logs: # enable, set to empty map to use defaults | subnet_self_links | Map of subnet self links keyed by name. | | | subnets | Subnet resources. | | | subnets_l7ilb | L7 ILB subnet resources. | | + + The key format is `subnet_region/subnet_name`. For example `europe-west1/my_subnet`. diff --git a/modules/net-vpn-dynamic/README.md b/modules/net-vpn-dynamic/README.md index 37b3d3f6..23333d1d 100644 --- a/modules/net-vpn-dynamic/README.md +++ b/modules/net-vpn-dynamic/README.md @@ -40,22 +40,23 @@ module "vpn-dynamic" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | VPN gateway name, and prefix used for dependent resources. | string | ✓ | | -| network | VPC used for the gateway and routes. | string | ✓ | | -| project_id | Project where resources will be created. | string | ✓ | | -| region | Region used for resources. | string | ✓ | | -| *gateway_address* | Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false. | string | | | -| *gateway_address_create* | Create external address assigned to the VPN gateway. Needs to be explicitly set to false to use address in gateway_address variable. | bool | | true | -| *route_priority* | Route priority, defaults to 1000. | number | | 1000 | -| *router_advertise_config* | Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions. | object({...}) | | null | -| *router_asn* | Router ASN used for auto-created router. | number | | 64514 | -| *router_create* | Create router. | bool | | true | -| *router_name* | Router name used for auto created router, or to specify existing router to use. Leave blank to use VPN name for auto created router. | string | | | -| *tunnels* | VPN tunnel configurations, bgp_peer_options is usually null. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| name | VPN gateway name, and prefix used for dependent resources. | string | ✓ | | +| network | VPC used for the gateway and routes. | string | ✓ | | +| project_id | Project where resources will be created. | string | ✓ | | +| region | Region used for resources. | string | ✓ | | +| gateway_address | Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false. | string | | "" | +| gateway_address_create | Create external address assigned to the VPN gateway. Needs to be explicitly set to false to use address in gateway_address variable. | bool | | true | +| route_priority | Route priority, defaults to 1000. | number | | 1000 | +| router_advertise_config | Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions. | object({…}) | | null | +| router_asn | Router ASN used for auto-created router. | number | | 64514 | +| router_create | Create router. | bool | | true | +| router_name | Router name used for auto created router, or to specify existing router to use. Leave blank to use VPN name for auto created router. | string | | "" | +| tunnels | VPN tunnel configurations, bgp_peer_options is usually null. | map(object({…})) | | {} | ## Outputs @@ -71,4 +72,6 @@ module "vpn-dynamic" { | tunnel_names | VPN tunnel names. | | | tunnel_self_links | VPN tunnel self links. | | | tunnels | VPN tunnel resources. | | + + diff --git a/modules/net-vpn-ha/README.md b/modules/net-vpn-ha/README.md index a01f9e9d..d464b289 100644 --- a/modules/net-vpn-ha/README.md +++ b/modules/net-vpn-ha/README.md @@ -140,24 +140,25 @@ module "vpn_ha" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | VPN Gateway name (if an existing VPN Gateway is not used), and prefix used for dependent resources. | string | ✓ | | -| network | VPC used for the gateway and routes. | string | ✓ | | -| project_id | Project where resources will be created. | string | ✓ | | -| region | Region used for resources. | string | ✓ | | -| *peer_external_gateway* | Configuration of an external VPN gateway to which this VPN is connected. | object({...}) | | null | -| *peer_gcp_gateway* | Self Link URL of the peer side HA GCP VPN gateway to which this VPN tunnel is connected. | string | | null | -| *route_priority* | Route priority, defaults to 1000. | number | | 1000 | -| *router_advertise_config* | Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions. | object({...}) | | null | -| *router_asn* | Router ASN used for auto-created router. | number | | 64514 | -| *router_create* | Create router. | bool | | true | -| *router_name* | Router name used for auto created router, or to specify an existing router to use if `router_create` is set to `true`. Leave blank to use VPN name for auto created router. | string | | | -| *tunnels* | VPN tunnel configurations, bgp_peer_options is usually null. | map(object({...})) | | {} | -| *vpn_gateway* | HA VPN Gateway Self Link for using an existing HA VPN Gateway, leave empty if `vpn_gateway_create` is set to `true`. | string | | null | -| *vpn_gateway_create* | Create HA VPN Gateway. | bool | | true | +|---|---|:---:|:---:|:---:| +| name | VPN Gateway name (if an existing VPN Gateway is not used), and prefix used for dependent resources. | string | ✓ | | +| network | VPC used for the gateway and routes. | string | ✓ | | +| project_id | Project where resources will be created. | string | ✓ | | +| region | Region used for resources. | string | ✓ | | +| peer_external_gateway | Configuration of an external VPN gateway to which this VPN is connected. | object({…}) | | null | +| peer_gcp_gateway | Self Link URL of the peer side HA GCP VPN gateway to which this VPN tunnel is connected. | string | | null | +| route_priority | Route priority, defaults to 1000. | number | | 1000 | +| router_advertise_config | Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions. | object({…}) | | null | +| router_asn | Router ASN used for auto-created router. | number | | 64514 | +| router_create | Create router. | bool | | true | +| router_name | Router name used for auto created router, or to specify an existing router to use if `router_create` is set to `true`. Leave blank to use VPN name for auto created router. | string | | "" | +| tunnels | VPN tunnel configurations, bgp_peer_options is usually null. | map(object({…})) | | {} | +| vpn_gateway | HA VPN Gateway Self Link for using an existing HA VPN Gateway, leave empty if `vpn_gateway_create` is set to `true`. | string | | null | +| vpn_gateway_create | Create HA VPN Gateway. | bool | | true | ## Outputs @@ -174,4 +175,6 @@ module "vpn_ha" { | tunnel_names | VPN tunnel names. | | | tunnel_self_links | VPN tunnel self links. | | | tunnels | VPN tunnel resources. | | + + diff --git a/modules/net-vpn-static/README.md b/modules/net-vpn-static/README.md index 96c1f43d..9713b043 100644 --- a/modules/net-vpn-static/README.md +++ b/modules/net-vpn-static/README.md @@ -33,19 +33,20 @@ module "vpn" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | VPN gateway name, and prefix used for dependent resources. | string | ✓ | | -| network | VPC used for the gateway and routes. | string | ✓ | | -| project_id | Project where resources will be created. | string | ✓ | | -| region | Region used for resources. | string | ✓ | | -| *gateway_address* | Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false. | string | | | -| *gateway_address_create* | Create external address assigned to the VPN gateway. Needs to be explicitly set to false to use address in gateway_address variable. | bool | | true | -| *remote_ranges* | Remote IP CIDR ranges. | list(string) | | [] | -| *route_priority* | Route priority, defaults to 1000. | number | | 1000 | -| *tunnels* | VPN tunnel configurations. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| name | VPN gateway name, and prefix used for dependent resources. | string | ✓ | | +| network | VPC used for the gateway and routes. | string | ✓ | | +| project_id | Project where resources will be created. | string | ✓ | | +| region | Region used for resources. | string | ✓ | | +| gateway_address | Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false. | string | | "" | +| gateway_address_create | Create external address assigned to the VPN gateway. Needs to be explicitly set to false to use address in gateway_address variable. | bool | | true | +| remote_ranges | Remote IP CIDR ranges. | list(string) | | [] | +| route_priority | Route priority, defaults to 1000. | number | | 1000 | +| tunnels | VPN tunnel configurations. | map(object({…})) | | {} | ## Outputs @@ -59,4 +60,6 @@ module "vpn" { | tunnel_names | VPN tunnel names. | | | tunnel_self_links | VPN tunnel self links. | | | tunnels | VPN tunnel resources. | | + + diff --git a/modules/organization/README.md b/modules/organization/README.md index 5f437b86..561ae064 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -236,27 +236,28 @@ module "org" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| organization_id | Organization id in organizations/nnnnnn format. | string | ✓ | | -| *contacts* | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES | map(list(string)) | | {} | -| *custom_roles* | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | -| *firewall_policies* | Hierarchical firewall policy rules created in the organization. | map(map(object({...}))) | | {} | -| *firewall_policy_attachments* | List of hierarchical firewall policy IDs attached to the organization. | map(string) | | {} | -| *firewall_policy_factory* | Configuration for the firewall policy factory. | object({...}) | | null | -| *group_iam* | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string)) | | {} | -| *iam* | IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *iam_additive* | Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *iam_additive_members* | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | map(list(string)) | | {} | -| *iam_audit_config* | Service audit logging configuration. Service as key, map of log permission (eg DATA_READ) and excluded members as value for each service. | map(map(list(string))) | | {} | -| *iam_audit_config_authoritative* | IAM Authoritative service audit logging configuration. Service as key, map of log permission (eg DATA_READ) and excluded members as value for each service. Audit config should also be authoritative when using authoritative bindings. Use with caution. | map(map(list(string))) | | null | -| *iam_bindings_authoritative* | IAM authoritative bindings, in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared. Bindings should also be authoritative when using authoritative audit config. Use with caution. | map(list(string)) | | null | -| *logging_exclusions* | Logging exclusions for this organization in the form {NAME -> FILTER}. | map(string) | | {} | -| *logging_sinks* | Logging sinks to create for this organization. | map(object({...})) | | {} | -| *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | -| *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| organization_id | Organization id in organizations/nnnnnn format. | string | ✓ | | +| contacts | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES | map(list(string)) | | {} | +| custom_roles | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | +| firewall_policies | Hierarchical firewall policy rules created in the organization. | map(map(object({…}))) | | {} | +| firewall_policy_attachments | List of hierarchical firewall policy IDs attached to the organization. | map(string) | | {} | +| firewall_policy_factory | Configuration for the firewall policy factory. | object({…}) | | null | +| group_iam | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string)) | | {} | +| iam | IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| iam_additive | Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| iam_additive_members | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | map(list(string)) | | {} | +| iam_audit_config | Service audit logging configuration. Service as key, map of log permission (eg DATA_READ) and excluded members as value for each service. | map(map(list(string))) | | {} | +| iam_audit_config_authoritative | IAM Authoritative service audit logging configuration. Service as key, map of log permission (eg DATA_READ) and excluded members as value for each service. Audit config should also be authoritative when using authoritative bindings. Use with caution. | map(map(list(string))) | | null | +| iam_bindings_authoritative | IAM authoritative bindings, in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared. Bindings should also be authoritative when using authoritative audit config. Use with caution. | map(list(string)) | | null | +| logging_exclusions | Logging exclusions for this organization in the form {NAME -> FILTER}. | map(string) | | {} | +| logging_sinks | Logging sinks to create for this organization. | map(object({…})) | | {} | +| policy_boolean | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | +| policy_list | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | map(object({…})) | | {} | ## Outputs @@ -268,4 +269,6 @@ module "org" { | firewall_policy_id | Map of firewall policy ids created in the organization. | | | organization_id | Organization id dependent on module resources. | | | sink_writer_identities | Writer identities created for each sink. | | + + diff --git a/modules/project/README.md b/modules/project/README.md index 44509064..aa21fa77 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -174,40 +174,41 @@ module "project" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | Project name and id suffix. | string | ✓ | | -| *auto_create_network* | Whether to create the default network for the project | bool | | false | -| *billing_account* | Billing account id. | string | | null | -| *contacts* | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES | map(list(string)) | | {} | -| *custom_roles* | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | -| *descriptive_name* | Name of the project name. Used for project name instead of `name` variable | string | | null | -| *group_iam* | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string)) | | {} | -| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *iam_additive* | IAM additive bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *iam_additive_members* | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | map(list(string)) | | {} | -| *labels* | Resource labels. | map(string) | | {} | -| *lien_reason* | If non-empty, creates a project lien with this description. | string | | | -| *logging_exclusions* | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | -| *logging_sinks* | Logging sinks to create for this project. | map(object({...})) | | {} | -| *oslogin* | Enable OS Login. | bool | | false | -| *oslogin_admins* | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | list(string) | | [] | -| *oslogin_users* | List of IAM-style identities that will be granted roles necessary for OS Login users. | list(string) | | [] | -| *parent* | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | ... | -| *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | -| *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | map(object({...})) | | {} | -| *prefix* | Prefix used to generate project id and name. | string | | null | -| *project_create* | Create project. When set to false, uses a data source to reference existing project. | bool | | true | -| *service_config* | Configure service API activation. | object({...}) | | ... | -| *service_encryption_key_ids* | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | map(list(string)) | | {} | -| *service_perimeter_bridges* | Name of VPC-SC Bridge perimeters to add project into. Specify the name in the form of 'accessPolicies/ACCESS_POLICY_NAME/servicePerimeters/PERIMETER_NAME'. | list(string) | | null | -| *service_perimeter_standard* | Name of VPC-SC Standard perimeter to add project into. Specify the name in the form of 'accessPolicies/ACCESS_POLICY_NAME/servicePerimeters/PERIMETER_NAME'. | string | | null | -| *services* | Service APIs to enable. | list(string) | | [] | -| *shared_vpc_host_config* | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({...}) | | ... | -| *shared_vpc_service_config* | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({...}) | | ... | -| *skip_delete* | Allows the underlying resources to be destroyed without destroying the project itself. | bool | | false | +|---|---|:---:|:---:|:---:| +| name | Project name and id suffix. | string | ✓ | | +| auto_create_network | Whether to create the default network for the project | bool | | false | +| billing_account | Billing account id. | string | | null | +| contacts | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES | map(list(string)) | | {} | +| custom_roles | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | +| descriptive_name | Name of the project name. Used for project name instead of `name` variable | string | | null | +| group_iam | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string)) | | {} | +| iam | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| iam_additive | IAM additive bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| iam_additive_members | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | map(list(string)) | | {} | +| labels | Resource labels. | map(string) | | {} | +| lien_reason | If non-empty, creates a project lien with this description. | string | | "" | +| logging_exclusions | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | +| logging_sinks | Logging sinks to create for this project. | map(object({…})) | | {} | +| oslogin | Enable OS Login. | bool | | false | +| oslogin_admins | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | list(string) | | [] | +| oslogin_users | List of IAM-style identities that will be granted roles necessary for OS Login users. | list(string) | | [] | +| parent | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +| policy_boolean | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | +| policy_list | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | map(object({…})) | | {} | +| prefix | Prefix used to generate project id and name. | string | | null | +| project_create | Create project. When set to false, uses a data source to reference existing project. | bool | | true | +| service_config | Configure service API activation. | object({…}) | | {…} | +| service_encryption_key_ids | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | map(list(string)) | | {} | +| service_perimeter_bridges | Name of VPC-SC Bridge perimeters to add project into. Specify the name in the form of 'accessPolicies/ACCESS_POLICY_NAME/servicePerimeters/PERIMETER_NAME'. | list(string) | | null | +| service_perimeter_standard | Name of VPC-SC Standard perimeter to add project into. Specify the name in the form of 'accessPolicies/ACCESS_POLICY_NAME/servicePerimeters/PERIMETER_NAME'. | string | | null | +| services | Service APIs to enable. | list(string) | | [] | +| shared_vpc_host_config | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | {…} | +| shared_vpc_service_config | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | +| skip_delete | Allows the underlying resources to be destroyed without destroying the project itself. | bool | | false | ## Outputs @@ -219,5 +220,7 @@ module "project" { | project_id | Project id. | | | service_accounts | Product robot service accounts in project. | | | sink_writer_identities | Writer identities created for each sink. | | + + diff --git a/modules/pubsub/README.md b/modules/pubsub/README.md index 0146b53e..fb5fb293 100644 --- a/modules/pubsub/README.md +++ b/modules/pubsub/README.md @@ -89,21 +89,22 @@ module "pubsub" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | PubSub topic name. | string | ✓ | | -| project_id | Project used for resources. | string | ✓ | | -| *dead_letter_configs* | Per-subscription dead letter policy configuration. | map(object({...})) | | {} | -| *defaults* | Subscription defaults for options. | object({...}) | | ... | -| *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *kms_key* | KMS customer managed encryption key. | string | | null | -| *labels* | Labels. | map(string) | | {} | -| *push_configs* | Push subscription configurations. | map(object({...})) | | {} | -| *regions* | List of regions used to set persistence policy. | list(string) | | [] | -| *subscription_iam* | IAM bindings for subscriptions in {SUBSCRIPTION => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | -| *subscriptions* | Topic subscriptions. Also define push configs for push subscriptions. If options is set to null subscription defaults will be used. Labels default to topic labels if set to null. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| name | PubSub topic name. | string | ✓ | | +| project_id | Project used for resources. | string | ✓ | | +| dead_letter_configs | Per-subscription dead letter policy configuration. | map(object({…})) | | {} | +| defaults | Subscription defaults for options. | object({…}) | | {…} | +| iam | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| kms_key | KMS customer managed encryption key. | string | | null | +| labels | Labels. | map(string) | | {} | +| push_configs | Push subscription configurations. | map(object({…})) | | {} | +| regions | List of regions used to set persistence policy. | list(string) | | [] | +| subscription_iam | IAM bindings for subscriptions in {SUBSCRIPTION => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | +| subscriptions | Topic subscriptions. Also define push configs for push subscriptions. If options is set to null subscription defaults will be used. Labels default to topic labels if set to null. | map(object({…})) | | {} | ## Outputs @@ -113,4 +114,6 @@ module "pubsub" { | subscription_id | Subscription ids. | | | subscriptions | Subscription resources. | | | topic | Topic resource. | | + + diff --git a/modules/secret-manager/README.md b/modules/secret-manager/README.md index 7025634a..dc3aca21 100644 --- a/modules/secret-manager/README.md +++ b/modules/secret-manager/README.md @@ -74,15 +74,16 @@ module "secret-manager" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project id where the keyring will be created. | string | ✓ | | -| *iam* | IAM bindings in {SECRET => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | -| *labels* | Optional labels for each secret. | map(map(string)) | | {} | -| *secrets* | Map of secrets to manage and their locations. If locations is null, automatic management will be set. | map(list(string)) | | {} | -| *versions* | Optional versions to manage for each secret. Version names are only used internally to track individual versions. | map(map(object({...}))) | | {} | +|---|---|:---:|:---:|:---:| +| project_id | Project id where the keyring will be created. | string | ✓ | | +| iam | IAM bindings in {SECRET => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | +| labels | Optional labels for each secret. | map(map(string)) | | {} | +| secrets | Map of secrets to manage and their locations. If locations is null, automatic management will be set. | map(list(string)) | | {} | +| versions | Optional versions to manage for each secret. Version names are only used internally to track individual versions. | map(map(object({…}))) | | {} | ## Outputs @@ -92,6 +93,8 @@ module "secret-manager" { | secrets | Secret resources. | | | version_ids | Version ids keyed by secret name : version name. | | | versions | Secret versions. | | + + ## Requirements diff --git a/modules/service-directory/README.md b/modules/service-directory/README.md index 9bc881df..0915400c 100644 --- a/modules/service-directory/README.md +++ b/modules/service-directory/README.md @@ -89,18 +89,19 @@ module "dns-sd" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| location | Namespace location. | string | ✓ | | -| name | Namespace name. | string | ✓ | | -| project_id | Project used for resources. | string | ✓ | | -| *endpoint_config* | Map of endpoint attributes, keys are in service/endpoint format. | map(object({...})) | | {} | -| *iam* | IAM bindings for namespace, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| *labels* | Labels. | map(string) | | {} | -| *service_iam* | IAM bindings for services, in {SERVICE => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | -| *services* | Service configuration, using service names as keys. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| location | Namespace location. | string | ✓ | | +| name | Namespace name. | string | ✓ | | +| project_id | Project used for resources. | string | ✓ | | +| endpoint_config | Map of endpoint attributes, keys are in service/endpoint format. | map(object({…})) | | {} | +| iam | IAM bindings for namespace, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| labels | Labels. | map(string) | | {} | +| service_iam | IAM bindings for services, in {SERVICE => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | +| services | Service configuration, using service names as keys. | map(object({…})) | | {} | ## Outputs @@ -113,4 +114,6 @@ module "dns-sd" { | service_id | Service ids (short names). | | | service_names | Service ids (long names). | | | services | Service resources. | | + + diff --git a/modules/source-repository/README.md b/modules/source-repository/README.md index f5a92995..a7473a7d 100644 --- a/modules/source-repository/README.md +++ b/modules/source-repository/README.md @@ -20,13 +20,14 @@ module "repo" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| name | Repository name. | string | ✓ | | -| project_id | Project used for resources. | string | ✓ | | -| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +|---|---|:---:|:---:|:---:| +| name | Repository name. | string | ✓ | | +| project_id | Project used for resources. | string | ✓ | | +| iam | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | ## Outputs @@ -34,4 +35,6 @@ module "repo" { |---|---|:---:| | id | Repository id. | | | url | Repository URL. | | + + diff --git a/modules/vpc-sc/README.md b/modules/vpc-sc/README.md index 1537c119..c1810872 100644 --- a/modules/vpc-sc/README.md +++ b/modules/vpc-sc/README.md @@ -232,22 +232,23 @@ module "vpc-sc-first" { ``` + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| organization_id | Organization id in organizations/nnnnnn format. | string | ✓ | | -| *access_level_perimeters* | Enforced mode -> Access Level -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | -| *access_levels* | Map of Access Levels to be created. For each Access Level you can specify 'ip_subnetworks, required_access_levels, members, negate or regions'. | map(object({...})) | | {} | -| *access_policy_create* | Enable autocreation of the Access Policy | bool | | true | -| *access_policy_name* | Referenced Access Policy name | string | | null | -| *access_policy_title* | Access Policy title to be created. | string | | null | -| *egress_policies* | List of EgressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#egress_policies) | | | null | -| *egress_policies_perimeters* | Enforced mode -> Egress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | -| *ingress_policies* | List of IngressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#ingress_policies) | | | null | -| *ingress_policies_perimeters* | Enforced mode -> Ingress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | -| *perimeter_projects* | Perimeter -> Enforced Mode -> Projects Number mapping. Enforced mode can be 'enforced' or 'dry_run'. | map(map(list(number))) | | {} | -| *perimeters* | Set of Perimeters. | map(object({...})) | | {} | +|---|---|:---:|:---:|:---:| +| organization_id | Organization id in organizations/nnnnnn format. | string | ✓ | | +| access_level_perimeters | Enforced mode -> Access Level -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | +| access_levels | Map of Access Levels to be created. For each Access Level you can specify 'ip_subnetworks, required_access_levels, members, negate or regions'. | map(object({…})) | | {} | +| access_policy_create | Enable autocreation of the Access Policy | bool | | true | +| access_policy_name | Referenced Access Policy name | string | | null | +| access_policy_title | Access Policy title to be created. | string | | null | +| egress_policies | List of EgressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#egress_policies) | | | null | +| egress_policies_perimeters | Enforced mode -> Egress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | +| ingress_policies | List of IngressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#ingress_policies) | | | null | +| ingress_policies_perimeters | Enforced mode -> Ingress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | +| perimeter_projects | Perimeter -> Enforced Mode -> Projects Number mapping. Enforced mode can be 'enforced' or 'dry_run'. | map(map(list(number))) | | {} | +| perimeters | Set of Perimeters. | map(object({…})) | | {} | ## Outputs @@ -258,4 +259,6 @@ module "vpc-sc-first" { | organization_id | Organization id dependent on module resources. | | | perimeters_bridge | VPC-SC bridge perimeter resources. | | | perimeters_standard | VPC-SC standard perimeter resources. | | + + diff --git a/networking/decentralized-firewall/README.md b/networking/decentralized-firewall/README.md index d5f56e40..2bdf6bbc 100644 --- a/networking/decentralized-firewall/README.md +++ b/networking/decentralized-firewall/README.md @@ -21,16 +21,17 @@ the two). There is an example of a YAML-based validator using [Yamale](https://g in the [`validator/`](validator/) subdirectory, which can be integrated as part of a CI/CD pipeline. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | -| root_node | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| *ip_ranges* | Subnet IP CIDR ranges. | map(string) | | ... | -| *project_services* | Service APIs enabled by default in new projects. | list(string) | | ... | -| *region* | Region used. | string | | europe-west1 | +|---|---|:---:|:---:|:---:| +| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | +| root_node | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | +| ip_ranges | Subnet IP CIDR ranges. | map(string) | | {…} | +| project_services | Service APIs enabled by default in new projects. | list(string) | | […] | +| region | Region used. | string | | "europe-west1" | ## Outputs @@ -39,4 +40,6 @@ in the [`validator/`](validator/) subdirectory, which can be integrated as part | fw_rules | Firewall rules. | | | projects | Project ids. | | | vpc | Shared VPCs. | | + + diff --git a/networking/filtering-proxy/README.md b/networking/filtering-proxy/README.md index 78eb3f3b..792301d9 100644 --- a/networking/filtering-proxy/README.md +++ b/networking/filtering-proxy/README.md @@ -16,22 +16,25 @@ You can optionally deploy the Squid server as [Managed Instance Group](https://c ![High-level diagram](squid.png "High-level diagram") + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account | Billing account id used as default for new projects. | string | ✓ | | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | -| root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| *allowed_domains* | List of domains allowed by the squid proxy. | list(string) | | ... | -| *cidrs* | CIDR ranges for subnets | map(string) | | ... | -| *mig* | Enables the creation of an autoscaling managed instance group of squid instances. | bool | | false | -| *nat_logging* | Enables Cloud NAT logging if not null, value is one of 'ERRORS_ONLY', 'TRANSLATIONS_ONLY', 'ALL'. | string | | ERRORS_ONLY | -| *region* | Default region for resources | string | | europe-west1 | +|---|---|:---:|:---:|:---:| +| billing_account | Billing account id used as default for new projects. | string | ✓ | | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | +| root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | +| allowed_domains | List of domains allowed by the squid proxy. | list(string) | | […] | +| cidrs | CIDR ranges for subnets | map(string) | | {…} | +| mig | Enables the creation of an autoscaling managed instance group of squid instances. | bool | | false | +| nat_logging | Enables Cloud NAT logging if not null, value is one of 'ERRORS_ONLY', 'TRANSLATIONS_ONLY', 'ALL'. | string | | "ERRORS_ONLY" | +| region | Default region for resources | string | | "europe-west1" | ## Outputs | name | description | sensitive | |---|---|:---:| | squid-address | IP address of the Squid proxy. | | + + diff --git a/networking/hub-and-spoke-peering/README.md b/networking/hub-and-spoke-peering/README.md index b91a98bf..ae19ed18 100644 --- a/networking/hub-and-spoke-peering/README.md +++ b/networking/hub-and-spoke-peering/README.md @@ -80,17 +80,18 @@ A few APIs need to be enabled in the project, if `apply` fails due to a service 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). + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project id used for all resources. | string | ✓ | | -| *ip_ranges* | IP CIDR ranges. | map(string) | | ... | -| *ip_secondary_ranges* | Secondary IP CIDR ranges. | map(string) | | ... | -| *prefix* | Arbitrary string used to prefix resource names. | string | | null | -| *private_service_ranges* | Private service IP CIDR ranges. | map(string) | | ... | -| *project_create* | Set to non null if project needs to be created. | object({...}) | | ... | -| *region* | VPC region. | string | | europe-west1 | +|---|---|:---:|:---:|:---:| +| project_id | Project id used for all resources. | string | ✓ | | +| ip_ranges | IP CIDR ranges. | map(string) | | {…} | +| ip_secondary_ranges | Secondary IP CIDR ranges. | map(string) | | {…} | +| prefix | Arbitrary string used to prefix resource names. | string | | null | +| private_service_ranges | Private service IP CIDR ranges. | map(string) | | {…} | +| project_create | Set to non null if project needs to be created. | object({…}) | | null | +| region | VPC region. | string | | "europe-west1" | ## Outputs @@ -98,4 +99,6 @@ The VPN used to connect the GKE masters VPC does not account for HA, upgrading t |---|---|:---:| | project | Project id. | | | vms | GCE VMs. | | + + diff --git a/networking/hub-and-spoke-vpn/README.md b/networking/hub-and-spoke-vpn/README.md index ab866ca2..da1dac6d 100644 --- a/networking/hub-and-spoke-vpn/README.md +++ b/networking/hub-and-spoke-vpn/README.md @@ -35,20 +35,23 @@ The example does not account for HA, but the VPN gateways can be easily upgraded If a single router and VPN gateway are used in the hub to manage all tunnels, particular care must be taken in announcing ranges from hub to spokes, as Cloud Router does not explicitly support transitivity and overlapping routes received from both sides create unintended side effects. The simple workaround is to announce a single aggregated route from hub to spokes so that it does not overlap with any of the ranges advertised by each spoke to the hub. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project id for all resources. | string | ✓ | | -| *bgp_asn* | BGP ASNs. | map(number) | | ... | -| *bgp_custom_advertisements* | BGP custom advertisement IP CIDR ranges. | map(string) | | ... | -| *bgp_interface_ranges* | BGP interface IP CIDR ranges. | map(string) | | ... | -| *ip_ranges* | IP CIDR ranges. | map(string) | | ... | -| *regions* | VPC regions. | map(string) | | ... | +|---|---|:---:|:---:|:---:| +| project_id | Project id for all resources. | string | ✓ | | +| bgp_asn | BGP ASNs. | map(number) | | {…} | +| bgp_custom_advertisements | BGP custom advertisement IP CIDR ranges. | map(string) | | {…} | +| bgp_interface_ranges | BGP interface IP CIDR ranges. | map(string) | | {…} | +| ip_ranges | IP CIDR ranges. | map(string) | | {…} | +| regions | VPC regions. | map(string) | | {…} | ## Outputs | name | description | sensitive | |---|---|:---:| | vms | GCE VMs. | | + + diff --git a/networking/ilb-next-hop/README.md b/networking/ilb-next-hop/README.md index 5b14f3b0..b6c496f7 100644 --- a/networking/ilb-next-hop/README.md +++ b/networking/ilb-next-hop/README.md @@ -61,18 +61,19 @@ A sample testing session using `tmux`: Test session screenshot + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Existing project id. | string | ✓ | | -| *ilb_right_enable* | Route right to left traffic through ILB. | bool | | false | -| *ilb_session_affinity* | Session affinity configuration for ILBs. | string | | CLIENT_IP | -| *ip_ranges* | IP CIDR ranges used for VPC subnets. | map(string) | | ... | -| *prefix* | Prefix used for resource names. | string | | ilb-test | -| *project_create* | Create project instead of using an existing one. | bool | | false | -| *region* | Region used for resources. | string | | europe-west1 | -| *zones* | Zone suffixes used for instances. | list(string) | | ["b", "c"] | +|---|---|:---:|:---:|:---:| +| project_id | Existing project id. | string | ✓ | | +| ilb_right_enable | Route right to left traffic through ILB. | bool | | false | +| ilb_session_affinity | Session affinity configuration for ILBs. | string | | "CLIENT_IP" | +| ip_ranges | IP CIDR ranges used for VPC subnets. | map(string) | | {…} | +| prefix | Prefix used for resource names. | string | | "ilb-test" | +| project_create | Create project instead of using an existing one. | bool | | false | +| region | Region used for resources. | string | | "europe-west1" | +| zones | Zone suffixes used for instances. | list(string) | | ["b", "c"] | ## Outputs @@ -84,4 +85,6 @@ A sample testing session using `tmux`: | ssh_gw | Command-line login to gateway VMs. | | | ssh_vm_left | Command-line login to left VMs. | | | ssh_vm_right | Command-line login to right VMs. | | + + diff --git a/networking/onprem-google-access-dns/README.md b/networking/onprem-google-access-dns/README.md index aadf9667..53d5c811 100644 --- a/networking/onprem-google-access-dns/README.md +++ b/networking/onprem-google-access-dns/README.md @@ -202,18 +202,19 @@ A single pre-existing project is used in this example to keep variables and comp The VPN-s used to connect to the on-premises environment do not account for HA, upgrading to use HA VPN is reasonably simple by using the relevant [module](../../modules/net-vpn-ha). + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project id for all resources. | string | ✓ | | -| *bgp_asn* | BGP ASNs. | map(number) | | ... | -| *bgp_interface_ranges* | BGP interface IP CIDR ranges. | map(string) | | ... | -| *dns_forwarder_address* | Address of the DNS server used to forward queries from on-premises. | string | | 10.0.0.2 | -| *forwarder_address* | GCP DNS inbound policy forwarder address. | string | | 10.0.0.2 | -| *ip_ranges* | IP CIDR ranges. | map(string) | | ... | -| *region* | VPC region. | map(string) | | ... | -| *ssh_source_ranges* | IP CIDR ranges that will be allowed to connect via SSH to the onprem instance. | list(string) | | ["0.0.0.0/0"] | +|---|---|:---:|:---:|:---:| +| project_id | Project id for all resources. | string | ✓ | | +| bgp_asn | BGP ASNs. | map(number) | | {…} | +| bgp_interface_ranges | BGP interface IP CIDR ranges. | map(string) | | {…} | +| dns_forwarder_address | Address of the DNS server used to forward queries from on-premises. | string | | "10.0.0.2" | +| forwarder_address | GCP DNS inbound policy forwarder address. | string | | "10.0.0.2" | +| ip_ranges | IP CIDR ranges. | map(string) | | {…} | +| region | VPC region. | map(string) | | {…} | +| ssh_source_ranges | IP CIDR ranges that will be allowed to connect via SSH to the onprem instance. | list(string) | | ["0.0.0.0/0"] | ## Outputs @@ -222,4 +223,6 @@ The VPN-s used to connect to the on-premises environment do not account for HA, | onprem-instance | Onprem instance details. | | | test-instance1 | Test instance details. | | | test-instance2 | Test instance details. | | + + diff --git a/networking/private-cloud-function-from-onprem/README.md b/networking/private-cloud-function-from-onprem/README.md index 43f58f0c..9c34d720 100644 --- a/networking/private-cloud-function-from-onprem/README.md +++ b/networking/private-cloud-function-from-onprem/README.md @@ -15,20 +15,23 @@ curl https://YOUR_REGION-YOUR_PROJECT_ID.cloudfunctions.net/YOUR_FUNCTION_NAME ![Cloud Function via Private Service Connect](diagram.png "High-level diagram") + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project id. | string | ✓ | | -| *ip_ranges* | IP ranges used for the VPCs. | object({...}) | | ... | -| *name* | Name used for new resources. | string | | cf-via-psc | -| *project_create* | If non null, creates project instead of using an existing one. | object({...}) | | null | -| *psc_endpoint* | IP used for the Private Service Connect endpoint, it must not overlap with the hub_ip_range. | string | | 172.16.32.1 | -| *region* | Region where the resources will be created. | string | | europe-west1 | +|---|---|:---:|:---:|:---:| +| project_id | Project id. | string | ✓ | | +| ip_ranges | IP ranges used for the VPCs. | object({…}) | | {…} | +| name | Name used for new resources. | string | | "cf-via-psc" | +| project_create | If non null, creates project instead of using an existing one. | object({…}) | | null | +| psc_endpoint | IP used for the Private Service Connect endpoint, it must not overlap with the hub_ip_range. | string | | "172.16.32.1" | +| region | Region where the resources will be created. | string | | "europe-west1" | ## Outputs | name | description | sensitive | |---|---|:---:| | function_url | URL of the Cloud Function. | | + + \ No newline at end of file diff --git a/networking/shared-vpc-gke/README.md b/networking/shared-vpc-gke/README.md index 0085b961..3bfa1dc5 100644 --- a/networking/shared-vpc-gke/README.md +++ b/networking/shared-vpc-gke/README.md @@ -43,22 +43,23 @@ alias k='HTTPS_PROXY=localhost:8888 kubectl $@' There's a minor glitch that can surface running `terraform destroy`, where the service project attachments to the Shared VPC will not get destroyed even with the relevant API call succeeding. We are investigating the issue, in the meantime just manually remove the attachment in the Cloud console or via the `gcloud beta compute shared-vpc associated-projects remove` command when `terraform destroy` fails, and then relaunch the command. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | -| root_node | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| *cluster_create* | Create GKE cluster and nodepool. | bool | | true | -| *ip_ranges* | Subnet IP CIDR ranges. | map(string) | | ... | -| *ip_secondary_ranges* | Secondary IP CIDR ranges. | map(string) | | ... | -| *owners_gce* | GCE project owners, in IAM format. | list(string) | | [] | -| *owners_gke* | GKE project owners, in IAM format. | list(string) | | [] | -| *owners_host* | Host project owners, in IAM format. | list(string) | | [] | -| *private_service_ranges* | Private service IP CIDR ranges. | map(string) | | ... | -| *project_services* | Service APIs enabled by default in new projects. | list(string) | | ... | -| *region* | Region used. | string | | europe-west1 | +|---|---|:---:|:---:|:---:| +| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | +| root_node | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | +| cluster_create | Create GKE cluster and nodepool. | bool | | true | +| ip_ranges | Subnet IP CIDR ranges. | map(string) | | {…} | +| ip_secondary_ranges | Secondary IP CIDR ranges. | map(string) | | {…} | +| owners_gce | GCE project owners, in IAM format. | list(string) | | [] | +| owners_gke | GKE project owners, in IAM format. | list(string) | | [] | +| owners_host | Host project owners, in IAM format. | list(string) | | [] | +| private_service_ranges | Private service IP CIDR ranges. | map(string) | | {…} | +| project_services | Service APIs enabled by default in new projects. | list(string) | | […] | +| region | Region used. | string | | "europe-west1" | ## Outputs @@ -68,4 +69,6 @@ There's a minor glitch that can surface running `terraform destroy`, where the s | projects | Project ids. | | | vms | GCE VMs. | | | vpc | Shared VPC. | | + + diff --git a/third-party-solutions/openshift/tf/README.md b/third-party-solutions/openshift/tf/README.md index 18ef5758..2af199c3 100644 --- a/third-party-solutions/openshift/tf/README.md +++ b/third-party-solutions/openshift/tf/README.md @@ -3,23 +3,24 @@ This example is a companion setup to the Python script in the parent folder, and is used to bootstrap OpenShift clusters on GCP. Refer to the documentation in the parent folder for usage instructions. + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| cluster_name | Name used for the cluster and DNS zone. | string | ✓ | | -| domain | Domain name used to derive the DNS zone. | string | ✓ | | -| fs_paths | Filesystem paths for commands and data, supports home path expansion. | object({...}) | ✓ | | -| host_project | Shared VPC project and network configuration. | object({...}) | ✓ | | -| service_project | Service project configuration. | object({...}) | ✓ | | -| *allowed_ranges* | Ranges that can SSH to the boostrap VM and API endpoint. | list(any) | | ["10.0.0.0/8"] | -| *disk_encryption_key* | Optional CMEK for disk encryption. | object({...}) | | null | -| *install_config_params* | OpenShift cluster configuration. | object({...}) | | ... | -| *post_bootstrap_config* | Name of the service account for the machine operator. Removes bootstrap resources when set. | object({...}) | | null | -| *region* | Region where resources will be created. | string | | europe-west1 | -| *rhcos_gcp_image* | RHCOS image used. | string | | projects/rhcos-cloud/global/images/rhcos-47-83-202102090044-0-gcp-x86-64 | -| *tags* | Additional tags for instances. | list(string) | | ["ssh"] | -| *zones* | Zones used for instances. | list(string) | | ["b", "c", "d"] | +|---|---|:---:|:---:|:---:| +| cluster_name | Name used for the cluster and DNS zone. | string | ✓ | | +| domain | Domain name used to derive the DNS zone. | string | ✓ | | +| fs_paths | Filesystem paths for commands and data, supports home path expansion. | object({…}) | ✓ | | +| host_project | Shared VPC project and network configuration. | object({…}) | ✓ | | +| service_project | Service project configuration. | object({…}) | ✓ | | +| allowed_ranges | Ranges that can SSH to the boostrap VM and API endpoint. | list(any) | | ["10.0.0.0/8"] | +| disk_encryption_key | Optional CMEK for disk encryption. | object({…}) | | null | +| install_config_params | OpenShift cluster configuration. | object({…}) | | {…} | +| post_bootstrap_config | Name of the service account for the machine operator. Removes bootstrap resources when set. | object({…}) | | null | +| region | Region where resources will be created. | string | | "europe-west1" | +| rhcos_gcp_image | RHCOS image used. | string | | "projects/rhcos-cloud/global/images/rhcos-47-83-202102090044-0-gcp-x86-64" | +| tags | Additional tags for instances. | list(string) | | ["ssh"] | +| zones | Zones used for instances. | list(string) | | ["b", "c", "d"] | ## Outputs @@ -28,4 +29,6 @@ This example is a companion setup to the Python script in the parent folder, and | backend-health | Command to monitor API internal backend health. | | | bootstrap-ssh | Command to SSH to the bootstrap instance. | | | masters-ssh | Command to SSH to the master instances. | | + + diff --git a/tools/check_documentation.py b/tools/check_documentation.py index f6617eec..cd8dd513 100755 --- a/tools/check_documentation.py +++ b/tools/check_documentation.py @@ -16,87 +16,44 @@ import enum import pathlib -import sys import click import tfdoc + BASEDIR = pathlib.Path(__file__).resolve().parents[1] -class DocState(enum.Enum): - OK = 1 - FAIL = 2 - UNKNOWN = 3 - - def __str__(self): - return { - self.FAIL.value: '✗', - self.OK.value: '✓', - self.UNKNOWN.value: '?' - }[self.value] +State = enum.Enum('State', 'OK FAIL SKIP') -def check_path(pathname): - path = BASEDIR / pathname - subpaths = sorted(list(path.iterdir())) - for subpath in subpaths: - errors = [] - if not subpath.is_dir(): - continue - if subpath.stem.startswith('_'): - continue - - doc = subpath / 'README.md' - if not doc.exists(): - errors.append(f'{doc} does not exist') - - variables = tfdoc.get_variables(subpath) - variable_names = [v.name for v in variables] - for variable in variables: - if not variable.description: - errors.append(f'variable {variable.name} has no description') - if sorted(variable_names) != variable_names: - message = f'variable order should be: {sorted(variable_names)}' - errors.append(message) - - outputs = tfdoc.get_outputs(subpath) - output_names = [v.name for v in outputs] - for output in outputs: - if not output.description: - errors.append(f'output {output.name} has no description') - if sorted(output_names) != output_names: - message = f'output order should be: {sorted(output_names)}' - errors.append(message) - - state = tfdoc.check_state(subpath) - if state is False: - errors.append("documentation is out of date") - elif state: - pass +def _check_dir(dir_name): + dir_path = BASEDIR / dir_name + for readme_path in dir_path.glob('**/README.md'): + readme = readme_path.read_text() + mod_name = str(readme_path.relative_to(dir_path).parent) + result = tfdoc.get_doc(readme) + if not result: + state = State.SKIP else: - yield DocState.UNKNOWN, subpath.stem, errors - continue - - yield DocState.FAIL if errors else DocState.OK, subpath.stem, errors + try: + new_doc = tfdoc.create_doc(readme_path.parent) + except SystemExit: + state = state.SKIP + else: + state = State.OK if new_doc == result['doc'] else State.FAIL + yield mod_name, state @click.command() -@click.argument('paths', type=str, nargs=-1) -def main(paths): - "Cycle through modules and ensure READMEs are up-to-date." - error = False - for path in paths: - print(f'checking {path}') - for state, name, errors in check_path(path): - if state == DocState.FAIL: - error = True - print(f' [{state}] {name}') - for error in errors: - print(f' {error}') - if error: - print('errors were present') - sys.exit(1) +@click.argument('dirs', type=str, nargs=-1) +def main(dirs): + 'Cycle through modules and ensure READMEs are up-to-date.' + state_labels = {State.FAIL: '✗', State.OK: '✓', State.SKIP: '?'} + for dir_name in dirs: + print(f'----- {dir_name} -----') + for mod_name, state in _check_dir(dir_name): + print(f'[{state_labels[state]}] {mod_name}') if __name__ == '__main__': diff --git a/tools/tfdoc.py b/tools/tfdoc.py index 38b6d419..8b610ceb 100755 --- a/tools/tfdoc.py +++ b/tools/tfdoc.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python3 # Copyright 2021 Google LLC # @@ -14,274 +14,350 @@ # See the License for the specific language governing permissions and # limitations under the License. +'''Generate tables for Terraform root module files, outputs and variables. + +This tool generates nicely formatted Markdown tables from Terraform source +code, that can be used in root modules README files. It makes a few assumptions +on the code structure: + +- that outputs are only in `outputs.tf` +- that variables are only in `variables.tf` +- that code has been formatted via `terraform fmt` + +The tool supports annotations using the `tfdoc:scope:key value` syntax. +Annotations are rendered in the optional file table by default, and in the +outputs and variables tables if the `--extra-fields` flag is used. Currently +supported annotations are: + +- `tfdoc:file:description` +- `tfdoc:output:consumers` +- `tfdoc:variable:source` + +Tables can optionally be directly injected/replaced in README files by using +the tags in the `MARK_BEGIN` and `MARK_END` constants, and setting the +`--replace` flag. +''' + import collections import enum import glob import os import re import string +import urllib.parse import click +__version__ = '2.0' + + +# TODO(ludomagno): decide if we want to support variables*.tf and outputs*.tf + + +FILE_DESC_DEFAULTS = { + 'main.tf': 'Module-level locals and resources.', + 'outputs.tf': 'Module outputs.', + 'providers.tf': 'Provider configurations.', + 'variables.tf': 'Module variables.', + 'versions.tf': 'Version pins.', +} +FILE_RE_MODULES = re.compile( + r'(?sm)module\s*"[^"]+"\s*\{[^\}]*?source\s*=\s*"([^"]+)"' +) +FILE_RE_RESOURCES = re.compile( + r'(?sm)resource\s*"([^"]+)"' +) +HEREDOC_RE = re.compile(r'(?sm)^<<\-?END(\s*.*?)\s*END$') MARK_BEGIN = '' MARK_END = '' -RE_OUTPUTS = re.compile(r'''(?smx) - (?:^\s*output\s*"([^"]+)"\s*\{$) | - (?:^\s*description\s*=\s*"((?:[^"\\]|\\")+)"\s*$) | - (?:^\s*sensitive\s*=\s*(\S+)\s*$) +OUT_ENUM = enum.Enum('O', 'OPEN ATTR ATTR_DATA CLOSE COMMENT TXT SKIP') +OUT_RE = re.compile(r'''(?smx) + # output open + (?:^\s*output\s*"([^"]+)"\s*\{\s*$) | + # attribute + (?:^\s{2}([a-z]+)\s*=\s*"?(.*?)"?\s*$) | + # output close + (?:^\s?(\})\s*$) | + # comment + (?:^\s*\#\s*(.*?)\s*$) | + # anything else + (?:^(.*?)$) ''') -RE_TYPE = re.compile(r'([\(\{\}\)])') -RE_VARIABLES = re.compile(r'''(?smx) - # empty lines - (^\s*$) | - # block comment - (^\s*/\*.*?\*/$) | - # line comment - (^\s*\#.*?$) | - # variable declaration start - (?:^\s*variable\s*"([^"]+)"\s*\{$) | - # variable description start - (?:^\s*description\s*=\s*"((?:[^"\\]|\\")+)"\s*$) | - # variable type start - (?:^\s*type\s*=\s*(.*?)$) | - # variable default start - (?:^\s*default\s*=\s*"?(.*?)"?\s*$) | - # variable body - (?:^\s*(\S.*?)$) +OUT_TEMPLATE = ('description', 'value', 'sensitive') +TAG_RE = re.compile(r'(?sm)^\s*#\stfdoc:([^:]+:\S+)\s+(.*?)\s*$') +UNESCAPED = string.digits + string.ascii_letters + ' .,;:_-' +VAR_ENUM = enum.Enum( + 'V', 'OPEN ATTR ATTR_DATA SKIP CLOSE COMMENT TXT') +VAR_RE = re.compile(r'''(?smx) + # variable open + (?:^\s*variable\s*"([^"]+)"\s*\{\s*$) | + # attribute + (?:^\s{2}([a-z]+)\s*=\s*"?(.*?)"?\s*$) | + # validation + (?:^\s+validation\s*(\{)\s*$) | + # variable close + (?:^\s?(\})\s*$) | + # comment + (?:^\s*\#\s*(.*?)\s*$) | + # anything else + (?:^(.*?)$) ''') -REPL_VALID = string.digits + string.ascii_letters + ' .,;:_-' +VAR_RE_TYPE = re.compile(r'([\(\{\}\)])') +VAR_TEMPLATE = ('default', 'description', 'type') -OutputData = collections.namedtuple('Output', 'name description sensitive') -OutputToken = enum.Enum('OutputToken', 'NAME DESCRIPTION SENSITIVE') -VariableData = collections.namedtuple( - 'Variable', 'name description type default required') -VariableToken = enum.Enum( - 'VariableToken', - 'EMPTY BLOCK_COMMENT LINE_COMMENT NAME DESCRIPTION TYPE DEFAULT REST') +File = collections.namedtuple('File', 'name description modules resources') +Output = collections.namedtuple('Output', + 'name description sensitive consumers') +Variable = collections.namedtuple( + 'Variable', 'name description type default required source') -class ItemParsed(Exception): - pass +# parsing functions -class Output(object): - "Output parsing helper class." - - def __init__(self): - self.in_progress = False - self.name = self.description = self.sensitive = None - - def parse_token(self, token_type, token_data): - if token_type == 'NAME': - if self.in_progress: - raise ItemParsed(self.close()) - self.in_progress = True - self.name = token_data - else: - setattr(self, token_type.lower(), token_data) - - def close(self): - return OutputData(self.name, self.description, self.sensitive) +def _extract_tags(body): + 'Extract and return tfdocs tags from content.' + return {k: v for k, v in TAG_RE.findall(body)} -class Variable(object): - "Variable parsing helper class." +def _parse(body, enum=VAR_ENUM, re=VAR_RE, template=VAR_TEMPLATE): + 'Low-level parsing function for outputs and variables.' + item = context = None + for m in re.finditer(body): + token = enum(m.lastindex) + data = m.group(m.lastindex) + # print(token, m.groups()) + if token == enum.OPEN: + item = {'name': data, 'tags': {}} + item.update({k: [] for k in template}) + context = None + elif token == enum.CLOSE: + if item: + yield item + item = context = None + elif token == enum.ATTR_DATA: + if not item: + continue + context = m.group(m.lastindex-1) + item[context].append(data) + elif token == enum.SKIP: + context = token + elif token == enum.COMMENT: + if item and data.startswith('tfdoc:'): + k, v = data.split(' ', 1) + item['tags'][k[6:]] = v + elif token == enum.TXT: + if context and context != enum.SKIP: + item[context].append(data) - def __init__(self): - self.in_progress = False - self.name = self.description = self.type = self.default = None - self._data = [] - self._data_context = None - def parse_token(self, token_type, token_data): - if token_type == 'NAME': - if self.in_progress: - raise ItemParsed(self.close()) - self.in_progress = True - self.name = token_data - elif token_type == 'DESCRIPTION': - setattr(self, token_type.lower(), token_data) - elif token_type in ('DEFAULT', 'TYPE'): - self._start(token_type.lower(), token_data) - elif token_type == 'REST': - self._data.append(token_data) +def parse_files(basepath): + 'Return a list of File named tuples in root module at basepath.' + for name in glob.glob(os.path.join(basepath, '*tf')): + try: + with open(name) as file: + body = file.read() + except (IOError, OSError) as e: + raise SystemExit(f'Cannot read file {name}: {e}') + shortname = os.path.basename(name) + tags = _extract_tags(body) + description = tags.get( + 'file:description', FILE_DESC_DEFAULTS.get(shortname)) + modules = set( + os.path.basename(urllib.parse.urlparse(m).path) + for m in FILE_RE_MODULES.findall(body) + ) + resources = set(FILE_RE_RESOURCES.findall(body)) + yield File(shortname, description, modules, resources) - def _close(self, strip=False): - if self._data_context: - data = self._data - if strip and '}' in data[-1]: - data = data[:-1] - setattr(self, self._data_context, ('\n'.join(data)).strip()) - def _start(self, context, data): - if context == self._data_context or getattr(self, context): - self._data.append("%s = %s" % (context, data)) - return - self._close() - self._data = [data] - self._data_context = context +def parse_outputs(basepath): + 'Return a list of Output named tuples for root module outputs.tf.' + try: + with open(os.path.join(basepath, 'outputs.tf')) as file: + body = file.read() + except (IOError, OSError) as e: + raise SystemExit(f'No outputs file in {basepath}.') + for item in _parse(body, enum=OUT_ENUM, re=OUT_RE, template=OUT_TEMPLATE): + yield Output(name=item['name'], description=''.join(item['description']), + sensitive=item['sensitive'] != [], + consumers=item['tags'].get('output:consumers', '')) - def close(self): - self._close(strip=True) - return VariableData(self.name, self.description, self.type, self.default, - self.default is None) + +def parse_variables(basepath): + 'Return a list of Output named tuples for root module variables.tf.' + try: + with open(os.path.join(basepath, 'variables.tf')) as file: + body = file.read() + except (IOError, OSError) as e: + raise SystemExit(f'No variables file in {basepath}.') + for item in _parse(body): + # print(item) + default = HEREDOC_RE.sub(r'\1', '\n'.join(item['default'])) + required = not item['default'] + vtype = '\n'.join(item['type']) + if not required and default != 'null' and vtype == 'string': + default = f'"{default}"' + yield Variable(name=item['name'], description=''.join(item['description']), + type=vtype, default=default, + required=required, + source=item['tags'].get('variable:source', '')) + + +# formatting functions def _escape(s): - "Basic, minimal HTML escaping" - return ''.join(c if c in REPL_VALID else ('&#%s;' % ord(c)) for c in s) + 'Basic, minimal HTML escaping' + return ''.join(c if c in UNESCAPED else ('&#%s;' % ord(c)) for c in s) -def format_outputs(outputs): - "Format outputs." - if not outputs: - return - outputs.sort(key=lambda v: v.name) - yield '| name | description | sensitive |' - yield '|---|---|:---:|' - for o in outputs: - yield '| {name} | {description} | {sensitive} |'.format( - name=o.name, description=o.description, - sensitive='✓' if o.sensitive else '') - - -def format_type(type_spec): - "Format variable type." - if not type_spec: - return '' +def format_doc(outputs, variables, files, show_extra=True): + 'Return formatted document.' buffer = [] - stack = [] - for t in RE_TYPE.split(type_spec.split("\n")[0]): - if not t: - continue - if t in '({': - stack.append(t) - elif t in '})': - stack.pop() - buffer.append(t) - for t in reversed(stack): - buffer.append(')' if t == '(' else '}') - return ''.join(buffer).replace('object({})', 'object({...})') - - -def format_variables(variables, required_first=True): - "Format variables." - if not variables: - return - variables.sort(key=lambda v: v.name) - variables.sort(key=lambda v: v.required, reverse=True) - yield '| name | description | type | required | default |' - yield '|---|---|:---: |:---:|:---:|' - row = ( - '| {name} | {description} | {type} ' - '| {required} | {default} |' - ) - for v in variables: - default = type_spec = '' - if not v.required: - default = '{default}' - if '\n' in v.default: - default = default.format(title=_escape(v.default), default='...') - else: - default = default.format(title='', default=v.default or '') - if v.type and '(' in v.type: - type_spec = _escape(v.type) - yield row.format( - name=v.name if v.required else '*%s*' % v.name, - description=v.description, required='✓' if v.required else '', - type=format_type(v.type), type_spec=type_spec, - default=default - ) - - -def get_doc(variables, outputs): - "Return formatted documentation." - buffer = ['## Variables\n'] - for line in format_variables(variables): - buffer.append(line) - buffer.append('\n## Outputs\n') - for line in format_outputs(outputs): - buffer.append(line) + if files: + buffer.append('\n## Files\n') + for line in format_files(files): + buffer.append(line) + buffer.append('\n') + if variables: + buffer.append('\n## Variables\n') + for line in format_variables(variables, show_extra): + buffer.append(line) + if outputs: + buffer.append('\n## Outputs\n') + for line in format_outputs(outputs, show_extra): + buffer.append(line) + buffer.append('\n') return '\n'.join(buffer) -def parse_items(content, item_re, item_enum, item_class, item_data_class): - "Parse variable or output items in data." - item = item_class() - for m in item_re.finditer(content): - try: - item.parse_token(item_enum(m.lastindex).name, m.group(m.lastindex)) - except ItemParsed as e: - item = item_class() - item.parse_token(item_enum(m.lastindex).name, m.group(m.lastindex)) - yield e.args[0] - if item.in_progress: - yield item.close() +def format_files(items): + 'Format files table.' + items.sort(key=lambda i: i.name) + yield '| name | description | modules | resources |' + yield '|---|---|---|---|' + for i in items: + modules = resources = '' + if i.modules: + modules = '%s' % ' · '.join( + sorted(i.modules)) + if i.resources: + resources = '%s' % ' · '.join( + sorted(i.resources)) + yield f'| [{i.name}](./{i.name}) | {i.description} | {modules} | {resources} |' -def replace_doc(module, doc): - "Replace document in module's README.md file." - try: - readme = open(os.path.join(module, 'README.md')).read() - m = re.search('(?sm)%s.*%s' % (MARK_BEGIN, MARK_END), readme) - if not m: - raise SystemExit('Pattern not found in README file.') - replacement = "{pre}{begin}\n{doc}\n{end}{post}".format( - pre=readme[:m.start()], begin=MARK_BEGIN, doc=doc, - end=MARK_END, post=readme[m.end():]) - open(os.path.join(module, 'README.md'), 'w').write(replacement) - except (IOError, OSError) as e: - raise SystemExit('Error replacing in README: %s' % e) - - -def get_variables(path): - "Get variables for the module in a path" - variables = [] - for path in glob.glob(os.path.join(path, 'variables*tf')): - with open(path) as file: - variables += [v for v in parse_items( - file.read(), RE_VARIABLES, VariableToken, Variable, VariableData)] - return variables - - -def get_outputs(path): - "Get outputs for the module in a path" - outputs = [] - for path in glob.glob(os.path.join(path, 'outputs*tf')): - with open(path) as file: - outputs += [o for o in parse_items( - file.read(), RE_OUTPUTS, OutputToken, Output, OutputData)] - return outputs - - -def check_state(path): - """Determine if a module's README has all its variables and outputs - documentation up-to-date.""" - try: - variables = get_variables(path) - outputs = get_outputs(path) - readme = open(os.path.join(path, 'README.md')).read() - except (IOError, OSError): +def format_outputs(items, show_extra=True): + 'Format outputs table.' + if not items: return - m = re.search('(?sm)%s.*%s' % (MARK_BEGIN, MARK_END), readme) + items.sort(key=lambda i: i.name) + yield '| name | description | sensitive |' + ( + ' consumers |' if show_extra else '' + ) + yield '|---|---|:---:|' + ( + '---|' if show_extra else '' + ) + for i in items: + consumers = i.consumers or '' + if consumers: + consumers = '%s' % ' · '.join( + consumers.split()) + sensitive = '✓' if i.sensitive else '' + format = f'| {i.name} | {i.description or ""} | {sensitive} |' + format += f' {consumers} |' if show_extra else '' + yield format + + +def format_variables(items, show_extra=True): + 'Format variables table.' + if not items: + return + items.sort(key=lambda i: i.name) + items.sort(key=lambda i: i.required, reverse=True) + yield '| name | description | type | required | default |' + ( + ' producer |' if show_extra else '' + ) + yield '|---|---|:---:|:---:|:---:|' + ( + ':---:|' if show_extra else '' + ) + for i in items: + vars = { + 'default': f'{_escape(i.default)}' if i.default else '', + 'required': '✓' if i.required else '', + 'source': f'{i.source}' if i.source else '', + 'type': f'{_escape(i.type)}' + } + for k in ('default', 'type'): + title = getattr(i, k) + if '\n' in title: + value = title.split('\n') + # remove indent + title = '\n'.join([value[0]] + [l[2:] for l in value[1:]]) + if len(value[0]) >= 18 or len(value[-1]) >= 18: + value = '…' + else: + value = f'{value[0]}…{value[-1].strip()}' + vars[k] = f'{_escape(value)}' + format = ( + f'| {i.name} | {i.description or ""} | {vars["type"]} ' + f'| {vars["required"]} | {vars["default"]} |' + ) + format += f' {vars["source"]} |' if show_extra else '' + yield format + + +# replace functions + + +def get_doc(readme): + 'Check if README file is marked, and return current doc.' + m = re.search('(?sm)%s\n(.*)\n%s' % (MARK_BEGIN, MARK_END), readme) if not m: return - return get_doc(variables, outputs) in readme + return {'doc': m.group(1), 'start': m.start(), 'end': m.end()} -@click.command() -@click.argument('module', type=click.Path(exists=True)) -@click.option('--replace/--no-replace', default=True) -def main(module=None, replace=True): - "Program entry point." +def create_doc(module_path, files=False, show_extra=False): try: - variables = get_variables(module) - outputs = get_outputs(module) + mod_files = list(parse_files(module_path)) if files else None + mod_variables = list(parse_variables(module_path)) + mod_outputs = list(parse_outputs(module_path)) except (IOError, OSError) as e: raise SystemExit(e) - doc = get_doc(variables, outputs) + return format_doc(mod_outputs, mod_variables, mod_files, show_extra) + + +def replace_doc(module_path, doc): + 'Replace document in module\'s README.md file.' + readme_path = os.path.join(module_path, 'README.md') + try: + readme = open(readme_path).read() + except (IOError, OSError) as e: + raise SystemExit(f'Error opening README {readme_path}: {e}') + result = get_doc(readme) + if not result: + raise SystemExit(f'Mark not found in README {readme_path}') + if doc == result['doc']: + return + try: + open(readme_path, 'w').write(readme.replace(result['doc'], doc)) + except (IOError, OSError) as e: + raise SystemExit(f'Error replacing README {readme_path}: {e}') + + +@ click.command() +@ click.argument('module', type=click.Path(exists=True)) +@ click.option('--show-extra/--no-show-extra', default=False) +@ click.option('--files/--no-files', default=False) +@ click.option('--replace/--no-replace', default=True) +def main(module=None, annotate=False, files=False, replace=True, + show_extra=True): + 'Program entry point.' + doc = create_doc(module, files, show_extra) if replace: replace_doc(module, doc) else: From b63114849bd0899405114db92aff6e4af4afb7ac Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 21 Dec 2021 08:52:20 +0100 Subject: [PATCH 21/52] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75f76836..dc86b8fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - **incompatible change** added support for partitioned tables to `organization` module sinks - **incompatible change** renamed `private_service_networking_range` variable to `psc_ranges` in `net-vpc`module, and changed its type to `list(string)` - added a firewall policy factory to `organization` module +- refactored `tfdoc` ## [8.0.0] - 2021-10-21 From 0c21fecff8fafc1e2c7152a6bd6551527b0c1505 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Tue, 21 Dec 2021 09:22:10 +0100 Subject: [PATCH 22/52] Add metric scopes support (#397) * Add metric scopes support * Fix typo * new tfdoc block in README Co-authored-by: Ludovico Magnocavallo --- CHANGELOG.md | 1 + modules/project/README.md | 1 + modules/project/main.tf | 7 +++++++ modules/project/variables.tf | 6 ++++++ 4 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc86b8fa..0a5078b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - **incompatible change** renamed `private_service_networking_range` variable to `psc_ranges` in `net-vpc`module, and changed its type to `list(string)` - added a firewall policy factory to `organization` module - refactored `tfdoc` +- added support for metric scopes to the `project` module ## [8.0.0] - 2021-10-21 diff --git a/modules/project/README.md b/modules/project/README.md index aa21fa77..d07d1ffb 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -193,6 +193,7 @@ module "project" { | lien_reason | If non-empty, creates a project lien with this description. | string | | "" | | logging_exclusions | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | | logging_sinks | Logging sinks to create for this project. | map(object({…})) | | {} | +| metric_scopes | List of projects that will act as metric scopes for this project. | list(string) | | null | | oslogin | Enable OS Login. | bool | | false | | oslogin_admins | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | list(string) | | [] | | oslogin_users | List of IAM-style identities that will be granted roles necessary for OS Login users. | list(string) | | [] | diff --git a/modules/project/main.tf b/modules/project/main.tf index bb4ca986..abd15fed 100644 --- a/modules/project/main.tf +++ b/modules/project/main.tf @@ -389,3 +389,10 @@ resource "google_kms_crypto_key_iam_member" "crypto_key" { data.google_storage_project_service_account.gcs_sa, ] } + +resource "google_monitoring_monitored_project" "primary" { + provider = google-beta + for_each = toset(coalesce(var.metric_scopes, [])) + metrics_scope = each.value + name = local.project.project_id +} diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 0db91975..34db467b 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -100,6 +100,12 @@ variable "logging_sinks" { default = {} } +variable "metric_scopes" { + description = "List of projects that will act as metric scopes for this project." + type = list(string) + default = null +} + variable "name" { description = "Project name and id suffix." type = string From 173def118f70ffb4dd1e6bf3792e95c2d884731e Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 21 Dec 2021 11:51:15 +0100 Subject: [PATCH 23/52] fix tfdoc replacement (#398) --- tools/tfdoc.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/tfdoc.py b/tools/tfdoc.py index 8b610ceb..70a50eca 100755 --- a/tools/tfdoc.py +++ b/tools/tfdoc.py @@ -49,7 +49,7 @@ import urllib.parse import click -__version__ = '2.0' +__version__ = '2.0.1' # TODO(ludomagno): decide if we want to support variables*.tf and outputs*.tf @@ -344,7 +344,13 @@ def replace_doc(module_path, doc): if doc == result['doc']: return try: - open(readme_path, 'w').write(readme.replace(result['doc'], doc)) + open(readme_path, 'w').write('\n'.join([ + readme[:result['start']], + MARK_BEGIN, + doc, + MARK_END, + readme[result['end']:] + ])) except (IOError, OSError) as e: raise SystemExit(f'Error replacing README {readme_path}: {e}') From 54367f2947695b4a8e2f928fa5cbcf2649f4e260 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 22 Dec 2021 10:06:31 +0100 Subject: [PATCH 24/52] Update CHANGELOG.md --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a5078b6..a02927b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] + +## [9.0.0] - 2021-12-22 + - new `cloud-run` module - added gVNIC support to `compute-vm` module - added a rule factory to `net-vpc-firewall` module @@ -383,7 +386,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/v8.0.0...HEAD +[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.0...HEAD +[9.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v8.0.0...v9.0.0 [8.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v7.0.0...v8.0.0 [7.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v6.0.0...v7.0.0 [6.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v5.1.0...v6.0.0 From e2f5b96f4aecf420c3949f4685f0e5cab5d66799 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 22 Dec 2021 10:46:27 +0100 Subject: [PATCH 25/52] Ludo hfw fixes (#400) * fix tfdoc replacement * ignore changes to rule description * add folder example, fixes #339 --- modules/folder/README.md | 55 +++++++++++++++++++++++++ modules/folder/firewall-policy.tf | 5 +++ modules/organization/firewall-policy.tf | 5 +++ 3 files changed, 65 insertions(+) diff --git a/modules/folder/README.md b/modules/folder/README.md index 3b0ae8b7..41ece1f2 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -47,6 +47,61 @@ module "folder" { # tftest:modules=1:resources=4 ``` +### Firewall policy factory + +In the same way as for the [organization]()../organization) module, the in-built factory allows you to define a single policy, using one file for rules, and an optional file for CIDR range substitution variables. Remember that non-absolute paths are relative to the root module (the folder where you run `terraform`). + +```hcl +module "folder" { + source = "./modules/folder" + parent = "organizations/1234567890" + name = "Folder name" + firewall_policy_factory = { + cidr_file = "data/cidrs.yaml + policy_name = null + rules_file = "data/rules.yaml" + } +} +# tftest:skip +``` + +```yaml +# cidrs.yaml + +rfc1918: + - 10.0.0.0/8 + - 172.168.0.0/12 + - 192.168.0.0/16 +``` + +```yaml +# rules.yaml + +allow-admins: + description: Access from the admin subnet to all subnets + direction: INGRESS + action: allow + priority: 1000 + ranges: + - $rfc1918 + ports: + all: [] + target_resources: null + enable_logging: false + +allow-ssh-from-iap: + description: Enable SSH from IAP + direction: INGRESS + action: allow + priority: 1002 + ranges: + - 35.235.240.0/20 + ports: + tcp: ["22"] + target_resources: null + enable_logging: false +``` + ### Logging Sinks ```hcl diff --git a/modules/folder/firewall-policy.tf b/modules/folder/firewall-policy.tf index 383fdffe..784a33ba 100644 --- a/modules/folder/firewall-policy.tf +++ b/modules/folder/firewall-policy.tf @@ -85,6 +85,11 @@ resource "google_compute_organization_security_policy_rule" "rule" { } } } + # TODO: remove once provider issues is fixed + # https://github.com/hashicorp/terraform-provider-google/issues/7790 + lifecycle { + ignore_changes = [description] + } } resource "google_compute_organization_security_policy_association" "attachment" { diff --git a/modules/organization/firewall-policy.tf b/modules/organization/firewall-policy.tf index 084da343..620c9ebf 100644 --- a/modules/organization/firewall-policy.tf +++ b/modules/organization/firewall-policy.tf @@ -92,6 +92,11 @@ resource "google_compute_organization_security_policy_rule" "rule" { } } } + # TODO: remove once provider issues is fixed + # https://github.com/hashicorp/terraform-provider-google/issues/7790 + lifecycle { + ignore_changes = [description] + } } resource "google_compute_organization_security_policy_association" "attachment" { From 564dc4325d45c4e07ab2ba0d86747565ab0d2c23 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 22 Dec 2021 10:48:08 +0100 Subject: [PATCH 26/52] Update CHANGELOG.md --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a02927b5..a29b764e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [9.0.1] - 2021-12-22 + +- ignore description changes in firewall policy rule to avoid permadiff, add factory example to `folder` module documentation + ## [9.0.0] - 2021-12-22 - new `cloud-run` module @@ -12,7 +16,7 @@ All notable changes to this project will be documented in this file. - added a subnet factory to `net-vpc` module - **incompatible change** added support for partitioned tables to `organization` module sinks - **incompatible change** renamed `private_service_networking_range` variable to `psc_ranges` in `net-vpc`module, and changed its type to `list(string)` -- added a firewall policy factory to `organization` module +- added a firewall policy factory to `organization` and `firewall` module - refactored `tfdoc` - added support for metric scopes to the `project` module @@ -386,7 +390,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/v9.0.0...HEAD +[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.1...HEAD +[9.0.1]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.0...v9.0.1 [9.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v8.0.0...v9.0.0 [8.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v7.0.0...v8.0.0 [7.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v6.0.0...v7.0.0 From 197e7cbe192e2665ad11da9c2d1e4290cbe01064 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 22 Dec 2021 11:00:44 +0100 Subject: [PATCH 27/52] Fix ignore changes for hfw rules (#401) * fix tfdoc replacement * ignore changes to rule description * add folder example, fixes #339 * fix ignore changes in org and folder module --- modules/folder/firewall-policy.tf | 2 +- modules/organization/firewall-policy.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/folder/firewall-policy.tf b/modules/folder/firewall-policy.tf index 784a33ba..abda1187 100644 --- a/modules/folder/firewall-policy.tf +++ b/modules/folder/firewall-policy.tf @@ -88,7 +88,7 @@ resource "google_compute_organization_security_policy_rule" "rule" { # TODO: remove once provider issues is fixed # https://github.com/hashicorp/terraform-provider-google/issues/7790 lifecycle { - ignore_changes = [description] + ignore_changes = [match.0.description] } } diff --git a/modules/organization/firewall-policy.tf b/modules/organization/firewall-policy.tf index 620c9ebf..4d111023 100644 --- a/modules/organization/firewall-policy.tf +++ b/modules/organization/firewall-policy.tf @@ -95,7 +95,7 @@ resource "google_compute_organization_security_policy_rule" "rule" { # TODO: remove once provider issues is fixed # https://github.com/hashicorp/terraform-provider-google/issues/7790 lifecycle { - ignore_changes = [description] + ignore_changes = [match.0.description] } } From a98feaee4f254aaa2aadec30b1b52c00c1c71f9a Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 22 Dec 2021 11:01:14 +0100 Subject: [PATCH 28/52] Update CHANGELOG.md --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a29b764e..2f3a2a8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -## [9.0.1] - 2021-12-22 +## [9.0.2] - 2021-12-22 - ignore description changes in firewall policy rule to avoid permadiff, add factory example to `folder` module documentation @@ -390,8 +390,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/v9.0.1...HEAD -[9.0.1]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.0...v9.0.1 +[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.2...HEAD +[9.0.2]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.0...v9.0.2 [9.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v8.0.0...v9.0.0 [8.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v7.0.0...v8.0.0 [7.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v6.0.0...v7.0.0 From a09485201ae1d0090334edfa0da495551ffd940d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 22 Dec 2021 15:35:12 +0100 Subject: [PATCH 29/52] update documentation check --- tools/check_documentation.py | 15 +++++++++--- tools/tfutils.py | 45 ++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100755 tools/tfutils.py diff --git a/tools/check_documentation.py b/tools/check_documentation.py index cd8dd513..051a4213 100755 --- a/tools/check_documentation.py +++ b/tools/check_documentation.py @@ -27,9 +27,11 @@ BASEDIR = pathlib.Path(__file__).resolve().parents[1] State = enum.Enum('State', 'OK FAIL SKIP') -def _check_dir(dir_name): +def _check_dir(dir_name, files=False, show_extra=False): dir_path = BASEDIR / dir_name for readme_path in dir_path.glob('**/README.md'): + if '.terraform' in str(readme_path): + continue readme = readme_path.read_text() mod_name = str(readme_path.relative_to(dir_path).parent) result = tfdoc.get_doc(readme) @@ -37,7 +39,8 @@ def _check_dir(dir_name): state = State.SKIP else: try: - new_doc = tfdoc.create_doc(readme_path.parent) + new_doc = tfdoc.create_doc( + readme_path.parent, files=files, show_extra=show_extra) except SystemExit: state = state.SKIP else: @@ -47,13 +50,19 @@ def _check_dir(dir_name): @click.command() @click.argument('dirs', type=str, nargs=-1) -def main(dirs): +@ click.option('--show-extra/--no-show-extra', default=False) +@ click.option('--files/--no-files', default=False) +def main(dirs, files=False, show_extra=False): 'Cycle through modules and ensure READMEs are up-to-date.' + errors = 0 state_labels = {State.FAIL: '✗', State.OK: '✓', State.SKIP: '?'} for dir_name in dirs: print(f'----- {dir_name} -----') for mod_name, state in _check_dir(dir_name): + errors += 1 if state == State.FAIL else 0 print(f'[{state_labels[state]}] {mod_name}') + if errors: + raise SystemExit('Errors found.') if __name__ == '__main__': diff --git a/tools/tfutils.py b/tools/tfutils.py new file mode 100755 index 00000000..c426c90c --- /dev/null +++ b/tools/tfutils.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +# Copyright 2021 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 pathlib + +import click + + +def main(**kw): + pass + + +@click.group() +@click.option('--dry-run', is_flag=True, default=False) +def cli(**kwargs): + basedir = pathlib.Path(source or '.') + for f in basedir.glob('**/*.tf'): + if '.terraform' in f: + continue + print(f) + + +@cli.command() +@click.argument('source', nargs=-1) +@click.option('--from-static/--to-static', default=True) +def mod_source(source=None, from_static=True): + print('mod_source') + print(source, from_static) + + +if __name__ == '__main__': + cli() From bf1e2e3bed70c3d1d9be24a40be0c2a25462ceef Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 22 Dec 2021 15:44:43 +0100 Subject: [PATCH 30/52] backport tfdoc and check doc fixes --- tools/check_documentation.py | 2 +- tools/tfdoc.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/check_documentation.py b/tools/check_documentation.py index 051a4213..144787ad 100755 --- a/tools/check_documentation.py +++ b/tools/check_documentation.py @@ -58,7 +58,7 @@ def main(dirs, files=False, show_extra=False): state_labels = {State.FAIL: '✗', State.OK: '✓', State.SKIP: '?'} for dir_name in dirs: print(f'----- {dir_name} -----') - for mod_name, state in _check_dir(dir_name): + for mod_name, state in _check_dir(dir_name, files, show_extra): errors += 1 if state == State.FAIL else 0 print(f'[{state_labels[state]}] {mod_name}') if errors: diff --git a/tools/tfdoc.py b/tools/tfdoc.py index 70a50eca..69974d3e 100755 --- a/tools/tfdoc.py +++ b/tools/tfdoc.py @@ -214,7 +214,7 @@ def _escape(s): return ''.join(c if c in UNESCAPED else ('&#%s;' % ord(c)) for c in s) -def format_doc(outputs, variables, files, show_extra=True): +def format_doc(outputs, variables, files, show_extra=False): 'Return formatted document.' buffer = [] if files: @@ -323,7 +323,7 @@ def get_doc(readme): def create_doc(module_path, files=False, show_extra=False): try: - mod_files = list(parse_files(module_path)) if files else None + mod_files = list(parse_files(module_path)) if files else [] mod_variables = list(parse_variables(module_path)) mod_outputs = list(parse_outputs(module_path)) except (IOError, OSError) as e: From dbb3b7a9ed636e31892283eef109b41b77239df8 Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 23 Dec 2021 14:26:47 +0800 Subject: [PATCH 31/52] Change README, add diagram ( gcpdraw a6e4ec4f-7d6c-4796-b63e-ee4ce7b1792f ) --- .../README.md | 4 +--- .../diagram.png | Bin 27679 -> 110368 bytes 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index 69c6e589..d60b1d38 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -42,9 +42,7 @@ You can also create a dashboard connecting [Datalab](https://datastudio.google.c Regular file-based exports of Cloud Asset Inventory may be useful for scale-out network dependency discovery like [Planet Exporter](https://github.com/williamchanrico/planet-exporter) or to update asset tracking or configuration management workflows. Bigquery supports multiple [export formats](https://cloud.google.com/bigquery/docs/exporting-data#export_formats_and_compression_types) and one may upload objects to Storage Bucket using provided Cloud Function. Specify `job.DestinationFormat` as defined in [documentation](https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.job.DestinationFormat.html), e.g. `NEWLINE_DELIMITED_JSON`. -It helps to create custom [scheduled query](https://cloud.google.com/bigquery/docs/scheduling-queries#console) for to comply with downstream systems' fields, and to time it with CAI export into BQ for freshness. See [sample queries](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery-sample-queries). - -Note: Cloud Function's Service Account needs write-capable IAM role on `bucket`. +It helps to create custom [scheduled query](https://cloud.google.com/bigquery/docs/scheduling-queries#console) from CAI export tables, and to write out results in to dedicated table (with overwrites). Define such query's output columns to comply with downstream systems' fields requirements, and time query execution after CAI export into BQ for freshness. See [sample queries](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery-sample-queries). diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/diagram.png b/cloud-operations/scheduled-asset-inventory-export-bq/diagram.png index b91591042cfd491a78e9ca0ec65966c9e765df24..1e0957bac0d94acabb9f03d4984b6786e357fd09 100644 GIT binary patch literal 110368 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 z zJ-fUrXmmLBaxNRkP&jO4Lt7Y~SXod0$OKBm)}%702>PzS%l~O|?FqT>zJASp-Im{Y z*~Hk~(V<#?sA5>tH)=lS@jb?=Q-640|7?J5Eqbck^!#17Qbh^udKce(p6NdOnAto7 zHM>o^e0tF$0MJlryzf1z>%Cl0{n-3iXP|G*F=?$ZaY^1J0<#xaR3&Mt_sd#-f0jOZ z`_2fcRaaxR^;0^GWz*mS03Pk$-qe*>mz+p;q1%3u(jyIyI`k>CEM+rU}vY6qB^RF(Lm{jJ6{edN5-^DlVctF zw`a{woQ1ILw|nk3LY7d^1g|T)Tpkr6YNne`q#5=1ziX$>KPes3>#iTZ$RX7thBFPt zIDMt|q2I^&uS_jL82Eq2EAGP?XOdqt_txt}`+!m#3~?gf>i zS@RYCeXPi&mc^OhkOM$6K(P+(ck|M}zbI>YfBYJR%;z+N0|ez|?zb%C@g;3gc|Vq4 z-)Tk42JkW@k}0@AQxKK?_TrzFH1!+(8`{;OAzM*w3Y_d085&7aj=%oclU$@`Rm^wHklmd?WQXaF%6 z*M<*Syj)uKufKo$@^~_H6i9!Z6KN|tb+Ncw5=I4xFLESnfcy$Lw=(lvTuM?L84h8S z7YbM$4aUN`%-m@;eU1XLstezUi4Ujr{57~=Bvf)!7A+pP7gk{SNnzYTOxJLC_Kg#T?Rp65<7fXZ#i}DQDF|5=~Sk0U$=cpa5gZz=y zuT5$k%BM;vQt^Mr18??m5gW7*pZCRsz7xE@S(&aXJ+nvRPxhV~=!L4NlUALx`#Nh_ z=k`2Lz#As$G@ieH?xwu=?q#X+bhlek0Q{SuM+GdsS<@w$GEcSo>d|YKp5M_B2TFD= z9Q13nv~dR!eIL7Rl~KOQKD3;%SdOcq{@Ft%7wwZrZ(Uh2&roRJ(g?=k#BfmF*A- z5BYD@J`KCg?XbKYKw5e=CW+@0X=3@!tN;2zw4aF@X?SkT}MEwfI+wYqmz z?W)?n<*Dao8-zOjv>8N`H|E�ZHf63Lbm-DXH2m2&0kXCpS@v+l<4q1E%j00e@yq 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 C4*Ws@ From 19f6127e8a1179dccb2c53295155ca9c448ff8cf Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 23 Dec 2021 17:38:58 +0800 Subject: [PATCH 32/52] Change README to two diagrams --- .../README.md | 8 ++++++-- .../diagram.png | Bin 110368 -> 27679 bytes .../diagram_optional.png | Bin 0 -> 54786 bytes 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/diagram_optional.png diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index d60b1d38..3aed5250 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -38,12 +38,16 @@ Once resources are created, you can run queries on the data you exported on Bigq You can also create a dashboard connecting [Datalab](https://datastudio.google.com/) or any other BI tools of your choice to your Bigquery datase. -## File exporter for JSON, CSV. +## File exporter for JSON, CSV (optional). -Regular file-based exports of Cloud Asset Inventory may be useful for scale-out network dependency discovery like [Planet Exporter](https://github.com/williamchanrico/planet-exporter) or to update asset tracking or configuration management workflows. Bigquery supports multiple [export formats](https://cloud.google.com/bigquery/docs/exporting-data#export_formats_and_compression_types) and one may upload objects to Storage Bucket using provided Cloud Function. Specify `job.DestinationFormat` as defined in [documentation](https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.job.DestinationFormat.html), e.g. `NEWLINE_DELIMITED_JSON`. +Regular file-based exports of data from Cloud Asset Inventory may be useful for e.g. scale-out network dependencies discovery tools like [Planet Exporter](https://github.com/williamchanrico/planet-exporter), or to update legacy workloads tracking or configuration management systems. Bigquery supports multiple [export formats](https://cloud.google.com/bigquery/docs/exporting-data#export_formats_and_compression_types) and one may upload objects to Storage Bucket using provided Cloud Function. Specify `job.DestinationFormat` as defined in [documentation](https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.job.DestinationFormat.html), e.g. `NEWLINE_DELIMITED_JSON`. It helps to create custom [scheduled query](https://cloud.google.com/bigquery/docs/scheduling-queries#console) from CAI export tables, and to write out results in to dedicated table (with overwrites). Define such query's output columns to comply with downstream systems' fields requirements, and time query execution after CAI export into BQ for freshness. See [sample queries](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery-sample-queries). +This is an optional part, and if it is expressed with correct variables, the high level diagram extends to the following: + + + ## Variables diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/diagram.png b/cloud-operations/scheduled-asset-inventory-export-bq/diagram.png index 1e0957bac0d94acabb9f03d4984b6786e357fd09..b91591042cfd491a78e9ca0ec65966c9e765df24 100644 GIT binary patch literal 27679 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 z zJ-fUrXmmLBaxNRkP&jO4Lt7Y~SXod0$OKBm)}%702>PzS%l~O|?FqT>zJASp-Im{Y z*~Hk~(V<#?sA5>tH)=lS@jb?=Q-640|7?J5Eqbck^!#17Qbh^udKce(p6NdOnAto7 zHM>o^e0tF$0MJlryzf1z>%Cl0{n-3iXP|G*F=?$ZaY^1J0<#xaR3&Mt_sd#-f0jOZ z`_2fcRaaxR^;0^GWz*mS03Pk$-qe*>mz+p;q1%3u(jyIyI`k>CEM+rU}vY6qB^RF(Lm{jJ6{edN5-^DlVctF zw`a{woQ1ILw|nk3LY7d^1g|T)Tpkr6YNne`q#5=1ziX$>KPes3>#iTZ$RX7thBFPt zIDMt|q2I^&uS_jL82Eq2EAGP?XOdqt_txt}`+!m#3~?gf>i zS@RYCeXPi&mc^OhkOM$6K(P+(ck|M}zbI>YfBYJR%;z+N0|ez|?zb%C@g;3gc|Vq4 z-)Tk42JkW@k}0@AQxKK?_TrzFH1!+(8`{;OAzM*w3Y_d085&7aj=%oclU$@`Rm^wHklmd?WQXaF%6 z*M<*Syj)uKufKo$@^~_H6i9!Z6KN|tb+Ncw5=I4xFLESnfcy$Lw=(lvTuM?L84h8S z7YbM$4aUN`%-m@;eU1XLstezUi4Ujr{57~=Bvf)!7A+pP7gk{SNnzYTOxJLC_Kg#T?Rp65<7fXZ#i}DQDF|5=~Sk0U$=cpa5gZz=y zuT5$k%BM;vQt^Mr18??m5gW7*pZCRsz7xE@S(&aXJ+nvRPxhV~=!L4NlUALx`#Nh_ z=k`2Lz#As$G@ieH?xwu=?q#X+bhlek0Q{SuM+GdsS<@w$GEcSo>d|YKp5M_B2TFD= z9Q13nv~dR!eIL7Rl~KOQKD3;%SdOcq{@Ft%7wwZrZ(Uh2&roRJ(g?=k#BfmF*A- z5BYD@J`KCg?XbKYKw5e=CW+@0X=3@!tN;2zw4aF@X?SkT}MEwfI+wYqmz z?W)?n<*Dao8-zOjv>8N`H|E�ZHf63Lbm-DXH2m2&0kXCpS@v+l<4q1E%j00e@yq z6N8_mObpN8fb1bu$dU5b%`43(=RpxPJR*+)cJEc#x1wE`T z+{DD>QOk0XpekHQ$xvwMP>I@Du91pC)~N(94<9~Ze0pr!T81A;@{-^^aE0u%eWO+1 zSyJ+ju2{_d8oSOXY{j=TZ{9GRaSV}Lj+Y~7`-~r5ZThi8ol!8hh{ybrvPsg--37=f zXLk!1$)Gog&c98e%usMs%aKle5Q5b(u`^0mz|qUmAi&qo!y=E zoTo3Fe{+eoQXs7T7&?#fC{;lJ(b5w(G`+~l*PuIhG!ceKiS8q|MWKpOUBmN>h%5cA z`R-qsXbt6|90I_2>)y98m|ukjb)?*nHYCkl&k^Qdu4&9%fQ z9Wr!IjvhPZM1QVyto8&jVDq5#ey^q?;9AaS;q9?pZi2?%^y>=B>9k}Xr-tdlPO^;M^KbqM6hCdI^5apJmubB{qhQtVrvt!;;PBXEhG+FW8`iD&oS${IS}@4p*gzM%1G z7(}VHwwJRhqhY-}jHhVvb6$YG{Ur_XcikV$4DKPXxb_u!qI; zaKG?7vD>jrpzzSPtsbT!tbNbGa}4lf|K6}BXFu9sZ&@1L`+&(sf(m$ZwzLR5{rs>- zO^E=Rc@2|`V1a~nnvqm_dpQ!C>wI1Yk(wVHhfI0R@9}hc`VTh^@xHIabyzWEN(L@V z27>qs%i>cBjT4^OKALF`t^FahLsp`7IhaVv?H)&z#=5<2_Wd)_Xl~wdELr5En7`bc z{?QOikSO=xtaPmgtAlQL*VJf}8ts+Z;T-K;aj_EiR1UL==S&LXTL~s>zbh9o{47XY z@Aw{>b}lL-RRicH^t;9FX@oendR)!&xearfZ(dkO2>8wS((N%LG%#-cX|vfTxf9$6 z(ZTro@Wyg+@6OBU4zeU}Wo*KMiz6=1is&#nEvc1&RXpJhp?nh#IkV5TK{L?!n2b#H z>O(R+j8C7=6T&(`69h>ATokf~@UUE*52~?E5lBGSiIP&*1ne68a9@1`9~D}PZzhR# zh%tT)a1S*VjN039vQJp>J`3M%q5i1SoNoJk(NQsx^+~yDBgWKPN?x4WHtZ`)AsjQifhE+Tz+BQy0%ihS^=pO!YpJzE*m zixf>(dJe)^bMyxQfI3$8vlFWSyOhHmGhfP}cnww5THPL|$sc%ag6h3Li{o6SIC!UB z+&nJ|3g{&ZZHz9}-{0e!6QzC}l$eb6%!x{E7OAuN+*2OsYZ`p&I@p*5VSaa8318iE zV~}C0M$8m+-u>ko0g^ELyE5fi`LYsBdcjf<-FbES`U^3dK?ItuS#NRp+@v4M=xb(s z>de!&h@QRbqU`VAx}*N^6dy3vHPJvq16y(TnF~bk9E5EOj^N^1Q6n&AJ3&9M5{be&qvkm0bUKXUbrbB!4k1fE*%(Z2& zfLJT=>pRcu`Jv21xg-0#r>+vm+*N9Bz<|0cRW_+S9j3D!UX?c2y*zWnsGYO^H;YTD z5cj&*QY%nNZ0EVbX=n+hp}Bu|*81*^@JB!`;hT%fJ=qeKyN#0}lu+XmAgv-Q%GS1$ zczBdt@xH;KiUlEeMDckaHd^e!#C?hCc#pkPVELrIT3%+_ybAAz4||ZMv7YQ)U{Pdv z()Eh&a{q6zV;}wNGWF!>7~Wc;h$|rwVFgsctn2dy64JR?v+aaD$IZ&If~=YLYO<_C zzuHYqv=dBLHeo%_-~KRd(mOfO2+j|0az_;47Jud&BI5@w?Gha)t`f=3)w#d<^zgFW zUb8O{$HLijtMya|k^u7HetLroK;ZT{Nc?XufSN*qz?hsBzNPFE@#Nz6HO2BP#OHBb z3|yBBH-4NNy2%k;yS6_&vM!LJkxP8dUv^Fi2jB{AALj}>`L=fKE^~RqsE(l7~K^WW>+A)V}zfsJO3Gk0Z|gx+Rp#B z$Bxamcy;+t=jC;(zQ0-$eQfvR+P?Q=b%q(uf%~vdYi^M4Li24%Qewl=e=k-G`H7)-+Jp-uatZ z^>(0N`VLAcU;CJTX&^J;iXCg)^5USdVM3UG_2B-2{hVY2N({hfmMeu-JQMgP{dK8$ zH_a#zzwybHd~jgI~C+M#|VIS zyMd*_HU^G|@o2RtmR$~*SbD|3ajX)I@(#Zb1~!CgaLc~QF5G6CP30?Y6v|n@ewb&Y zWB^FK)|`Lw<3R4ZH3E5LGXw>Oqb3WNIN|HvH0lEYj+U-0RfsX10b|=0@_hV{YxhHi zLnup7an}I&Dij}p#tXM>GC!$$v-9)IR{p{UwnIz}Jh`9iPfniOwc`91WrVW#hvtbS{Im~5)!_#DD{vOg# zTjI~9M5rNMPj{bs4u72(ovi@|1nax*0pgP6;ZC+heUV)8sS^yS=WK!fV>;cb~>=xdD z!8X>mX6O@7qB}_4O=>wpxg}fzDT&7ov`OY3u1j@?W;fbhguIX3jbF?j+uRiu5CHaE z@UKWEd+>S43O9^3h5n7X$lkNWZBO`KhTX`3i&=|YPaZS zhwsFI?B8Bimt80TK*u$b06-HFN<)7jWne`67x6FMPnTnr>uJ+^__6jWUQWqS%#Av8 z>88(i-EU3Y|9Cb?ILDCv28Hqld(uGA#aZBPI>+&pztc9NN6;CQw<^OfpaPG|UTNwp z@`hZvS8YvBmF?zgl@BQTEfxB`cK}_P#1~`KtRQA%bG@8S*0u~UrZLCpx91xQCVyn4 zf;)`_syNrOj6Y#9Ay>b7qcba@j9?ah3A{b$nBj015mqMvv+UO27es2j)NyksOr#nF zzo)NR!_BwMjoWiFB$-_bNGoTviL3)rzU=zIb2~bDiLW>q-y=HzB~!#hnV}s@P*G5} zV!}>)Iv?M`Gt>V&)&Oz8 z6pOJ*btNS7;2_>!(K#l6$T)oKu$@op*Fi^dlCzn{?noq+H3k9H0Fs37IN;7S&1f+= zSP!w<>BO5hSm+ziSAR0&&Q|1{`KFL(srph6wWC>g7YVr4;=^)H(6Y|1oQi*nK;a(@CF6l`U9ZkDFjC zOB=U?I%eG8b*^`I7YIVM>8cIbt6*)Ny=IrmtV6M6uw3$4v|rkegaw##K|lM(F>+R* zPCUbqniZDnfZ|_|S4!5*0PRm>al*oP2Lx4}s_0(Wv+%7Ut{$iFYVrn~-nEqh zw*hw(8kZy3@zZ3O5N5Vfk_$++X?+=$w-addvUb0vu?lc$N;x;QcTQ`e2m zyF{T0{^hYp> ztm!5CQ5%?jE~`zh3h?!5y!nU}_`8xMO-=Iq7nmFbV%evIqF@TDOF_ohD%H+W7R99XSv3qblh8`L zWahv@)MZ$)!^y1Wp%NPpGa%4UH=8v6mkpyulS=%@5>^rJdyX z5i?`zo<1?MT-CY{ImAf;0;gIkRpB5i0CuQl?UAH<()jI?p{34o4ZN`z!+*!}L#N4V z4&_4J>(XecZ?*n#ypIWrf}Y+JzKnd*9h<;ctLk(=_rjoOTagQ!KfzcbB`4aPXJtoT zf^fm?wQ4n%GgMe8rg%$Fm+rN^%(?XI5Xt>BJzRv1gX%Wta<2mEF1TAIrR-L~c|I>rusNuQ5n$9F$ZaET$u89L_72vYTFcnT&)Nz}rjxZDv<4vjk~V3sTIG%dLpyKvk)Q&J6O1(27n!I92E!{t zT`!XTN7irkWQy#z2{2YfD+{v56qZapI0V{}r35|`Vvw&I2*u?DPIf(3Pye=>rnLy| zdoKB~P+85}G+USI+qCvxkD;d=@2f4&67T3}2~ z^;EN`O+EnKi`XiUAZY}}{>XkKb&@%TyKPJv~;ZVOOqnJn`g-->ESuNM#!LPAd zDs>It$(BU#5J1*1W;-{#Ue8#n27S+$Lvi-f0}G_eg$oqw1OU=h*Uoj(75Cq+wwqKi z5gA3gtGhDO#*t_Z6_SMVVH5h|NJL#3ZlU`|wcw%b^Ko{~1jAhN4~1Ia$(Z{Y0C;DjIP(;?QdSWoHNOwq}vP zmI|)z9LI9p9jOZro5V~4Tn&$qw>LKpiPQ`DU10^>`4yFBf*ZKBdU*EF zEuQQ--aK^OI?f-1U1px>74RK%#az$+&UL)mtqB0{<*cC?y=>mztg!%@W(q7unU@!a zjx9uLmlq42d?(J9ylVEhOy{S3Dyb7=jCcnsh!9Du>t9kJ(l28KKop!*&e#$ zC-CE+Q*nxUGgnszL)X`gqC@e;KO64#h4kX!(=nB|$@Pcr9^D-kzo8u^w1^b&DD83S za2NrJJpTZfrpk-+m`#M4J0Ia5@w6H7H5QPWA7U+q9DDbrVEtS_@P=tWqP_ClE{MJ|-vZC+G>}^I@Q3t@+9?{2( z5?qSNV4sJ_JHSM+6;S|y{;@wz-2BWH4?@8l?pw1eAMX8HmIiD+8oLC4Cdrk(#_i6D_+E5xDo5!_KhR&==2a>yrC7y8~LykT!2Kxi1~{|Ks8u4I8JoVvzMh|B~Qrh z@M~Q|uq#g?*mK0&F7X6f2Mu`{kl$^m&uZ}eD}DZ)oq!Ss0f3#7Z+7WKk7SECmcm7x zKWn_vA#vkx2qDk1YRfTz4%{8*8(Ed`Rh`-?zNyqP3!YfnRDXPD@b&2aJjY{le}Lu` z?ApQDN}9}4=y{D^a|>C78ND1QZ0{{JJFpmtxo=3C9AdpxjvIA#ykygq)C_rC6=9u< zioN&UE~%-mrHg|JFtuOsoGhco|C;-!!%4*Yn?_q{z%O;w1~xTb1qmSMAVlW>3u*( z4ZfKtFmq3A;d|(D`B8ur+kR#8d?w>I5l+ebt5Ebj>ReTub3|cWh%oD223S4Ho0ByR}F64xzFaZ(?pN^{FFKE9BE4{Eq>Zq_!2C`roF|=*~EPzCil5u zO>f`CwdH=evIj61kwMtmL8I%X6?6r0rp3O{JjV_$u3$;Ps9zObVvo~ywO|lD$i`Fp z+yhBoF`Ga+uBNCec}r9(ZGPj1Y{ikN9md}N$ks+n2ngidm*ZT%ld(ceJ4XMP9tVDe z${HZA{XIC&eB8}(EWHT|B{yt%Y-(MWKg&mg%U-38uwrGykJ-gWua10JTk}XkjQn>f zx+vfp^>)!6bNPdQ)>knc3|GuHmI&5JtgM`(n4e6k3z={`Pwk(38W`YUnI~6@>`NgPKtqa^71-R7_C6uMs2Pb?$_rQHMk1?HWxUaFo z9xvyCZvkb2%a@R|!8@d2$rz-PDD&KeyE2sMkj$l)g>SPD$?>(#$wN|6cg{L(;DdP3 zUI$7faZoj;?>s%lv*QY(jib@~Cc#<$S>hpAnB+uOn<&NFvs&jQ59fTB9hT)2%61kF z^)?CO7{!EEvERdk`{I?4@2nN)`@^**kmYH1dQ$~pDpjwcnX%{7{E+F^NHkJUt3_*T z-IS{6!TQ}u=E@Z(T8=E%RZYz3%L7>OXrLt*_Fz`{*6^*FE^m4wt&pWeS5@BUABqls z)P={L8(Id#DVy1NCo!sZ?XorKdBtW{KRxMAmAp|IG-q}6C|W)$tq@cL0P$+Y=*|}z z*7CIvXZ+AFF_yK@Aq&s2P|4~_o|@YPZP+HY7JEEt-CF8Pr99R{=hDnGHe2^fAh)%A zz2;jH5A|e2uhU9(kJ(!-I~+`Q}uhN04i8B8#j1wH`xsM?A3Sqq*EN7vvG89EZk=6fS*KE`i<3K+yMdm zqd>Jj13n#Fu8?3U6$4|YegPc;+WVdJ$r7z*Y)3Zzv@Vy2OPk;zHLkh&4PK3RDXSq1 z>vDx}<1y-jzUr0&s2Kp+Qf5CyH>-`_oWY20x4&36%ZO6X-V?Z`WV2XNu}j4!>Za?d zXs5E4DX11xAFW;NN={=~c-pVsj|{$hHIp=l;wi!Xq?`7j9%7CpH7)77qQ}l?8i2J{ zeJA}}K>5{NE|msni9I{NPLq0*6hxTLuw*G^oDLfmt;pD?#o>^7B0VY<%V^?@(RR7J zov$Q5vynKI#+ap0W@hWRK8Lg%#xPWFueL#79Zjt5F9A72`@z1VyMM=YqG=g6x=&u4 zVZ-0jbP){2DDGg}^1O23Vv*Xv?->)noF>}j$|xOp%bAPU4d|V`;OiR}=Ha3A`2Kud zw>zn%sBe+BFZu#mw2~J3B$02vIRoi;UPr3x>n?9R)tkK)`ct&Du`+hY7TM~k&1h59 zY87s8_pQiwrU6rQApv%#M0#z_v5Gw}xJl#_|c&DS6HU$ZIk1LHHS^2_bN8j>Z^*mwu57ovde5Rk-X zF=ujo*8n(aKTSP)s%H!J5G-ve;FFYKaUQLhq-mhwfu{CTjerdJ+UU~^8lv|xMo}uUs*lQ|Kt6AQ;e#KWxrR{j%PU# zi#^;;m8!=}>A0KPjSU0S{as!jbslxnHsY%dou3kGX2WX;C<{dB@co9Wdw(fv$s&QH z+}f*_h~^kKYLx9(qr^a!Toq(vJPB$n%O z4sVK(26=Bxw=yTLHHSH*BVT|LoajJ_OcedNdpPxy!1q6>n`km_GGmL#SZ z^Y$qNSqG#Y6nR^7ndiCZ5ig|yxHTtyZ~6LnhY;9Mfne6{(>VSd?9*?Tn?4rz+mwm} z{0!Nl6b7;N{8b8qQzK`0ax5c4l0GcH!%NYGF*oJC8|dmbPziiCVWQ1Bz6^)UleQly zvfhm3jD>}*J$4Tj@7AneZN;VyWnIY>?x%~MJBDqmQUY1$o6d{e&Avz!&(|ds+lpJbTAO_df69$hKJJ=aggfM5Lj9N*mBQ-!L_jZx2Cs@=@%SV z6GXj^QpLriW%5A3<|hVGfphiMw|{{p>*5>!79eVDMFD~W9BZR)dS9;>;LB-^;cP(_ zX>*Oyj+Dh+A*We$$dqW1^z0}P_gZI_r|UYfyX8Xy@=rUc!+tG;BdWJvnozaPR)sno zPVRD5E}Wx!k0yZl0}(JlOhVQ{Hw%RAdaUVAVqyyYG$uV07puguc#oKb?B}eTmT{SQ z>?1O4Jx~d0?~u&rSYN;|(n^x&HexDfc^_p)6MVQt>fbeFMCJ!?yAua8(;_hU-3|Kq zD{^b2IYvjwBW)d#!^8`V9xe_#G&Z>Ng?rmb?hj-LuxjnU9*dtopJ(a2<_rG@H;;dL zLr7F4mlS1DmU)9jDxaj!)S6)-3I;Ha+}Crk#DTjLwUgC!U4CU;8J2QbL|i>M&8@cXRng&(CW)q`lBUYdCHuGDoPYY(=qGDA$g}^8-fAYj zU2JWR%*{2gTJrZ^8}EzVr^gd}@;0>&m*-S7krQoKG>e7CWBQg_$rN;2zuKvIr7~*w z8TfQXvObH}pDUd>hnr$`Ce5VHrc}%Me}m)8Hu(##@V_9~)!iTT$*TK@0B;BB=iBvU z_E>g*cdD3Q0pR7OKNG}FYP>xzS(6inRq}jG6j&9V)Twf1u4O!8eA77?g9ir$a4`!Y zUA?UkJBZfV02vS(Du&)CrnNWJ>M4`_iWHuDx-Uu2pz^6Er=@nh;lvJZV$K}nj}7z!gA>CrqUd}_t`8Y za~vV>Fmdd|MZmF(({_ggBoKu(Pnoo-4iX&*lsXt}V zjdUxZbUJN$OSmHM^FQX%qHJ8S_qpnEtULQ#&t&SRT^Nz3f~ApJ;p4dgQU9al)+ zgL$;EvkD1%mZUBDijSU7feZZ;__QcVq}cTxKa~NwF^5{#k#Z4Khl}2T%Q{P4~QFWV%5wDwm#M6G(A4U~LG>7@=UeW&#m*$6x zT`KHD*mZ=N>#K5e&Z zXBh(rhxoLc`$sswcoTtmab#4+x6vsiK(X5WZciTm_pOq_t+4l_??|*_0>&ik+!|iq z4Pd1o2glFdh5gOKic>PbME!T+vFr8 ztvF-9j2A`!y*D2JwHL1E(jXQYblV-4Gf34oLB?PBqvcu^qK*agKD$)=7N>QP*MYb{?y#+qzKCvHc82FaSKD708Nm$Jf_rm$Rj{|b#j~9_gcc3F zMB;Ne3%G-E<3GY$N7?yHQJAxL%6BN*EQKv2b4jpre5s_*r3mKlR?t62Qc8-_eP$V$ zgeH<&H(l=K)qcPi|FIRO0>_rbk?)dn>;C`IO9uY`n}S~!vmRM|3HVSLi}Gp9C!PIfINF%(xch2^h$XEeEqB>T`g(+{r>@O C4*Ws@ literal 110368 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

On Prem in a Box

-

${instance_name}

- - + # Minimal nginx index page + - path: /var/lib/docker-compose/onprem/nginx/index.html + owner: root:root + permissions: "0644" + content: | + + + + +

On Prem in a Box

+

${instance_name}

+ + runcmd: -- [systemctl, daemon-reload] -- [ sh, -c, 'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -' ] -- [ sh, -c, 'add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"' ] -- [ sh, -c, 'apt update' ] -- [ sh, -c, 'apt install -y docker-ce docker-ce-cli containerd.io' ] -- [ sh, -c, 'curl -L https://github.com/docker/compose/releases/download/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep "tag_name" | cut -d \" -f4)/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose' ] -- [ sh, -c, 'chmod 755 /usr/local/bin/docker-compose' ] -- [systemctl, enable, docker.service] -- [systemctl, start, docker.service] -- [systemctl, enable, docker-onprem.service] -- [systemctl, start, docker-onprem.service] + - [systemctl, daemon-reload] + - [ + sh, + -c, + "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -", + ] + - [ + sh, + -c, + 'add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"', + ] + - [sh, -c, "apt update"] + - [sh, -c, "apt install -y docker-ce docker-ce-cli containerd.io"] + - [ + sh, + -c, + 'curl -L https://github.com/docker/compose/releases/download/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep "tag_name" | cut -d \" -f4)/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose', + ] + - [sh, -c, "chmod 755 /usr/local/bin/docker-compose"] + - [systemctl, enable, docker.service] + - [systemctl, start, docker.service] + - [systemctl, enable, docker-onprem.service] + - [systemctl, start, docker-onprem.service] diff --git a/modules/cloud-config-container/onprem/variables.tf b/modules/cloud-config-container/onprem/variables.tf index 1517221f..3b09e236 100644 --- a/modules/cloud-config-container/onprem/variables.tf +++ b/modules/cloud-config-container/onprem/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/onprem/versions.tf b/modules/cloud-config-container/onprem/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/cloud-config-container/onprem/versions.tf +++ b/modules/cloud-config-container/onprem/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/outputs-instance.tf b/modules/cloud-config-container/outputs-instance.tf index a9223ce9..182c0212 100644 --- a/modules/cloud-config-container/outputs-instance.tf +++ b/modules/cloud-config-container/outputs-instance.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/squid/cloud-config.yaml b/modules/cloud-config-container/squid/cloud-config.yaml index abd58a70..b0c4b7fe 100644 --- a/modules/cloud-config-container/squid/cloud-config.yaml +++ b/modules/cloud-config-container/squid/cloud-config.yaml @@ -1,6 +1,6 @@ #cloud-config -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/squid/docker/Dockerfile b/modules/cloud-config-container/squid/docker/Dockerfile index 180971da..bdbc7d04 100644 --- a/modules/cloud-config-container/squid/docker/Dockerfile +++ b/modules/cloud-config-container/squid/docker/Dockerfile @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,13 +15,13 @@ FROM debian:buster-slim ENV SQUID_VERSION=4.6 \ - SQUID_CACHE_DIR=/var/spool/squid \ - SQUID_LOG_DIR=/var/log/squid \ - SQUID_USER=proxy + SQUID_CACHE_DIR=/var/spool/squid \ + SQUID_LOG_DIR=/var/log/squid \ + SQUID_USER=proxy RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install -y squid=${SQUID_VERSION}* \ - && rm -rf /var/lib/apt/lists/* + && DEBIAN_FRONTEND=noninteractive apt-get install -y squid=${SQUID_VERSION}* \ + && rm -rf /var/lib/apt/lists/* COPY entrypoint.sh /sbin/entrypoint.sh RUN chmod 755 /sbin/entrypoint.sh diff --git a/modules/cloud-config-container/squid/docker/cloudbuild.yaml b/modules/cloud-config-container/squid/docker/cloudbuild.yaml index a942320a..e2e725fb 100644 --- a/modules/cloud-config-container/squid/docker/cloudbuild.yaml +++ b/modules/cloud-config-container/squid/docker/cloudbuild.yaml @@ -1,5 +1,4 @@ - -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,15 +16,15 @@ # $ gcloud builds submit . steps: -- name: 'gcr.io/cloud-builders/docker' - args: - - build - - --tag=gcr.io/$PROJECT_ID/squid:${_IMAGE_VERSION} - - --tag=gcr.io/$PROJECT_ID/squid:latest - - . + - name: "gcr.io/cloud-builders/docker" + args: + - build + - --tag=gcr.io/$PROJECT_ID/squid:${_IMAGE_VERSION} + - --tag=gcr.io/$PROJECT_ID/squid:latest + - . substitutions: - _IMAGE_VERSION: "20210215" + _IMAGE_VERSION: "20210215" images: - - 'gcr.io/$PROJECT_ID/squid:${_IMAGE_VERSION}' - - 'gcr.io/$PROJECT_ID/squid:latest' + - "gcr.io/$PROJECT_ID/squid:${_IMAGE_VERSION}" + - "gcr.io/$PROJECT_ID/squid:latest" diff --git a/modules/cloud-config-container/squid/docker/entrypoint.sh b/modules/cloud-config-container/squid/docker/entrypoint.sh index 5349208c..880eaf3d 100755 --- a/modules/cloud-config-container/squid/docker/entrypoint.sh +++ b/modules/cloud-config-container/squid/docker/entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/squid/main.tf b/modules/cloud-config-container/squid/main.tf index 6805a799..ad895c17 100644 --- a/modules/cloud-config-container/squid/main.tf +++ b/modules/cloud-config-container/squid/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/squid/outputs.tf b/modules/cloud-config-container/squid/outputs.tf index 33dcc551..7d8d4165 100644 --- a/modules/cloud-config-container/squid/outputs.tf +++ b/modules/cloud-config-container/squid/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/squid/variables.tf b/modules/cloud-config-container/squid/variables.tf index 20c05084..eaaca730 100644 --- a/modules/cloud-config-container/squid/variables.tf +++ b/modules/cloud-config-container/squid/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/squid/versions.tf b/modules/cloud-config-container/squid/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/cloud-config-container/squid/versions.tf +++ b/modules/cloud-config-container/squid/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/variables-instance.tf b/modules/cloud-config-container/variables-instance.tf index e5e5f346..3697133e 100644 --- a/modules/cloud-config-container/variables-instance.tf +++ b/modules/cloud-config-container/variables-instance.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-function/main.tf b/modules/cloud-function/main.tf index 74fab3e7..3a37a63f 100644 --- a/modules/cloud-function/main.tf +++ b/modules/cloud-function/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-function/outputs.tf b/modules/cloud-function/outputs.tf index 0b625b12..1071270e 100644 --- a/modules/cloud-function/outputs.tf +++ b/modules/cloud-function/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-function/variables.tf b/modules/cloud-function/variables.tf index 92a49b73..fe5e80ab 100644 --- a/modules/cloud-function/variables.tf +++ b/modules/cloud-function/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-function/versions.tf b/modules/cloud-function/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/cloud-function/versions.tf +++ b/modules/cloud-function/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-identity-group/main.tf b/modules/cloud-identity-group/main.tf index 7e21d851..2bed4435 100644 --- a/modules/cloud-identity-group/main.tf +++ b/modules/cloud-identity-group/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-identity-group/outputs.tf b/modules/cloud-identity-group/outputs.tf index cd08ebdf..1a058933 100644 --- a/modules/cloud-identity-group/outputs.tf +++ b/modules/cloud-identity-group/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-identity-group/variables.tf b/modules/cloud-identity-group/variables.tf index a85a268a..9dc579d8 100644 --- a/modules/cloud-identity-group/variables.tf +++ b/modules/cloud-identity-group/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-identity-group/versions.tf b/modules/cloud-identity-group/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/cloud-identity-group/versions.tf +++ b/modules/cloud-identity-group/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-run/main.tf b/modules/cloud-run/main.tf index 083e411f..d471f42e 100644 --- a/modules/cloud-run/main.tf +++ b/modules/cloud-run/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-run/outputs.tf b/modules/cloud-run/outputs.tf index 4caaef0e..105e77ec 100644 --- a/modules/cloud-run/outputs.tf +++ b/modules/cloud-run/outputs.tf @@ -1,6 +1,6 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,4 +47,4 @@ output "service_name" { output "vpc_connector" { description = "VPC connector resource if created." value = try(google_vpc_access_connector.connector.0.id, null) -} \ No newline at end of file +} diff --git a/modules/cloud-run/variables.tf b/modules/cloud-run/variables.tf index c2f73eb6..da0590d1 100644 --- a/modules/cloud-run/variables.tf +++ b/modules/cloud-run/variables.tf @@ -1,6 +1,6 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-run/versions.tf b/modules/cloud-run/versions.tf index 72cab149..b709800c 100644 --- a/modules/cloud-run/versions.tf +++ b/modules/cloud-run/versions.tf @@ -1,6 +1,6 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ terraform { required_version = ">= 0.12.6" -} \ No newline at end of file +} diff --git a/modules/cloudsql-instance/main.tf b/modules/cloudsql-instance/main.tf index 3803e9fd..f50121ab 100644 --- a/modules/cloudsql-instance/main.tf +++ b/modules/cloudsql-instance/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloudsql-instance/outputs.tf b/modules/cloudsql-instance/outputs.tf index 3af88394..caa28e83 100644 --- a/modules/cloudsql-instance/outputs.tf +++ b/modules/cloudsql-instance/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloudsql-instance/variables.tf b/modules/cloudsql-instance/variables.tf index 094ba4e6..0093d4a3 100644 --- a/modules/cloudsql-instance/variables.tf +++ b/modules/cloudsql-instance/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloudsql-instance/versions.tf b/modules/cloudsql-instance/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/cloudsql-instance/versions.tf +++ b/modules/cloudsql-instance/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/compute-mig/main.tf b/modules/compute-mig/main.tf index 5b3a9242..7b225359 100644 --- a/modules/compute-mig/main.tf +++ b/modules/compute-mig/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/compute-mig/outputs.tf b/modules/compute-mig/outputs.tf index 5679cfc5..93de9223 100644 --- a/modules/compute-mig/outputs.tf +++ b/modules/compute-mig/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/compute-mig/variables.tf b/modules/compute-mig/variables.tf index 31da4aa3..274fb235 100644 --- a/modules/compute-mig/variables.tf +++ b/modules/compute-mig/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/compute-mig/versions.tf b/modules/compute-mig/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/compute-mig/versions.tf +++ b/modules/compute-mig/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/compute-vm/main.tf b/modules/compute-vm/main.tf index 94f8d0a1..50c1fea4 100644 --- a/modules/compute-vm/main.tf +++ b/modules/compute-vm/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/compute-vm/outputs.tf b/modules/compute-vm/outputs.tf index b541e32a..523924f9 100644 --- a/modules/compute-vm/outputs.tf +++ b/modules/compute-vm/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/compute-vm/variables.tf b/modules/compute-vm/variables.tf index 9a27cd98..cfdd40dc 100644 --- a/modules/compute-vm/variables.tf +++ b/modules/compute-vm/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/compute-vm/versions.tf b/modules/compute-vm/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/compute-vm/versions.tf +++ b/modules/compute-vm/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/container-registry/main.tf b/modules/container-registry/main.tf index f69cb283..74097510 100644 --- a/modules/container-registry/main.tf +++ b/modules/container-registry/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/container-registry/outputs.tf b/modules/container-registry/outputs.tf index 46a76aa7..53f0fc6b 100644 --- a/modules/container-registry/outputs.tf +++ b/modules/container-registry/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/container-registry/variables.tf b/modules/container-registry/variables.tf index d1191b03..ae35034c 100644 --- a/modules/container-registry/variables.tf +++ b/modules/container-registry/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/container-registry/versions.tf b/modules/container-registry/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/container-registry/versions.tf +++ b/modules/container-registry/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/datafusion/main.tf b/modules/datafusion/main.tf index 967e36a5..d4225517 100644 --- a/modules/datafusion/main.tf +++ b/modules/datafusion/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/datafusion/outputs.tf b/modules/datafusion/outputs.tf index 9c626a72..513675e4 100644 --- a/modules/datafusion/outputs.tf +++ b/modules/datafusion/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/datafusion/variables.tf b/modules/datafusion/variables.tf index bda621be..57acf224 100644 --- a/modules/datafusion/variables.tf +++ b/modules/datafusion/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/datafusion/versions.tf b/modules/datafusion/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/datafusion/versions.tf +++ b/modules/datafusion/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/dns/main.tf b/modules/dns/main.tf index b6a3bfaa..c2b567d2 100644 --- a/modules/dns/main.tf +++ b/modules/dns/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/dns/outputs.tf b/modules/dns/outputs.tf index 8269faa7..c0e47737 100644 --- a/modules/dns/outputs.tf +++ b/modules/dns/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/dns/variables.tf b/modules/dns/variables.tf index a8268e5d..ba44c7d8 100644 --- a/modules/dns/variables.tf +++ b/modules/dns/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/dns/versions.tf b/modules/dns/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/dns/versions.tf +++ b/modules/dns/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/endpoints/main.tf b/modules/endpoints/main.tf index f5bc055f..dc8c61ba 100644 --- a/modules/endpoints/main.tf +++ b/modules/endpoints/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/endpoints/outputs.tf b/modules/endpoints/outputs.tf index 2a4d45f8..27fe3f60 100644 --- a/modules/endpoints/outputs.tf +++ b/modules/endpoints/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/endpoints/variables.tf b/modules/endpoints/variables.tf index 8ecbb5d0..f590670e 100644 --- a/modules/endpoints/variables.tf +++ b/modules/endpoints/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/endpoints/versions.tf b/modules/endpoints/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/endpoints/versions.tf +++ b/modules/endpoints/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/folder/firewall-policy.tf b/modules/folder/firewall-policy.tf index 312d9adb..96224c56 100644 --- a/modules/folder/firewall-policy.tf +++ b/modules/folder/firewall-policy.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/folder/main.tf b/modules/folder/main.tf index 917e2769..80847871 100644 --- a/modules/folder/main.tf +++ b/modules/folder/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/folder/outputs.tf b/modules/folder/outputs.tf index da368210..37babc6f 100644 --- a/modules/folder/outputs.tf +++ b/modules/folder/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/folder/variables.tf b/modules/folder/variables.tf index 4bf5a83c..365a7561 100644 --- a/modules/folder/variables.tf +++ b/modules/folder/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/folder/versions.tf b/modules/folder/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/folder/versions.tf +++ b/modules/folder/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/folders-unit/locals.tf b/modules/folders-unit/locals.tf index a275da1c..bd513509 100644 --- a/modules/folders-unit/locals.tf +++ b/modules/folders-unit/locals.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/folders-unit/main.tf b/modules/folders-unit/main.tf index 22894c52..f609fffc 100644 --- a/modules/folders-unit/main.tf +++ b/modules/folders-unit/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/folders-unit/outputs.tf b/modules/folders-unit/outputs.tf index 1b619e8e..8e4d1066 100644 --- a/modules/folders-unit/outputs.tf +++ b/modules/folders-unit/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/folders-unit/variables.tf b/modules/folders-unit/variables.tf index 8b2f8823..10a167d4 100644 --- a/modules/folders-unit/variables.tf +++ b/modules/folders-unit/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/folders-unit/versions.tf b/modules/folders-unit/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/folders-unit/versions.tf +++ b/modules/folders-unit/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/gcs/main.tf b/modules/gcs/main.tf index 19e2441d..c0aea373 100644 --- a/modules/gcs/main.tf +++ b/modules/gcs/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -128,4 +128,4 @@ resource "google_pubsub_topic" "topic" { count = local.notification ? 1 : 0 project = var.project_id name = var.notification_config.topic_name -} \ No newline at end of file +} diff --git a/modules/gcs/outputs.tf b/modules/gcs/outputs.tf index ee21b9d1..415b9463 100644 --- a/modules/gcs/outputs.tf +++ b/modules/gcs/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/gcs/variables.tf b/modules/gcs/variables.tf index 2a50f591..43673beb 100644 --- a/modules/gcs/variables.tf +++ b/modules/gcs/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -152,4 +152,4 @@ variable "website" { not_found_page = string }) default = null -} \ No newline at end of file +} diff --git a/modules/gcs/versions.tf b/modules/gcs/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/gcs/versions.tf +++ b/modules/gcs/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/gke-cluster/main.tf b/modules/gke-cluster/main.tf index 572e35c1..24f4e0d0 100644 --- a/modules/gke-cluster/main.tf +++ b/modules/gke-cluster/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -304,4 +304,4 @@ resource "google_pubsub_topic" "notifications" { labels = { content = "gke-notifications" } -} \ No newline at end of file +} diff --git a/modules/gke-cluster/outputs.tf b/modules/gke-cluster/outputs.tf index df0b40c1..eb5985e7 100644 --- a/modules/gke-cluster/outputs.tf +++ b/modules/gke-cluster/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/gke-cluster/variables.tf b/modules/gke-cluster/variables.tf index 4c2bf2ec..ddb754e2 100644 --- a/modules/gke-cluster/variables.tf +++ b/modules/gke-cluster/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/gke-cluster/versions.tf b/modules/gke-cluster/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/gke-cluster/versions.tf +++ b/modules/gke-cluster/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/gke-nodepool/main.tf b/modules/gke-nodepool/main.tf index 536b31ae..0b7268ff 100644 --- a/modules/gke-nodepool/main.tf +++ b/modules/gke-nodepool/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/gke-nodepool/outputs.tf b/modules/gke-nodepool/outputs.tf index e3f56aca..2736acbf 100644 --- a/modules/gke-nodepool/outputs.tf +++ b/modules/gke-nodepool/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/gke-nodepool/variables.tf b/modules/gke-nodepool/variables.tf index 0053d7aa..8b3f6d10 100644 --- a/modules/gke-nodepool/variables.tf +++ b/modules/gke-nodepool/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/gke-nodepool/versions.tf b/modules/gke-nodepool/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/gke-nodepool/versions.tf +++ b/modules/gke-nodepool/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf index 71955908..f4d6952f 100644 --- a/modules/iam-service-account/main.tf +++ b/modules/iam-service-account/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/iam-service-account/outputs.tf b/modules/iam-service-account/outputs.tf index cf12f530..8653ccc7 100644 --- a/modules/iam-service-account/outputs.tf +++ b/modules/iam-service-account/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/iam-service-account/variables.tf b/modules/iam-service-account/variables.tf index d1f5d3e3..f906baa6 100644 --- a/modules/iam-service-account/variables.tf +++ b/modules/iam-service-account/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/iam-service-account/versions.tf b/modules/iam-service-account/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/iam-service-account/versions.tf +++ b/modules/iam-service-account/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/kms/main.tf b/modules/kms/main.tf index 7708a5cb..59cbcf65 100644 --- a/modules/kms/main.tf +++ b/modules/kms/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/kms/outputs.tf b/modules/kms/outputs.tf index 917d7b32..43f7997c 100644 --- a/modules/kms/outputs.tf +++ b/modules/kms/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/kms/variables.tf b/modules/kms/variables.tf index 66b9e18f..d2c425ba 100644 --- a/modules/kms/variables.tf +++ b/modules/kms/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/kms/versions.tf b/modules/kms/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/kms/versions.tf +++ b/modules/kms/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/logging-bucket/main.tf b/modules/logging-bucket/main.tf index ee405507..743f77cd 100644 --- a/modules/logging-bucket/main.tf +++ b/modules/logging-bucket/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/logging-bucket/outputs.tf b/modules/logging-bucket/outputs.tf index b335d6b0..7100237e 100644 --- a/modules/logging-bucket/outputs.tf +++ b/modules/logging-bucket/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/logging-bucket/variables.tf b/modules/logging-bucket/variables.tf index 5e1a4996..350cad68 100644 --- a/modules/logging-bucket/variables.tf +++ b/modules/logging-bucket/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/logging-bucket/versions.tf b/modules/logging-bucket/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/logging-bucket/versions.tf +++ b/modules/logging-bucket/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/naming-convention/main.tf b/modules/naming-convention/main.tf index 5ec35806..aa7d406b 100644 --- a/modules/naming-convention/main.tf +++ b/modules/naming-convention/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/naming-convention/outputs.tf b/modules/naming-convention/outputs.tf index 99482337..d96cfa98 100644 --- a/modules/naming-convention/outputs.tf +++ b/modules/naming-convention/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/naming-convention/variables.tf b/modules/naming-convention/variables.tf index 37ebe374..3ba8cd2c 100644 --- a/modules/naming-convention/variables.tf +++ b/modules/naming-convention/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/naming-convention/versions.tf b/modules/naming-convention/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/naming-convention/versions.tf +++ b/modules/naming-convention/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/net-address/main.tf b/modules/net-address/main.tf index 972afa0b..caab92a0 100644 --- a/modules/net-address/main.tf +++ b/modules/net-address/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-address/outputs.tf b/modules/net-address/outputs.tf index 3c6a26b8..da691273 100644 --- a/modules/net-address/outputs.tf +++ b/modules/net-address/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-address/variables.tf b/modules/net-address/variables.tf index 823d79a6..bb3043dc 100644 --- a/modules/net-address/variables.tf +++ b/modules/net-address/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-address/versions.tf b/modules/net-address/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/net-address/versions.tf +++ b/modules/net-address/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/net-cloudnat/main.tf b/modules/net-cloudnat/main.tf index f39f31f4..a7ef2260 100644 --- a/modules/net-cloudnat/main.tf +++ b/modules/net-cloudnat/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-cloudnat/outputs.tf b/modules/net-cloudnat/outputs.tf index 0125cde0..d9e95d4b 100644 --- a/modules/net-cloudnat/outputs.tf +++ b/modules/net-cloudnat/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-cloudnat/variables.tf b/modules/net-cloudnat/variables.tf index 9ffeea3f..fcf79944 100644 --- a/modules/net-cloudnat/variables.tf +++ b/modules/net-cloudnat/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-cloudnat/versions.tf b/modules/net-cloudnat/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/net-cloudnat/versions.tf +++ b/modules/net-cloudnat/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/net-ilb/main.tf b/modules/net-ilb/main.tf index 329a5698..aa4addcc 100644 --- a/modules/net-ilb/main.tf +++ b/modules/net-ilb/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-ilb/outputs.tf b/modules/net-ilb/outputs.tf index c8526d52..55b454e1 100644 --- a/modules/net-ilb/outputs.tf +++ b/modules/net-ilb/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-ilb/variables.tf b/modules/net-ilb/variables.tf index 875967cd..638aee52 100644 --- a/modules/net-ilb/variables.tf +++ b/modules/net-ilb/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-ilb/versions.tf b/modules/net-ilb/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/net-ilb/versions.tf +++ b/modules/net-ilb/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/net-interconnect-attachment-direct/main.tf b/modules/net-interconnect-attachment-direct/main.tf index 01dd8dbc..ae832bcc 100644 --- a/modules/net-interconnect-attachment-direct/main.tf +++ b/modules/net-interconnect-attachment-direct/main.tf @@ -1,6 +1,6 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-interconnect-attachment-direct/outputs.tf b/modules/net-interconnect-attachment-direct/outputs.tf index 392f4d12..a7905fef 100644 --- a/modules/net-interconnect-attachment-direct/outputs.tf +++ b/modules/net-interconnect-attachment-direct/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-interconnect-attachment-direct/variables.tf b/modules/net-interconnect-attachment-direct/variables.tf index 69d837c2..881147fb 100644 --- a/modules/net-interconnect-attachment-direct/variables.tf +++ b/modules/net-interconnect-attachment-direct/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-interconnect-attachment-direct/versions.tf b/modules/net-interconnect-attachment-direct/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/net-interconnect-attachment-direct/versions.tf +++ b/modules/net-interconnect-attachment-direct/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/net-vpc-firewall/main.tf b/modules/net-vpc-firewall/main.tf index 158e0991..ef4767d3 100644 --- a/modules/net-vpc-firewall/main.tf +++ b/modules/net-vpc-firewall/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpc-firewall/outputs.tf b/modules/net-vpc-firewall/outputs.tf index b64db000..f784583c 100644 --- a/modules/net-vpc-firewall/outputs.tf +++ b/modules/net-vpc-firewall/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpc-firewall/variables.tf b/modules/net-vpc-firewall/variables.tf index 7682a3f3..356b8bd8 100644 --- a/modules/net-vpc-firewall/variables.tf +++ b/modules/net-vpc-firewall/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpc-firewall/versions.tf b/modules/net-vpc-firewall/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/net-vpc-firewall/versions.tf +++ b/modules/net-vpc-firewall/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/net-vpc-peering/main.tf b/modules/net-vpc-peering/main.tf index 987bf0b7..1bade5f1 100644 --- a/modules/net-vpc-peering/main.tf +++ b/modules/net-vpc-peering/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpc-peering/outputs.tf b/modules/net-vpc-peering/outputs.tf index 8b380b61..558c4bac 100644 --- a/modules/net-vpc-peering/outputs.tf +++ b/modules/net-vpc-peering/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpc-peering/variables.tf b/modules/net-vpc-peering/variables.tf index 0874dda1..908578fa 100644 --- a/modules/net-vpc-peering/variables.tf +++ b/modules/net-vpc-peering/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpc-peering/versions.tf b/modules/net-vpc-peering/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/net-vpc-peering/versions.tf +++ b/modules/net-vpc-peering/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/net-vpc/main.tf b/modules/net-vpc/main.tf index ad663d31..1abd7c62 100644 --- a/modules/net-vpc/main.tf +++ b/modules/net-vpc/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpc/outputs.tf b/modules/net-vpc/outputs.tf index cf17e7f9..0118d7ec 100644 --- a/modules/net-vpc/outputs.tf +++ b/modules/net-vpc/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf index 5be9c923..824c6f28 100644 --- a/modules/net-vpc/variables.tf +++ b/modules/net-vpc/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpc/versions.tf b/modules/net-vpc/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/net-vpc/versions.tf +++ b/modules/net-vpc/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-dynamic/main.tf b/modules/net-vpn-dynamic/main.tf index bed5032c..4da48c12 100644 --- a/modules/net-vpn-dynamic/main.tf +++ b/modules/net-vpn-dynamic/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-dynamic/outputs.tf b/modules/net-vpn-dynamic/outputs.tf index e29c9e93..09e5959e 100644 --- a/modules/net-vpn-dynamic/outputs.tf +++ b/modules/net-vpn-dynamic/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-dynamic/variables.tf b/modules/net-vpn-dynamic/variables.tf index 6b97f676..6da5c0ac 100644 --- a/modules/net-vpn-dynamic/variables.tf +++ b/modules/net-vpn-dynamic/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-dynamic/versions.tf b/modules/net-vpn-dynamic/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/net-vpn-dynamic/versions.tf +++ b/modules/net-vpn-dynamic/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-ha/main.tf b/modules/net-vpn-ha/main.tf index cb2ba687..8c487031 100644 --- a/modules/net-vpn-ha/main.tf +++ b/modules/net-vpn-ha/main.tf @@ -1,6 +1,6 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-ha/outputs.tf b/modules/net-vpn-ha/outputs.tf index c988c103..181305b6 100644 --- a/modules/net-vpn-ha/outputs.tf +++ b/modules/net-vpn-ha/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-ha/variables.tf b/modules/net-vpn-ha/variables.tf index 6f5832be..4e8d17ac 100644 --- a/modules/net-vpn-ha/variables.tf +++ b/modules/net-vpn-ha/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-ha/versions.tf b/modules/net-vpn-ha/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/net-vpn-ha/versions.tf +++ b/modules/net-vpn-ha/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-static/main.tf b/modules/net-vpn-static/main.tf index c8540aab..3c8f5cb8 100644 --- a/modules/net-vpn-static/main.tf +++ b/modules/net-vpn-static/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-static/outputs.tf b/modules/net-vpn-static/outputs.tf index e3970f2c..8a764601 100644 --- a/modules/net-vpn-static/outputs.tf +++ b/modules/net-vpn-static/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-static/variables.tf b/modules/net-vpn-static/variables.tf index b5e5025c..90b15f53 100644 --- a/modules/net-vpn-static/variables.tf +++ b/modules/net-vpn-static/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/net-vpn-static/versions.tf b/modules/net-vpn-static/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/net-vpn-static/versions.tf +++ b/modules/net-vpn-static/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/organization/firewall-policy.tf b/modules/organization/firewall-policy.tf index 2c304601..20dd3f29 100644 --- a/modules/organization/firewall-policy.tf +++ b/modules/organization/firewall-policy.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/organization/iam.tf b/modules/organization/iam.tf index 3e594935..bafa5e16 100644 --- a/modules/organization/iam.tf +++ b/modules/organization/iam.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/organization/logging.tf b/modules/organization/logging.tf index ad9d8534..dfe47e25 100644 --- a/modules/organization/logging.tf +++ b/modules/organization/logging.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/organization/main.tf b/modules/organization/main.tf index 4f19c524..60aa65a0 100644 --- a/modules/organization/main.tf +++ b/modules/organization/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/organization/outputs.tf b/modules/organization/outputs.tf index 13b11de3..c1f501d8 100644 --- a/modules/organization/outputs.tf +++ b/modules/organization/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf index 374b7270..1ca33635 100644 --- a/modules/organization/variables.tf +++ b/modules/organization/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/organization/versions.tf b/modules/organization/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/organization/versions.tf +++ b/modules/organization/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/project/main.tf b/modules/project/main.tf index abd15fed..13edb181 100644 --- a/modules/project/main.tf +++ b/modules/project/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/project/outputs.tf b/modules/project/outputs.tf index 9b04ec97..417febe3 100644 --- a/modules/project/outputs.tf +++ b/modules/project/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/project/service_accounts.tf b/modules/project/service_accounts.tf index c601d281..48e85820 100644 --- a/modules/project/service_accounts.tf +++ b/modules/project/service_accounts.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 34db467b..971244cc 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/project/versions.tf b/modules/project/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/project/versions.tf +++ b/modules/project/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/pubsub/main.tf b/modules/pubsub/main.tf index f66c7324..545abbbd 100644 --- a/modules/pubsub/main.tf +++ b/modules/pubsub/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/pubsub/outputs.tf b/modules/pubsub/outputs.tf index 3e3cc8cf..c26eb4d9 100644 --- a/modules/pubsub/outputs.tf +++ b/modules/pubsub/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/pubsub/variables.tf b/modules/pubsub/variables.tf index 1bf0f409..78dd2fa6 100644 --- a/modules/pubsub/variables.tf +++ b/modules/pubsub/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/pubsub/versions.tf b/modules/pubsub/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/pubsub/versions.tf +++ b/modules/pubsub/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/secret-manager/main.tf b/modules/secret-manager/main.tf index 6b6154e6..ed0af26b 100644 --- a/modules/secret-manager/main.tf +++ b/modules/secret-manager/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/secret-manager/outputs.tf b/modules/secret-manager/outputs.tf index eef3e5c7..7267fa12 100644 --- a/modules/secret-manager/outputs.tf +++ b/modules/secret-manager/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/secret-manager/variables.tf b/modules/secret-manager/variables.tf index 69cde3db..f8ed1111 100644 --- a/modules/secret-manager/variables.tf +++ b/modules/secret-manager/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/secret-manager/versions.tf b/modules/secret-manager/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/secret-manager/versions.tf +++ b/modules/secret-manager/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/service-directory/main.tf b/modules/service-directory/main.tf index 618d875c..781bae60 100644 --- a/modules/service-directory/main.tf +++ b/modules/service-directory/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/service-directory/outputs.tf b/modules/service-directory/outputs.tf index b3947890..a7656002 100644 --- a/modules/service-directory/outputs.tf +++ b/modules/service-directory/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/service-directory/variables.tf b/modules/service-directory/variables.tf index b164b113..326aeff8 100644 --- a/modules/service-directory/variables.tf +++ b/modules/service-directory/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/service-directory/versions.tf b/modules/service-directory/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/service-directory/versions.tf +++ b/modules/service-directory/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/source-repository/main.tf b/modules/source-repository/main.tf index 0077b68b..c4057d76 100644 --- a/modules/source-repository/main.tf +++ b/modules/source-repository/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/source-repository/outputs.tf b/modules/source-repository/outputs.tf index 5564c636..d1a4b25e 100644 --- a/modules/source-repository/outputs.tf +++ b/modules/source-repository/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/source-repository/variables.tf b/modules/source-repository/variables.tf index 88a63b7d..e592f358 100644 --- a/modules/source-repository/variables.tf +++ b/modules/source-repository/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/source-repository/versions.tf b/modules/source-repository/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/source-repository/versions.tf +++ b/modules/source-repository/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/vpc-sc/access_levels.tf b/modules/vpc-sc/access_levels.tf index 585570ee..f8c34355 100644 --- a/modules/vpc-sc/access_levels.tf +++ b/modules/vpc-sc/access_levels.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/vpc-sc/main.tf b/modules/vpc-sc/main.tf index 73947036..0b06b481 100644 --- a/modules/vpc-sc/main.tf +++ b/modules/vpc-sc/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/vpc-sc/outputs.tf b/modules/vpc-sc/outputs.tf index 3412e234..42795948 100644 --- a/modules/vpc-sc/outputs.tf +++ b/modules/vpc-sc/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/vpc-sc/service_perimeters_bridge.tf b/modules/vpc-sc/service_perimeters_bridge.tf index 660736af..3c57f6b9 100644 --- a/modules/vpc-sc/service_perimeters_bridge.tf +++ b/modules/vpc-sc/service_perimeters_bridge.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/vpc-sc/service_perimeters_regular.tf b/modules/vpc-sc/service_perimeters_regular.tf index 2f4eae3c..d3069c57 100644 --- a/modules/vpc-sc/service_perimeters_regular.tf +++ b/modules/vpc-sc/service_perimeters_regular.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/vpc-sc/variables.tf b/modules/vpc-sc/variables.tf index fcfef359..9df6989e 100644 --- a/modules/vpc-sc/variables.tf +++ b/modules/vpc-sc/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/decentralized-firewall/backend.tf.sample b/networking/decentralized-firewall/backend.tf.sample index 99f84b17..4f2bb336 100644 --- a/networking/decentralized-firewall/backend.tf.sample +++ b/networking/decentralized-firewall/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/decentralized-firewall/firewall/common/common-egress.yaml b/networking/decentralized-firewall/firewall/common/common-egress.yaml index 716c1498..c4540e7a 100644 --- a/networking/decentralized-firewall/firewall/common/common-egress.yaml +++ b/networking/decentralized-firewall/firewall/common/common-egress.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,29 +15,29 @@ # Deny all egress (egress traffic is allowed by default) deny-all: deny: - - ports: [] - protocol: all + - ports: [] + protocol: all direction: EGRESS priority: 65535 destination_ranges: - - 0.0.0.0/0 + - 0.0.0.0/0 -# Allow access to GCP APIs via Private Google Access +# Allow access to GCP APIs via Private Google Access # https://cloud.google.com/vpc/docs/access-apis-external-ip#config gcp-pga-apis: allow: - - ports: [443] - protocol: tcp + - ports: [443] + protocol: tcp direction: EGRESS priority: 500 destination_ranges: - - 199.36.153.8/30 + - 199.36.153.8/30 # Allow egress to internal networks internal-egress: allow: - - ports: [] - protocol: tcp + - ports: [] + protocol: tcp direction: EGRESS destination_ranges: - - 10.0.0.0/16 + - 10.0.0.0/16 diff --git a/networking/decentralized-firewall/firewall/common/iap-access.yaml b/networking/decentralized-firewall/firewall/common/iap-access.yaml index 931a180e..588af162 100644 --- a/networking/decentralized-firewall/firewall/common/iap-access.yaml +++ b/networking/decentralized-firewall/firewall/common/iap-access.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,13 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Access via SSH from IAP to all instancess https://cloud.google.com/iap/docs/using-tcp-forwarding#create-firewall-rule +# Access via SSH from IAP to all instancess https://cloud.google.com/iap/docs/using-tcp-forwarding#create-firewall-rule iap-ssh-access: allow: - - ports: [22] - protocol: tcp + - ports: [22] + protocol: tcp direction: INGRESS priority: 1001 source_ranges: - - 35.235.240.0/20 - \ No newline at end of file + - 35.235.240.0/20 diff --git a/networking/decentralized-firewall/firewall/common/lb-access.yaml b/networking/decentralized-firewall/firewall/common/lb-access.yaml index 975d3ca0..bd20e976 100644 --- a/networking/decentralized-firewall/firewall/common/lb-access.yaml +++ b/networking/decentralized-firewall/firewall/common/lb-access.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,10 +15,10 @@ # Access from GCP LBs https://cloud.google.com/load-balancing/docs/https/#firewall_rules lb-health-checks: allow: - - ports: [] - protocol: tcp + - ports: [] + protocol: tcp direction: INGRESS priority: 1001 source_ranges: - - 35.191.0.0/16 - - 130.211.0.0/22 + - 35.191.0.0/16 + - 130.211.0.0/22 diff --git a/networking/decentralized-firewall/firewall/dev/app-1/app1-rules.yaml b/networking/decentralized-firewall/firewall/dev/app-1/app1-rules.yaml index 9a26650b..1691fdf7 100644 --- a/networking/decentralized-firewall/firewall/dev/app-1/app1-rules.yaml +++ b/networking/decentralized-firewall/firewall/dev/app-1/app1-rules.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,17 +15,17 @@ # Allow traffic from the frontend VMs app1-backend: allow: - - ports: ['443', '80'] - protocol: tcp + - ports: ["443", "80"] + protocol: tcp direction: INGRESS - source_tags: ['app1-frontend'] - target_tags: ['app1-backend'] + source_tags: ["app1-frontend"] + target_tags: ["app1-backend"] # Allow traffic to MySQL Servers from App1 backend app1-db: allow: - - ports: ['3306'] - protocol: tcp + - ports: ["3306"] + protocol: tcp direction: INGRESS - source_tags: ['app1-backend'] - target_tags: ['mysql-server'] + source_tags: ["app1-backend"] + target_tags: ["mysql-server"] diff --git a/networking/decentralized-firewall/firewall/dev/app-2/app2-rules.yaml b/networking/decentralized-firewall/firewall/dev/app-2/app2-rules.yaml index d7b79b63..f979397d 100644 --- a/networking/decentralized-firewall/firewall/dev/app-2/app2-rules.yaml +++ b/networking/decentralized-firewall/firewall/dev/app-2/app2-rules.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,17 +15,17 @@ # Allow traffic from app1 frontend app2-backend: allow: - - ports: ['443', '80'] - protocol: tcp + - ports: ["443", "80"] + protocol: tcp direction: INGRESS - source_tags: ['app1-frontend'] - target_tags: ['app2-backend'] + source_tags: ["app1-frontend"] + target_tags: ["app2-backend"] # Allow traffic to MySQL servers from App2 backend app2-db: allow: - - ports: ['3306'] - protocol: tcp + - ports: ["3306"] + protocol: tcp direction: INGRESS - source_tags: ['app2-backend'] - target_tags: ['mysql-server'] + source_tags: ["app2-backend"] + target_tags: ["mysql-server"] diff --git a/networking/decentralized-firewall/firewall/prod/app-1/app1-rules.yaml b/networking/decentralized-firewall/firewall/prod/app-1/app1-rules.yaml index 9a26650b..1691fdf7 100644 --- a/networking/decentralized-firewall/firewall/prod/app-1/app1-rules.yaml +++ b/networking/decentralized-firewall/firewall/prod/app-1/app1-rules.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,17 +15,17 @@ # Allow traffic from the frontend VMs app1-backend: allow: - - ports: ['443', '80'] - protocol: tcp + - ports: ["443", "80"] + protocol: tcp direction: INGRESS - source_tags: ['app1-frontend'] - target_tags: ['app1-backend'] + source_tags: ["app1-frontend"] + target_tags: ["app1-backend"] # Allow traffic to MySQL Servers from App1 backend app1-db: allow: - - ports: ['3306'] - protocol: tcp + - ports: ["3306"] + protocol: tcp direction: INGRESS - source_tags: ['app1-backend'] - target_tags: ['mysql-server'] + source_tags: ["app1-backend"] + target_tags: ["mysql-server"] diff --git a/networking/decentralized-firewall/main.tf b/networking/decentralized-firewall/main.tf index b9267124..99294ce0 100644 --- a/networking/decentralized-firewall/main.tf +++ b/networking/decentralized-firewall/main.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/decentralized-firewall/outputs.tf b/networking/decentralized-firewall/outputs.tf index 95501f30..9542b892 100644 --- a/networking/decentralized-firewall/outputs.tf +++ b/networking/decentralized-firewall/outputs.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/decentralized-firewall/validator/Dockerfile b/networking/decentralized-firewall/validator/Dockerfile index be4b22b4..ddcbb453 100644 --- a/networking/decentralized-firewall/validator/Dockerfile +++ b/networking/decentralized-firewall/validator/Dockerfile @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/decentralized-firewall/validator/action.yml b/networking/decentralized-firewall/validator/action.yml index d6e6177c..00c4819e 100644 --- a/networking/decentralized-firewall/validator/action.yml +++ b/networking/decentralized-firewall/validator/action.yml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,33 +12,33 @@ # See the License for the specific language governing permissions and # limitations under the License. # -name: 'Validate firewall rules' -description: 'Validate firewall rule YAML files' +name: "Validate firewall rules" +description: "Validate firewall rule YAML files" inputs: files: - description: 'Files to scan (supports wildcards)' + description: "Files to scan (supports wildcards)" required: false - default: '/github/workspace/firewall/**/*.yaml' + default: "/github/workspace/firewall/**/*.yaml" mode: - description: 'Mode (validate or approve)' + description: "Mode (validate or approve)" required: false - default: 'validate' + default: "validate" schema: - description: 'Schema' + description: "Schema" required: false - default: '/schemas/firewallSchema.yaml' + default: "/schemas/firewallSchema.yaml" outputs: ok: - description: 'Validation successful' + description: "Validation successful" errors: - description: 'Validation results' + description: "Validation results" runs: - using: 'docker' - image: 'Dockerfile' + using: "docker" + image: "Dockerfile" args: - ${{ inputs.files }} - "--mode" - ${{ inputs.mode }} - "--schema" - ${{ inputs.schema }} - - "--github" \ No newline at end of file + - "--github" diff --git a/networking/decentralized-firewall/validator/firewallSchema.yaml b/networking/decentralized-firewall/validator/firewallSchema.yaml index 697d982a..82a52a07 100644 --- a/networking/decentralized-firewall/validator/firewallSchema.yaml +++ b/networking/decentralized-firewall/validator/firewallSchema.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/decentralized-firewall/validator/firewallSchemaAutoApprove.yaml b/networking/decentralized-firewall/validator/firewallSchemaAutoApprove.yaml index a5a425f3..3c907c52 100644 --- a/networking/decentralized-firewall/validator/firewallSchemaAutoApprove.yaml +++ b/networking/decentralized-firewall/validator/firewallSchemaAutoApprove.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/decentralized-firewall/validator/firewallSchemaSettings.yaml b/networking/decentralized-firewall/validator/firewallSchemaSettings.yaml index 77c5ec65..13b3ff1c 100644 --- a/networking/decentralized-firewall/validator/firewallSchemaSettings.yaml +++ b/networking/decentralized-firewall/validator/firewallSchemaSettings.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,37 +13,37 @@ # limitations under the License. allowedPorts: -- ports: 22 # SSH - approved: false -- ports: 80 # HTTP - approved: true -- ports: 443 # HTTPS - approved: true -- ports: 3306 # MySQL - approved: false -- ports: 8000-8999 - approved: true + - ports: 22 # SSH + approved: false + - ports: 80 # HTTP + approved: true + - ports: 443 # HTTPS + approved: true + - ports: 3306 # MySQL + approved: false + - ports: 8000-8999 + approved: true allowedSourceRanges: -- cidr: 10.0.0.0/8 # Example on-premise range - approved: true -- cidr: 35.191.0.0/16 # Load balancing & health checks - approved: true -- cidr: 130.211.0.0/22 # Load balancing & health checks - approved: false -- cidr: 35.235.240.0/20 # IAP source range - approved: true + - cidr: 10.0.0.0/8 # Example on-premise range + approved: true + - cidr: 35.191.0.0/16 # Load balancing & health checks + approved: true + - cidr: 130.211.0.0/22 # Load balancing & health checks + approved: false + - cidr: 35.235.240.0/20 # IAP source range + approved: true allowedDestinationRanges: -- cidr: 10.0.0.0/8 - approved: true -- cidr: 0.0.0.0/0 - approved: false + - cidr: 10.0.0.0/8 + approved: true + - cidr: 0.0.0.0/0 + approved: false allowedNetworkTags: -- tag: '*' - approved: true + - tag: "*" + approved: true allowedServiceAccounts: -- serviceAccount: '*' - approved: true \ No newline at end of file + - serviceAccount: "*" + approved: true diff --git a/networking/decentralized-firewall/validator/requirements.txt b/networking/decentralized-firewall/validator/requirements.txt index 05fa91c4..df645f6b 100644 --- a/networking/decentralized-firewall/validator/requirements.txt +++ b/networking/decentralized-firewall/validator/requirements.txt @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/decentralized-firewall/validator/validator.py b/networking/decentralized-firewall/validator/validator.py index ca625882..0daa40e3 100644 --- a/networking/decentralized-firewall/validator/validator.py +++ b/networking/decentralized-firewall/validator/validator.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -199,7 +199,8 @@ class FirewallValidator: self.schema = yamale.make_schema(path=schema, validators=self.validators) def set_schema_from_string(self, schema): - self.schema = yamale.make_schema(content=schema, validators=self.validators) + self.schema = yamale.make_schema( + content=schema, validators=self.validators) def validate_file(self, file): print('Validating %s...' % (file), file=sys.stderr) diff --git a/networking/decentralized-firewall/variables.tf b/networking/decentralized-firewall/variables.tf index 9001eacf..76a3e1cd 100644 --- a/networking/decentralized-firewall/variables.tf +++ b/networking/decentralized-firewall/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/decentralized-firewall/versions.tf b/networking/decentralized-firewall/versions.tf index 1cc6bf89..29041268 100644 --- a/networking/decentralized-firewall/versions.tf +++ b/networking/decentralized-firewall/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/filtering-proxy/main.tf b/networking/filtering-proxy/main.tf index 1abcd369..0f0f52dc 100644 --- a/networking/filtering-proxy/main.tf +++ b/networking/filtering-proxy/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/filtering-proxy/outputs.tf b/networking/filtering-proxy/outputs.tf index 1a849229..687ce3cd 100644 --- a/networking/filtering-proxy/outputs.tf +++ b/networking/filtering-proxy/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/filtering-proxy/variables.tf b/networking/filtering-proxy/variables.tf index a48d94ff..19489899 100644 --- a/networking/filtering-proxy/variables.tf +++ b/networking/filtering-proxy/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/filtering-proxy/versions.tf b/networking/filtering-proxy/versions.tf index 1cc6bf89..29041268 100644 --- a/networking/filtering-proxy/versions.tf +++ b/networking/filtering-proxy/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-peering/backend.tf.sample b/networking/hub-and-spoke-peering/backend.tf.sample index 99f84b17..4f2bb336 100644 --- a/networking/hub-and-spoke-peering/backend.tf.sample +++ b/networking/hub-and-spoke-peering/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-peering/main.tf b/networking/hub-and-spoke-peering/main.tf index e6f436ca..aa8ef566 100644 --- a/networking/hub-and-spoke-peering/main.tf +++ b/networking/hub-and-spoke-peering/main.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-peering/outputs.tf b/networking/hub-and-spoke-peering/outputs.tf index 03f9bd57..7010404a 100644 --- a/networking/hub-and-spoke-peering/outputs.tf +++ b/networking/hub-and-spoke-peering/outputs.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-peering/variables.tf b/networking/hub-and-spoke-peering/variables.tf index 0017e603..fdaf4e83 100644 --- a/networking/hub-and-spoke-peering/variables.tf +++ b/networking/hub-and-spoke-peering/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-peering/versions.tf b/networking/hub-and-spoke-peering/versions.tf index 1cc6bf89..29041268 100644 --- a/networking/hub-and-spoke-peering/versions.tf +++ b/networking/hub-and-spoke-peering/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-vpn/backend.tf.sample b/networking/hub-and-spoke-vpn/backend.tf.sample index 99f84b17..4f2bb336 100644 --- a/networking/hub-and-spoke-vpn/backend.tf.sample +++ b/networking/hub-and-spoke-vpn/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-vpn/main.tf b/networking/hub-and-spoke-vpn/main.tf index c0a09300..3bf7e391 100644 --- a/networking/hub-and-spoke-vpn/main.tf +++ b/networking/hub-and-spoke-vpn/main.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-vpn/outputs.tf b/networking/hub-and-spoke-vpn/outputs.tf index 2ccd2ca7..f69ca109 100644 --- a/networking/hub-and-spoke-vpn/outputs.tf +++ b/networking/hub-and-spoke-vpn/outputs.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-vpn/provider.tf b/networking/hub-and-spoke-vpn/provider.tf index c58f735a..b36fb5ee 100644 --- a/networking/hub-and-spoke-vpn/provider.tf +++ b/networking/hub-and-spoke-vpn/provider.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-vpn/variables.tf b/networking/hub-and-spoke-vpn/variables.tf index d02bcd2e..f30bebc5 100644 --- a/networking/hub-and-spoke-vpn/variables.tf +++ b/networking/hub-and-spoke-vpn/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/hub-and-spoke-vpn/versions.tf b/networking/hub-and-spoke-vpn/versions.tf index 1cc6bf89..29041268 100644 --- a/networking/hub-and-spoke-vpn/versions.tf +++ b/networking/hub-and-spoke-vpn/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/ilb-next-hop/assets/gw.yaml b/networking/ilb-next-hop/assets/gw.yaml index 9a7d3ceb..215821c4 100644 --- a/networking/ilb-next-hop/assets/gw.yaml +++ b/networking/ilb-next-hop/assets/gw.yaml @@ -1,6 +1,6 @@ #cloud-config -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/ilb-next-hop/backend.tf.sample b/networking/ilb-next-hop/backend.tf.sample index 99f84b17..4f2bb336 100644 --- a/networking/ilb-next-hop/backend.tf.sample +++ b/networking/ilb-next-hop/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/ilb-next-hop/gateways.tf b/networking/ilb-next-hop/gateways.tf index 55b4c1cd..ee401af7 100644 --- a/networking/ilb-next-hop/gateways.tf +++ b/networking/ilb-next-hop/gateways.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/ilb-next-hop/main.tf b/networking/ilb-next-hop/main.tf index 39f7c3fd..138eec05 100644 --- a/networking/ilb-next-hop/main.tf +++ b/networking/ilb-next-hop/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/ilb-next-hop/outputs.tf b/networking/ilb-next-hop/outputs.tf index 80c3e77a..17702e83 100644 --- a/networking/ilb-next-hop/outputs.tf +++ b/networking/ilb-next-hop/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/ilb-next-hop/variables.tf b/networking/ilb-next-hop/variables.tf index 6d083472..2450c4eb 100644 --- a/networking/ilb-next-hop/variables.tf +++ b/networking/ilb-next-hop/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/ilb-next-hop/versions.tf b/networking/ilb-next-hop/versions.tf index 1cc6bf89..29041268 100644 --- a/networking/ilb-next-hop/versions.tf +++ b/networking/ilb-next-hop/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/ilb-next-hop/vms.tf b/networking/ilb-next-hop/vms.tf index 3a00b346..5b0fe22b 100644 --- a/networking/ilb-next-hop/vms.tf +++ b/networking/ilb-next-hop/vms.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/ilb-next-hop/vpc-left.tf b/networking/ilb-next-hop/vpc-left.tf index c5f8df22..e286790e 100644 --- a/networking/ilb-next-hop/vpc-left.tf +++ b/networking/ilb-next-hop/vpc-left.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/ilb-next-hop/vpc-right.tf b/networking/ilb-next-hop/vpc-right.tf index 1bf590e9..e2f604c5 100644 --- a/networking/ilb-next-hop/vpc-right.tf +++ b/networking/ilb-next-hop/vpc-right.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/onprem-google-access-dns/backend.tf.sample b/networking/onprem-google-access-dns/backend.tf.sample index 99f84b17..4f2bb336 100644 --- a/networking/onprem-google-access-dns/backend.tf.sample +++ b/networking/onprem-google-access-dns/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/onprem-google-access-dns/main.tf b/networking/onprem-google-access-dns/main.tf index 6c651a57..5027581b 100644 --- a/networking/onprem-google-access-dns/main.tf +++ b/networking/onprem-google-access-dns/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/onprem-google-access-dns/outputs.tf b/networking/onprem-google-access-dns/outputs.tf index b02b92d9..35a8e900 100644 --- a/networking/onprem-google-access-dns/outputs.tf +++ b/networking/onprem-google-access-dns/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/onprem-google-access-dns/variables.tf b/networking/onprem-google-access-dns/variables.tf index e0ae1712..a32f4b54 100644 --- a/networking/onprem-google-access-dns/variables.tf +++ b/networking/onprem-google-access-dns/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/onprem-google-access-dns/versions.tf b/networking/onprem-google-access-dns/versions.tf index 1cc6bf89..29041268 100644 --- a/networking/onprem-google-access-dns/versions.tf +++ b/networking/onprem-google-access-dns/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/private-cloud-function-from-onprem/assets/main.py b/networking/private-cloud-function-from-onprem/assets/main.py index 45236c37..6534cbd6 100644 --- a/networking/private-cloud-function-from-onprem/assets/main.py +++ b/networking/private-cloud-function-from-onprem/assets/main.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,10 +13,10 @@ # limitations under the License. def main(request): - request_json = request.get_json() - if request.args and 'message' in request.args: - return request.args.get('message') - elif request_json and 'message' in request_json: - return request_json['message'] - else: - return f'Hello World!!1\n' + request_json = request.get_json() + if request.args and 'message' in request.args: + return request.args.get('message') + elif request_json and 'message' in request_json: + return request_json['message'] + else: + return f'Hello World!!1\n' diff --git a/networking/private-cloud-function-from-onprem/main.tf b/networking/private-cloud-function-from-onprem/main.tf index 2cb858d6..b8dda561 100644 --- a/networking/private-cloud-function-from-onprem/main.tf +++ b/networking/private-cloud-function-from-onprem/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/private-cloud-function-from-onprem/outputs.tf b/networking/private-cloud-function-from-onprem/outputs.tf index 76a2fc68..a710c1d3 100644 --- a/networking/private-cloud-function-from-onprem/outputs.tf +++ b/networking/private-cloud-function-from-onprem/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ output "function_url" { description = "URL of the Cloud Function." value = module.function-hello.function.https_trigger_url -} \ No newline at end of file +} diff --git a/networking/private-cloud-function-from-onprem/variables.tf b/networking/private-cloud-function-from-onprem/variables.tf index 70355e17..d7f1aa00 100644 --- a/networking/private-cloud-function-from-onprem/variables.tf +++ b/networking/private-cloud-function-from-onprem/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/networking/private-cloud-function-from-onprem/versions.tf b/networking/private-cloud-function-from-onprem/versions.tf index 1cc6bf89..29041268 100644 --- a/networking/private-cloud-function-from-onprem/versions.tf +++ b/networking/private-cloud-function-from-onprem/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/shared-vpc-gke/backend.tf.sample b/networking/shared-vpc-gke/backend.tf.sample index 99f84b17..4f2bb336 100644 --- a/networking/shared-vpc-gke/backend.tf.sample +++ b/networking/shared-vpc-gke/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/shared-vpc-gke/main.tf b/networking/shared-vpc-gke/main.tf index e60478f6..62946cd4 100644 --- a/networking/shared-vpc-gke/main.tf +++ b/networking/shared-vpc-gke/main.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/shared-vpc-gke/outputs.tf b/networking/shared-vpc-gke/outputs.tf index 699f0610..c84254ad 100644 --- a/networking/shared-vpc-gke/outputs.tf +++ b/networking/shared-vpc-gke/outputs.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/shared-vpc-gke/variables.tf b/networking/shared-vpc-gke/variables.tf index 24f8c58b..daa1d72d 100644 --- a/networking/shared-vpc-gke/variables.tf +++ b/networking/shared-vpc-gke/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/networking/shared-vpc-gke/versions.tf b/networking/shared-vpc-gke/versions.tf index 1cc6bf89..29041268 100644 --- a/networking/shared-vpc-gke/versions.tf +++ b/networking/shared-vpc-gke/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/__init__.py b/tests/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/asset_inventory_feed_remediation/__init__.py b/tests/cloud_operations/asset_inventory_feed_remediation/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/cloud_operations/asset_inventory_feed_remediation/__init__.py +++ b/tests/cloud_operations/asset_inventory_feed_remediation/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/asset_inventory_feed_remediation/fixture/main.tf b/tests/cloud_operations/asset_inventory_feed_remediation/fixture/main.tf index 8f683ffc..79f84c5d 100644 --- a/tests/cloud_operations/asset_inventory_feed_remediation/fixture/main.tf +++ b/tests/cloud_operations/asset_inventory_feed_remediation/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/asset_inventory_feed_remediation/fixture/variables.tf b/tests/cloud_operations/asset_inventory_feed_remediation/fixture/variables.tf index d071e308..3d884c25 100644 --- a/tests/cloud_operations/asset_inventory_feed_remediation/fixture/variables.tf +++ b/tests/cloud_operations/asset_inventory_feed_remediation/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/asset_inventory_feed_remediation/test_plan.py b/tests/cloud_operations/asset_inventory_feed_remediation/test_plan.py index 693ddd37..ac90b5de 100644 --- a/tests/cloud_operations/asset_inventory_feed_remediation/test_plan.py +++ b/tests/cloud_operations/asset_inventory_feed_remediation/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/dns_fine_grained_iam/__init__.py b/tests/cloud_operations/dns_fine_grained_iam/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/cloud_operations/dns_fine_grained_iam/__init__.py +++ b/tests/cloud_operations/dns_fine_grained_iam/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/dns_fine_grained_iam/fixture/main.tf b/tests/cloud_operations/dns_fine_grained_iam/fixture/main.tf index 58eb2305..2ca8a398 100644 --- a/tests/cloud_operations/dns_fine_grained_iam/fixture/main.tf +++ b/tests/cloud_operations/dns_fine_grained_iam/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/dns_fine_grained_iam/fixture/variables.tf b/tests/cloud_operations/dns_fine_grained_iam/fixture/variables.tf index 25a51830..4f92d75d 100644 --- a/tests/cloud_operations/dns_fine_grained_iam/fixture/variables.tf +++ b/tests/cloud_operations/dns_fine_grained_iam/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/dns_fine_grained_iam/test_plan.py b/tests/cloud_operations/dns_fine_grained_iam/test_plan.py index 6ce318bb..5e22663f 100644 --- a/tests/cloud_operations/dns_fine_grained_iam/test_plan.py +++ b/tests/cloud_operations/dns_fine_grained_iam/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/dns_shared_vpc/__init__.py b/tests/cloud_operations/dns_shared_vpc/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/cloud_operations/dns_shared_vpc/__init__.py +++ b/tests/cloud_operations/dns_shared_vpc/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/dns_shared_vpc/fixture/main.tf b/tests/cloud_operations/dns_shared_vpc/fixture/main.tf index 52fd490c..b9160511 100644 --- a/tests/cloud_operations/dns_shared_vpc/fixture/main.tf +++ b/tests/cloud_operations/dns_shared_vpc/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/dns_shared_vpc/fixture/variables.tf b/tests/cloud_operations/dns_shared_vpc/fixture/variables.tf index e813a8b0..c6eeb83e 100644 --- a/tests/cloud_operations/dns_shared_vpc/fixture/variables.tf +++ b/tests/cloud_operations/dns_shared_vpc/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/dns_shared_vpc/test_plan.py b/tests/cloud_operations/dns_shared_vpc/test_plan.py index 74609e3d..38caca21 100644 --- a/tests/cloud_operations/dns_shared_vpc/test_plan.py +++ b/tests/cloud_operations/dns_shared_vpc/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/iam_delegated_role_grants/__init__.py b/tests/cloud_operations/iam_delegated_role_grants/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/cloud_operations/iam_delegated_role_grants/__init__.py +++ b/tests/cloud_operations/iam_delegated_role_grants/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/iam_delegated_role_grants/fixture/main.tf b/tests/cloud_operations/iam_delegated_role_grants/fixture/main.tf index 78b313e8..44fa4510 100644 --- a/tests/cloud_operations/iam_delegated_role_grants/fixture/main.tf +++ b/tests/cloud_operations/iam_delegated_role_grants/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/iam_delegated_role_grants/fixture/variables.tf b/tests/cloud_operations/iam_delegated_role_grants/fixture/variables.tf index da4bff27..626af011 100644 --- a/tests/cloud_operations/iam_delegated_role_grants/fixture/variables.tf +++ b/tests/cloud_operations/iam_delegated_role_grants/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/iam_delegated_role_grants/test_plan.py b/tests/cloud_operations/iam_delegated_role_grants/test_plan.py index e6c860e8..e8a070fc 100644 --- a/tests/cloud_operations/iam_delegated_role_grants/test_plan.py +++ b/tests/cloud_operations/iam_delegated_role_grants/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,7 +22,8 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixture") def test_resources(e2e_plan_runner): "Test that plan works and the numbers of resources is as expected." - modules, resources = e2e_plan_runner(FIXTURES_DIR, include_bare_resources=True) + modules, resources = e2e_plan_runner( + FIXTURES_DIR, include_bare_resources=True) assert len(modules) == 1 assert len(resources) == 4 diff --git a/tests/cloud_operations/onprem_sa_key_management/__init__.py b/tests/cloud_operations/onprem_sa_key_management/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/cloud_operations/onprem_sa_key_management/__init__.py +++ b/tests/cloud_operations/onprem_sa_key_management/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/onprem_sa_key_management/fixture/main.tf b/tests/cloud_operations/onprem_sa_key_management/fixture/main.tf index 1f609552..a6469b23 100644 --- a/tests/cloud_operations/onprem_sa_key_management/fixture/main.tf +++ b/tests/cloud_operations/onprem_sa_key_management/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/onprem_sa_key_management/fixture/variables.tf b/tests/cloud_operations/onprem_sa_key_management/fixture/variables.tf index 7014ccb5..ab2117be 100644 --- a/tests/cloud_operations/onprem_sa_key_management/fixture/variables.tf +++ b/tests/cloud_operations/onprem_sa_key_management/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/onprem_sa_key_management/test_plan.py b/tests/cloud_operations/onprem_sa_key_management/test_plan.py index 7de84db5..13ece55a 100644 --- a/tests/cloud_operations/onprem_sa_key_management/test_plan.py +++ b/tests/cloud_operations/onprem_sa_key_management/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/packer_image_builder/__init__.py b/tests/cloud_operations/packer_image_builder/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/cloud_operations/packer_image_builder/__init__.py +++ b/tests/cloud_operations/packer_image_builder/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/packer_image_builder/fixture/main.tf b/tests/cloud_operations/packer_image_builder/fixture/main.tf index 261411ab..5b7e81e1 100644 --- a/tests/cloud_operations/packer_image_builder/fixture/main.tf +++ b/tests/cloud_operations/packer_image_builder/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/packer_image_builder/fixture/variables.tf b/tests/cloud_operations/packer_image_builder/fixture/variables.tf index 37ef4ce7..5320fb69 100644 --- a/tests/cloud_operations/packer_image_builder/fixture/variables.tf +++ b/tests/cloud_operations/packer_image_builder/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/packer_image_builder/test_plan.py b/tests/cloud_operations/packer_image_builder/test_plan.py index f8323f4d..765456c6 100644 --- a/tests/cloud_operations/packer_image_builder/test_plan.py +++ b/tests/cloud_operations/packer_image_builder/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,11 +22,13 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixture") def test_resources(e2e_plan_runner): "Test that plan works and the numbers of resources is as expected." - modules, resources = e2e_plan_runner(FIXTURES_DIR, include_bare_resources="true") + modules, resources = e2e_plan_runner( + FIXTURES_DIR, include_bare_resources="true") assert len(modules) == 6 assert len(resources) == 16 - modules, resources = e2e_plan_runner(FIXTURES_DIR, include_bare_resources="true", create_packer_vars="true") + modules, resources = e2e_plan_runner( + FIXTURES_DIR, include_bare_resources="true", create_packer_vars="true") assert len(modules) == 6 assert len(resources) == 17 diff --git a/tests/cloud_operations/quota_monitoring/__init__.py b/tests/cloud_operations/quota_monitoring/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/cloud_operations/quota_monitoring/__init__.py +++ b/tests/cloud_operations/quota_monitoring/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/quota_monitoring/fixture/main.tf b/tests/cloud_operations/quota_monitoring/fixture/main.tf index d62e0bae..6b5f89ea 100644 --- a/tests/cloud_operations/quota_monitoring/fixture/main.tf +++ b/tests/cloud_operations/quota_monitoring/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/quota_monitoring/fixture/variables.tf b/tests/cloud_operations/quota_monitoring/fixture/variables.tf index 25a51830..4f92d75d 100644 --- a/tests/cloud_operations/quota_monitoring/fixture/variables.tf +++ b/tests/cloud_operations/quota_monitoring/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/quota_monitoring/test_plan.py b/tests/cloud_operations/quota_monitoring/test_plan.py index 134970fc..46993ff3 100644 --- a/tests/cloud_operations/quota_monitoring/test_plan.py +++ b/tests/cloud_operations/quota_monitoring/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/__init__.py b/tests/cloud_operations/scheduled_asset_inventory_export_bq/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/__init__.py +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf index f7bd01af..3b72369d 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf index d80431e3..fb30df60 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py b/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py index 484496a5..3d0299ab 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/conftest.py b/tests/conftest.py index ad8cc5ae..f1e8e760 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -57,7 +57,8 @@ def e2e_plan_runner(_plan_runner): def run_plan(fixture_path, targets=None, refresh=True, include_bare_resources=False, **tf_vars): "Runs Terraform plan on an end-to-end module using defaults, returns data." - plan = _plan_runner(fixture_path, targets=targets, refresh=refresh, **tf_vars) + plan = _plan_runner(fixture_path, targets=targets, + refresh=refresh, **tf_vars) # skip the fixture root_module = plan.root_module['child_modules'][0] modules = dict((mod['address'], mod['resources']) diff --git a/tests/data_solutions/cmek_via_centralized_kms/__init__.py b/tests/data_solutions/cmek_via_centralized_kms/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/data_solutions/cmek_via_centralized_kms/__init__.py +++ b/tests/data_solutions/cmek_via_centralized_kms/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/cmek_via_centralized_kms/fixture/main.tf b/tests/data_solutions/cmek_via_centralized_kms/fixture/main.tf index 03f175c2..bf0afe19 100644 --- a/tests/data_solutions/cmek_via_centralized_kms/fixture/main.tf +++ b/tests/data_solutions/cmek_via_centralized_kms/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/cmek_via_centralized_kms/fixture/variables.tf b/tests/data_solutions/cmek_via_centralized_kms/fixture/variables.tf index 499e1e46..6a534739 100644 --- a/tests/data_solutions/cmek_via_centralized_kms/fixture/variables.tf +++ b/tests/data_solutions/cmek_via_centralized_kms/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/cmek_via_centralized_kms/test_plan.py b/tests/data_solutions/cmek_via_centralized_kms/test_plan.py index d52e85de..665f28cb 100644 --- a/tests/data_solutions/cmek_via_centralized_kms/test_plan.py +++ b/tests/data_solutions/cmek_via_centralized_kms/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/data_platform_foundations/__init__.py b/tests/data_solutions/data_platform_foundations/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/data_solutions/data_platform_foundations/__init__.py +++ b/tests/data_solutions/data_platform_foundations/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/data_platform_foundations/fixture/main.tf b/tests/data_solutions/data_platform_foundations/fixture/main.tf index 66de8aca..b73101f5 100644 --- a/tests/data_solutions/data_platform_foundations/fixture/main.tf +++ b/tests/data_solutions/data_platform_foundations/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/data_platform_foundations/fixture/variables.tf b/tests/data_solutions/data_platform_foundations/fixture/variables.tf index 499e1e46..6a534739 100644 --- a/tests/data_solutions/data_platform_foundations/fixture/variables.tf +++ b/tests/data_solutions/data_platform_foundations/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/data_platform_foundations/test_plan.py b/tests/data_solutions/data_platform_foundations/test_plan.py index 157c9f03..af0130b2 100644 --- a/tests/data_solutions/data_platform_foundations/test_plan.py +++ b/tests/data_solutions/data_platform_foundations/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/gcs_to_bq_with_dataflow/__init__.py b/tests/data_solutions/gcs_to_bq_with_dataflow/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/data_solutions/gcs_to_bq_with_dataflow/__init__.py +++ b/tests/data_solutions/gcs_to_bq_with_dataflow/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/gcs_to_bq_with_dataflow/fixture/main.tf b/tests/data_solutions/gcs_to_bq_with_dataflow/fixture/main.tf index 9d73be40..e9818661 100644 --- a/tests/data_solutions/gcs_to_bq_with_dataflow/fixture/main.tf +++ b/tests/data_solutions/gcs_to_bq_with_dataflow/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/gcs_to_bq_with_dataflow/fixture/variables.tf b/tests/data_solutions/gcs_to_bq_with_dataflow/fixture/variables.tf index 8c3b0169..53157297 100644 --- a/tests/data_solutions/gcs_to_bq_with_dataflow/fixture/variables.tf +++ b/tests/data_solutions/gcs_to_bq_with_dataflow/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/data_solutions/gcs_to_bq_with_dataflow/test_plan.py b/tests/data_solutions/gcs_to_bq_with_dataflow/test_plan.py index 0ef9c865..18d272c8 100644 --- a/tests/data_solutions/gcs_to_bq_with_dataflow/test_plan.py +++ b/tests/data_solutions/gcs_to_bq_with_dataflow/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/examples/conftest.py b/tests/examples/conftest.py index dee8fefb..a2fb5e4d 100644 --- a/tests/examples/conftest.py +++ b/tests/examples/conftest.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,15 +23,16 @@ MODULES_PATH = Path(__file__).parents[2] / 'modules/' def pytest_generate_tests(metafunc): if 'example' in metafunc.fixturenames: modules = [ - x for x in MODULES_PATH.iterdir() - if x.is_dir() + 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 + if not readme.exists(): + continue doc = marko.parse(readme.read_text()) index = 0 for child in doc.children: diff --git a/tests/examples/test_plan.py b/tests/examples/test_plan.py index 7ea19759..a7d6f1f3 100644 --- a/tests/examples/test_plan.py +++ b/tests/examples/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/examples/variables.tf b/tests/examples/variables.tf index 0f13eaba..38fb3db3 100644 --- a/tests/examples/variables.tf +++ b/tests/examples/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/factories/__init__.py b/tests/factories/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/factories/__init__.py +++ b/tests/factories/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/factories/firewall_hierarchical_policies/__init__.py b/tests/factories/firewall_hierarchical_policies/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/factories/firewall_hierarchical_policies/__init__.py +++ b/tests/factories/firewall_hierarchical_policies/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/factories/firewall_hierarchical_policies/fixture/main.tf b/tests/factories/firewall_hierarchical_policies/fixture/main.tf index 9e171641..ffa485c2 100644 --- a/tests/factories/firewall_hierarchical_policies/fixture/main.tf +++ b/tests/factories/firewall_hierarchical_policies/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/factories/firewall_hierarchical_policies/test_plan.py b/tests/factories/firewall_hierarchical_policies/test_plan.py index aef8ec1d..77cf7036 100644 --- a/tests/factories/firewall_hierarchical_policies/test_plan.py +++ b/tests/factories/firewall_hierarchical_policies/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/factories/subnets/__init__.py b/tests/factories/subnets/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/factories/subnets/__init__.py +++ b/tests/factories/subnets/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/factories/subnets/fixture/main.tf b/tests/factories/subnets/fixture/main.tf index 815bac66..f59530c6 100644 --- a/tests/factories/subnets/fixture/main.tf +++ b/tests/factories/subnets/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ module "subnets" { source = "../../../../factories/subnets" config_folder = "conf" -} \ No newline at end of file +} diff --git a/tests/factories/subnets/test_plan.py b/tests/factories/subnets/test_plan.py index 0020964b..e49710cd 100644 --- a/tests/factories/subnets/test_plan.py +++ b/tests/factories/subnets/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/factories/vpc_firewall/__init__.py b/tests/factories/vpc_firewall/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/factories/vpc_firewall/__init__.py +++ b/tests/factories/vpc_firewall/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/factories/vpc_firewall/flat/__init__.py b/tests/factories/vpc_firewall/flat/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/factories/vpc_firewall/flat/__init__.py +++ b/tests/factories/vpc_firewall/flat/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/factories/vpc_firewall/flat/fixture/main.tf b/tests/factories/vpc_firewall/flat/fixture/main.tf index 2fca3a87..4d20edd3 100644 --- a/tests/factories/vpc_firewall/flat/fixture/main.tf +++ b/tests/factories/vpc_firewall/flat/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/factories/vpc_firewall/flat/fixture/rules/common.yaml b/tests/factories/vpc_firewall/flat/fixture/rules/common.yaml index 829556ec..cbe8466f 100644 --- a/tests/factories/vpc_firewall/flat/fixture/rules/common.yaml +++ b/tests/factories/vpc_firewall/flat/fixture/rules/common.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,24 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. - # allow ingress from GCLB to all instances in the network lb-health-checks: allow: - - ports: [] - protocol: tcp + - ports: [] + protocol: tcp direction: INGRESS priority: 1001 source_ranges: - - 35.191.0.0/16 - - 130.211.0.0/22 + - 35.191.0.0/16 + - 130.211.0.0/22 # deny all egress deny-all: deny: - - ports: [] - protocol: all + - ports: [] + protocol: all direction: EGRESS priority: 65535 destination_ranges: - - 0.0.0.0/0 + - 0.0.0.0/0 diff --git a/tests/factories/vpc_firewall/flat/fixture/variables.tf b/tests/factories/vpc_firewall/flat/fixture/variables.tf index 690f2878..018289fe 100644 --- a/tests/factories/vpc_firewall/flat/fixture/variables.tf +++ b/tests/factories/vpc_firewall/flat/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/factories/vpc_firewall/flat/test_plan.py b/tests/factories/vpc_firewall/flat/test_plan.py index 684a323e..f4b9655f 100644 --- a/tests/factories/vpc_firewall/flat/test_plan.py +++ b/tests/factories/vpc_firewall/flat/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,30 +21,30 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') def test_firewall_simple(plan_runner): - "Test firewall rules from rules/common.yaml with no extra options." - _, resources = plan_runner(FIXTURES_DIR) - assert len(resources) == 4 - assert set(r['type'] for r in resources) == set([ - 'google_compute_firewall', 'time_static' - ]) - firewall_values = [r['values'] for r in resources if r['type'] - == 'google_compute_firewall'] - assert set([f['project'] for f in firewall_values]) == set(['my-project']) - assert set([f['network'] for f in firewall_values]) == set(['my-network']) + "Test firewall rules from rules/common.yaml with no extra options." + _, resources = plan_runner(FIXTURES_DIR) + assert len(resources) == 4 + assert set(r['type'] for r in resources) == set([ + 'google_compute_firewall', 'time_static' + ]) + firewall_values = [r['values'] for r in resources if r['type'] + == 'google_compute_firewall'] + assert set([f['project'] for f in firewall_values]) == set(['my-project']) + assert set([f['network'] for f in firewall_values]) == set(['my-network']) def test_firewall_log_config(plan_runner): - "Test firewall rules log configuration." - log_config = """ { + "Test firewall rules log configuration." + log_config = """ { metadata = "INCLUDE_ALL_METADATA" } """ - log_config_value = [{"metadata": "INCLUDE_ALL_METADATA"}] - _, resources = plan_runner(FIXTURES_DIR, log_config=log_config) - assert len(resources) == 4 - assert set(r['type'] for r in resources) == set([ - 'google_compute_firewall', 'time_static' - ]) - firewall_values = [r['values'] for r in resources if r['type'] - == 'google_compute_firewall'] - assert all(f['log_config'] == log_config_value for f in firewall_values) + log_config_value = [{"metadata": "INCLUDE_ALL_METADATA"}] + _, resources = plan_runner(FIXTURES_DIR, log_config=log_config) + assert len(resources) == 4 + assert set(r['type'] for r in resources) == set([ + 'google_compute_firewall', 'time_static' + ]) + firewall_values = [r['values'] for r in resources if r['type'] + == 'google_compute_firewall'] + assert all(f['log_config'] == log_config_value for f in firewall_values) diff --git a/tests/factories/vpc_firewall/nested/__init__.py b/tests/factories/vpc_firewall/nested/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/factories/vpc_firewall/nested/__init__.py +++ b/tests/factories/vpc_firewall/nested/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/factories/vpc_firewall/nested/fixture/main.tf b/tests/factories/vpc_firewall/nested/fixture/main.tf index d50ebb79..f4a2a7dc 100644 --- a/tests/factories/vpc_firewall/nested/fixture/main.tf +++ b/tests/factories/vpc_firewall/nested/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/factories/vpc_firewall/nested/test_plan.py b/tests/factories/vpc_firewall/nested/test_plan.py index 01cee1b7..bfb51f2b 100644 --- a/tests/factories/vpc_firewall/nested/test_plan.py +++ b/tests/factories/vpc_firewall/nested/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/foundations/__init__.py b/tests/foundations/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/foundations/__init__.py +++ b/tests/foundations/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/foundations/business_units/__init__.py b/tests/foundations/business_units/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/foundations/business_units/__init__.py +++ b/tests/foundations/business_units/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/foundations/business_units/fixture/main.tf b/tests/foundations/business_units/fixture/main.tf index 2cf33229..252759ea 100644 --- a/tests/foundations/business_units/fixture/main.tf +++ b/tests/foundations/business_units/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/foundations/business_units/fixture/variables.tf b/tests/foundations/business_units/fixture/variables.tf index db4dbe86..a8997595 100644 --- a/tests/foundations/business_units/fixture/variables.tf +++ b/tests/foundations/business_units/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/foundations/business_units/test_plan.py b/tests/foundations/business_units/test_plan.py index 97c118cf..7fce7e71 100644 --- a/tests/foundations/business_units/test_plan.py +++ b/tests/foundations/business_units/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/foundations/environments/__init__.py b/tests/foundations/environments/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/foundations/environments/__init__.py +++ b/tests/foundations/environments/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/foundations/environments/fixture/main.tf b/tests/foundations/environments/fixture/main.tf index 428a8b53..51fd8dfa 100644 --- a/tests/foundations/environments/fixture/main.tf +++ b/tests/foundations/environments/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/foundations/environments/fixture/variables.tf b/tests/foundations/environments/fixture/variables.tf index f17010e1..48ce5fde 100644 --- a/tests/foundations/environments/fixture/variables.tf +++ b/tests/foundations/environments/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/foundations/environments/test_plan.py b/tests/foundations/environments/test_plan.py index 86470834..ef2f37aa 100644 --- a/tests/foundations/environments/test_plan.py +++ b/tests/foundations/environments/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/__init__.py b/tests/modules/__init__.py index bb2436ab..6d6d1266 100644 --- a/tests/modules/__init__.py +++ b/tests/modules/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,5 +11,3 @@ # 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/apigee_organization/__init__.py b/tests/modules/apigee_organization/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/apigee_organization/__init__.py +++ b/tests/modules/apigee_organization/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/apigee_organization/fixture/main.tf b/tests/modules/apigee_organization/fixture/main.tf index e3b60b6d..49ad78b1 100644 --- a/tests/modules/apigee_organization/fixture/main.tf +++ b/tests/modules/apigee_organization/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/apigee_organization/fixture/variables.tf b/tests/modules/apigee_organization/fixture/variables.tf index 3e910934..50e68089 100644 --- a/tests/modules/apigee_organization/fixture/variables.tf +++ b/tests/modules/apigee_organization/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,4 +22,4 @@ variable "analytics_region" { variable "network" { type = string default = "apigee-vpc" -} \ No newline at end of file +} diff --git a/tests/modules/apigee_organization/test_plan.py b/tests/modules/apigee_organization/test_plan.py index 680d3cab..95a34db3 100644 --- a/tests/modules/apigee_organization/test_plan.py +++ b/tests/modules/apigee_organization/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ def test_resource_count(resources): def test_envgroup_attachment(resources): "Test Apigee Envgroup Attachments." attachments = [r['values'] for r in resources if r['type'] - == 'google_apigee_envgroup_attachment'] + == 'google_apigee_envgroup_attachment'] assert len(attachments) == 2 assert set(a['environment'] for a in attachments) == set(['eval1', 'eval2']) @@ -42,7 +42,7 @@ def test_envgroup_attachment(resources): def test_envgroup(resources): "Test env group." envgroups = [r['values'] for r in resources if r['type'] - == 'google_apigee_envgroup'] + == 'google_apigee_envgroup'] assert len(envgroups) == 1 assert envgroups[0]['name'] == 'eval' assert len(envgroups[0]['hostnames']) == 1 diff --git a/tests/modules/apigee_x_instance/__init__.py b/tests/modules/apigee_x_instance/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/apigee_x_instance/__init__.py +++ b/tests/modules/apigee_x_instance/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/apigee_x_instance/fixture/main.tf b/tests/modules/apigee_x_instance/fixture/main.tf index 9915ef20..a7c8c9a0 100644 --- a/tests/modules/apigee_x_instance/fixture/main.tf +++ b/tests/modules/apigee_x_instance/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,4 +25,4 @@ module "apigee-x-instance" { "eval1", "eval2" ] -} \ No newline at end of file +} diff --git a/tests/modules/apigee_x_instance/fixture/variables.tf b/tests/modules/apigee_x_instance/fixture/variables.tf index 603ec508..4fc58d62 100644 --- a/tests/modules/apigee_x_instance/fixture/variables.tf +++ b/tests/modules/apigee_x_instance/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,4 +22,4 @@ variable "name" { variable "region" { type = string default = "europe-west1" -} \ No newline at end of file +} diff --git a/tests/modules/apigee_x_instance/test_plan.py b/tests/modules/apigee_x_instance/test_plan.py index 4b3a9256..a499d72e 100644 --- a/tests/modules/apigee_x_instance/test_plan.py +++ b/tests/modules/apigee_x_instance/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ def test_resource_count(resources): def test_instance_attachment(resources): "Test Apigee Instance Attachments." attachments = [r['values'] for r in resources if r['type'] - == 'google_apigee_instance_attachment'] + == 'google_apigee_instance_attachment'] assert len(attachments) == 2 assert set(a['environment'] for a in attachments) == set(['eval1', 'eval2']) @@ -42,9 +42,8 @@ def test_instance_attachment(resources): def test_instance(resources): "Test Instance." instances = [r['values'] for r in resources if r['type'] - == 'google_apigee_instance'] + == 'google_apigee_instance'] assert len(instances) == 1 assert instances[0]['peering_cidr_range'] == 'SLASH_22' assert instances[0]['name'] == 'my-test-instance' assert instances[0]['location'] == 'europe-west1' - diff --git a/tests/modules/bigquery_dataset/__init__.py b/tests/modules/bigquery_dataset/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/bigquery_dataset/__init__.py +++ b/tests/modules/bigquery_dataset/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/bigquery_dataset/fixture/main.tf b/tests/modules/bigquery_dataset/fixture/main.tf index edb4ee3c..a3314118 100644 --- a/tests/modules/bigquery_dataset/fixture/main.tf +++ b/tests/modules/bigquery_dataset/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/bigquery_dataset/test_plan.py b/tests/modules/bigquery_dataset/test_plan.py index bd0db209..ce02715d 100644 --- a/tests/modules/bigquery_dataset/test_plan.py +++ b/tests/modules/bigquery_dataset/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/bigtable_instance/__init__.py b/tests/modules/bigtable_instance/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/bigtable_instance/__init__.py +++ b/tests/modules/bigtable_instance/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/bigtable_instance/fixture/main.tf b/tests/modules/bigtable_instance/fixture/main.tf index 7e746bf3..fa74a6c8 100644 --- a/tests/modules/bigtable_instance/fixture/main.tf +++ b/tests/modules/bigtable_instance/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/bigtable_instance/fixture/variables.tf b/tests/modules/bigtable_instance/fixture/variables.tf index 4573745f..c8a4b5da 100644 --- a/tests/modules/bigtable_instance/fixture/variables.tf +++ b/tests/modules/bigtable_instance/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/bigtable_instance/test_plan.py b/tests/modules/bigtable_instance/test_plan.py index d296f4c9..557eadd2 100644 --- a/tests/modules/bigtable_instance/test_plan.py +++ b/tests/modules/bigtable_instance/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/billing_budget/__init__.py b/tests/modules/billing_budget/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/billing_budget/__init__.py +++ b/tests/modules/billing_budget/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/billing_budget/fixture/main.tf b/tests/modules/billing_budget/fixture/main.tf index 91c05e4a..25cd25f5 100644 --- a/tests/modules/billing_budget/fixture/main.tf +++ b/tests/modules/billing_budget/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/billing_budget/fixture/variables.tf b/tests/modules/billing_budget/fixture/variables.tf index 6eb8e4e3..0cbcb447 100644 --- a/tests/modules/billing_budget/fixture/variables.tf +++ b/tests/modules/billing_budget/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/billing_budget/test_plan.py b/tests/modules/billing_budget/test_plan.py index 5692bf0a..ddb848e7 100644 --- a/tests/modules/billing_budget/test_plan.py +++ b/tests/modules/billing_budget/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,23 +26,25 @@ def test_pubsub(plan_runner): assert len(resources) == 1 resource = resources[0] assert resource['values']['all_updates_rule'] == [ - {'disable_default_iam_recipients': False, - 'monitoring_notification_channels': [], - 'pubsub_topic': 'topic', - 'schema_version': '1.0'} + {'disable_default_iam_recipients': False, + 'monitoring_notification_channels': [], + 'pubsub_topic': 'topic', + 'schema_version': '1.0'} ] + def test_channel(plan_runner): _, resources = plan_runner(FIXTURES_DIR, notification_channels='["channel"]') assert len(resources) == 1 resource = resources[0] assert resource['values']['all_updates_rule'] == [ - {'disable_default_iam_recipients': True, - 'monitoring_notification_channels': ['channel'], - 'pubsub_topic': None, - 'schema_version': '1.0'} + {'disable_default_iam_recipients': True, + 'monitoring_notification_channels': ['channel'], + 'pubsub_topic': None, + 'schema_version': '1.0'} ] + def test_emails(plan_runner): email_recipients = '{project_id = "project", emails = ["a@b.com", "c@d.com"]}' _, resources = plan_runner(FIXTURES_DIR, email_recipients=email_recipients) @@ -59,11 +61,11 @@ def test_absolute_amount(plan_runner): assert amount['last_period_amount'] is None assert amount['specified_amount'] == [{'nanos': None, 'units': '100'}] - assert resource['values']['threshold_rules'] == [ - {'spend_basis': 'CURRENT_SPEND', - 'threshold_percent': 0.5}, - {'spend_basis': 'CURRENT_SPEND', - 'threshold_percent': 1}, - {'spend_basis': 'FORECASTED_SPEND', - 'threshold_percent': 1} + assert resource['values']['threshold_rules'] == [ + {'spend_basis': 'CURRENT_SPEND', + 'threshold_percent': 0.5}, + {'spend_basis': 'CURRENT_SPEND', + 'threshold_percent': 1}, + {'spend_basis': 'FORECASTED_SPEND', + 'threshold_percent': 1} ] diff --git a/tests/modules/cloud_config_container_coredns/__init__.py b/tests/modules/cloud_config_container_coredns/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/cloud_config_container_coredns/__init__.py +++ b/tests/modules/cloud_config_container_coredns/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_config_container_coredns/fixture/main.tf b/tests/modules/cloud_config_container_coredns/fixture/main.tf index 8b1bd352..69a92678 100644 --- a/tests/modules/cloud_config_container_coredns/fixture/main.tf +++ b/tests/modules/cloud_config_container_coredns/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_config_container_coredns/fixture/outputs.tf b/tests/modules/cloud_config_container_coredns/fixture/outputs.tf index 103987dc..6aa222fd 100644 --- a/tests/modules/cloud_config_container_coredns/fixture/outputs.tf +++ b/tests/modules/cloud_config_container_coredns/fixture/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_config_container_coredns/fixture/variables.tf b/tests/modules/cloud_config_container_coredns/fixture/variables.tf index 091e27d5..b8b2e27b 100644 --- a/tests/modules/cloud_config_container_coredns/fixture/variables.tf +++ b/tests/modules/cloud_config_container_coredns/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_config_container_coredns/test_apply.py b/tests/modules/cloud_config_container_coredns/test_apply.py index b4f01a9d..d1447bb9 100644 --- a/tests/modules/cloud_config_container_coredns/test_apply.py +++ b/tests/modules/cloud_config_container_coredns/test_apply.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_config_container_mysql/__init__.py b/tests/modules/cloud_config_container_mysql/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/cloud_config_container_mysql/__init__.py +++ b/tests/modules/cloud_config_container_mysql/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_config_container_mysql/fixture/main.tf b/tests/modules/cloud_config_container_mysql/fixture/main.tf index 347e0ebc..41528d0c 100644 --- a/tests/modules/cloud_config_container_mysql/fixture/main.tf +++ b/tests/modules/cloud_config_container_mysql/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_config_container_mysql/fixture/outputs.tf b/tests/modules/cloud_config_container_mysql/fixture/outputs.tf index 103987dc..6aa222fd 100644 --- a/tests/modules/cloud_config_container_mysql/fixture/outputs.tf +++ b/tests/modules/cloud_config_container_mysql/fixture/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_config_container_mysql/fixture/variables.tf b/tests/modules/cloud_config_container_mysql/fixture/variables.tf index 2ada8a5e..8f78592a 100644 --- a/tests/modules/cloud_config_container_mysql/fixture/variables.tf +++ b/tests/modules/cloud_config_container_mysql/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_config_container_mysql/test_apply.py b/tests/modules/cloud_config_container_mysql/test_apply.py index 23c80108..302438a4 100644 --- a/tests/modules/cloud_config_container_mysql/test_apply.py +++ b/tests/modules/cloud_config_container_mysql/test_apply.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_function/__init__.py b/tests/modules/cloud_function/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/cloud_function/__init__.py +++ b/tests/modules/cloud_function/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_function/fixture/bundle/main.py b/tests/modules/cloud_function/fixture/bundle/main.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/cloud_function/fixture/bundle/main.py +++ b/tests/modules/cloud_function/fixture/bundle/main.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_function/fixture/main.tf b/tests/modules/cloud_function/fixture/main.tf index e94a9f63..a6fc7278 100644 --- a/tests/modules/cloud_function/fixture/main.tf +++ b/tests/modules/cloud_function/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_function/fixture/variables.tf b/tests/modules/cloud_function/fixture/variables.tf index 3223c162..60084085 100644 --- a/tests/modules/cloud_function/fixture/variables.tf +++ b/tests/modules/cloud_function/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_function/test_plan.py b/tests/modules/cloud_function/test_plan.py index a6990172..ffd1b601 100644 --- a/tests/modules/cloud_function/test_plan.py +++ b/tests/modules/cloud_function/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_identity_group/__init__.py b/tests/modules/cloud_identity_group/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/cloud_identity_group/__init__.py +++ b/tests/modules/cloud_identity_group/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_identity_group/fixture/main.tf b/tests/modules/cloud_identity_group/fixture/main.tf index 7d43ae14..a4058573 100644 --- a/tests/modules/cloud_identity_group/fixture/main.tf +++ b/tests/modules/cloud_identity_group/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_identity_group/fixture/variables.tf b/tests/modules/cloud_identity_group/fixture/variables.tf index 20c8e273..936282d8 100644 --- a/tests/modules/cloud_identity_group/fixture/variables.tf +++ b/tests/modules/cloud_identity_group/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_identity_group/test_plan.py b/tests/modules/cloud_identity_group/test_plan.py index e2b5a20c..6d7d9823 100644 --- a/tests/modules/cloud_identity_group/test_plan.py +++ b/tests/modules/cloud_identity_group/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -40,8 +40,8 @@ def test_members(plan_runner): resource_types = Counter([r['type'] for r in resources]) assert resource_types == { - 'google_cloud_identity_group': 1, - 'google_cloud_identity_group_membership': 1, + 'google_cloud_identity_group': 1, + 'google_cloud_identity_group_membership': 1, } values = next(r['values'] for r in resources if r['name'] == 'members') diff --git a/tests/modules/cloud_run/__init__.py b/tests/modules/cloud_run/__init__.py index bb2436ab..6d6d1266 100644 --- a/tests/modules/cloud_run/__init__.py +++ b/tests/modules/cloud_run/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,5 +11,3 @@ # 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/cloud_run/fixture/bundle/main.py b/tests/modules/cloud_run/fixture/bundle/main.py index 0446db3c..6d6d1266 100644 --- a/tests/modules/cloud_run/fixture/bundle/main.py +++ b/tests/modules/cloud_run/fixture/bundle/main.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -10,4 +10,4 @@ # 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. \ No newline at end of file +# limitations under the License. diff --git a/tests/modules/cloud_run/fixture/main.tf b/tests/modules/cloud_run/fixture/main.tf index d0872c22..f4aa8afa 100644 --- a/tests/modules/cloud_run/fixture/main.tf +++ b/tests/modules/cloud_run/fixture/main.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/cloud_run/fixture/variables.tf b/tests/modules/cloud_run/fixture/variables.tf index 0446db3c..6d6d1266 100644 --- a/tests/modules/cloud_run/fixture/variables.tf +++ b/tests/modules/cloud_run/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -10,4 +10,4 @@ # 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. \ No newline at end of file +# limitations under the License. diff --git a/tests/modules/cloud_run/test_plan.py b/tests/modules/cloud_run/test_plan.py index 13cd3ecb..719e6d4c 100644 --- a/tests/modules/cloud_run/test_plan.py +++ b/tests/modules/cloud_run/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ def test_resource_count(resources): "Test number of resources created." assert len(resources) == 5 + def test_iam(resources): "Test IAM binding resources." bindings = [r['values'] for r in resources if r['type'] @@ -37,14 +38,16 @@ def test_iam(resources): assert len(bindings) == 1 assert bindings[0]['role'] == 'roles/run.invoker' + def test_audit_log_triggers(resources): "Test audit logs Eventarc trigger resources." audit_log_triggers = [r['values'] for r in resources if r['type'] - == 'google_eventarc_trigger' and r['name'] == 'audit_log_triggers'] + == 'google_eventarc_trigger' and r['name'] == 'audit_log_triggers'] assert len(audit_log_triggers) == 1 + def test_pubsub_triggers(resources): "Test Pub/Sub Eventarc trigger resources." pubsub_triggers = [r['values'] for r in resources if r['type'] - == 'google_eventarc_trigger' and r['name'] == 'pubsub_triggers'] + == 'google_eventarc_trigger' and r['name'] == 'pubsub_triggers'] assert len(pubsub_triggers) == 2 diff --git a/tests/modules/cloudsql_instance/__init__.py b/tests/modules/cloudsql_instance/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/cloudsql_instance/__init__.py +++ b/tests/modules/cloudsql_instance/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/cloudsql_instance/fixture/main.tf b/tests/modules/cloudsql_instance/fixture/main.tf index feafb4ce..075ee4f1 100644 --- a/tests/modules/cloudsql_instance/fixture/main.tf +++ b/tests/modules/cloudsql_instance/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloudsql_instance/fixture/variables.tf b/tests/modules/cloudsql_instance/fixture/variables.tf index a3ff8e0f..62ff27a3 100644 --- a/tests/modules/cloudsql_instance/fixture/variables.tf +++ b/tests/modules/cloudsql_instance/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/cloudsql_instance/test_plan.py b/tests/modules/cloudsql_instance/test_plan.py index f58a0625..97c65e00 100644 --- a/tests/modules/cloudsql_instance/test_plan.py +++ b/tests/modules/cloudsql_instance/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/compute_mig/__init__.py b/tests/modules/compute_mig/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/compute_mig/__init__.py +++ b/tests/modules/compute_mig/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/compute_mig/fixture/main.tf b/tests/modules/compute_mig/fixture/main.tf index a18b237e..5d87f40f 100644 --- a/tests/modules/compute_mig/fixture/main.tf +++ b/tests/modules/compute_mig/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/compute_mig/fixture/variables.tf b/tests/modules/compute_mig/fixture/variables.tf index c025665c..b9fde834 100644 --- a/tests/modules/compute_mig/fixture/variables.tf +++ b/tests/modules/compute_mig/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/compute_mig/test_plan.py b/tests/modules/compute_mig/test_plan.py index a6987904..fe7b81b6 100644 --- a/tests/modules/compute_mig/test_plan.py +++ b/tests/modules/compute_mig/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ def test_defaults(plan_runner): assert mig['values']['target_size'] == 2 assert mig['values']['region'] + def test_health_check(plan_runner): "Test health check resource." health_check_config = '{type="tcp", check={port=80}, config=null, logging=false}' @@ -75,19 +76,20 @@ def test_autoscaler(plan_runner): assert len(resources) == 2 autoscaler = resources[0] assert autoscaler['type'] == 'google_compute_region_autoscaler' - + + def test_stateful_mig(plan_runner): "Test stateful instances - mig." stateful_config = ( - '{' + '{' 'per_instance_config = {},' 'mig_config = {' - 'stateful_disks = {' - 'persistent-disk-1 = {delete_rule="NEVER"}' - '}' + 'stateful_disks = {' + 'persistent-disk-1 = {delete_rule="NEVER"}' + '}' + '}' '}' - '}' ) _, resources = plan_runner( FIXTURES_DIR, stateful_config=stateful_config) @@ -98,37 +100,38 @@ def test_stateful_mig(plan_runner): 'device_name': 'persistent-disk-1', 'delete_rule': 'NEVER', }] - + + def test_stateful_instance(plan_runner): "Test stateful instances - instance." stateful_config = ( - '{' + '{' 'per_instance_config = {' - 'instance-1 = {' - 'stateful_disks = {' - 'persistent-disk-1 = {' - 'source = "test-disk",' - 'mode = "READ_ONLY",' - 'delete_rule= "NEVER",' - '},' - '},' - 'metadata = {' - 'foo = "bar"' - '},' - 'update_config = {' - 'minimal_action = "NONE",' - 'most_disruptive_allowed_action = "REPLACE",' - 'remove_instance_state_on_destroy = false,' - - '},' - '},' + 'instance-1 = {' + 'stateful_disks = {' + 'persistent-disk-1 = {' + 'source = "test-disk",' + 'mode = "READ_ONLY",' + 'delete_rule= "NEVER",' + '},' + '},' + 'metadata = {' + 'foo = "bar"' + '},' + 'update_config = {' + 'minimal_action = "NONE",' + 'most_disruptive_allowed_action = "REPLACE",' + 'remove_instance_state_on_destroy = false,' + + '},' + '},' '},' 'mig_config = {' - 'stateful_disks = {' - 'persistent-disk-1 = {delete_rule="NEVER"}' - '}' + 'stateful_disks = {' + 'persistent-disk-1 = {delete_rule="NEVER"}' + '}' + '}' '}' - '}' ) _, resources = plan_runner( FIXTURES_DIR, stateful_config=stateful_config) @@ -137,19 +140,19 @@ def test_stateful_instance(plan_runner): assert instanceconfig['type'] == 'google_compute_instance_group_manager' instanceconfig = resources[1] assert instanceconfig['type'] == 'google_compute_per_instance_config' - + assert instanceconfig['values']['preserved_state'] == [{ 'disk': [{ - 'device_name': 'persistent-disk-1', - 'delete_rule': 'NEVER', - 'source': 'test-disk', - 'mode': 'READ_ONLY', + 'device_name': 'persistent-disk-1', + 'delete_rule': 'NEVER', + 'source': 'test-disk', + 'mode': 'READ_ONLY', }], 'metadata': { - 'foo': 'bar' + 'foo': 'bar' } }] assert instanceconfig['values']['minimal_action'] == 'NONE' assert instanceconfig['values']['most_disruptive_allowed_action'] == 'REPLACE' - assert instanceconfig['values']['remove_instance_state_on_destroy'] == False \ No newline at end of file + assert instanceconfig['values']['remove_instance_state_on_destroy'] == False diff --git a/tests/modules/compute_vm/__init__.py b/tests/modules/compute_vm/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/compute_vm/__init__.py +++ b/tests/modules/compute_vm/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/compute_vm/fixture/main.tf b/tests/modules/compute_vm/fixture/main.tf index 31903e6b..5815f25f 100644 --- a/tests/modules/compute_vm/fixture/main.tf +++ b/tests/modules/compute_vm/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/compute_vm/fixture/variables.tf b/tests/modules/compute_vm/fixture/variables.tf index cd9cdb47..1c28d2d9 100644 --- a/tests/modules/compute_vm/fixture/variables.tf +++ b/tests/modules/compute_vm/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/compute_vm/test_plan.py b/tests/modules/compute_vm/test_plan.py index 6db0c301..7c4d4237 100644 --- a/tests/modules/compute_vm/test_plan.py +++ b/tests/modules/compute_vm/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/compute_vm/test_plan_disks.py b/tests/modules/compute_vm/test_plan_disks.py index 711d9f09..42d36537 100644 --- a/tests/modules/compute_vm/test_plan_disks.py +++ b/tests/modules/compute_vm/test_plan_disks.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/compute_vm/test_plan_interfaces.py b/tests/modules/compute_vm/test_plan_interfaces.py index 9efdeafe..fed07966 100644 --- a/tests/modules/compute_vm/test_plan_interfaces.py +++ b/tests/modules/compute_vm/test_plan_interfaces.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/container_registry/__init__.py b/tests/modules/container_registry/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/container_registry/__init__.py +++ b/tests/modules/container_registry/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/container_registry/fixture/main.tf b/tests/modules/container_registry/fixture/main.tf index d505e38a..52866b54 100644 --- a/tests/modules/container_registry/fixture/main.tf +++ b/tests/modules/container_registry/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/container_registry/fixture/variables.tf b/tests/modules/container_registry/fixture/variables.tf index 4480e669..c76b0889 100644 --- a/tests/modules/container_registry/fixture/variables.tf +++ b/tests/modules/container_registry/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/container_registry/test_plan.py b/tests/modules/container_registry/test_plan.py index dc5affe0..c428adf7 100644 --- a/tests/modules/container_registry/test_plan.py +++ b/tests/modules/container_registry/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/dns/__init__.py b/tests/modules/dns/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/dns/__init__.py +++ b/tests/modules/dns/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/dns/fixture/main.tf b/tests/modules/dns/fixture/main.tf index 51f8928a..bab31920 100644 --- a/tests/modules/dns/fixture/main.tf +++ b/tests/modules/dns/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/dns/fixture/variables.tf b/tests/modules/dns/fixture/variables.tf index a08a0420..522b238a 100644 --- a/tests/modules/dns/fixture/variables.tf +++ b/tests/modules/dns/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/dns/test_plan.py b/tests/modules/dns/test_plan.py index 682c2e1a..b1bc9f66 100644 --- a/tests/modules/dns/test_plan.py +++ b/tests/modules/dns/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/endpoints/__init__.py b/tests/modules/endpoints/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/endpoints/__init__.py +++ b/tests/modules/endpoints/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/endpoints/fixture/main.tf b/tests/modules/endpoints/fixture/main.tf index 5c1c7736..39694298 100644 --- a/tests/modules/endpoints/fixture/main.tf +++ b/tests/modules/endpoints/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/endpoints/fixture/openapi.yaml b/tests/modules/endpoints/fixture/openapi.yaml index d46dbae5..6d6d1266 100644 --- a/tests/modules/endpoints/fixture/openapi.yaml +++ b/tests/modules/endpoints/fixture/openapi.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/endpoints/fixture/variables.tf b/tests/modules/endpoints/fixture/variables.tf index d323e720..6efe07c3 100644 --- a/tests/modules/endpoints/fixture/variables.tf +++ b/tests/modules/endpoints/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/endpoints/test_plan.py b/tests/modules/endpoints/test_plan.py index 3837e2b9..f504d76d 100644 --- a/tests/modules/endpoints/test_plan.py +++ b/tests/modules/endpoints/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/folder/__init__.py b/tests/modules/folder/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/folder/__init__.py +++ b/tests/modules/folder/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/folder/fixture/main.tf b/tests/modules/folder/fixture/main.tf index d9bd34ba..05fe8105 100644 --- a/tests/modules/folder/fixture/main.tf +++ b/tests/modules/folder/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/folder/fixture/variables.tf b/tests/modules/folder/fixture/variables.tf index 824560d9..5917d7dc 100644 --- a/tests/modules/folder/fixture/variables.tf +++ b/tests/modules/folder/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/folder/test_plan.py b/tests/modules/folder/test_plan.py index e325627c..a38cf151 100644 --- a/tests/modules/folder/test_plan.py +++ b/tests/modules/folder/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/folder/test_plan_firewall_policy.py b/tests/modules/folder/test_plan_firewall_policy.py index 9a7a31b5..5369eecc 100644 --- a/tests/modules/folder/test_plan_firewall_policy.py +++ b/tests/modules/folder/test_plan_firewall_policy.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -60,7 +60,7 @@ def test_firweall_policy(plan_runner): assert len(resources) == 5 policies = [r for r in resources - if r['type'] == 'google_compute_firewall_policy'] + if r['type'] == 'google_compute_firewall_policy'] assert len(policies) == 1 rules = [r for r in resources @@ -78,18 +78,16 @@ def test_firweall_policy(plan_runner): rule_values.append((name, index, action, direction, priority, match)) assert sorted(rule_values) == sorted([ - ('rule', 'policy1-allow-ingress', 'allow', 'INGRESS', 100,[ - { - 'dest_ip_ranges': None, - 'layer4_configs': [{'ip_protocol': 'tcp', 'ports': ['22']}], - 'src_ip_ranges': ['10.0.0.0/8'] - }]), - ('rule', 'policy1-deny-egress', 'deny', 'EGRESS', 200, [ - { - 'dest_ip_ranges': ['192.168.0.0/24'], - 'layer4_configs': [{'ip_protocol': 'tcp', 'ports': ['443']}], - 'src_ip_ranges': None - }]) + ('rule', 'policy1-allow-ingress', 'allow', 'INGRESS', 100, [ + { + 'dest_ip_ranges': None, + 'layer4_configs': [{'ip_protocol': 'tcp', 'ports': ['22']}], + 'src_ip_ranges': ['10.0.0.0/8'] + }]), + ('rule', 'policy1-deny-egress', 'deny', 'EGRESS', 200, [ + { + 'dest_ip_ranges': ['192.168.0.0/24'], + 'layer4_configs': [{'ip_protocol': 'tcp', 'ports': ['443']}], + 'src_ip_ranges': None + }]) ]) - - diff --git a/tests/modules/folder/test_plan_logging.py b/tests/modules/folder/test_plan_logging.py index 9bd7688f..c57364f2 100644 --- a/tests/modules/folder/test_plan_logging.py +++ b/tests/modules/folder/test_plan_logging.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixture") def test_sinks(plan_runner): - "Test folder-level sinks." - logging_sinks = """ { + "Test folder-level sinks." + logging_sinks = """ { warning = { type = "gcs" destination = "mybucket" @@ -61,110 +61,111 @@ def test_sinks(plan_runner): } } """ - _, resources = plan_runner(FIXTURES_DIR, logging_sinks=logging_sinks) - assert len(resources) == 9 + _, resources = plan_runner(FIXTURES_DIR, logging_sinks=logging_sinks) + assert len(resources) == 9 - resource_types = Counter([r["type"] for r in resources]) - assert resource_types == { - "google_logging_folder_sink": 4, - "google_folder": 1, - "google_bigquery_dataset_iam_member": 1, - "google_project_iam_member": 1, - "google_pubsub_topic_iam_member": 1, - "google_storage_bucket_iam_member": 1, - } + resource_types = Counter([r["type"] for r in resources]) + assert resource_types == { + "google_logging_folder_sink": 4, + "google_folder": 1, + "google_bigquery_dataset_iam_member": 1, + "google_project_iam_member": 1, + "google_pubsub_topic_iam_member": 1, + "google_storage_bucket_iam_member": 1, + } - sinks = [r for r in resources if r["type"] == "google_logging_folder_sink"] - assert sorted([r["index"] for r in sinks]) == [ - "debug", - "info", - "notice", - "warning", - ] - values = [ - ( - r["index"], - r["values"]["filter"], - r["values"]["destination"], - r["values"]["include_children"], - ) - for r in sinks - ] - assert sorted(values) == [ - ( - "debug", - "severity=DEBUG", - "logging.googleapis.com/projects/myproject/locations/global/buckets/mybucket", - False, - ), - ( - "info", - "severity=INFO", - "bigquery.googleapis.com/projects/myproject/datasets/mydataset", - True, - ), - ( - "notice", - "severity=NOTICE", - "pubsub.googleapis.com/projects/myproject/topics/mytopic", - False, - ), - ("warning", "severity=WARNING", "storage.googleapis.com/mybucket", True), - ] + sinks = [r for r in resources if r["type"] == "google_logging_folder_sink"] + assert sorted([r["index"] for r in sinks]) == [ + "debug", + "info", + "notice", + "warning", + ] + values = [ + ( + r["index"], + r["values"]["filter"], + r["values"]["destination"], + r["values"]["include_children"], + ) + for r in sinks + ] + assert sorted(values) == [ + ( + "debug", + "severity=DEBUG", + "logging.googleapis.com/projects/myproject/locations/global/buckets/mybucket", + False, + ), + ( + "info", + "severity=INFO", + "bigquery.googleapis.com/projects/myproject/datasets/mydataset", + True, + ), + ( + "notice", + "severity=NOTICE", + "pubsub.googleapis.com/projects/myproject/topics/mytopic", + False, + ), + ("warning", "severity=WARNING", "storage.googleapis.com/mybucket", True), + ] - bindings = [r for r in resources if "member" in r["type"]] - values = [(r["index"], r["type"], r["values"]["role"]) for r in bindings] - assert sorted(values) == [ - ("debug", "google_project_iam_member", "roles/logging.bucketWriter"), - ("info", "google_bigquery_dataset_iam_member", "roles/bigquery.dataEditor"), - ("notice", "google_pubsub_topic_iam_member", "roles/pubsub.publisher"), - ("warning", "google_storage_bucket_iam_member", "roles/storage.objectCreator"), - ] + bindings = [r for r in resources if "member" in r["type"]] + values = [(r["index"], r["type"], r["values"]["role"]) for r in bindings] + assert sorted(values) == [ + ("debug", "google_project_iam_member", "roles/logging.bucketWriter"), + ("info", "google_bigquery_dataset_iam_member", "roles/bigquery.dataEditor"), + ("notice", "google_pubsub_topic_iam_member", "roles/pubsub.publisher"), + ("warning", "google_storage_bucket_iam_member", "roles/storage.objectCreator"), + ] - exclusions = [(r["index"], r["values"]["exclusions"]) for r in sinks] - assert sorted(exclusions) == [ - ( - "debug", - [ - { - "description": None, - "disabled": False, - "filter": "logName:compute", - "name": "no-compute", - }, - { - "description": None, - "disabled": False, - "filter": "logName:container", - "name": "no-container", - }, - ], - ), - ("info", []), - ("notice", []), - ("warning", []), - ] + exclusions = [(r["index"], r["values"]["exclusions"]) for r in sinks] + assert sorted(exclusions) == [ + ( + "debug", + [ + { + "description": None, + "disabled": False, + "filter": "logName:compute", + "name": "no-compute", + }, + { + "description": None, + "disabled": False, + "filter": "logName:container", + "name": "no-container", + }, + ], + ), + ("info", []), + ("notice", []), + ("warning", []), + ] def test_exclusions(plan_runner): - "Test folder-level logging exclusions." - logging_exclusions = ( - "{" - 'exclusion1 = "resource.type=gce_instance", ' - 'exclusion2 = "severity=NOTICE", ' - "}" - ) - _, resources = plan_runner(FIXTURES_DIR, logging_exclusions=logging_exclusions) - assert len(resources) == 3 - exclusions = [ - r for r in resources if r["type"] == "google_logging_folder_exclusion" - ] - assert sorted([r["index"] for r in exclusions]) == [ - "exclusion1", - "exclusion2", - ] - values = [(r["index"], r["values"]["filter"]) for r in exclusions] - assert sorted(values) == [ - ("exclusion1", "resource.type=gce_instance"), - ("exclusion2", "severity=NOTICE"), - ] + "Test folder-level logging exclusions." + logging_exclusions = ( + "{" + 'exclusion1 = "resource.type=gce_instance", ' + 'exclusion2 = "severity=NOTICE", ' + "}" + ) + _, resources = plan_runner( + FIXTURES_DIR, logging_exclusions=logging_exclusions) + assert len(resources) == 3 + exclusions = [ + r for r in resources if r["type"] == "google_logging_folder_exclusion" + ] + assert sorted([r["index"] for r in exclusions]) == [ + "exclusion1", + "exclusion2", + ] + values = [(r["index"], r["values"]["filter"]) for r in exclusions] + assert sorted(values) == [ + ("exclusion1", "resource.type=gce_instance"), + ("exclusion2", "severity=NOTICE"), + ] diff --git a/tests/modules/folder/test_plan_org_policies.py b/tests/modules/folder/test_plan_org_policies.py index 205ed3f5..af6233fe 100644 --- a/tests/modules/folder/test_plan_org_policies.py +++ b/tests/modules/folder/test_plan_org_policies.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -69,7 +69,7 @@ def test_exclussions(plan_runner): 'policy-a', 'policy-b', 'policy-c' ] assert values[0]['list_policy'][0]['allow'] == [ - {'all': True, 'values': None}] + {'all': True, 'values': None}] assert values[1]['list_policy'][0]['deny'] == [ - {'all': False, 'values': ["bar"]}] + {'all': False, 'values': ["bar"]}] assert values[2]['restore_policy'] == [{'default': True}] diff --git a/tests/modules/gcs/__init__.py b/tests/modules/gcs/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/gcs/__init__.py +++ b/tests/modules/gcs/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/gcs/fixture/main.tf b/tests/modules/gcs/fixture/main.tf index 7e63ae0b..ea2e994f 100644 --- a/tests/modules/gcs/fixture/main.tf +++ b/tests/modules/gcs/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/gcs/fixture/variables.tf b/tests/modules/gcs/fixture/variables.tf index 44680c5e..455d9a4b 100644 --- a/tests/modules/gcs/fixture/variables.tf +++ b/tests/modules/gcs/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/gcs/test_plan.py b/tests/modules/gcs/test_plan.py index 79f86056..8152067b 100644 --- a/tests/modules/gcs/test_plan.py +++ b/tests/modules/gcs/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import pytest FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + def test_buckets(plan_runner): "Test bucket resources." _, resources = plan_runner(FIXTURES_DIR) @@ -28,6 +29,7 @@ def test_buckets(plan_runner): assert r['values']['name'] == 'bucket-a' assert r['values']['project'] == 'my-project' + def test_prefix(plan_runner): "Test bucket name when prefix is set." _, resources = plan_runner(FIXTURES_DIR, prefix='foo') @@ -37,9 +39,9 @@ def test_prefix(plan_runner): def test_config_values(plan_runner): "Test that variables set the correct attributes on buckets." variables = dict( - uniform_bucket_level_access='true', - force_destroy='true', - versioning='true' + uniform_bucket_level_access='true', + force_destroy='true', + versioning='true' ) _, resources = plan_runner(FIXTURES_DIR, **variables) assert len(resources) == 1 @@ -49,7 +51,7 @@ def test_config_values(plan_runner): assert r['values']['versioning'] == [{'enabled': True}] assert r['values']['logging'] == [{'log_bucket': 'foo'}] assert r['values']['retention_policy'] == [ - {'is_locked': False, 'retention_period': 5} + {'is_locked': False, 'retention_period': 5} ] diff --git a/tests/modules/gke_nodepool/__init__.py b/tests/modules/gke_nodepool/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/gke_nodepool/__init__.py +++ b/tests/modules/gke_nodepool/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/gke_nodepool/fixture/main.tf b/tests/modules/gke_nodepool/fixture/main.tf index c9b6410d..33a4f165 100644 --- a/tests/modules/gke_nodepool/fixture/main.tf +++ b/tests/modules/gke_nodepool/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/gke_nodepool/fixture/variables.tf b/tests/modules/gke_nodepool/fixture/variables.tf index f4c9c1da..cbb4331b 100644 --- a/tests/modules/gke_nodepool/fixture/variables.tf +++ b/tests/modules/gke_nodepool/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/gke_nodepool/test_plan.py b/tests/modules/gke_nodepool/test_plan.py index e8f4ffde..3a8e5b2f 100644 --- a/tests/modules/gke_nodepool/test_plan.py +++ b/tests/modules/gke_nodepool/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/iam_service_account/__init__.py b/tests/modules/iam_service_account/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/iam_service_account/__init__.py +++ b/tests/modules/iam_service_account/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/iam_service_account/fixture/main.tf b/tests/modules/iam_service_account/fixture/main.tf index 99707d0d..53513983 100644 --- a/tests/modules/iam_service_account/fixture/main.tf +++ b/tests/modules/iam_service_account/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/iam_service_account/fixture/variables.tf b/tests/modules/iam_service_account/fixture/variables.tf index 71e6c289..0a4781e0 100644 --- a/tests/modules/iam_service_account/fixture/variables.tf +++ b/tests/modules/iam_service_account/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/iam_service_account/test_plan.py b/tests/modules/iam_service_account/test_plan.py index 5686a050..cea89807 100644 --- a/tests/modules/iam_service_account/test_plan.py +++ b/tests/modules/iam_service_account/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ def test_resources(plan_runner): def test_iam_roles(plan_runner): "Test iam roles with one member." - iam=('{"roles/iam.serviceAccountUser" = ["user:a@b.com"]}') + iam = ('{"roles/iam.serviceAccountUser" = ["user:a@b.com"]}') _, resources = plan_runner(FIXTURES_DIR, iam=iam) assert len(resources) == 2 iam_resources = [r for r in resources @@ -44,7 +44,7 @@ def test_iam_roles(plan_runner): assert len(iam_resources) == 1 iam_resource = iam_resources[0] - assert iam_resource['type'] == 'google_service_account_iam_binding' - assert iam_resource['index'] == 'roles/iam.serviceAccountUser' - assert iam_resource['values']['role'] == 'roles/iam.serviceAccountUser' - assert iam_resource['values']['members'] == ["user:a@b.com"] + assert iam_resource['type'] == 'google_service_account_iam_binding' + assert iam_resource['index'] == 'roles/iam.serviceAccountUser' + assert iam_resource['values']['role'] == 'roles/iam.serviceAccountUser' + assert iam_resource['values']['members'] == ["user:a@b.com"] diff --git a/tests/modules/kms/__init__.py b/tests/modules/kms/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/kms/__init__.py +++ b/tests/modules/kms/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/kms/fixture/main.tf b/tests/modules/kms/fixture/main.tf index d60f1a4e..42132f46 100644 --- a/tests/modules/kms/fixture/main.tf +++ b/tests/modules/kms/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/kms/fixture/outputs.tf b/tests/modules/kms/fixture/outputs.tf index a96b87ff..523c5274 100644 --- a/tests/modules/kms/fixture/outputs.tf +++ b/tests/modules/kms/fixture/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/kms/fixture/variables.tf b/tests/modules/kms/fixture/variables.tf index 7ec6f846..aa03fd83 100644 --- a/tests/modules/kms/fixture/variables.tf +++ b/tests/modules/kms/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/kms/test_plan.py b/tests/modules/kms/test_plan.py index 53e7b087..011b7d4b 100644 --- a/tests/modules/kms/test_plan.py +++ b/tests/modules/kms/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/logging_bucket/__init__.py b/tests/modules/logging_bucket/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/logging_bucket/__init__.py +++ b/tests/modules/logging_bucket/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/logging_bucket/fixture/main.tf b/tests/modules/logging_bucket/fixture/main.tf index 335bd782..25ddc229 100644 --- a/tests/modules/logging_bucket/fixture/main.tf +++ b/tests/modules/logging_bucket/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/logging_bucket/fixture/variables.tf b/tests/modules/logging_bucket/fixture/variables.tf index ef53e30d..bfb80a18 100644 --- a/tests/modules/logging_bucket/fixture/variables.tf +++ b/tests/modules/logging_bucket/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/logging_bucket/test_plan.py b/tests/modules/logging_bucket/test_plan.py index 45793b8a..d0e62dda 100644 --- a/tests/modules/logging_bucket/test_plan.py +++ b/tests/modules/logging_bucket/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,66 +21,67 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixture") def test_project_logging_bucket(plan_runner): - "Test project logging bucket." - _, resources = plan_runner(FIXTURES_DIR, parent_type="project", parent="myproject") - assert len(resources) == 1 + "Test project logging bucket." + _, resources = plan_runner( + FIXTURES_DIR, parent_type="project", parent="myproject") + assert len(resources) == 1 - resource = resources[0] - assert resource["type"] == "google_logging_project_bucket_config" - assert resource["values"] == { - "bucket_id": "mybucket", - "project": "myproject", - "location": "global", - "retention_days": 30, - } + resource = resources[0] + assert resource["type"] == "google_logging_project_bucket_config" + assert resource["values"] == { + "bucket_id": "mybucket", + "project": "myproject", + "location": "global", + "retention_days": 30, + } def test_folder_logging_bucket(plan_runner): - "Test project logging bucket." - _, resources = plan_runner( - FIXTURES_DIR, parent_type="folder", parent="folders/0123456789" - ) - assert len(resources) == 1 + "Test project logging bucket." + _, resources = plan_runner( + FIXTURES_DIR, parent_type="folder", parent="folders/0123456789" + ) + assert len(resources) == 1 - resource = resources[0] - assert resource["type"] == "google_logging_folder_bucket_config" - assert resource["values"] == { - "bucket_id": "mybucket", - "folder": "folders/0123456789", - "location": "global", - "retention_days": 30, - } + resource = resources[0] + assert resource["type"] == "google_logging_folder_bucket_config" + assert resource["values"] == { + "bucket_id": "mybucket", + "folder": "folders/0123456789", + "location": "global", + "retention_days": 30, + } def test_organization_logging_bucket(plan_runner): - "Test project logging bucket." - _, resources = plan_runner( - FIXTURES_DIR, parent_type="organization", parent="organizations/0123456789" - ) - assert len(resources) == 1 + "Test project logging bucket." + _, resources = plan_runner( + FIXTURES_DIR, parent_type="organization", parent="organizations/0123456789" + ) + assert len(resources) == 1 - resource = resources[0] - assert resource["type"] == "google_logging_organization_bucket_config" - assert resource["values"] == { - "bucket_id": "mybucket", - "organization": "organizations/0123456789", - "location": "global", - "retention_days": 30, - } + resource = resources[0] + assert resource["type"] == "google_logging_organization_bucket_config" + assert resource["values"] == { + "bucket_id": "mybucket", + "organization": "organizations/0123456789", + "location": "global", + "retention_days": 30, + } def test_billing_account_logging_bucket(plan_runner): - "Test project logging bucket." - _, resources = plan_runner( - FIXTURES_DIR, parent_type="billing_account", parent="0123456789" - ) - assert len(resources) == 1 + "Test project logging bucket." + _, resources = plan_runner( + FIXTURES_DIR, parent_type="billing_account", parent="0123456789" + ) + assert len(resources) == 1 - resource = resources[0] - assert resource["type"] == "google_logging_billing_account_bucket_config" - assert resource["values"] == { - "bucket_id": "mybucket", - "billing_account": "0123456789", - "location": "global", - "retention_days": 30, - } + resource = resources[0] + assert resource["type"] == "google_logging_billing_account_bucket_config" + assert resource["values"] == { + "bucket_id": "mybucket", + "billing_account": "0123456789", + "location": "global", + "retention_days": 30, + } diff --git a/tests/modules/naming_convention/__init__.py b/tests/modules/naming_convention/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/naming_convention/__init__.py +++ b/tests/modules/naming_convention/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/naming_convention/fixture/main.tf b/tests/modules/naming_convention/fixture/main.tf index 051de008..d0387f95 100644 --- a/tests/modules/naming_convention/fixture/main.tf +++ b/tests/modules/naming_convention/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/naming_convention/test_plan.py b/tests/modules/naming_convention/test_plan.py index 1a77df0a..ae19c176 100644 --- a/tests/modules/naming_convention/test_plan.py +++ b/tests/modules/naming_convention/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_address/__init__.py b/tests/modules/net_address/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/net_address/__init__.py +++ b/tests/modules/net_address/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_address/fixture/main.tf b/tests/modules/net_address/fixture/main.tf index 5e62fe13..e716ad1a 100644 --- a/tests/modules/net_address/fixture/main.tf +++ b/tests/modules/net_address/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_address/fixture/outputs.tf b/tests/modules/net_address/fixture/outputs.tf index a96b87ff..523c5274 100644 --- a/tests/modules/net_address/fixture/outputs.tf +++ b/tests/modules/net_address/fixture/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_address/fixture/variables.tf b/tests/modules/net_address/fixture/variables.tf index 32813859..d8272803 100644 --- a/tests/modules/net_address/fixture/variables.tf +++ b/tests/modules/net_address/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_address/test_plan.py b/tests/modules/net_address/test_plan.py index 6c6e413e..1ecdfd80 100644 --- a/tests/modules/net_address/test_plan.py +++ b/tests/modules/net_address/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_ilb/__init__.py b/tests/modules/net_ilb/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/net_ilb/__init__.py +++ b/tests/modules/net_ilb/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_ilb/fixture/main.tf b/tests/modules/net_ilb/fixture/main.tf index 79dc4766..c592421f 100644 --- a/tests/modules/net_ilb/fixture/main.tf +++ b/tests/modules/net_ilb/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_ilb/fixture/variables.tf b/tests/modules/net_ilb/fixture/variables.tf index 9c295137..b00b49a6 100644 --- a/tests/modules/net_ilb/fixture/variables.tf +++ b/tests/modules/net_ilb/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_ilb/test_plan.py b/tests/modules/net_ilb/test_plan.py index 8114fd71..d05a93ee 100644 --- a/tests/modules/net_ilb/test_plan.py +++ b/tests/modules/net_ilb/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_interconnect_attachment_direct/__init__.py b/tests/modules/net_interconnect_attachment_direct/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/net_interconnect_attachment_direct/__init__.py +++ b/tests/modules/net_interconnect_attachment_direct/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_interconnect_attachment_direct/fixture/main.tf b/tests/modules/net_interconnect_attachment_direct/fixture/main.tf index 58c3de4d..c4b1be3e 100644 --- a/tests/modules/net_interconnect_attachment_direct/fixture/main.tf +++ b/tests/modules/net_interconnect_attachment_direct/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_interconnect_attachment_direct/fixture/variables.tf b/tests/modules/net_interconnect_attachment_direct/fixture/variables.tf index 08126b34..55894d4e 100644 --- a/tests/modules/net_interconnect_attachment_direct/fixture/variables.tf +++ b/tests/modules/net_interconnect_attachment_direct/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_interconnect_attachment_direct/test_plan.py b/tests/modules/net_interconnect_attachment_direct/test_plan.py index d7b399df..f5591ed4 100644 --- a/tests/modules/net_interconnect_attachment_direct/test_plan.py +++ b/tests/modules/net_interconnect_attachment_direct/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -50,61 +50,61 @@ _VAR_ROUTER_CONFIG = ( def test_router_create_false(plan_runner): - "Test with no router creation." - _, resources = plan_runner(FIXTURES_DIR, router_create='false') - assert len(resources) == 3 + "Test with no router creation." + _, resources = plan_runner(FIXTURES_DIR, router_create='false') + assert len(resources) == 3 def test_vlanattachment(plan_runner): - "Test vlan attachment" - _, resources = plan_runner(FIXTURES_DIR, bgp=_VAR_BGP, config=_VAR_CONFIG) - assert len(resources) == 4 - for r in resources: - if r['type'] != 'google_compute_interconnect_attachment': - continue - assert r['values']['interconnect'].endswith( - 'interconnects/mylab-interconnect-1') - assert r['values']['name'] == 'vlan-603' - assert r['values']['vlan_tag8021q'] == 603 - assert r['values']['candidate_subnets'] == ['169.254.63.0/29'] - assert r['values']['bandwidth'] == 'BPS_10G' - assert r['values']['mtu'] == '1440' - assert r['values']['admin_enabled'] == True + "Test vlan attachment" + _, resources = plan_runner(FIXTURES_DIR, bgp=_VAR_BGP, config=_VAR_CONFIG) + assert len(resources) == 4 + for r in resources: + if r['type'] != 'google_compute_interconnect_attachment': + continue + assert r['values']['interconnect'].endswith( + 'interconnects/mylab-interconnect-1') + assert r['values']['name'] == 'vlan-603' + assert r['values']['vlan_tag8021q'] == 603 + assert r['values']['candidate_subnets'] == ['169.254.63.0/29'] + assert r['values']['bandwidth'] == 'BPS_10G' + assert r['values']['mtu'] == '1440' + assert r['values']['admin_enabled'] == True - def test_router(plan_runner): - "Test router" - _, resources = plan_runner(FIXTURES_DIR, router_config=_VAR_ROUTER_CONFIG) - assert len(resources) == 4 - for r in resources: - if r['type'] != 'google_compute_router': - continue - assert r['values']['name'] == 'router-vlan-attachment' - assert r['values']['network'] == 'my-vpc' - assert r['values']['bgp'] == [{ - 'advertise_mode': 'CUSTOM', - 'advertised_groups': ['ALL_SUBNETS'], - 'advertised_ip_ranges': [{'description': 'custom', 'range': '199.36.153.8/30'}], - 'asn': 65003, - }] + def test_router(plan_runner): + "Test router" + _, resources = plan_runner(FIXTURES_DIR, router_config=_VAR_ROUTER_CONFIG) + assert len(resources) == 4 + for r in resources: + if r['type'] != 'google_compute_router': + continue + assert r['values']['name'] == 'router-vlan-attachment' + assert r['values']['network'] == 'my-vpc' + assert r['values']['bgp'] == [{ + 'advertise_mode': 'CUSTOM', + 'advertised_groups': ['ALL_SUBNETS'], + 'advertised_ip_ranges': [{'description': 'custom', 'range': '199.36.153.8/30'}], + 'asn': 65003, + }] - def test_router_peer(plan_runner): - "Test router peer" - _, resources = plan_runner(FIXTURES_DIR, bgp=_VAR_BGP) - assert len(resources) == 4 - for r in resources: - if r['type'] != 'google_compute_router_peer': - continue - assert r['values']['peer_ip_address'] == '169.254.63.2' - assert r['values']['peer_asn'] == 65418 - assert r['values']['interface'] == 'vlan-603' + def test_router_peer(plan_runner): + "Test router peer" + _, resources = plan_runner(FIXTURES_DIR, bgp=_VAR_BGP) + assert len(resources) == 4 + for r in resources: + if r['type'] != 'google_compute_router_peer': + continue + assert r['values']['peer_ip_address'] == '169.254.63.2' + assert r['values']['peer_asn'] == 65418 + assert r['values']['interface'] == 'vlan-603' - def test_router_interface(plan_runner): - "Test router interface" - _, resources = plan_runner(FIXTURES_DIR, bgp=_VAR_BGP) - assert len(resources) == 4 - for r in resources: - if r['type'] != 'google_compute_router_interface': - continue - assert r['values']['name'] == 'interface-vlan-603' - assert r['values']['ip_range'] == '169.254.63.1/29' - assert r['values']['interconnect_attachment'] == 'vlan-603' + def test_router_interface(plan_runner): + "Test router interface" + _, resources = plan_runner(FIXTURES_DIR, bgp=_VAR_BGP) + assert len(resources) == 4 + for r in resources: + if r['type'] != 'google_compute_router_interface': + continue + assert r['values']['name'] == 'interface-vlan-603' + assert r['values']['ip_range'] == '169.254.63.1/29' + assert r['values']['interconnect_attachment'] == 'vlan-603' diff --git a/tests/modules/net_vpc/__init__.py b/tests/modules/net_vpc/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/net_vpc/__init__.py +++ b/tests/modules/net_vpc/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc/fixture/data/factory-subnet.yaml b/tests/modules/net_vpc/fixture/data/factory-subnet.yaml index dd70105a..03bd1f98 100644 --- a/tests/modules/net_vpc/fixture/data/factory-subnet.yaml +++ b/tests/modules/net_vpc/fixture/data/factory-subnet.yaml @@ -1,23 +1,23 @@ -# Copyright 2021 Google LLC -# +# Copyright 2022 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. -region: europe-west1 -description: Sample description -ip_cidr_range: 10.128.0.0/24 -private_ip_google_access: false -iam_users: ["foobar@example.com"] -iam_groups: ["lorem@example.com"] +region: europe-west1 +description: Sample description +ip_cidr_range: 10.128.0.0/24 +private_ip_google_access: false +iam_users: ["foobar@example.com"] +iam_groups: ["lorem@example.com"] iam_service_accounts: ["foobar@project-id.iam.gserviceaccount.com"] -secondary_ip_range: - secondary-range-a: 192.168.128.0/24 \ No newline at end of file +secondary_ip_range: + secondary-range-a: 192.168.128.0/24 diff --git a/tests/modules/net_vpc/fixture/main.tf b/tests/modules/net_vpc/fixture/main.tf index f959c3bb..3c36af9a 100644 --- a/tests/modules/net_vpc/fixture/main.tf +++ b/tests/modules/net_vpc/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc/fixture/variables.tf b/tests/modules/net_vpc/fixture/variables.tf index d23497e2..918521b5 100644 --- a/tests/modules/net_vpc/fixture/variables.tf +++ b/tests/modules/net_vpc/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc/test_plan.py b/tests/modules/net_vpc/test_plan.py index e04d62a5..540663ac 100644 --- a/tests/modules/net_vpc/test_plan.py +++ b/tests/modules/net_vpc/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc/test_plan_psn.py b/tests/modules/net_vpc/test_plan_psn.py index f80cd1d9..e76ff4ad 100644 --- a/tests/modules/net_vpc/test_plan_psn.py +++ b/tests/modules/net_vpc/test_plan_psn.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc/test_plan_subnets.py b/tests/modules/net_vpc/test_plan_subnets.py index 166487b7..0333fb81 100644 --- a/tests/modules/net_vpc/test_plan_subnets.py +++ b/tests/modules/net_vpc/test_plan_subnets.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc_firewall/__init__.py b/tests/modules/net_vpc_firewall/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/net_vpc_firewall/__init__.py +++ b/tests/modules/net_vpc_firewall/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc_firewall/fixture/config/cidr_template.yaml b/tests/modules/net_vpc_firewall/fixture/config/cidr_template.yaml index b33125de..b150c305 100644 --- a/tests/modules/net_vpc_firewall/fixture/config/cidr_template.yaml +++ b/tests/modules/net_vpc_firewall/fixture/config/cidr_template.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc_firewall/fixture/config/firewall/load_balancers.yaml b/tests/modules/net_vpc_firewall/fixture/config/firewall/load_balancers.yaml index 558e65b1..508c9efe 100644 --- a/tests/modules/net_vpc_firewall/fixture/config/firewall/load_balancers.yaml +++ b/tests/modules/net_vpc_firewall/fixture/config/firewall/load_balancers.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc_firewall/fixture/main.tf b/tests/modules/net_vpc_firewall/fixture/main.tf index 59201cb2..26237cd6 100644 --- a/tests/modules/net_vpc_firewall/fixture/main.tf +++ b/tests/modules/net_vpc_firewall/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc_firewall/fixture/variables.tf b/tests/modules/net_vpc_firewall/fixture/variables.tf index 7f261a6f..1ff9cff3 100644 --- a/tests/modules/net_vpc_firewall/fixture/variables.tf +++ b/tests/modules/net_vpc_firewall/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/net_vpc_firewall/test_plan.py b/tests/modules/net_vpc_firewall/test_plan.py index 8159a7fc..b54d23b0 100644 --- a/tests/modules/net_vpc_firewall/test_plan.py +++ b/tests/modules/net_vpc_firewall/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/organization/__init__.py b/tests/modules/organization/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/organization/__init__.py +++ b/tests/modules/organization/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/organization/fixture/data/firewall-cidrs.yaml b/tests/modules/organization/fixture/data/firewall-cidrs.yaml index 61291fcd..939bec32 100644 --- a/tests/modules/organization/fixture/data/firewall-cidrs.yaml +++ b/tests/modules/organization/fixture/data/firewall-cidrs.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/organization/fixture/data/firewall-rules.yaml b/tests/modules/organization/fixture/data/firewall-rules.yaml index 62d4798b..1b90983e 100644 --- a/tests/modules/organization/fixture/data/firewall-rules.yaml +++ b/tests/modules/organization/fixture/data/firewall-rules.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/organization/fixture/main.tf b/tests/modules/organization/fixture/main.tf index e875cae2..3a57a790 100644 --- a/tests/modules/organization/fixture/main.tf +++ b/tests/modules/organization/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/organization/fixture/variables.tf b/tests/modules/organization/fixture/variables.tf index bd3d2ce3..6881f15e 100644 --- a/tests/modules/organization/fixture/variables.tf +++ b/tests/modules/organization/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/organization/test_plan.py b/tests/modules/organization/test_plan.py index 5fd1b0ab..06f80bbc 100644 --- a/tests/modules/organization/test_plan.py +++ b/tests/modules/organization/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/organization/test_plan_firewall.py b/tests/modules/organization/test_plan_firewall.py index 1aa8c1cb..a5ffb032 100644 --- a/tests/modules/organization/test_plan_firewall.py +++ b/tests/modules/organization/test_plan_firewall.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/organization/test_plan_logging.py b/tests/modules/organization/test_plan_logging.py index fdb592e4..a8a64c99 100644 --- a/tests/modules/organization/test_plan_logging.py +++ b/tests/modules/organization/test_plan_logging.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/project/__init__.py b/tests/modules/project/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/project/__init__.py +++ b/tests/modules/project/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/project/fixture/main.tf b/tests/modules/project/fixture/main.tf index 73263e0a..2d2e53da 100644 --- a/tests/modules/project/fixture/main.tf +++ b/tests/modules/project/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/project/fixture/variables.tf b/tests/modules/project/fixture/variables.tf index 2417fe62..4af03a1e 100644 --- a/tests/modules/project/fixture/variables.tf +++ b/tests/modules/project/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/project/test_iam.py b/tests/modules/project/test_iam.py index 273dfc93..ecc32fb0 100644 --- a/tests/modules/project/test_iam.py +++ b/tests/modules/project/test_iam.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/project/test_plan.py b/tests/modules/project/test_plan.py index 892d662e..3a58dec7 100644 --- a/tests/modules/project/test_plan.py +++ b/tests/modules/project/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/project/test_plan_logging.py b/tests/modules/project/test_plan_logging.py index cf5c992e..cc96230f 100644 --- a/tests/modules/project/test_plan_logging.py +++ b/tests/modules/project/test_plan_logging.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixture") def test_sinks(plan_runner): - "Test folder-level sinks." - logging_sinks = """ { + "Test folder-level sinks." + logging_sinks = """ { warning = { type = "gcs" destination = "mybucket" @@ -61,110 +61,111 @@ def test_sinks(plan_runner): } } """ - _, resources = plan_runner(FIXTURES_DIR, logging_sinks=logging_sinks) - assert len(resources) == 9 + _, resources = plan_runner(FIXTURES_DIR, logging_sinks=logging_sinks) + assert len(resources) == 9 - resource_types = Counter([r["type"] for r in resources]) - assert resource_types == { - "google_logging_project_sink": 4, - "google_bigquery_dataset_iam_member": 1, - "google_project": 1, - "google_project_iam_member": 1, - "google_pubsub_topic_iam_member": 1, - "google_storage_bucket_iam_member": 1, - } + resource_types = Counter([r["type"] for r in resources]) + assert resource_types == { + "google_logging_project_sink": 4, + "google_bigquery_dataset_iam_member": 1, + "google_project": 1, + "google_project_iam_member": 1, + "google_pubsub_topic_iam_member": 1, + "google_storage_bucket_iam_member": 1, + } - sinks = [r for r in resources if r["type"] == "google_logging_project_sink"] - assert sorted([r["index"] for r in sinks]) == [ - "debug", - "info", - "notice", - "warning", - ] - values = [ - ( - r["index"], - r["values"]["filter"], - r["values"]["destination"], - r["values"]["unique_writer_identity"], - ) - for r in sinks - ] - assert sorted(values) == [ - ( - "debug", - "severity=DEBUG", - "logging.googleapis.com/projects/myproject/locations/global/buckets/mybucket", - True, - ), - ( - "info", - "severity=INFO", - "bigquery.googleapis.com/projects/myproject/datasets/mydataset", - False, - ), - ( - "notice", - "severity=NOTICE", - "pubsub.googleapis.com/projects/myproject/topics/mytopic", - False, - ), - ("warning", "severity=WARNING", "storage.googleapis.com/mybucket", False), - ] + sinks = [r for r in resources if r["type"] == "google_logging_project_sink"] + assert sorted([r["index"] for r in sinks]) == [ + "debug", + "info", + "notice", + "warning", + ] + values = [ + ( + r["index"], + r["values"]["filter"], + r["values"]["destination"], + r["values"]["unique_writer_identity"], + ) + for r in sinks + ] + assert sorted(values) == [ + ( + "debug", + "severity=DEBUG", + "logging.googleapis.com/projects/myproject/locations/global/buckets/mybucket", + True, + ), + ( + "info", + "severity=INFO", + "bigquery.googleapis.com/projects/myproject/datasets/mydataset", + False, + ), + ( + "notice", + "severity=NOTICE", + "pubsub.googleapis.com/projects/myproject/topics/mytopic", + False, + ), + ("warning", "severity=WARNING", "storage.googleapis.com/mybucket", False), + ] - bindings = [r for r in resources if "member" in r["type"]] - values = [(r["index"], r["type"], r["values"]["role"]) for r in bindings] - assert sorted(values) == [ - ("debug", "google_project_iam_member", "roles/logging.bucketWriter"), - ("info", "google_bigquery_dataset_iam_member", "roles/bigquery.dataEditor"), - ("notice", "google_pubsub_topic_iam_member", "roles/pubsub.publisher"), - ("warning", "google_storage_bucket_iam_member", "roles/storage.objectCreator"), - ] + bindings = [r for r in resources if "member" in r["type"]] + values = [(r["index"], r["type"], r["values"]["role"]) for r in bindings] + assert sorted(values) == [ + ("debug", "google_project_iam_member", "roles/logging.bucketWriter"), + ("info", "google_bigquery_dataset_iam_member", "roles/bigquery.dataEditor"), + ("notice", "google_pubsub_topic_iam_member", "roles/pubsub.publisher"), + ("warning", "google_storage_bucket_iam_member", "roles/storage.objectCreator"), + ] - exclusions = [(r["index"], r["values"]["exclusions"]) for r in sinks] - assert sorted(exclusions) == [ - ( - "debug", - [ - { - "description": None, - "disabled": False, - "filter": "logName:compute", - "name": "no-compute", - }, - { - "description": None, - "disabled": False, - "filter": "logName:container", - "name": "no-container", - }, - ], - ), - ("info", []), - ("notice", []), - ("warning", []), - ] + exclusions = [(r["index"], r["values"]["exclusions"]) for r in sinks] + assert sorted(exclusions) == [ + ( + "debug", + [ + { + "description": None, + "disabled": False, + "filter": "logName:compute", + "name": "no-compute", + }, + { + "description": None, + "disabled": False, + "filter": "logName:container", + "name": "no-container", + }, + ], + ), + ("info", []), + ("notice", []), + ("warning", []), + ] def test_exclusions(plan_runner): - "Test folder-level logging exclusions." - logging_exclusions = ( - "{" - 'exclusion1 = "resource.type=gce_instance", ' - 'exclusion2 = "severity=NOTICE", ' - "}" - ) - _, resources = plan_runner(FIXTURES_DIR, logging_exclusions=logging_exclusions) - assert len(resources) == 3 - exclusions = [ - r for r in resources if r["type"] == "google_logging_project_exclusion" - ] - assert sorted([r["index"] for r in exclusions]) == [ - "exclusion1", - "exclusion2", - ] - values = [(r["index"], r["values"]["filter"]) for r in exclusions] - assert sorted(values) == [ - ("exclusion1", "resource.type=gce_instance"), - ("exclusion2", "severity=NOTICE"), - ] + "Test folder-level logging exclusions." + logging_exclusions = ( + "{" + 'exclusion1 = "resource.type=gce_instance", ' + 'exclusion2 = "severity=NOTICE", ' + "}" + ) + _, resources = plan_runner( + FIXTURES_DIR, logging_exclusions=logging_exclusions) + assert len(resources) == 3 + exclusions = [ + r for r in resources if r["type"] == "google_logging_project_exclusion" + ] + assert sorted([r["index"] for r in exclusions]) == [ + "exclusion1", + "exclusion2", + ] + values = [(r["index"], r["values"]["filter"]) for r in exclusions] + assert sorted(values) == [ + ("exclusion1", "resource.type=gce_instance"), + ("exclusion2", "severity=NOTICE"), + ] diff --git a/tests/modules/project/test_plan_org_policies.py b/tests/modules/project/test_plan_org_policies.py index 995781ad..1d125a43 100644 --- a/tests/modules/project/test_plan_org_policies.py +++ b/tests/modules/project/test_plan_org_policies.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/pubsub/__init__.py b/tests/modules/pubsub/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/pubsub/__init__.py +++ b/tests/modules/pubsub/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/pubsub/fixture/main.tf b/tests/modules/pubsub/fixture/main.tf index 2a1f1aec..ef068206 100644 --- a/tests/modules/pubsub/fixture/main.tf +++ b/tests/modules/pubsub/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/pubsub/fixture/variables.tf b/tests/modules/pubsub/fixture/variables.tf index dce4bd9f..8371c619 100644 --- a/tests/modules/pubsub/fixture/variables.tf +++ b/tests/modules/pubsub/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/pubsub/test_plan.py b/tests/modules/pubsub/test_plan.py index 3c60130a..5e382bb7 100644 --- a/tests/modules/pubsub/test_plan.py +++ b/tests/modules/pubsub/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/secret_manager/__init__.py b/tests/modules/secret_manager/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/secret_manager/__init__.py +++ b/tests/modules/secret_manager/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/secret_manager/fixture/main.tf b/tests/modules/secret_manager/fixture/main.tf index da0cb70d..294504c7 100644 --- a/tests/modules/secret_manager/fixture/main.tf +++ b/tests/modules/secret_manager/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/secret_manager/fixture/variables.tf b/tests/modules/secret_manager/fixture/variables.tf index d38baef7..ce6f5559 100644 --- a/tests/modules/secret_manager/fixture/variables.tf +++ b/tests/modules/secret_manager/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/secret_manager/test_plan.py b/tests/modules/secret_manager/test_plan.py index 47c5426b..197e1326 100644 --- a/tests/modules/secret_manager/test_plan.py +++ b/tests/modules/secret_manager/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/service_directory/__init__.py b/tests/modules/service_directory/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/service_directory/__init__.py +++ b/tests/modules/service_directory/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/service_directory/fixture/main.tf b/tests/modules/service_directory/fixture/main.tf index 6862ca21..00794778 100644 --- a/tests/modules/service_directory/fixture/main.tf +++ b/tests/modules/service_directory/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/service_directory/fixture/variables.tf b/tests/modules/service_directory/fixture/variables.tf index dce4bd9f..8371c619 100644 --- a/tests/modules/service_directory/fixture/variables.tf +++ b/tests/modules/service_directory/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/service_directory/test_plan.py b/tests/modules/service_directory/test_plan.py index dd239724..1c040ac7 100644 --- a/tests/modules/service_directory/test_plan.py +++ b/tests/modules/service_directory/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/source_repository/__init__.py b/tests/modules/source_repository/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/source_repository/__init__.py +++ b/tests/modules/source_repository/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/source_repository/fixture/main.tf b/tests/modules/source_repository/fixture/main.tf index ecceb3f1..00dd7bd3 100644 --- a/tests/modules/source_repository/fixture/main.tf +++ b/tests/modules/source_repository/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/source_repository/fixture/variables.tf b/tests/modules/source_repository/fixture/variables.tf index 612bbf9f..a1c540a5 100644 --- a/tests/modules/source_repository/fixture/variables.tf +++ b/tests/modules/source_repository/fixture/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/source_repository/test_plan.py b/tests/modules/source_repository/test_plan.py index 0728b5f7..792b3061 100644 --- a/tests/modules/source_repository/test_plan.py +++ b/tests/modules/source_repository/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/vpc_sc/__init__.py b/tests/modules/vpc_sc/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/modules/vpc_sc/__init__.py +++ b/tests/modules/vpc_sc/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/modules/vpc_sc/fixture/main.tf b/tests/modules/vpc_sc/fixture/main.tf index f13f5f82..e7501b76 100644 --- a/tests/modules/vpc_sc/fixture/main.tf +++ b/tests/modules/vpc_sc/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/modules/vpc_sc/test_plan.py b/tests/modules/vpc_sc/test_plan.py index b4b88547..cb5082e6 100644 --- a/tests/modules/vpc_sc/test_plan.py +++ b/tests/modules/vpc_sc/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/__init__.py b/tests/networking/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/networking/__init__.py +++ b/tests/networking/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/decentralized_firewall/__init__.py b/tests/networking/decentralized_firewall/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/networking/decentralized_firewall/__init__.py +++ b/tests/networking/decentralized_firewall/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/decentralized_firewall/fixture/main.tf b/tests/networking/decentralized_firewall/fixture/main.tf index 9bef2d73..df0aee3f 100644 --- a/tests/networking/decentralized_firewall/fixture/main.tf +++ b/tests/networking/decentralized_firewall/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/networking/decentralized_firewall/fixture/variables.tf b/tests/networking/decentralized_firewall/fixture/variables.tf index 9646fe1b..59933f9a 100644 --- a/tests/networking/decentralized_firewall/fixture/variables.tf +++ b/tests/networking/decentralized_firewall/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/decentralized_firewall/test_plan.py b/tests/networking/decentralized_firewall/test_plan.py index cb1764a9..d9b5d08c 100644 --- a/tests/networking/decentralized_firewall/test_plan.py +++ b/tests/networking/decentralized_firewall/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixture") def test_resources(e2e_plan_runner): - "Test that plan works and the numbers of resources is as expected." - modules, resources = e2e_plan_runner(FIXTURES_DIR) - assert len(modules) == 8 - assert len(resources) == 50 + "Test that plan works and the numbers of resources is as expected." + modules, resources = e2e_plan_runner(FIXTURES_DIR) + assert len(modules) == 8 + assert len(resources) == 50 diff --git a/tests/networking/filtering_proxy/__init__.py b/tests/networking/filtering_proxy/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/networking/filtering_proxy/__init__.py +++ b/tests/networking/filtering_proxy/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/filtering_proxy/fixture/main.tf b/tests/networking/filtering_proxy/fixture/main.tf index 62451c80..01ed4f70 100644 --- a/tests/networking/filtering_proxy/fixture/main.tf +++ b/tests/networking/filtering_proxy/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/networking/filtering_proxy/fixture/variables.tf b/tests/networking/filtering_proxy/fixture/variables.tf index a01f3674..f4b4c588 100644 --- a/tests/networking/filtering_proxy/fixture/variables.tf +++ b/tests/networking/filtering_proxy/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/filtering_proxy/test_plan.py b/tests/networking/filtering_proxy/test_plan.py index 9fff1c43..d0d9ac42 100644 --- a/tests/networking/filtering_proxy/test_plan.py +++ b/tests/networking/filtering_proxy/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/hub_and_spoke_peering/__init__.py b/tests/networking/hub_and_spoke_peering/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/networking/hub_and_spoke_peering/__init__.py +++ b/tests/networking/hub_and_spoke_peering/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/hub_and_spoke_peering/fixture/main.tf b/tests/networking/hub_and_spoke_peering/fixture/main.tf index 899a622e..aa4e5be0 100644 --- a/tests/networking/hub_and_spoke_peering/fixture/main.tf +++ b/tests/networking/hub_and_spoke_peering/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/networking/hub_and_spoke_peering/fixture/variables.tf b/tests/networking/hub_and_spoke_peering/fixture/variables.tf index da4bff27..626af011 100644 --- a/tests/networking/hub_and_spoke_peering/fixture/variables.tf +++ b/tests/networking/hub_and_spoke_peering/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/hub_and_spoke_peering/test_plan.py b/tests/networking/hub_and_spoke_peering/test_plan.py index 22872449..5778d055 100644 --- a/tests/networking/hub_and_spoke_peering/test_plan.py +++ b/tests/networking/hub_and_spoke_peering/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/hub_and_spoke_vpn/__init__.py b/tests/networking/hub_and_spoke_vpn/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/networking/hub_and_spoke_vpn/__init__.py +++ b/tests/networking/hub_and_spoke_vpn/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/hub_and_spoke_vpn/fixture/main.tf b/tests/networking/hub_and_spoke_vpn/fixture/main.tf index 423db6ba..211568f1 100644 --- a/tests/networking/hub_and_spoke_vpn/fixture/main.tf +++ b/tests/networking/hub_and_spoke_vpn/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/networking/hub_and_spoke_vpn/fixture/variables.tf b/tests/networking/hub_and_spoke_vpn/fixture/variables.tf index da4bff27..626af011 100644 --- a/tests/networking/hub_and_spoke_vpn/fixture/variables.tf +++ b/tests/networking/hub_and_spoke_vpn/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/hub_and_spoke_vpn/test_plan.py b/tests/networking/hub_and_spoke_vpn/test_plan.py index d27f7418..b261ae08 100644 --- a/tests/networking/hub_and_spoke_vpn/test_plan.py +++ b/tests/networking/hub_and_spoke_vpn/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/ilb_next_hop/__init__.py b/tests/networking/ilb_next_hop/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/networking/ilb_next_hop/__init__.py +++ b/tests/networking/ilb_next_hop/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/ilb_next_hop/fixture/main.tf b/tests/networking/ilb_next_hop/fixture/main.tf index f53b1258..bfa8b2c5 100644 --- a/tests/networking/ilb_next_hop/fixture/main.tf +++ b/tests/networking/ilb_next_hop/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/networking/ilb_next_hop/fixture/variables.tf b/tests/networking/ilb_next_hop/fixture/variables.tf index d071e308..3d884c25 100644 --- a/tests/networking/ilb_next_hop/fixture/variables.tf +++ b/tests/networking/ilb_next_hop/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/ilb_next_hop/test_plan.py b/tests/networking/ilb_next_hop/test_plan.py index 74be0e56..77c815bd 100644 --- a/tests/networking/ilb_next_hop/test_plan.py +++ b/tests/networking/ilb_next_hop/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/onprem_google_access_dns/__init__.py b/tests/networking/onprem_google_access_dns/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/networking/onprem_google_access_dns/__init__.py +++ b/tests/networking/onprem_google_access_dns/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/onprem_google_access_dns/fixture/main.tf b/tests/networking/onprem_google_access_dns/fixture/main.tf index e8a0b8d0..427dd6a9 100644 --- a/tests/networking/onprem_google_access_dns/fixture/main.tf +++ b/tests/networking/onprem_google_access_dns/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/networking/onprem_google_access_dns/fixture/variables.tf b/tests/networking/onprem_google_access_dns/fixture/variables.tf index da4bff27..626af011 100644 --- a/tests/networking/onprem_google_access_dns/fixture/variables.tf +++ b/tests/networking/onprem_google_access_dns/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/onprem_google_access_dns/test_plan.py b/tests/networking/onprem_google_access_dns/test_plan.py index e1b1fb71..25c2ea72 100644 --- a/tests/networking/onprem_google_access_dns/test_plan.py +++ b/tests/networking/onprem_google_access_dns/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/private_cloud_function_from_onprem/__init__.py b/tests/networking/private_cloud_function_from_onprem/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/networking/private_cloud_function_from_onprem/__init__.py +++ b/tests/networking/private_cloud_function_from_onprem/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/private_cloud_function_from_onprem/fixture/main.tf b/tests/networking/private_cloud_function_from_onprem/fixture/main.tf index 9c87eebe..201a4b38 100644 --- a/tests/networking/private_cloud_function_from_onprem/fixture/main.tf +++ b/tests/networking/private_cloud_function_from_onprem/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/networking/private_cloud_function_from_onprem/test_plan.py b/tests/networking/private_cloud_function_from_onprem/test_plan.py index ca01efe1..7b9bd1b8 100644 --- a/tests/networking/private_cloud_function_from_onprem/test_plan.py +++ b/tests/networking/private_cloud_function_from_onprem/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/shared_vpc_gke/__init__.py b/tests/networking/shared_vpc_gke/__init__.py index d46dbae5..6d6d1266 100644 --- a/tests/networking/shared_vpc_gke/__init__.py +++ b/tests/networking/shared_vpc_gke/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/shared_vpc_gke/fixture/main.tf b/tests/networking/shared_vpc_gke/fixture/main.tf index 4d35ff3c..4a19e0c9 100644 --- a/tests/networking/shared_vpc_gke/fixture/main.tf +++ b/tests/networking/shared_vpc_gke/fixture/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/networking/shared_vpc_gke/fixture/variables.tf b/tests/networking/shared_vpc_gke/fixture/variables.tf index 9646fe1b..59933f9a 100644 --- a/tests/networking/shared_vpc_gke/fixture/variables.tf +++ b/tests/networking/shared_vpc_gke/fixture/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/networking/shared_vpc_gke/test_plan.py b/tests/networking/shared_vpc_gke/test_plan.py index d367412e..fb4244a7 100644 --- a/tests/networking/shared_vpc_gke/test_plan.py +++ b/tests/networking/shared_vpc_gke/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/prepare.py b/third-party-solutions/openshift/prepare.py index 73fcf70e..3c513a2f 100755 --- a/third-party-solutions/openshift/prepare.py +++ b/third-party-solutions/openshift/prepare.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/tf/bootstrap.tf b/third-party-solutions/openshift/tf/bootstrap.tf index e5ae8cf6..346ac9d2 100644 --- a/third-party-solutions/openshift/tf/bootstrap.tf +++ b/third-party-solutions/openshift/tf/bootstrap.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/tf/dns.tf b/third-party-solutions/openshift/tf/dns.tf index 12b818bf..4e7968f6 100644 --- a/third-party-solutions/openshift/tf/dns.tf +++ b/third-party-solutions/openshift/tf/dns.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/tf/firewall.tf b/third-party-solutions/openshift/tf/firewall.tf index bfc3060d..19456fbd 100644 --- a/third-party-solutions/openshift/tf/firewall.tf +++ b/third-party-solutions/openshift/tf/firewall.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/tf/iam.tf b/third-party-solutions/openshift/tf/iam.tf index 7b11f0fd..8b813528 100644 --- a/third-party-solutions/openshift/tf/iam.tf +++ b/third-party-solutions/openshift/tf/iam.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/tf/ilb.tf b/third-party-solutions/openshift/tf/ilb.tf index cfa4c2fe..df83f1ce 100644 --- a/third-party-solutions/openshift/tf/ilb.tf +++ b/third-party-solutions/openshift/tf/ilb.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/tf/main.tf b/third-party-solutions/openshift/tf/main.tf index 7bfb8835..d06cb4d2 100644 --- a/third-party-solutions/openshift/tf/main.tf +++ b/third-party-solutions/openshift/tf/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/tf/masters.tf b/third-party-solutions/openshift/tf/masters.tf index 82fbc789..97209b4a 100644 --- a/third-party-solutions/openshift/tf/masters.tf +++ b/third-party-solutions/openshift/tf/masters.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/tf/outputs.tf b/third-party-solutions/openshift/tf/outputs.tf index 9ff82b60..e1fcb297 100644 --- a/third-party-solutions/openshift/tf/outputs.tf +++ b/third-party-solutions/openshift/tf/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/tf/variables.tf b/third-party-solutions/openshift/tf/variables.tf index 53496eb1..3017403e 100644 --- a/third-party-solutions/openshift/tf/variables.tf +++ b/third-party-solutions/openshift/tf/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/third-party-solutions/openshift/tf/versions.tf b/third-party-solutions/openshift/tf/versions.tf index 1cc6bf89..29041268 100644 --- a/third-party-solutions/openshift/tf/versions.tf +++ b/third-party-solutions/openshift/tf/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tools/__init__.py b/tools/__init__.py index c3c623b1..bb545f47 100644 --- a/tools/__init__.py +++ b/tools/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,4 +11,3 @@ # 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/tools/check_boilerplate.py b/tools/check_boilerplate.py index 230703b8..6bbd4cb2 100755 --- a/tools/check_boilerplate.py +++ b/tools/check_boilerplate.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tools/check_documentation.py b/tools/check_documentation.py index 144787ad..1968f3b4 100755 --- a/tools/check_documentation.py +++ b/tools/check_documentation.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tools/tfutils.py b/tools/tfutils.py index c426c90c..053e0664 100755 --- a/tools/tfutils.py +++ b/tools/tfutils.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 347a4c6b69c8775898e6d971f27d5986ed92877d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 3 Jan 2022 15:27:00 +0100 Subject: [PATCH 51/52] remove lifecycle block from vpc sc perimeters (#412) --- modules/vpc-sc/README.md | 2 ++ modules/vpc-sc/access_levels.tf | 2 +- modules/vpc-sc/service_perimeters_bridge.tf | 6 +++--- modules/vpc-sc/service_perimeters_regular.tf | 6 +++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/vpc-sc/README.md b/modules/vpc-sc/README.md index 5fa2037a..a7bcaf4f 100644 --- a/modules/vpc-sc/README.md +++ b/modules/vpc-sc/README.md @@ -76,7 +76,9 @@ The regular perimeters variable exposes all the complexity of the underlying res If you need to refer to access levels created by the same module in regular service perimeters, simply use the module's outputs in the provided variables. The example below shows how to do this in practice. +/* Resources for both perimeters have a `lifecycle` block that ignores changes to `spec` and `status` resources (projects), to allow using the additive resource `google_access_context_manager_service_perimeter_resource` at project creation. If this is not needed, the `lifecycle` blocks can be safely commented in the code. +*/ #### Bridge type diff --git a/modules/vpc-sc/access_levels.tf b/modules/vpc-sc/access_levels.tf index f8c34355..b732f080 100644 --- a/modules/vpc-sc/access_levels.tf +++ b/modules/vpc-sc/access_levels.tf @@ -21,7 +21,7 @@ # google_access_context_manager_access_levels resource resource "google_access_context_manager_access_level" "basic" { - for_each = var.access_levels + for_each = var.access_levels == null ? {} : var.access_levels parent = "accessPolicies/${local.access_policy}" name = "accessPolicies/${local.access_policy}/accessLevels/${each.key}" title = each.key diff --git a/modules/vpc-sc/service_perimeters_bridge.tf b/modules/vpc-sc/service_perimeters_bridge.tf index 3c57f6b9..180dffda 100644 --- a/modules/vpc-sc/service_perimeters_bridge.tf +++ b/modules/vpc-sc/service_perimeters_bridge.tf @@ -31,9 +31,9 @@ resource "google_access_context_manager_service_perimeter" "bridge" { status { resources = each.value.status_resources == null ? [] : each.value.status_resources } - lifecycle { - ignore_changes = [spec[0].resources, status[0].resources] - } + # lifecycle { + # ignore_changes = [spec[0].resources, status[0].resources] + # } depends_on = [ google_access_context_manager_access_policy.default, google_access_context_manager_access_level.basic diff --git a/modules/vpc-sc/service_perimeters_regular.tf b/modules/vpc-sc/service_perimeters_regular.tf index d3069c57..e93ea5a6 100644 --- a/modules/vpc-sc/service_perimeters_regular.tf +++ b/modules/vpc-sc/service_perimeters_regular.tf @@ -301,9 +301,9 @@ resource "google_access_context_manager_service_perimeter" "regular" { # end vpc_accessible_services } } - lifecycle { - ignore_changes = [spec[0].resources, status[0].resources] - } + # lifecycle { + # ignore_changes = [spec[0].resources, status[0].resources] + # } depends_on = [ google_access_context_manager_access_policy.default, google_access_context_manager_access_level.basic From 98ac4d8f020a268ca5aaabe4463943e7e7b63e85 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 3 Jan 2022 15:28:12 +0100 Subject: [PATCH 52/52] Update CHANGELOG.md --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2140cf36..beeab16a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [1.0.1] - 2022-01-03 + +- remove lifecycle block from vpc sc perimeter resources + ## [10.0.0] - 2021-12-31 - fix cases where bridge perimeter status resources are `null` in `vpc-sc` module @@ -404,7 +408,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/v10.0.0...HEAD +[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v10.0.1...HEAD +[10.0.1]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v10.0.0...v10.0.1 [10.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.3...v10.0.0 [9.0.3]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.2...v9.0.3 [9.0.2]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.0...v9.0.2

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 Gg8v&GR{Ed- literal 0 HcmV?d00001 From 6717abf0e1adcc55c886c9a2d98e080ae1c13aa7 Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 23 Dec 2021 22:07:14 +0800 Subject: [PATCH 33/52] Add count to toggle optional modules --- .../README.md | 1 + .../main.tf | 4 ++ .../variables.tf | 68 ++++++++++--------- .../fixture/main.tf | 1 + .../fixture/variables.tf | 5 ++ 5 files changed, 48 insertions(+), 31 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index 3aed5250..710aa672 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -59,6 +59,7 @@ This is an optional part, and if it is expressed with correct variables, the hig | *billing_account* | Billing account id used as default for new projects. | string | | null | | *bundle_path* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle.zip | | *bundle_path_cffile* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle_cffile.zip | +| *cai_gcs_export* | Enable optional part to export tables to GCS | bool | | false | | *file_config* | Optional BQ table as a file export function config. | object({...}) | | ... | | *location* | Appe Engine location used in the example. | string | | europe-west | | *name* | Arbitrary string used to name created resources. | string | | asset-inventory | diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index ef27264f..39c1e37d 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -14,6 +14,8 @@ * limitations under the License. */ + + ############################################################################### # Projects # ############################################################################### @@ -106,6 +108,7 @@ module "cf" { } module "cffile" { + count = var.cai_gcs_export ? 1 : 0 source = "../../modules/cloud-function" project_id = module.project.project_id region = var.region @@ -164,6 +167,7 @@ resource "google_cloud_scheduler_job" "job" { } resource "google_cloud_scheduler_job" "job_file" { + count = var.cai_gcs_export ? 1 : 0 project = google_app_engine_application.app.project region = var.region name = "file-export-job" diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf index c2137d89..de9aca0d 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf @@ -26,12 +26,6 @@ variable "bundle_path" { default = "./bundle.zip" } -variable "bundle_path_cffile" { - description = "Path used to write the intermediate Cloud Function code bundle." - type = string - default = "./bundle_cffile.zip" -} - variable "cai_config" { description = "Cloud Asset Inventory export config." type = object({ @@ -42,24 +36,6 @@ variable "cai_config" { }) } -variable "file_config" { - description = "Optional BQ table as a file export function config." - type = object({ - bucket = string - filename = string - format = string - bq_dataset = string - bq_table = string - }) - default = { - bucket = null - filename = null - format = null - bq_dataset = null - bq_table = null - } -} - variable "location" { description = "Appe Engine location used in the example." type = string @@ -73,13 +49,6 @@ variable "name" { default = "asset-inventory" } - -variable "name_cffile" { - description = "Arbitrary string used to name created resources." - type = string - default = "cffile-exporter" -} - variable "project_create" { description = "Create project instead ofusing an existing one." type = bool @@ -102,3 +71,40 @@ variable "root_node" { type = string default = null } + +variable "cai_gcs_export" { + description = "Enable optional part to export tables to GCS" + type = bool + default = false +} + + +variable "name_cffile" { + description = "Arbitrary string used to name created resources." + type = string + default = "cffile-exporter" +} + +variable "file_config" { + description = "Optional BQ table as a file export function config." + type = object({ + bucket = string + filename = string + format = string + bq_dataset = string + bq_table = string + }) + default = { + bucket = null + filename = null + format = null + bq_dataset = null + bq_table = null + } +} + +variable "bundle_path_cffile" { + description = "Path used to write the intermediate Cloud Function code bundle." + type = string + default = "./bundle_cffile.zip" +} \ No newline at end of file diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf index fd5e22d2..f7bd01af 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/main.tf @@ -18,6 +18,7 @@ module "test" { source = "../../../../cloud-operations/scheduled-asset-inventory-export-bq" billing_account = var.billing_account cai_config = var.cai_config + cai_gcs_export = var.cai_gcs_export file_config = var.file_config project_create = var.project_create project_id = var.project_id diff --git a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf index 8b5212f7..d80431e3 100644 --- a/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf +++ b/tests/cloud_operations/scheduled_asset_inventory_export_bq/fixture/variables.tf @@ -32,6 +32,11 @@ variable "cai_config" { } } +variable "cai_gcs_export" { + type = bool + default = true +} + variable "file_config" { type = object({ bucket = string From 002ef657e3441ee0ea8c11a512fe2cab2f0df87f Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 23 Dec 2021 22:16:27 +0800 Subject: [PATCH 34/52] Change README --- .../scheduled-asset-inventory-export-bq/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index 710aa672..9e2905ac 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -40,11 +40,13 @@ You can also create a dashboard connecting [Datalab](https://datastudio.google.c ## File exporter for JSON, CSV (optional). +This is an optional part. + Regular file-based exports of data from Cloud Asset Inventory may be useful for e.g. scale-out network dependencies discovery tools like [Planet Exporter](https://github.com/williamchanrico/planet-exporter), or to update legacy workloads tracking or configuration management systems. Bigquery supports multiple [export formats](https://cloud.google.com/bigquery/docs/exporting-data#export_formats_and_compression_types) and one may upload objects to Storage Bucket using provided Cloud Function. Specify `job.DestinationFormat` as defined in [documentation](https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.job.DestinationFormat.html), e.g. `NEWLINE_DELIMITED_JSON`. It helps to create custom [scheduled query](https://cloud.google.com/bigquery/docs/scheduling-queries#console) from CAI export tables, and to write out results in to dedicated table (with overwrites). Define such query's output columns to comply with downstream systems' fields requirements, and time query execution after CAI export into BQ for freshness. See [sample queries](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery-sample-queries). -This is an optional part, and if it is expressed with correct variables, the high level diagram extends to the following: +If this part is expressed with correct variable `cai_gcs_export`, the high level diagram extends to the following: From 28f7697b8358a2f3ffa8bf05e3508158dfe6d694 Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 23 Dec 2021 22:23:59 +0800 Subject: [PATCH 35/52] Resolve order of variables test --- .../variables.tf | 80 ++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf index de9aca0d..b31291b8 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf @@ -26,6 +26,13 @@ variable "bundle_path" { default = "./bundle.zip" } + +variable "bundle_path_cffile" { + description = "Path used to write the intermediate Cloud Function code bundle." + type = string + default = "./bundle_cffile.zip" +} + variable "cai_config" { description = "Cloud Asset Inventory export config." type = object({ @@ -36,6 +43,33 @@ variable "cai_config" { }) } + +variable "cai_gcs_export" { + description = "Enable optional part to export tables to GCS" + type = bool + default = false +} + + +variable "file_config" { + description = "Optional BQ table as a file export function config." + type = object({ + bucket = string + filename = string + format = string + bq_dataset = string + bq_table = string + }) + default = { + bucket = null + filename = null + format = null + bq_dataset = null + bq_table = null + } +} + + variable "location" { description = "Appe Engine location used in the example." type = string @@ -49,6 +83,15 @@ variable "name" { default = "asset-inventory" } + + +variable "name_cffile" { + description = "Arbitrary string used to name created resources." + type = string + default = "cffile-exporter" +} + + variable "project_create" { description = "Create project instead ofusing an existing one." type = bool @@ -71,40 +114,3 @@ variable "root_node" { type = string default = null } - -variable "cai_gcs_export" { - description = "Enable optional part to export tables to GCS" - type = bool - default = false -} - - -variable "name_cffile" { - description = "Arbitrary string used to name created resources." - type = string - default = "cffile-exporter" -} - -variable "file_config" { - description = "Optional BQ table as a file export function config." - type = object({ - bucket = string - filename = string - format = string - bq_dataset = string - bq_table = string - }) - default = { - bucket = null - filename = null - format = null - bq_dataset = null - bq_table = null - } -} - -variable "bundle_path_cffile" { - description = "Path used to write the intermediate Cloud Function code bundle." - type = string - default = "./bundle_cffile.zip" -} \ No newline at end of file From 115e7570ac28091ed58b5efd558fbb9dbb4b41a5 Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 23 Dec 2021 23:20:43 +0800 Subject: [PATCH 36/52] Re-run pydoc --- .../scheduled-asset-inventory-export-bq/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index 9e2905ac..768695ce 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -36,7 +36,7 @@ Once done testing, you can clean up resources by running `terraform destroy`. To Once resources are created, you can run queries on the data you exported on Bigquery. [Here](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery#querying_an_asset_snapshot) you can find some example of queries you can run. -You can also create a dashboard connecting [Datalab](https://datastudio.google.com/) or any other BI tools of your choice to your Bigquery datase. +You can also create a dashboard connecting [Datalab](https://datastudio.google.com/) or any other BI tools of your choice to your Bigquery dataset. ## File exporter for JSON, CSV (optional). @@ -46,7 +46,7 @@ Regular file-based exports of data from Cloud Asset Inventory may be useful for It helps to create custom [scheduled query](https://cloud.google.com/bigquery/docs/scheduling-queries#console) from CAI export tables, and to write out results in to dedicated table (with overwrites). Define such query's output columns to comply with downstream systems' fields requirements, and time query execution after CAI export into BQ for freshness. See [sample queries](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery-sample-queries). -If this part is expressed with correct variable `cai_gcs_export`, the high level diagram extends to the following: +This is an optional part, created if `cai_gcs_export` is set to `true`. The high level diagram extends to the following: From 10df625f0ff76a5975a5c1b05148727715f3eb03 Mon Sep 17 00:00:00 2001 From: Arseny Chernov Date: Thu, 23 Dec 2021 23:33:39 +0800 Subject: [PATCH 37/52] Redo pydoc + local linting checks --- .../README.md | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index 768695ce..f1fabbe8 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -51,24 +51,26 @@ This is an optional part, created if `cai_gcs_export` is set to `true`. The high + + ## Variables | name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| cai_config | Cloud Asset Inventory export config. | object({...}) | ✓ | | -| project_id | Project id that references existing project. | string | ✓ | | -| *billing_account* | Billing account id used as default for new projects. | string | | null | -| *bundle_path* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle.zip | -| *bundle_path_cffile* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle_cffile.zip | -| *cai_gcs_export* | Enable optional part to export tables to GCS | bool | | false | -| *file_config* | Optional BQ table as a file export function config. | object({...}) | | ... | -| *location* | Appe Engine location used in the example. | string | | europe-west | -| *name* | Arbitrary string used to name created resources. | string | | asset-inventory | -| *name_cffile* | Arbitrary string used to name created resources. | string | | cffile-exporter | -| *project_create* | Create project instead ofusing an existing one. | bool | | true | -| *region* | Compute region used in the example. | string | | europe-west1 | -| *root_node* | The resource name of the parent folder or organization for project creation, in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +|---|---|:---:|:---:|:---:| +| cai_config | Cloud Asset Inventory export config. | object({…}) | ✓ | | +| project_id | Project id that references existing project. | string | ✓ | | +| billing_account | Billing account id used as default for new projects. | string | | null | +| bundle_path | Path used to write the intermediate Cloud Function code bundle. | string | | "./bundle.zip" | +| bundle_path_cffile | Path used to write the intermediate Cloud Function code bundle. | string | | "./bundle_cffile.zip" | +| cai_gcs_export | Enable optional part to export tables to GCS | bool | | false | +| file_config | Optional BQ table as a file export function config. | object({…}) | | {…} | +| location | Appe Engine location used in the example. | string | | "europe-west" | +| name | Arbitrary string used to name created resources. | string | | "asset-inventory" | +| name_cffile | Arbitrary string used to name created resources. | string | | "cffile-exporter" | +| project_create | Create project instead ofusing an existing one. | bool | | true | +| region | Compute region used in the example. | string | | "europe-west1" | +| root_node | The resource name of the parent folder or organization for project creation, in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | ## Outputs @@ -76,4 +78,7 @@ This is an optional part, created if `cai_gcs_export` is set to `true`. The high |---|---|:---:| | bq-dataset | Bigquery instance details. | | | cloud-function | Cloud Function instance details. | | + + + From 2bc5f7d33c589702b6c7a7fdba6499bbc068fc08 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 30 Dec 2021 10:56:19 +0100 Subject: [PATCH 38/52] update tfdoc (#404) --- .../README.md | 3 +- .../dns-fine-grained-iam/README.md | 3 +- cloud-operations/dns-shared-vpc/README.md | 3 +- .../iam-delegated-role-grants/README.md | 3 +- .../onprem-sa-key-management/README.md | 3 +- .../packer-image-builder/README.md | 3 +- cloud-operations/quota-monitoring/README.md | 3 +- .../README.md | 3 +- .../cmek-via-centralized-kms/README.md | 3 +- .../01-environment/README.md | 3 +- .../02-resources/README.md | 3 +- .../gcs-to-bq-with-dataflow/README.md | 3 +- .../firewall-hierarchical-policies/README.md | 3 +- factories/firewall-vpc-rules/flat/README.md | 3 +- factories/firewall-vpc-rules/nested/README.md | 3 +- factories/subnets/README.md | 3 +- foundations/business-units/README.md | 3 +- foundations/environments/README.md | 3 +- modules/__experimental/net-neg/README.md | 3 +- modules/apigee-organization/README.md | 3 +- modules/apigee-x-instance/README.md | 3 +- modules/artifact-registry/README.md | 3 +- modules/bigquery-dataset/README.md | 3 +- modules/bigtable-instance/README.md | 3 +- modules/billing-budget/README.md | 3 +- .../cloud-config-container/coredns/README.md | 3 +- .../cos-generic-metadata/README.md | 3 +- .../envoy-traffic-director/README.md | 3 +- .../cloud-config-container/mysql/README.md | 3 +- .../cloud-config-container/nginx/README.md | 3 +- .../cloud-config-container/onprem/README.md | 3 +- .../cloud-config-container/squid/README.md | 3 +- modules/cloud-function/README.md | 3 +- modules/cloud-identity-group/README.md | 3 +- modules/cloud-run/README.md | 3 +- modules/cloudsql-instance/README.md | 3 +- modules/compute-mig/README.md | 3 +- modules/compute-vm/README.md | 3 +- modules/container-registry/README.md | 3 +- modules/datafusion/README.md | 3 +- modules/dns/README.md | 3 +- modules/endpoints/README.md | 3 +- modules/folder/README.md | 3 +- modules/folders-unit/README.md | 3 +- modules/gcs/README.md | 3 +- modules/gke-cluster/README.md | 3 +- modules/gke-nodepool/README.md | 3 +- modules/iam-service-account/README.md | 3 +- modules/kms/README.md | 3 +- modules/logging-bucket/README.md | 3 +- modules/naming-convention/README.md | 3 +- modules/net-address/README.md | 3 +- modules/net-cloudnat/README.md | 3 +- modules/net-ilb/README.md | 3 +- .../README.md | 3 +- modules/net-vpc-firewall/README.md | 3 +- modules/net-vpc-peering/README.md | 3 +- modules/net-vpc/README.md | 3 +- modules/net-vpn-dynamic/README.md | 3 +- modules/net-vpn-ha/README.md | 3 +- modules/net-vpn-static/README.md | 3 +- modules/organization/README.md | 3 +- modules/project/README.md | 3 +- modules/pubsub/README.md | 3 +- modules/secret-manager/README.md | 3 +- modules/service-directory/README.md | 3 +- modules/source-repository/README.md | 3 +- modules/vpc-sc/README.md | 3 +- networking/decentralized-firewall/README.md | 3 +- networking/filtering-proxy/README.md | 3 +- networking/hub-and-spoke-peering/README.md | 3 +- networking/hub-and-spoke-vpn/README.md | 3 +- networking/ilb-next-hop/README.md | 3 +- networking/onprem-google-access-dns/README.md | 3 +- .../README.md | 4 +- networking/shared-vpc-gke/README.md | 3 +- third-party-solutions/openshift/tf/README.md | 3 +- tools/tfdoc.py | 41 ++++++++++--------- 78 files changed, 175 insertions(+), 98 deletions(-) diff --git a/cloud-operations/asset-inventory-feed-remediation/README.md b/cloud-operations/asset-inventory-feed-remediation/README.md index 0668a62a..2a7e4c52 100644 --- a/cloud-operations/asset-inventory-feed-remediation/README.md +++ b/cloud-operations/asset-inventory-feed-remediation/README.md @@ -52,6 +52,7 @@ Run the `subscription_pull` command until it returns nothing, then run the follo - the `tag_show` command to verify that the function output matches the resource state + ## Variables @@ -73,6 +74,6 @@ Run the `subscription_pull` command until it returns nothing, then run the follo | tag_add | Instance add tag command. | | | tag_show | Instance add tag command. | | - + diff --git a/cloud-operations/dns-fine-grained-iam/README.md b/cloud-operations/dns-fine-grained-iam/README.md index 36781137..b9cdea91 100644 --- a/cloud-operations/dns-fine-grained-iam/README.md +++ b/cloud-operations/dns-fine-grained-iam/README.md @@ -99,6 +99,7 @@ dig app1.svc.example.org +short # 127.0.0.7 ``` + ## Variables @@ -118,5 +119,5 @@ dig app1.svc.example.org +short | gcloud_commands | Commands used to SSH to the VMs. | | | vms | VM names. | | - + diff --git a/cloud-operations/dns-shared-vpc/README.md b/cloud-operations/dns-shared-vpc/README.md index 267b39ef..899ddfba 100644 --- a/cloud-operations/dns-shared-vpc/README.md +++ b/cloud-operations/dns-shared-vpc/README.md @@ -19,6 +19,7 @@ The resources created in this example are shown in the high level diagram below: Note that Terraform 0.13 at least is required due to the use of `for_each` with modules. + ## Variables @@ -39,5 +40,5 @@ Note that Terraform 0.13 at least is required due to the use of `for_each` with |---|---|:---:| | teams | Team resources | | - + diff --git a/cloud-operations/iam-delegated-role-grants/README.md b/cloud-operations/iam-delegated-role-grants/README.md index f2dfd04c..2efd818d 100644 --- a/cloud-operations/iam-delegated-role-grants/README.md +++ b/cloud-operations/iam-delegated-role-grants/README.md @@ -64,6 +64,7 @@ If you get any warnings, check the roles and remove any of them granting any of - `resourcemanager.organizations.setIamPolicy` + ## Variables @@ -77,5 +78,5 @@ If you get any warnings, check the roles and remove any of them granting any of | project_create | Create project instead of using an existing one. | bool | | false | | restricted_role_grant | Role grant to which the restrictions will apply. | string | | "roles/resourcemanager.projectIamAdmin" | - + diff --git a/cloud-operations/onprem-sa-key-management/README.md b/cloud-operations/onprem-sa-key-management/README.md index c1939136..3c2d35b9 100644 --- a/cloud-operations/onprem-sa-key-management/README.md +++ b/cloud-operations/onprem-sa-key-management/README.md @@ -61,6 +61,7 @@ gcloud auth activate-service-account --key-file data-uploader.json terraform destroy -var project_id=$GOOGLE_CLOUD_PROJECT ``` + ## Variables @@ -78,5 +79,5 @@ terraform destroy -var project_id=$GOOGLE_CLOUD_PROJECT |---|---|:---:| | sa-credentials | SA json key templates. | | - + diff --git a/cloud-operations/packer-image-builder/README.md b/cloud-operations/packer-image-builder/README.md index 6339c72f..a04e79af 100644 --- a/cloud-operations/packer-image-builder/README.md +++ b/cloud-operations/packer-image-builder/README.md @@ -67,6 +67,7 @@ The following example assumes that provisioning of a Compute Engine VM requires the resources over the Internet (i.e. to install OS packages). Since Compute VM has no public IP address for security reasons, Internet connectivity is done with [Cloud NAT](https://cloud.google.com/nat/docs/overview). + ## Variables @@ -93,5 +94,5 @@ address for security reasons, Internet connectivity is done with [Cloud NAT](htt | compute_subnetwork | Name of a subnetwork for Packer's temporary VM. | | | compute_zone | Name of a compute engine zone for Packer's temporary VM. | | - + diff --git a/cloud-operations/quota-monitoring/README.md b/cloud-operations/quota-monitoring/README.md index 47611aa6..2c9f2df7 100644 --- a/cloud-operations/quota-monitoring/README.md +++ b/cloud-operations/quota-monitoring/README.md @@ -23,6 +23,7 @@ Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/c - `terraform init` - `terraform apply -var project_id=my-project-id` + ## Variables @@ -37,6 +38,6 @@ Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/c | region | Compute region used in the example. | string | | "europe-west1" | | schedule_config | Schedule timer configuration in crontab format | string | | "0 * * * *" | - + diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index f1fabbe8..56153ff8 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -52,6 +52,7 @@ This is an optional part, created if `cai_gcs_export` is set to `true`. The high + ## Variables @@ -79,6 +80,6 @@ This is an optional part, created if `cai_gcs_export` is set to `true`. The high | bq-dataset | Bigquery instance details. | | | cloud-function | Cloud Function instance details. | | - + diff --git a/data-solutions/cmek-via-centralized-kms/README.md b/data-solutions/cmek-via-centralized-kms/README.md index a476b70a..7c5af4fb 100644 --- a/data-solutions/cmek-via-centralized-kms/README.md +++ b/data-solutions/cmek-via-centralized-kms/README.md @@ -30,6 +30,7 @@ This sample creates several distinct groups of resources: - GCS - One bucket encrypted with a CMEK Cryptokey hosted in Cloud KMS + ## Variables @@ -56,5 +57,5 @@ This sample creates several distinct groups of resources: | vm | GCE VM. | | | vm_keys | GCE VM Cloud KMS crypto keys. | | - + diff --git a/data-solutions/data-platform-foundations/01-environment/README.md b/data-solutions/data-platform-foundations/01-environment/README.md index d7bca97d..4add9b7c 100644 --- a/data-solutions/data-platform-foundations/01-environment/README.md +++ b/data-solutions/data-platform-foundations/01-environment/README.md @@ -47,6 +47,7 @@ gcloud access-context-manager perimeters list --format="json" | grep name The script use 'google_access_context_manager_service_perimeter_resource' terraform resource. If this resource is used alongside the 'vpc-sc' module, remember to uncomment the lifecycle block in the 'vpc-sc' module so they don't fight over which resources should be in the perimeter. + ## Variables @@ -70,5 +71,5 @@ The script use 'google_access_context_manager_service_perimeter_resource' terraf | service_account | Main service account. | | | service_encryption_key_ids | Cloud KMS encryption keys in {LOCATION => [KEY_URL]} format. | | - + diff --git a/data-solutions/data-platform-foundations/02-resources/README.md b/data-solutions/data-platform-foundations/02-resources/README.md index e34cdef4..e340fcd5 100644 --- a/data-solutions/data-platform-foundations/02-resources/README.md +++ b/data-solutions/data-platform-foundations/02-resources/README.md @@ -51,6 +51,7 @@ Once done testing, you can clean up resources by running `terraform destroy`. ### CMEK configuration You can configure GCP resources to use existing CMEK keys configuring the 'service_encryption_key_ids' variable. You need to specify a 'global' and a 'multiregional' key. + ## Variables @@ -81,5 +82,5 @@ You can configure GCP resources to use existing CMEK keys configuring the 'servi | transformation-buckets | List of buckets created for the transformation project. | | | transformation-vpc | Transformation VPC details | | - + diff --git a/data-solutions/gcs-to-bq-with-dataflow/README.md b/data-solutions/gcs-to-bq-with-dataflow/README.md index 5ab92d73..9d214e20 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/README.md +++ b/data-solutions/gcs-to-bq-with-dataflow/README.md @@ -110,6 +110,7 @@ schema_bq_import.json You can check data imported into Google BigQuery from the Google Cloud Console UI. + ## Variables @@ -136,5 +137,5 @@ You can check data imported into Google BigQuery from the Google Cloud Console U | projects | Project ids. | | | vm | GCE VM. | | - + diff --git a/factories/firewall-hierarchical-policies/README.md b/factories/firewall-hierarchical-policies/README.md index cf7cee30..1590e9b8 100644 --- a/factories/firewall-hierarchical-policies/README.md +++ b/factories/firewall-hierarchical-policies/README.md @@ -148,6 +148,7 @@ web_frontends: - web-frontends@project-wf2.iam.gserviceaccount.com ``` + ## Variables @@ -163,5 +164,5 @@ web_frontends: |---|---|:---:| | hierarchical-firewall-rules | Generated Hierarchical Firewall Rules | | - + diff --git a/factories/firewall-vpc-rules/flat/README.md b/factories/firewall-vpc-rules/flat/README.md index 96f3bc41..6baa5862 100644 --- a/factories/firewall-vpc-rules/flat/README.md +++ b/factories/firewall-vpc-rules/flat/README.md @@ -135,6 +135,7 @@ web-app-a-ingress: - web-app-a@myproject-id.iam.gserviceaccount.com ``` + ## Variables @@ -155,5 +156,5 @@ web-app-a-ingress: | ingress_allow_rules | Ingress rules with allow blocks. | | | ingress_deny_rules | Ingress rules with deny blocks. | | - + diff --git a/factories/firewall-vpc-rules/nested/README.md b/factories/firewall-vpc-rules/nested/README.md index 35bdf418..ae6be494 100644 --- a/factories/firewall-vpc-rules/nested/README.md +++ b/factories/firewall-vpc-rules/nested/README.md @@ -124,6 +124,7 @@ web_frontends: - web-frontends@project-wf2.iam.gserviceaccount.com ``` + ## Variables @@ -139,5 +140,5 @@ web_frontends: |---|---|:---:| | vpc-firewall-rules | Generated VPC Firewall Rules | | - + diff --git a/factories/subnets/README.md b/factories/subnets/README.md index 0c9a54e0..5b922632 100644 --- a/factories/subnets/README.md +++ b/factories/subnets/README.md @@ -52,6 +52,7 @@ secondary_ip_ranges: # Opt- List of secondary IP ranges # Secondary ranges in name: cidr format ``` + ## Variables @@ -66,5 +67,5 @@ secondary_ip_ranges: # Opt- List of secondary IP ranges |---|---|:---:| | subnet | Generated subnets | | - + diff --git a/foundations/business-units/README.md b/foundations/business-units/README.md index 12d0fa75..4664e6d5 100644 --- a/foundations/business-units/README.md +++ b/foundations/business-units/README.md @@ -25,6 +25,7 @@ The number of resources in this sample is kept to a minimum so as to make it gen This sample uses a top-level folder to encapsulate projects that host resources that are not specific to a single environment. If no shared services are needed,the Terraform and audit modules can be easily attached to the root node, and the shared services folder and project removed from `main.tf`. + ## Variables @@ -57,5 +58,5 @@ This sample uses a top-level folder to encapsulate projects that host resources | shared_resources_project | Project that holdes resources shared across business units. | | | terraform_project | Project that holds the base Terraform resources. | | - + diff --git a/foundations/environments/README.md b/foundations/environments/README.md index 67b41f0f..b20e9c05 100644 --- a/foundations/environments/README.md +++ b/foundations/environments/README.md @@ -27,6 +27,7 @@ For more complex setups where multiple shared services projects are needed to en If no shared services are needed, the shared service project module can of course be removed from `main.tf`. + ## Variables @@ -63,5 +64,5 @@ If no shared services are needed, the shared service project module can of cours | shared_services_project | Project that holdes resources shared across environments. | | | terraform_project | Project that holds the base Terraform resources. | | - + diff --git a/modules/__experimental/net-neg/README.md b/modules/__experimental/net-neg/README.md index 8915c344..8357296c 100644 --- a/modules/__experimental/net-neg/README.md +++ b/modules/__experimental/net-neg/README.md @@ -24,6 +24,7 @@ module "neg" { } ``` + ## Variables @@ -45,5 +46,5 @@ module "neg" { | self_lnk | Network endpoint group self link | | | size | Size of the network endpoint group | | - + diff --git a/modules/apigee-organization/README.md b/modules/apigee-organization/README.md index d2325e2f..33933efb 100644 --- a/modules/apigee-organization/README.md +++ b/modules/apigee-organization/README.md @@ -99,6 +99,7 @@ module "apigee-organization" { # tftest:modules=1:resources=6 ``` + ## Variables @@ -125,5 +126,5 @@ module "apigee-organization" { | org_id | Apigee Organization ID. | | | subscription_type | Apigee subscription type. | | - + diff --git a/modules/apigee-x-instance/README.md b/modules/apigee-x-instance/README.md index d63dcfaf..eab58179 100644 --- a/modules/apigee-x-instance/README.md +++ b/modules/apigee-x-instance/README.md @@ -43,6 +43,7 @@ module "apigee-x-instance" { # tftest:modules=1:resources=5 ``` + ## Variables @@ -66,5 +67,5 @@ module "apigee-x-instance" { | instance | Apigee instance. | | | port | Port number of the internal endpoint of the Apigee instance. | | - + diff --git a/modules/artifact-registry/README.md b/modules/artifact-registry/README.md index 507a6e8e..051d858e 100644 --- a/modules/artifact-registry/README.md +++ b/modules/artifact-registry/README.md @@ -20,6 +20,7 @@ module "docker_artifact_registry" { # tftest:modules=1:resources=2 ``` + ## Variables @@ -41,5 +42,5 @@ module "docker_artifact_registry" { | id | Repository id | | | name | Repository name | | - + diff --git a/modules/bigquery-dataset/README.md b/modules/bigquery-dataset/README.md index e9ff2d5c..1919dd55 100644 --- a/modules/bigquery-dataset/README.md +++ b/modules/bigquery-dataset/README.md @@ -174,6 +174,7 @@ module "bigquery-dataset" { # tftest:modules=1:resources=3 ``` + ## Variables @@ -208,6 +209,6 @@ module "bigquery-dataset" { | view_ids | Map of fully qualified view ids keyed by view ids. | | | views | View resources. | | - + diff --git a/modules/bigtable-instance/README.md b/modules/bigtable-instance/README.md index cbbae24f..c560a5bf 100644 --- a/modules/bigtable-instance/README.md +++ b/modules/bigtable-instance/README.md @@ -33,6 +33,7 @@ module "bigtable-instance" { # tftest:modules=1:resources=4 ``` + ## Variables @@ -61,6 +62,6 @@ module "bigtable-instance" { | table_ids | Map of fully qualified table ids keyed by table name. | | | tables | Table resources. | | - + diff --git a/modules/billing-budget/README.md b/modules/billing-budget/README.md index 54f2face..6a0f4607 100644 --- a/modules/billing-budget/README.md +++ b/modules/billing-budget/README.md @@ -62,6 +62,7 @@ module "pubsub" { # tftest:modules=2:resources=2 ``` + ## Variables @@ -87,5 +88,5 @@ module "pubsub" { | budget | Budget resource. | | | id | Budget ID. | | - + diff --git a/modules/cloud-config-container/coredns/README.md b/modules/cloud-config-container/coredns/README.md index 03c8d72b..e1af3eba 100644 --- a/modules/cloud-config-container/coredns/README.md +++ b/modules/cloud-config-container/coredns/README.md @@ -69,6 +69,7 @@ module "cos-coredns" { } ``` + ## Variables @@ -87,5 +88,5 @@ module "cos-coredns" { |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | - + diff --git a/modules/cloud-config-container/cos-generic-metadata/README.md b/modules/cloud-config-container/cos-generic-metadata/README.md index 70192670..51f9a601 100644 --- a/modules/cloud-config-container/cos-generic-metadata/README.md +++ b/modules/cloud-config-container/cos-generic-metadata/README.md @@ -58,6 +58,7 @@ module "cos-envoy" { } ``` + ## Variables @@ -85,5 +86,5 @@ module "cos-envoy" { |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | - + diff --git a/modules/cloud-config-container/envoy-traffic-director/README.md b/modules/cloud-config-container/envoy-traffic-director/README.md index 99615428..a0856f1d 100644 --- a/modules/cloud-config-container/envoy-traffic-director/README.md +++ b/modules/cloud-config-container/envoy-traffic-director/README.md @@ -46,6 +46,7 @@ module "vm-cos" { ``` + ## Variables @@ -61,5 +62,5 @@ module "vm-cos" { |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | - + diff --git a/modules/cloud-config-container/mysql/README.md b/modules/cloud-config-container/mysql/README.md index 0e894b5b..9abf5b63 100644 --- a/modules/cloud-config-container/mysql/README.md +++ b/modules/cloud-config-container/mysql/README.md @@ -74,6 +74,7 @@ module "cos-mysql" { } ``` + ## Variables @@ -94,5 +95,5 @@ module "cos-mysql" { |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | - + diff --git a/modules/cloud-config-container/nginx/README.md b/modules/cloud-config-container/nginx/README.md index 1db8ece9..c0d3a9e3 100644 --- a/modules/cloud-config-container/nginx/README.md +++ b/modules/cloud-config-container/nginx/README.md @@ -52,6 +52,7 @@ module "cos-nginx" { } ``` + ## Variables @@ -71,5 +72,5 @@ module "cos-nginx" { |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | - + diff --git a/modules/cloud-config-container/onprem/README.md b/modules/cloud-config-container/onprem/README.md index 560226fb..9dcc5a14 100644 --- a/modules/cloud-config-container/onprem/README.md +++ b/modules/cloud-config-container/onprem/README.md @@ -59,6 +59,7 @@ module "on-prem" { } ``` + ## Variables @@ -78,5 +79,5 @@ module "on-prem" { |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | - + diff --git a/modules/cloud-config-container/squid/README.md b/modules/cloud-config-container/squid/README.md index 368ece78..42b43d6d 100644 --- a/modules/cloud-config-container/squid/README.md +++ b/modules/cloud-config-container/squid/README.md @@ -56,6 +56,7 @@ module "cos-squid" { } ``` + ## Variables @@ -78,5 +79,5 @@ module "cos-squid" { |---|---|:---:| | cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | - + diff --git a/modules/cloud-function/README.md b/modules/cloud-function/README.md index 77516b90..b3c430b0 100644 --- a/modules/cloud-function/README.md +++ b/modules/cloud-function/README.md @@ -155,6 +155,7 @@ module "cf-http" { # tftest:skip ``` + ## Variables @@ -193,5 +194,5 @@ module "cf-http" { | service_account_iam_email | Service account email. | | | vpc_connector | VPC connector resource if created. | | - + diff --git a/modules/cloud-identity-group/README.md b/modules/cloud-identity-group/README.md index c3ff7864..ea0ab02e 100644 --- a/modules/cloud-identity-group/README.md +++ b/modules/cloud-identity-group/README.md @@ -32,6 +32,7 @@ module "group" { # tftest:modules=1:resources=4 ``` + ## Variables @@ -51,5 +52,5 @@ module "group" { | id | Group ID. | | | name | Group name. | | - + diff --git a/modules/cloud-run/README.md b/modules/cloud-run/README.md index cdc1e0a4..b7a2d184 100644 --- a/modules/cloud-run/README.md +++ b/modules/cloud-run/README.md @@ -207,6 +207,7 @@ module "cloud_run" { # tftest:modules=1:resources=1 ``` + ## Variables @@ -242,5 +243,5 @@ module "cloud_run" { | service_name | Cloud Run service name | | | vpc_connector | VPC connector resource if created. | | - + diff --git a/modules/cloudsql-instance/README.md b/modules/cloudsql-instance/README.md index 217fcd45..334aa79e 100644 --- a/modules/cloudsql-instance/README.md +++ b/modules/cloudsql-instance/README.md @@ -91,6 +91,7 @@ module "db" { # tftest:modules=1:resources=6 ``` + ## Variables @@ -131,5 +132,5 @@ module "db" { | self_links | Self links of all instances | | | user_passwords | Map of containing the password of all users created through terraform. | ✓ | - + diff --git a/modules/compute-mig/README.md b/modules/compute-mig/README.md index 841ff57a..51b49915 100644 --- a/modules/compute-mig/README.md +++ b/modules/compute-mig/README.md @@ -444,6 +444,7 @@ module "nginx-mig" { ``` + ## Variables @@ -474,9 +475,9 @@ module "nginx-mig" { | group_manager | Instance group resource. | | | health_check | Auto-created health-check resource. | | - + ## TODO - [✓] add support for instance groups diff --git a/modules/compute-vm/README.md b/modules/compute-vm/README.md index b29ba0a4..1634888c 100644 --- a/modules/compute-vm/README.md +++ b/modules/compute-vm/README.md @@ -293,6 +293,7 @@ module "instance-group" { # tftest:modules=1:resources=2 ``` + ## Variables @@ -344,9 +345,9 @@ module "instance-group" { | template | Template resource. | | | template_name | Template name. | | - + ## TODO - [ ] add support for instance groups diff --git a/modules/container-registry/README.md b/modules/container-registry/README.md index 04723dc9..12a4d186 100644 --- a/modules/container-registry/README.md +++ b/modules/container-registry/README.md @@ -16,6 +16,7 @@ module "container_registry" { # tftest:modules=1:resources=2 ``` + ## Variables @@ -32,5 +33,5 @@ module "container_registry" { |---|---|:---:| | bucket_id | ID of the GCS bucket created | | - + diff --git a/modules/datafusion/README.md b/modules/datafusion/README.md index c8e6013e..65c1aa68 100644 --- a/modules/datafusion/README.md +++ b/modules/datafusion/README.md @@ -34,6 +34,7 @@ module "datafusion" { # tftest:modules=1:resources=3 ``` + ## Variables @@ -66,5 +67,5 @@ module "datafusion" { | service_endpoint | DataFusion Service Endpoint. | | | version | DataFusion version. | | - + diff --git a/modules/dns/README.md b/modules/dns/README.md index 95d908d7..e4a252d6 100644 --- a/modules/dns/README.md +++ b/modules/dns/README.md @@ -53,6 +53,7 @@ module "private-dns" { # tftest:modules=1:resources=1 ``` + ## Variables @@ -85,5 +86,5 @@ module "private-dns" { | type | The DNS zone type. | | | zone | DNS zone resource. | | - + diff --git a/modules/endpoints/README.md b/modules/endpoints/README.md index 8d5636f3..09e294d7 100644 --- a/modules/endpoints/README.md +++ b/modules/endpoints/README.md @@ -23,6 +23,7 @@ module "endpoint" { [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. + ## Variables @@ -43,5 +44,5 @@ module "endpoint" { | endpoints_service | The Endpoint service resource. | | | service_name | The name of the service.. | | - + diff --git a/modules/folder/README.md b/modules/folder/README.md index 41ece1f2..2f9d0934 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -219,6 +219,7 @@ module "folder2" { # tftest:modules=2:resources=6 ``` + ## Variables @@ -251,5 +252,5 @@ module "folder2" { | name | Folder name. | | | sink_writer_identities | Writer identities created for each sink. | | - + diff --git a/modules/folders-unit/README.md b/modules/folders-unit/README.md index a3b6d2a2..d563ccfb 100644 --- a/modules/folders-unit/README.md +++ b/modules/folders-unit/README.md @@ -24,6 +24,7 @@ module "folders-unit" { # tftest:modules=1:resources=37 ``` + ## Variables @@ -55,5 +56,5 @@ module "folders-unit" { | env_service_accounts | Unit environments service accounts. | | | unit_folder | Unit top level folder. | | - + diff --git a/modules/gcs/README.md b/modules/gcs/README.md index a7f8297e..c65eaa27 100644 --- a/modules/gcs/README.md +++ b/modules/gcs/README.md @@ -107,6 +107,7 @@ module "bucket-gcs-notification" { # tftest:modules=1:resources=4 ``` + ## Variables @@ -141,5 +142,5 @@ module "bucket-gcs-notification" { | topic | Topic ID used by GCS. | | | url | Bucket URL. | | - + diff --git a/modules/gke-cluster/README.md b/modules/gke-cluster/README.md index 20c98797..9c591757 100644 --- a/modules/gke-cluster/README.md +++ b/modules/gke-cluster/README.md @@ -63,6 +63,7 @@ module "cluster-1" { # tftest:modules=1:resources=1 ``` + ## Variables @@ -120,5 +121,5 @@ module "cluster-1" { | name | Cluster name. | | | notifications | GKE PubSub notifications topic. | | - + diff --git a/modules/gke-nodepool/README.md b/modules/gke-nodepool/README.md index 12a22da1..8229b7bf 100644 --- a/modules/gke-nodepool/README.md +++ b/modules/gke-nodepool/README.md @@ -34,6 +34,7 @@ module "cluster-1-nodepool-1" { # tftest:modules=1:resources=2 ``` + ## Variables @@ -83,5 +84,5 @@ module "cluster-1-nodepool-1" { | service_account_email | Service account email. | | | service_account_iam_email | Service account email. | | - + diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index ee443ebb..ddea6fb5 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -25,6 +25,7 @@ module "myproject-default-service-accounts" { # tftest:modules=1:resources=5 ``` + ## Variables @@ -56,5 +57,5 @@ module "myproject-default-service-accounts" { | service_account | Service account resource. | | | service_account_credentials | Service account json credential templates for uploaded public keys data. | | - + diff --git a/modules/kms/README.md b/modules/kms/README.md index 9b2b55db..e17a3425 100644 --- a/modules/kms/README.md +++ b/modules/kms/README.md @@ -68,6 +68,7 @@ module "kms" { # tftest:modules=1:resources=4 ``` + ## Variables @@ -94,5 +95,5 @@ module "kms" { | location | Keyring location. | | | name | Keyring name. | | - + diff --git a/modules/logging-bucket/README.md b/modules/logging-bucket/README.md index 08c811c4..79b4815e 100644 --- a/modules/logging-bucket/README.md +++ b/modules/logging-bucket/README.md @@ -41,6 +41,7 @@ module "bucket-default" { ``` + ## Variables @@ -60,5 +61,5 @@ module "bucket-default" { |---|---|:---:| | id | ID of the created bucket. | | - + diff --git a/modules/naming-convention/README.md b/modules/naming-convention/README.md index d56629dc..4ef9d461 100644 --- a/modules/naming-convention/README.md +++ b/modules/naming-convention/README.md @@ -65,6 +65,7 @@ module "project-tf" { } ``` + ## Variables @@ -87,5 +88,5 @@ module "project-tf" { | labels | Per resource labels. | | | names | Per resource names. | | - + diff --git a/modules/net-address/README.md b/modules/net-address/README.md index c3f596d5..e51f820e 100644 --- a/modules/net-address/README.md +++ b/modules/net-address/README.md @@ -84,6 +84,7 @@ module "addresses" { # tftest:modules=1:resources=2 ``` + ## Variables @@ -108,5 +109,5 @@ module "addresses" { | psa_addresses | Allocated internal addresses for PSA endpoints. | | | psc_addresses | Allocated internal addresses for PSC endpoints. | | - + diff --git a/modules/net-cloudnat/README.md b/modules/net-cloudnat/README.md index 29b8cfd0..3dfe4e4d 100644 --- a/modules/net-cloudnat/README.md +++ b/modules/net-cloudnat/README.md @@ -15,6 +15,7 @@ module "nat" { # tftest:modules=1:resources=2 ``` + ## Variables @@ -45,5 +46,5 @@ module "nat" { | router | Cloud NAT router resources (if auto created). | | | router_name | Cloud NAT router name. | | - + diff --git a/modules/net-ilb/README.md b/modules/net-ilb/README.md index 6a242d36..18f9d441 100644 --- a/modules/net-ilb/README.md +++ b/modules/net-ilb/README.md @@ -108,6 +108,7 @@ module "ilb" { # tftest:modules=3:resources=7 ``` + ## Variables @@ -149,5 +150,5 @@ module "ilb" { | 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-interconnect-attachment-direct/README.md b/modules/net-interconnect-attachment-direct/README.md index eff2e556..85fd6b60 100644 --- a/modules/net-interconnect-attachment-direct/README.md +++ b/modules/net-interconnect-attachment-direct/README.md @@ -104,6 +104,7 @@ module "vlan-attachment-2" { # tftest:modules=2:resources=8 ``` + ## Variables @@ -130,5 +131,5 @@ module "vlan-attachment-2" { | interconnect_attachment | interconnect attachment | | | router | Router resource (only if auto-created). | | - + diff --git a/modules/net-vpc-firewall/README.md b/modules/net-vpc-firewall/README.md index 8288c6ac..4592463a 100644 --- a/modules/net-vpc-firewall/README.md +++ b/modules/net-vpc-firewall/README.md @@ -124,6 +124,7 @@ healthchecks: ``` + ## Variables @@ -153,5 +154,5 @@ healthchecks: | custom_ingress_deny_rules | Custom ingress rules with deny blocks. | | | rules | All google_compute_firewall resources created. | | - + diff --git a/modules/net-vpc-peering/README.md b/modules/net-vpc-peering/README.md index 5cf83210..61cf4f4d 100644 --- a/modules/net-vpc-peering/README.md +++ b/modules/net-vpc-peering/README.md @@ -41,6 +41,7 @@ module "peering-a-c" { # tftest:modules=2:resources=4 ``` + ## Variables @@ -61,5 +62,5 @@ module "peering-a-c" { | local_network_peering | Network peering resource. | | | peer_network_peering | Peer network peering resource. | | - + diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md index ee5da703..737f4eb5 100644 --- a/modules/net-vpc/README.md +++ b/modules/net-vpc/README.md @@ -202,6 +202,7 @@ flow_logs: # enable, set to empty map to use defaults - metadata: "INCLUDE_ALL_METADATA" ``` + ## Variables @@ -249,7 +250,7 @@ flow_logs: # enable, set to empty map to use defaults | subnets | Subnet resources. | | | subnets_l7ilb | L7 ILB subnet resources. | | - + The key format is `subnet_region/subnet_name`. For example `europe-west1/my_subnet`. diff --git a/modules/net-vpn-dynamic/README.md b/modules/net-vpn-dynamic/README.md index 23333d1d..7b1b0ca1 100644 --- a/modules/net-vpn-dynamic/README.md +++ b/modules/net-vpn-dynamic/README.md @@ -39,6 +39,7 @@ module "vpn-dynamic" { # tftest:modules=1:resources=10 ``` + ## Variables @@ -73,5 +74,5 @@ module "vpn-dynamic" { | tunnel_self_links | VPN tunnel self links. | | | tunnels | VPN tunnel resources. | | - + diff --git a/modules/net-vpn-ha/README.md b/modules/net-vpn-ha/README.md index d464b289..7f4317ab 100644 --- a/modules/net-vpn-ha/README.md +++ b/modules/net-vpn-ha/README.md @@ -139,6 +139,7 @@ module "vpn_ha" { # tftest:modules=1:resources=10 ``` + ## Variables @@ -176,5 +177,5 @@ module "vpn_ha" { | tunnel_self_links | VPN tunnel self links. | | | tunnels | VPN tunnel resources. | | - + diff --git a/modules/net-vpn-static/README.md b/modules/net-vpn-static/README.md index 9713b043..6eba9b20 100644 --- a/modules/net-vpn-static/README.md +++ b/modules/net-vpn-static/README.md @@ -32,6 +32,7 @@ module "vpn" { # tftest:modules=2:resources=8 ``` + ## Variables @@ -61,5 +62,5 @@ module "vpn" { | tunnel_self_links | VPN tunnel self links. | | | tunnels | VPN tunnel resources. | | - + diff --git a/modules/organization/README.md b/modules/organization/README.md index 561ae064..e3182b56 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -235,6 +235,7 @@ module "org" { # tftest:modules=1:resources=2 ``` + ## Variables @@ -270,5 +271,5 @@ module "org" { | organization_id | Organization id dependent on module resources. | | | sink_writer_identities | Writer identities created for each sink. | | - + diff --git a/modules/project/README.md b/modules/project/README.md index d07d1ffb..3f1b50dc 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -173,6 +173,7 @@ module "project" { # tftest:modules=1:resources=7 ``` + ## Variables @@ -222,6 +223,6 @@ module "project" { | service_accounts | Product robot service accounts in project. | | | sink_writer_identities | Writer identities created for each sink. | | - + diff --git a/modules/pubsub/README.md b/modules/pubsub/README.md index fb5fb293..d47a5c95 100644 --- a/modules/pubsub/README.md +++ b/modules/pubsub/README.md @@ -88,6 +88,7 @@ module "pubsub" { # tftest:modules=1:resources=3 ``` + ## Variables @@ -115,5 +116,5 @@ module "pubsub" { | subscriptions | Subscription resources. | | | topic | Topic resource. | | - + diff --git a/modules/secret-manager/README.md b/modules/secret-manager/README.md index dc3aca21..059eeb89 100644 --- a/modules/secret-manager/README.md +++ b/modules/secret-manager/README.md @@ -73,6 +73,7 @@ module "secret-manager" { # tftest:modules=1:resources=5 ``` + ## Variables @@ -94,9 +95,9 @@ module "secret-manager" { | version_ids | Version ids keyed by secret name : version name. | | | versions | Secret versions. | | - + ## Requirements These sections describe requirements for using this module. diff --git a/modules/service-directory/README.md b/modules/service-directory/README.md index 0915400c..3d6bc6b4 100644 --- a/modules/service-directory/README.md +++ b/modules/service-directory/README.md @@ -88,6 +88,7 @@ module "dns-sd" { # tftest:modules=2:resources=5 ``` + ## Variables @@ -115,5 +116,5 @@ module "dns-sd" { | service_names | Service ids (long names). | | | services | Service resources. | | - + diff --git a/modules/source-repository/README.md b/modules/source-repository/README.md index a7473a7d..cf0a7e04 100644 --- a/modules/source-repository/README.md +++ b/modules/source-repository/README.md @@ -19,6 +19,7 @@ module "repo" { # tftest:modules=1:resources=2 ``` + ## Variables @@ -36,5 +37,5 @@ module "repo" { | id | Repository id. | | | url | Repository URL. | | - + diff --git a/modules/vpc-sc/README.md b/modules/vpc-sc/README.md index c1810872..fded87d1 100644 --- a/modules/vpc-sc/README.md +++ b/modules/vpc-sc/README.md @@ -231,6 +231,7 @@ module "vpc-sc-first" { # tftest:modules=1:resources=2 ``` + ## Variables @@ -260,5 +261,5 @@ module "vpc-sc-first" { | perimeters_bridge | VPC-SC bridge perimeter resources. | | | perimeters_standard | VPC-SC standard perimeter resources. | | - + diff --git a/networking/decentralized-firewall/README.md b/networking/decentralized-firewall/README.md index 2bdf6bbc..dc988622 100644 --- a/networking/decentralized-firewall/README.md +++ b/networking/decentralized-firewall/README.md @@ -20,6 +20,7 @@ The rules can be validated either using an automated process or a manual process the two). There is an example of a YAML-based validator using [Yamale](https://github.com/23andMe/Yamale) in the [`validator/`](validator/) subdirectory, which can be integrated as part of a CI/CD pipeline. + ## Variables @@ -41,5 +42,5 @@ in the [`validator/`](validator/) subdirectory, which can be integrated as part | projects | Project ids. | | | vpc | Shared VPCs. | | - + diff --git a/networking/filtering-proxy/README.md b/networking/filtering-proxy/README.md index 792301d9..9edfe6a2 100644 --- a/networking/filtering-proxy/README.md +++ b/networking/filtering-proxy/README.md @@ -15,6 +15,7 @@ You can optionally deploy the Squid server as [Managed Instance Group](https://c ![High-level diagram](squid.png "High-level diagram") + ## Variables @@ -36,5 +37,5 @@ You can optionally deploy the Squid server as [Managed Instance Group](https://c |---|---|:---:| | squid-address | IP address of the Squid proxy. | | - + diff --git a/networking/hub-and-spoke-peering/README.md b/networking/hub-and-spoke-peering/README.md index ae19ed18..30c3ba1a 100644 --- a/networking/hub-and-spoke-peering/README.md +++ b/networking/hub-and-spoke-peering/README.md @@ -79,6 +79,7 @@ A few APIs need to be enabled in the project, if `apply` fails due to a service 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). + ## Variables @@ -100,5 +101,5 @@ The VPN used to connect the GKE masters VPC does not account for HA, upgrading t | project | Project id. | | | vms | GCE VMs. | | - + diff --git a/networking/hub-and-spoke-vpn/README.md b/networking/hub-and-spoke-vpn/README.md index da1dac6d..81cd1a1b 100644 --- a/networking/hub-and-spoke-vpn/README.md +++ b/networking/hub-and-spoke-vpn/README.md @@ -34,6 +34,7 @@ The example does not account for HA, but the VPN gateways can be easily upgraded If a single router and VPN gateway are used in the hub to manage all tunnels, particular care must be taken in announcing ranges from hub to spokes, as Cloud Router does not explicitly support transitivity and overlapping routes received from both sides create unintended side effects. The simple workaround is to announce a single aggregated route from hub to spokes so that it does not overlap with any of the ranges advertised by each spoke to the hub. + ## Variables @@ -53,5 +54,5 @@ If a single router and VPN gateway are used in the hub to manage all tunnels, pa |---|---|:---:| | vms | GCE VMs. | | - + diff --git a/networking/ilb-next-hop/README.md b/networking/ilb-next-hop/README.md index b6c496f7..66e337f3 100644 --- a/networking/ilb-next-hop/README.md +++ b/networking/ilb-next-hop/README.md @@ -60,6 +60,7 @@ A sample testing session using `tmux`: Test session screenshot + ## Variables @@ -86,5 +87,5 @@ A sample testing session using `tmux`: | ssh_vm_left | Command-line login to left VMs. | | | ssh_vm_right | Command-line login to right VMs. | | - + diff --git a/networking/onprem-google-access-dns/README.md b/networking/onprem-google-access-dns/README.md index 53d5c811..888171f4 100644 --- a/networking/onprem-google-access-dns/README.md +++ b/networking/onprem-google-access-dns/README.md @@ -201,6 +201,7 @@ A single pre-existing project is used in this example to keep variables and comp The VPN-s used to connect to the on-premises environment do not account for HA, upgrading to use HA VPN is reasonably simple by using the relevant [module](../../modules/net-vpn-ha). + ## Variables @@ -224,5 +225,5 @@ The VPN-s used to connect to the on-premises environment do not account for HA, | test-instance1 | Test instance details. | | | test-instance2 | Test instance details. | | - + diff --git a/networking/private-cloud-function-from-onprem/README.md b/networking/private-cloud-function-from-onprem/README.md index 9c34d720..f787e6fa 100644 --- a/networking/private-cloud-function-from-onprem/README.md +++ b/networking/private-cloud-function-from-onprem/README.md @@ -14,6 +14,7 @@ curl https://YOUR_REGION-YOUR_PROJECT_ID.cloudfunctions.net/YOUR_FUNCTION_NAME ![Cloud Function via Private Service Connect](diagram.png "High-level diagram") + ## Variables @@ -33,5 +34,4 @@ curl https://YOUR_REGION-YOUR_PROJECT_ID.cloudfunctions.net/YOUR_FUNCTION_NAME |---|---|:---:| | function_url | URL of the Cloud Function. | | - - \ No newline at end of file + diff --git a/networking/shared-vpc-gke/README.md b/networking/shared-vpc-gke/README.md index 3bfa1dc5..67658a22 100644 --- a/networking/shared-vpc-gke/README.md +++ b/networking/shared-vpc-gke/README.md @@ -42,6 +42,7 @@ alias k='HTTPS_PROXY=localhost:8888 kubectl $@' There's a minor glitch that can surface running `terraform destroy`, where the service project attachments to the Shared VPC will not get destroyed even with the relevant API call succeeding. We are investigating the issue, in the meantime just manually remove the attachment in the Cloud console or via the `gcloud beta compute shared-vpc associated-projects remove` command when `terraform destroy` fails, and then relaunch the command. + ## Variables @@ -70,5 +71,5 @@ There's a minor glitch that can surface running `terraform destroy`, where the s | vms | GCE VMs. | | | vpc | Shared VPC. | | - + diff --git a/third-party-solutions/openshift/tf/README.md b/third-party-solutions/openshift/tf/README.md index 2af199c3..8a76e93a 100644 --- a/third-party-solutions/openshift/tf/README.md +++ b/third-party-solutions/openshift/tf/README.md @@ -2,6 +2,7 @@ This example is a companion setup to the Python script in the parent folder, and is used to bootstrap OpenShift clusters on GCP. Refer to the documentation in the parent folder for usage instructions. + ## Variables @@ -30,5 +31,5 @@ This example is a companion setup to the Python script in the parent folder, and | bootstrap-ssh | Command to SSH to the bootstrap instance. | | | masters-ssh | Command to SSH to the master instances. | | - + diff --git a/tools/tfdoc.py b/tools/tfdoc.py index 69974d3e..892c1010 100755 --- a/tools/tfdoc.py +++ b/tools/tfdoc.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -49,7 +49,7 @@ import urllib.parse import click -__version__ = '2.0.1' +__version__ = '2.1.0' # TODO(ludomagno): decide if we want to support variables*.tf and outputs*.tf @@ -153,15 +153,18 @@ def _parse(body, enum=VAR_ENUM, re=VAR_RE, template=VAR_TEMPLATE): item[context].append(data) -def parse_files(basepath): +def parse_files(basepath, exclude_files=None): 'Return a list of File named tuples in root module at basepath.' + exclude_files = exclude_files or [] for name in glob.glob(os.path.join(basepath, '*tf')): + shortname = os.path.basename(name) + if shortname in exclude_files: + continue try: with open(name) as file: body = file.read() except (IOError, OSError) as e: raise SystemExit(f'Cannot read file {name}: {e}') - shortname = os.path.basename(name) tags = _extract_tags(body) description = tags.get( 'file:description', FILE_DESC_DEFAULTS.get(shortname)) @@ -218,19 +221,16 @@ def format_doc(outputs, variables, files, show_extra=False): 'Return formatted document.' buffer = [] if files: - buffer.append('\n## Files\n') - for line in format_files(files): - buffer.append(line) - buffer.append('\n') + buffer += ['', '## Files', ''] + buffer += list(format_files(files)) if variables: - buffer.append('\n## Variables\n') - for line in format_variables(variables, show_extra): - buffer.append(line) + buffer += ['', '## Variables', ''] + buffer += list(format_variables(variables, show_extra)) if outputs: - buffer.append('\n## Outputs\n') - for line in format_outputs(outputs, show_extra): - buffer.append(line) - buffer.append('\n') + buffer += ['', '## Outputs', ''] + buffer += list(format_outputs(outputs, show_extra)) + if buffer: + buffer.append('') return '\n'.join(buffer) @@ -321,9 +321,9 @@ def get_doc(readme): return {'doc': m.group(1), 'start': m.start(), 'end': m.end()} -def create_doc(module_path, files=False, show_extra=False): +def create_doc(module_path, files=False, show_extra=False, exclude_files=None): try: - mod_files = list(parse_files(module_path)) if files else [] + mod_files = list(parse_files(module_path, exclude_files)) if files else [] mod_variables = list(parse_variables(module_path)) mod_outputs = list(parse_outputs(module_path)) except (IOError, OSError) as e: @@ -357,13 +357,14 @@ def replace_doc(module_path, doc): @ click.command() @ click.argument('module', type=click.Path(exists=True)) -@ click.option('--show-extra/--no-show-extra', default=False) +@click.option('--exclude-file', '-x', multiple=True) @ click.option('--files/--no-files', default=False) @ click.option('--replace/--no-replace', default=True) -def main(module=None, annotate=False, files=False, replace=True, +@ click.option('--show-extra/--no-show-extra', default=False) +def main(module=None, annotate=False, exclude_file=None, files=False, replace=True, show_extra=True): 'Program entry point.' - doc = create_doc(module, files, show_extra) + doc = create_doc(module, files, show_extra, exclude_file) if replace: replace_doc(module, doc) else: From 640a5fe39c324423ae2b4ec1b0183a3e95754ece Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Fri, 31 Dec 2021 12:20:42 +0100 Subject: [PATCH 39/52] Org/Folder: Allow for policy association when using rule factory (#405) * Org/Folder: Allow for policy association when using rule factory * Fix linting issue --- modules/folder/README.md | 9 +++++++-- modules/folder/outputs.tf | 2 +- modules/organization/README.md | 9 +++++++-- modules/organization/outputs.tf | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/modules/folder/README.md b/modules/folder/README.md index 2f9d0934..10a1e990 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -57,10 +57,13 @@ module "folder" { parent = "organizations/1234567890" name = "Folder name" firewall_policy_factory = { - cidr_file = "data/cidrs.yaml + cidr_file = "data/cidrs.yaml" policy_name = null rules_file = "data/rules.yaml" } + firewall_policy_attachments = { + factory-policy = module.folder.firewall_policy_id["factory"] + } } # tftest:skip ``` @@ -70,7 +73,7 @@ module "folder" { rfc1918: - 10.0.0.0/8 - - 172.168.0.0/12 + - 172.16.0.0/12 - 192.168.0.0/16 ``` @@ -220,6 +223,7 @@ module "folder2" { ``` + ## Variables @@ -254,3 +258,4 @@ module "folder2" { + diff --git a/modules/folder/outputs.tf b/modules/folder/outputs.tf index f095a367..8e1a9f0b 100644 --- a/modules/folder/outputs.tf +++ b/modules/folder/outputs.tf @@ -24,7 +24,7 @@ output "firewall_policies" { output "firewall_policy_id" { description = "Map of firewall policy ids created in this folder." value = { - for name, _ in var.firewall_policies : + for name, _ in local.firewall_policies : name => google_compute_organization_security_policy.policy[name].id } } diff --git a/modules/organization/README.md b/modules/organization/README.md index e3182b56..d0956d47 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -93,10 +93,13 @@ module "org" { source = "./modules/organization" organization_id = var.organization_id firewall_policy_factory = { - cidr_file = "data/cidrs.yaml + cidr_file = "data/cidrs.yaml" policy_name = null rules_file = "data/rules.yaml" } + firewall_policy_attachments = { + factory-policy = module.org.firewall_policy_id["factory"] + } } # tftest:skip ``` @@ -106,7 +109,7 @@ module "org" { rfc1918: - 10.0.0.0/8 - - 172.168.0.0/12 + - 172.16.0.0/12 - 192.168.0.0/16 ``` @@ -236,6 +239,7 @@ module "org" { ``` + ## Variables @@ -273,3 +277,4 @@ module "org" { + diff --git a/modules/organization/outputs.tf b/modules/organization/outputs.tf index 5b685b57..3435242f 100644 --- a/modules/organization/outputs.tf +++ b/modules/organization/outputs.tf @@ -44,7 +44,7 @@ output "firewall_policies" { output "firewall_policy_id" { description = "Map of firewall policy ids created in the organization." value = { - for name, _ in var.firewall_policies : + for name, _ in local.firewall_policies : name => google_compute_organization_security_policy.policy[name].id } } From f78902aee85d1ab10f1acf3b9727d188c349cdb9 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 31 Dec 2021 11:36:14 +0000 Subject: [PATCH 40/52] Update hierarchical firewall resource This replaces all the `google_compute_organization_security_*` resources with the newer `google_compute_firewall_*` resources. --- modules/folder/README.md | 22 ++++---- modules/folder/firewall-policy.tf | 51 ++++++++----------- modules/folder/outputs.tf | 10 +--- modules/folder/variables.tf | 4 +- modules/organization/README.md | 14 +++-- modules/organization/firewall-policy.tf | 50 ++++++++---------- modules/organization/outputs.tf | 14 ++--- modules/organization/variables.tf | 4 +- tests/modules/folder/fixture/main.tf | 2 +- tests/modules/folder/fixture/variables.tf | 2 +- .../folder/test_plan_firewall_policy.py | 20 ++++---- tests/modules/organization/fixture/main.tf | 2 +- .../modules/organization/fixture/variables.tf | 2 +- .../organization/test_plan_firewall.py | 14 ++--- 14 files changed, 87 insertions(+), 124 deletions(-) diff --git a/modules/folder/README.md b/modules/folder/README.md index 10a1e990..593b875c 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -192,22 +192,20 @@ module "folder1" { firewall_policies = { iap-policy = { allow-iap-ssh = { - description = "Always allow ssh from IAP" - direction = "INGRESS" - action = "allow" - priority = 100 - ranges = ["35.235.240.0/20"] - ports = { - tcp = ["22"] - } + description = "Always allow ssh from IAP" + direction = "INGRESS" + action = "allow" + priority = 100 + ranges = ["35.235.240.0/20"] + ports = { tcp = ["22"] } target_service_accounts = null target_resources = null logging = false } } } - firewall_policy_attachments = { - iap-policy = module.folder1.firewall_policy_id["iap-policy"] + firewall_policy_association = { + iap-policy = "iap-policy" } } @@ -215,7 +213,7 @@ module "folder2" { source = "./modules/folder" parent = var.organization_id name = "hf2" - firewall_policy_attachments = { + firewall_policy_association = { iap-policy = module.folder1.firewall_policy_id["iap-policy"] } } @@ -232,7 +230,7 @@ module "folder2" { |---|---|:---:|:---:|:---:| | contacts | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES | map(list(string)) | | {} | | firewall_policies | Hierarchical firewall policies created in this folder. | map(map(object({…}))) | | {} | -| firewall_policy_attachments | List of hierarchical firewall policy IDs to attached to this folder. | map(string) | | {} | +| firewall_policy_association | The hierarchical firewall policy to associate to this folder. Must be either a key in the `firewall_policies` map or the id of a policy defined somewhere else. | map(string) | | {} | | firewall_policy_factory | Configuration for the firewall policy factory. | object({…}) | | null | | folder_create | Create folder. When set to false, uses id to reference an existing folder. | bool | | true | | group_iam | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string)) | | {} | diff --git a/modules/folder/firewall-policy.tf b/modules/folder/firewall-policy.tf index abda1187..312d9adb 100644 --- a/modules/folder/firewall-policy.tf +++ b/modules/folder/firewall-policy.tf @@ -52,17 +52,15 @@ locals { } } -resource "google_compute_organization_security_policy" "policy" { - provider = google-beta - for_each = local.firewall_policies - display_name = each.key - parent = local.folder.id +resource "google_compute_firewall_policy" "policy" { + for_each = local.firewall_policies + short_name = each.key + parent = local.folder.id } -resource "google_compute_organization_security_policy_rule" "rule" { - provider = google-beta +resource "google_compute_firewall_policy_rule" "rule" { for_each = local.firewall_rules - policy_id = google_compute_organization_security_policy.policy[each.value.policy].id + firewall_policy = google_compute_firewall_policy.policy[each.value.policy].id action = each.value.action direction = each.value.direction priority = try(each.value.priority, null) @@ -70,33 +68,26 @@ resource "google_compute_organization_security_policy_rule" "rule" { target_service_accounts = try(each.value.target_service_accounts, null) enable_logging = try(each.value.logging, null) # preview = each.value.preview + description = each.value.description match { - description = each.value.description - config { - src_ip_ranges = each.value.direction == "INGRESS" ? each.value.ranges : null - dest_ip_ranges = each.value.direction == "EGRESS" ? each.value.ranges : null - dynamic "layer4_config" { - for_each = each.value.ports - iterator = port - content { - ip_protocol = port.key - ports = port.value - } + src_ip_ranges = each.value.direction == "INGRESS" ? each.value.ranges : null + dest_ip_ranges = each.value.direction == "EGRESS" ? each.value.ranges : null + dynamic "layer4_configs" { + for_each = each.value.ports + iterator = port + content { + ip_protocol = port.key + ports = port.value } } } - # TODO: remove once provider issues is fixed - # https://github.com/hashicorp/terraform-provider-google/issues/7790 - lifecycle { - ignore_changes = [match.0.description] - } } -resource "google_compute_organization_security_policy_association" "attachment" { - provider = google-beta - for_each = var.firewall_policy_attachments - name = "${local.folder.id}-${each.key}" - attachment_id = local.folder.id - policy_id = each.value + +resource "google_compute_firewall_policy_association" "association" { + for_each = var.firewall_policy_association + name = replace(local.folder.id, "/", "-") + attachment_target = local.folder.id + firewall_policy = try(google_compute_firewall_policy.policy[each.value].id, each.value) } diff --git a/modules/folder/outputs.tf b/modules/folder/outputs.tf index 8e1a9f0b..da368210 100644 --- a/modules/folder/outputs.tf +++ b/modules/folder/outputs.tf @@ -15,18 +15,12 @@ */ output "firewall_policies" { description = "Map of firewall policy resources created in this folder." - value = { - for name, _ in var.firewall_policies : - name => google_compute_organization_security_policy.policy[name] - } + value = { for k, v in google_compute_firewall_policy.policy : k => v } } output "firewall_policy_id" { description = "Map of firewall policy ids created in this folder." - value = { - for name, _ in local.firewall_policies : - name => google_compute_organization_security_policy.policy[name].id - } + value = { for k, v in google_compute_firewall_policy.policy : k => v.id } } output "folder" { diff --git a/modules/folder/variables.tf b/modules/folder/variables.tf index 0abcc48e..4bf5a83c 100644 --- a/modules/folder/variables.tf +++ b/modules/folder/variables.tf @@ -36,8 +36,8 @@ variable "firewall_policies" { default = {} } -variable "firewall_policy_attachments" { - description = "List of hierarchical firewall policy IDs to attached to this folder." +variable "firewall_policy_association" { + description = "The hierarchical firewall policy to associate to this folder. Must be either a key in the `firewall_policies` map or the id of a policy defined somewhere else." type = map(string) default = {} } diff --git a/modules/organization/README.md b/modules/organization/README.md index d0956d47..45a298a0 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -52,7 +52,7 @@ Hirerarchical firewall policies can be managed in two ways: - via the `firewall_policies` variable, to directly define policies and rules in Terraform - via the `firewall_policy_factory` variable, to leverage external YaML files via a simple "factory" embedded in the module ([see here](../../factories) for more context on factories) -Once you have policies (either created via the module or externally), you can attach them using the `firewall_policy_attachments` variable. +Once you have policies (either created via the module or externally), you can associate them using the `firewall_policy_association` variable. ### Directly defined firewall policies @@ -77,8 +77,8 @@ module "org" { } } } - firewall_policy_attachments = { - iap_policy = module.org.firewall_policy_id["iap-policy"] + firewall_policy_association = { + iap_policy = "iap-policy" } } # tftest:modules=1:resources=3 @@ -250,7 +250,7 @@ module "org" { | contacts | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES | map(list(string)) | | {} | | custom_roles | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | | firewall_policies | Hierarchical firewall policy rules created in the organization. | map(map(object({…}))) | | {} | -| firewall_policy_attachments | List of hierarchical firewall policy IDs attached to the organization. | map(string) | | {} | +| firewall_policy_association | The hierarchical firewall policy to associate to this folder. Must be either a key in the `firewall_policies` map or the id of a policy defined somewhere else. | map(string) | | {} | | firewall_policy_factory | Configuration for the firewall policy factory. | object({…}) | | null | | group_iam | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string)) | | {} | | iam | IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | @@ -270,11 +270,9 @@ module "org" { |---|---|:---:| | custom_role_id | Map of custom role IDs created in the organization. | | | custom_roles | Map of custom roles resources created in the organization. | | -| firewall_policies | Map of firewall policy resources created in the organization. | | -| firewall_policy_id | Map of firewall policy ids created in the organization. | | +| firewall_policies | Map of firewall policy resources created in this folder. | | +| firewall_policy_id | Map of firewall policy ids created in this folder. | | | organization_id | Organization id dependent on module resources. | | | sink_writer_identities | Writer identities created for each sink. | | - - diff --git a/modules/organization/firewall-policy.tf b/modules/organization/firewall-policy.tf index 4d111023..2c304601 100644 --- a/modules/organization/firewall-policy.tf +++ b/modules/organization/firewall-policy.tf @@ -52,11 +52,10 @@ locals { } } -resource "google_compute_organization_security_policy" "policy" { - provider = google-beta - for_each = local.firewall_policies - display_name = each.key - parent = var.organization_id +resource "google_compute_firewall_policy" "policy" { + for_each = local.firewall_policies + short_name = each.key + parent = var.organization_id depends_on = [ google_organization_iam_audit_config.config, google_organization_iam_binding.authoritative, @@ -66,10 +65,9 @@ resource "google_compute_organization_security_policy" "policy" { ] } -resource "google_compute_organization_security_policy_rule" "rule" { - provider = google-beta +resource "google_compute_firewall_policy_rule" "rule" { for_each = local.firewall_rules - policy_id = google_compute_organization_security_policy.policy[each.value.policy].id + firewall_policy = google_compute_firewall_policy.policy[each.value.policy].id action = each.value.action direction = each.value.direction priority = try(each.value.priority, null) @@ -77,33 +75,25 @@ resource "google_compute_organization_security_policy_rule" "rule" { target_service_accounts = try(each.value.target_service_accounts, null) enable_logging = try(each.value.logging, null) # preview = each.value.preview + description = each.value.description match { - description = each.value.description - config { - src_ip_ranges = each.value.direction == "INGRESS" ? each.value.ranges : null - dest_ip_ranges = each.value.direction == "EGRESS" ? each.value.ranges : null - dynamic "layer4_config" { - for_each = each.value.ports - iterator = port - content { - ip_protocol = port.key - ports = port.value - } + src_ip_ranges = each.value.direction == "INGRESS" ? each.value.ranges : null + dest_ip_ranges = each.value.direction == "EGRESS" ? each.value.ranges : null + dynamic "layer4_configs" { + for_each = each.value.ports + iterator = port + content { + ip_protocol = port.key + ports = port.value } } } - # TODO: remove once provider issues is fixed - # https://github.com/hashicorp/terraform-provider-google/issues/7790 - lifecycle { - ignore_changes = [match.0.description] - } } -resource "google_compute_organization_security_policy_association" "attachment" { - provider = google-beta - for_each = var.firewall_policy_attachments - name = "${var.organization_id}-${each.key}" - attachment_id = var.organization_id - policy_id = each.value +resource "google_compute_firewall_policy_association" "association" { + for_each = var.firewall_policy_association + name = replace(var.organization_id, "/", "-") + attachment_target = var.organization_id + firewall_policy = try(google_compute_firewall_policy.policy[each.value].id, each.value) } diff --git a/modules/organization/outputs.tf b/modules/organization/outputs.tf index 3435242f..4c67183c 100644 --- a/modules/organization/outputs.tf +++ b/modules/organization/outputs.tf @@ -34,19 +34,13 @@ output "custom_roles" { } output "firewall_policies" { - description = "Map of firewall policy resources created in the organization." - value = { - for name, _ in var.firewall_policies : - name => google_compute_organization_security_policy.policy[name] - } + description = "Map of firewall policy resources created in this folder." + value = { for k, v in google_compute_firewall_policy.policy : k => v } } output "firewall_policy_id" { - description = "Map of firewall policy ids created in the organization." - value = { - for name, _ in local.firewall_policies : - name => google_compute_organization_security_policy.policy[name].id - } + description = "Map of firewall policy ids created in this folder." + value = { for k, v in google_compute_firewall_policy.policy : k => v.id } } output "organization_id" { diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf index 2ca79a6d..374b7270 100644 --- a/modules/organization/variables.tf +++ b/modules/organization/variables.tf @@ -43,8 +43,8 @@ variable "firewall_policies" { default = {} } -variable "firewall_policy_attachments" { - description = "List of hierarchical firewall policy IDs attached to the organization." +variable "firewall_policy_association" { + description = "The hierarchical firewall policy to associate to this folder. Must be either a key in the `firewall_policies` map or the id of a policy defined somewhere else." type = map(string) default = {} } diff --git a/tests/modules/folder/fixture/main.tf b/tests/modules/folder/fixture/main.tf index 2d6e8ff9..d9bd34ba 100644 --- a/tests/modules/folder/fixture/main.tf +++ b/tests/modules/folder/fixture/main.tf @@ -22,7 +22,7 @@ module "test" { policy_boolean = var.policy_boolean policy_list = var.policy_list firewall_policies = var.firewall_policies - firewall_policy_attachments = var.firewall_policy_attachments + firewall_policy_association = var.firewall_policy_association logging_sinks = var.logging_sinks logging_exclusions = var.logging_exclusions } diff --git a/tests/modules/folder/fixture/variables.tf b/tests/modules/folder/fixture/variables.tf index 9d994bc0..824560d9 100644 --- a/tests/modules/folder/fixture/variables.tf +++ b/tests/modules/folder/fixture/variables.tf @@ -49,7 +49,7 @@ variable "firewall_policies" { default = {} } -variable "firewall_policy_attachments" { +variable "firewall_policy_association" { type = map(string) default = {} } diff --git a/tests/modules/folder/test_plan_firewall_policy.py b/tests/modules/folder/test_plan_firewall_policy.py index 9754d437..9a7a31b5 100644 --- a/tests/modules/folder/test_plan_firewall_policy.py +++ b/tests/modules/folder/test_plan_firewall_policy.py @@ -54,17 +54,17 @@ def test_firweall_policy(plan_runner): } } """ - attachment = '{ iap_policy = "policy1" }' + association = '{policy1="policy1"}' _, resources = plan_runner(FIXTURES_DIR, firewall_policies=policy, - firewall_policy_attachments=attachment) + firewall_policy_association=association) assert len(resources) == 5 - + policies = [r for r in resources - if r['type'] == 'google_compute_organization_security_policy'] + if r['type'] == 'google_compute_firewall_policy'] assert len(policies) == 1 rules = [r for r in resources - if r['type'] == 'google_compute_organization_security_policy_rule'] + if r['type'] == 'google_compute_firewall_policy_rule'] assert len(rules) == 2 rule_values = [] @@ -74,22 +74,20 @@ def test_firweall_policy(plan_runner): action = rule['values']['action'] direction = rule['values']['direction'] priority = rule['values']['priority'] - config = rule['values']['match'] - assert len(config) == 1 - config = config[0]['config'] - rule_values.append((name, index, action, direction, priority, config)) + match = rule['values']['match'] + rule_values.append((name, index, action, direction, priority, match)) assert sorted(rule_values) == sorted([ ('rule', 'policy1-allow-ingress', 'allow', 'INGRESS', 100,[ { 'dest_ip_ranges': None, - 'layer4_config': [{'ip_protocol': 'tcp', 'ports': ['22']}], + 'layer4_configs': [{'ip_protocol': 'tcp', 'ports': ['22']}], 'src_ip_ranges': ['10.0.0.0/8'] }]), ('rule', 'policy1-deny-egress', 'deny', 'EGRESS', 200, [ { 'dest_ip_ranges': ['192.168.0.0/24'], - 'layer4_config': [{'ip_protocol': 'tcp', 'ports': ['443']}], + 'layer4_configs': [{'ip_protocol': 'tcp', 'ports': ['443']}], 'src_ip_ranges': None }]) ]) diff --git a/tests/modules/organization/fixture/main.tf b/tests/modules/organization/fixture/main.tf index 6c948c38..e875cae2 100644 --- a/tests/modules/organization/fixture/main.tf +++ b/tests/modules/organization/fixture/main.tf @@ -26,7 +26,7 @@ module "test" { policy_boolean = var.policy_boolean policy_list = var.policy_list firewall_policies = var.firewall_policies - firewall_policy_attachments = var.firewall_policy_attachments + firewall_policy_association = var.firewall_policy_association firewall_policy_factory = var.firewall_policy_factory logging_sinks = var.logging_sinks logging_exclusions = var.logging_exclusions diff --git a/tests/modules/organization/fixture/variables.tf b/tests/modules/organization/fixture/variables.tf index 8e04290c..bd3d2ce3 100644 --- a/tests/modules/organization/fixture/variables.tf +++ b/tests/modules/organization/fixture/variables.tf @@ -74,7 +74,7 @@ variable "firewall_policies" { default = {} } -variable "firewall_policy_attachments" { +variable "firewall_policy_association" { type = map(string) default = {} } diff --git a/tests/modules/organization/test_plan_firewall.py b/tests/modules/organization/test_plan_firewall.py index 8998bf7b..1aa8c1cb 100644 --- a/tests/modules/organization/test_plan_firewall.py +++ b/tests/modules/organization/test_plan_firewall.py @@ -80,9 +80,9 @@ def test_custom(plan_runner): _, resources = plan_runner(FIXTURES_DIR, firewall_policies=_POLICIES) assert len(resources) == 5 policies = [r for r in resources - if r['type'] == 'google_compute_organization_security_policy'] + if r['type'] == 'google_compute_firewall_policy'] rules = [r for r in resources - if r['type'] == 'google_compute_organization_security_policy_rule'] + if r['type'] == 'google_compute_firewall_policy_rule'] assert set(r['index'] for r in policies) == set([ 'policy1', 'policy2' ]) @@ -96,9 +96,9 @@ def test_factory(plan_runner): _, resources = plan_runner(FIXTURES_DIR, firewall_policy_factory=_FACTORY) assert len(resources) == 3 policies = [r for r in resources - if r['type'] == 'google_compute_organization_security_policy'] + if r['type'] == 'google_compute_firewall_policy'] rules = [r for r in resources - if r['type'] == 'google_compute_organization_security_policy_rule'] + if r['type'] == 'google_compute_firewall_policy_rule'] assert set(r['index'] for r in policies) == set([ 'factory-1' ]) @@ -113,7 +113,7 @@ def test_factory_name(plan_runner): _, resources = plan_runner(FIXTURES_DIR, firewall_policy_factory=factory) assert len(resources) == 3 policies = [r for r in resources - if r['type'] == 'google_compute_organization_security_policy'] + if r['type'] == 'google_compute_firewall_policy'] assert set(r['index'] for r in policies) == set([ 'factory' ]) @@ -125,9 +125,9 @@ def test_combined(plan_runner): firewall_policy_factory=_FACTORY) assert len(resources) == 8 policies = [r for r in resources - if r['type'] == 'google_compute_organization_security_policy'] + if r['type'] == 'google_compute_firewall_policy'] rules = [r for r in resources - if r['type'] == 'google_compute_organization_security_policy_rule'] + if r['type'] == 'google_compute_firewall_policy_rule'] assert set(r['index'] for r in policies) == set([ 'factory-1', 'policy1', 'policy2' ]) From d4adcaced0fd6f4519be51d98f54c458780a2823 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 31 Dec 2021 13:20:21 +0100 Subject: [PATCH 41/52] Fix typo. --- modules/organization/README.md | 6 ++++-- modules/organization/outputs.tf | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/organization/README.md b/modules/organization/README.md index 45a298a0..7a6ffeca 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -240,6 +240,7 @@ module "org" { + ## Variables @@ -270,9 +271,10 @@ module "org" { |---|---|:---:| | custom_role_id | Map of custom role IDs created in the organization. | | | custom_roles | Map of custom roles resources created in the organization. | | -| firewall_policies | Map of firewall policy resources created in this folder. | | -| firewall_policy_id | Map of firewall policy ids created in this folder. | | +| firewall_policies | Map of firewall policy resources created in the organization. | | +| firewall_policy_id | Map of firewall policy ids created in the organization. | | | organization_id | Organization id dependent on module resources. | | | sink_writer_identities | Writer identities created for each sink. | | + diff --git a/modules/organization/outputs.tf b/modules/organization/outputs.tf index 4c67183c..13b11de3 100644 --- a/modules/organization/outputs.tf +++ b/modules/organization/outputs.tf @@ -34,12 +34,12 @@ output "custom_roles" { } output "firewall_policies" { - description = "Map of firewall policy resources created in this folder." + description = "Map of firewall policy resources created in the organization." value = { for k, v in google_compute_firewall_policy.policy : k => v } } output "firewall_policy_id" { - description = "Map of firewall policy ids created in this folder." + description = "Map of firewall policy ids created in the organization." value = { for k, v in google_compute_firewall_policy.policy : k => v.id } } From 4494951554a53f299b7d79f44a725f18c6c2e2d0 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 31 Dec 2021 13:26:56 +0100 Subject: [PATCH 42/52] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f3a2a8c..c3be7e17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- update hierarchical firewall resources to use the newer `google_compute_firewall_*` resources +- **incompatible chaange** rename `firewall_policy_attachments` to `firewall_policy_association` in the `organization` and `folder` modules + ## [9.0.2] - 2021-12-22 - ignore description changes in firewall policy rule to avoid permadiff, add factory example to `folder` module documentation From c7dd5bc1d6cd47f210b209474570d87c76e7a94c Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 31 Dec 2021 13:27:25 +0100 Subject: [PATCH 43/52] Typo fix --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3be7e17..1ed14e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - update hierarchical firewall resources to use the newer `google_compute_firewall_*` resources -- **incompatible chaange** rename `firewall_policy_attachments` to `firewall_policy_association` in the `organization` and `folder` modules +- **incompatible change** rename `firewall_policy_attachments` to `firewall_policy_association` in the `organization` and `folder` modules ## [9.0.2] - 2021-12-22 From 2c7dab3bb2b572c4d60bc91ba48eb9e4ffbb8913 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 31 Dec 2021 13:29:22 +0100 Subject: [PATCH 44/52] New vpc-sc module implementation (#406) * first implementation * minimal output * split service perimeters in regular and bridge * tests and fixes * new vpc-sc implementation * remove providers file used for testing * remove provider used during development --- modules/vpc-sc/README.md | 378 +++++++----------- modules/vpc-sc/access_levels.tf | 78 ++++ modules/vpc-sc/main.tf | 338 +--------------- modules/vpc-sc/outputs.tf | 48 +-- modules/vpc-sc/service_perimeters_bridge.tf | 41 ++ modules/vpc-sc/service_perimeters_regular.tf | 311 ++++++++++++++ modules/vpc-sc/variables.tf | 191 +++++---- tests/conftest.py | 5 +- .../modules/vpc_sc/__init__.py | 18 +- tests/modules/vpc_sc/fixture/main.tf | 146 +++++++ tests/modules/vpc_sc/test_plan.py | 49 +++ tests/requirements.txt | 2 +- 12 files changed, 924 insertions(+), 681 deletions(-) create mode 100644 modules/vpc-sc/access_levels.tf create mode 100644 modules/vpc-sc/service_perimeters_bridge.tf create mode 100644 modules/vpc-sc/service_perimeters_regular.tf rename modules/vpc-sc/versions.tf => tests/modules/vpc_sc/__init__.py (63%) create mode 100644 tests/modules/vpc_sc/fixture/main.tf create mode 100644 tests/modules/vpc_sc/test_plan.py diff --git a/modules/vpc-sc/README.md b/modules/vpc-sc/README.md index fded87d1..103ce001 100644 --- a/modules/vpc-sc/README.md +++ b/modules/vpc-sc/README.md @@ -1,236 +1,148 @@ -# VPC Service Control Module +# VPC Service Controls -This module allows managing VPC Service Control (VPC-SC) properties: +This module offers a unified interface to manage VPC Service Controls [Access Policy](https://cloud.google.com/access-context-manager/docs/create-access-policy), [Access Levels](https://cloud.google.com/access-context-manager/docs/manage-access-levels), and [Service Perimeters](https://cloud.google.com/vpc-service-controls/docs/service-perimeters). -- [Access Policy](https://cloud.google.com/access-context-manager/docs/create-access-policy) -- [Access Levels](https://cloud.google.com/access-context-manager/docs/manage-access-levels) -- [VPC-SC Perimeters](https://cloud.google.com/vpc-service-controls/docs/service-perimeters) +Given the complexity of the underlying resources, the module intentionally mimics their interfaces to make it easier to map their documentation onto its variables, and reduce the internal complexity. The tradeoff is some verbosity, and a very complex type for the `service_perimeters_regular` variable (while [optional type attributes](https://www.terraform.io/language/expressions/type-constraints#experimental-optional-object-type-attributes) are still an experiment). -The Use of this module requires credentials with the [correct permissions](https://cloud.google.com/access-context-manager/docs/access-control) to use Access Context Manager. +If you are using [Application Default Credentials](https://cloud.google.com/sdk/gcloud/reference/auth/application-default) with Terraform and run into permissions issues, make sure to check out the recommended provider configuration in the [VPC SC resources documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_access_level). -## Example VCP-SC standard perimeter +## Examples + +### Access policy + +By default, the module is configured to use an existing policy, passed in by name in the `access_policy` variable: ```hcl -module "vpc-sc" { - source = "./modules/vpc-sc" - organization_id = "organizations/112233" - access_policy_title = "My Access Policy" +module "test" { + source = "./modules/vpc-sc" + access_policy = "accessPolicies/12345678" +} +# tftest:modules=0:resources=0 +``` + +If you need the module to create the policy for you, use the `access_policy_create` variable, and set `access_policy` to `null`: + +```hcl +module "test" { + source = "./modules/vpc-sc" + access_policy = null + access_policy_create = { + parent = "organizations/123456" + title = "vpcsc-policy" + } +} +# tftest:modules=1:resources=1 +``` + +### Access levels + +As highlighted above, the `access_levels` type replicates the underlying resource structure. + +```hcl +module "test" { + source = "./modules/vpc-sc" + access_policy = "accessPolicies/12345678" access_levels = { - my_trusted_proxy = { - combining_function = "AND" + a1 = { + combining_function = null conditions = [{ - ip_subnetworks = ["85.85.85.52/32"] - required_access_levels = null - members = [] - negate = false - regions = null + members = ["user:ludomagno@google.com"], + device_policy = null, ip_subnetworks = null, negate = null, + regions = null, required_access_levels = null + }] + } + a2 = { + combining_function = "OR" + conditions = [{ + regions = ["IT", "FR"], + device_policy = null, ip_subnetworks = null, members = null, + negate = null, required_access_levels = null + },{ + ip_subnetworks = ["101.101.101.0/24"], + device_policy = null, members = null, negate = null, + regions = null, required_access_levels = null }] } } - access_level_perimeters = { - enforced = { - my_trusted_proxy = ["perimeter"] - } - } - ingress_policies = { - ingress_1 = { - ingress_from = { - identity_type = "ANY_IDENTITY" - } - ingress_to = { - resources = ["*"] - operations = { - "storage.googleapis.com" = [{ method = "google.storage.objects.create" }] - "bigquery.googleapis.com" = [{ method = "BigQueryStorage.ReadRows" }] - } - } - } - } - ingress_policies_perimeters = { - enforced = { - ingress_1 = ["default"] - } - } - - egress_policies = { - egress_1 = { - egress_from = { - identity_type = "ANY_USER_ACCOUNT" - } - egress_to = { - resources = ["*"] - operations = { - "storage.googleapis.com" = [{ method = "google.storage.objects.create" }], - "bigquery.googleapis.com" = [{ method = "BigQueryStorage.ReadRows" },{ method = "TableService.ListTables" }, { permission = "bigquery.jobs.get" }] - } - } - } - } - egress_policies_perimeters = { - enforced = { - egress_1 = ["perimeter"] - } - } - perimeters = { - perimeter = { - 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] - } - } } -# 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" - organization_id = "organizations/112233" - access_policy_title = "My Access Policy" - access_levels = { - my_trusted_proxy = { - combining_function = "AND" - conditions = [{ - ip_subnetworks = ["85.85.85.52/32"] - required_access_levels = null - members = [] - negate = false - regions = null - }] - } - } - access_level_perimeters = { - enforced = { - my_trusted_proxy = ["perimeter"] - } - } - perimeters = { - perimeter = { - 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"] - } - } - } - perimeter_projects = { - perimeter = { - enforced = [111111111, 222222222] - dry_run = [333333333] - } - } -} -# tftest:modules=1:resources=3 -``` - -## Example VCP-SC: 2 standard perimeters with one bridge between the two (dry run mode). -```hcl -module "vpc-sc" { - source = "./modules/vpc-sc" - organization_id = "organizations/112233" - access_policy_title = "My Access Policy" - perimeters = { - perimeter_1 = { - 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 = null - } - perimeter_2 = { - 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 = null - } - perimeter_bridge = { - type = "PERIMETER_TYPE_BRIDGE" - dry_run_config = null - enforced_config = null - } - } - perimeter_projects = { - perimeter_1 = { - enforced = [] - dry_run = [111111111] - } - perimeter_2 = { - enforced = [] - dry_run = [222222222] - } - perimeter_bridge = { - enforced = [] - dry_run = [111111111, 222222222] - } - } -} -# tftest:modules=1:resources=4 -``` - -## Example VCP-SC standard perimeter with one service and one project in dry run mode in a Organization with an already existent access policy -```hcl -module "vpc-sc-first" { - source = "./modules/vpc-sc" - organization_id = "organizations/112233" - access_policy_create = false - access_policy_name = "My Access Policy" - access_levels = { - my_trusted_proxy = { - combining_function = "AND" - conditions = [{ - ip_subnetworks = ["85.85.85.52/32"] - required_access_levels = null - members = [] - negate = false - regions = null - }] - } - } - access_level_perimeters = { - enforced = { - my_trusted_proxy = ["perimeter"] - } - } - perimeters = { - perimeter = { - 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"] - } - } - } - perimeter_projects = { - perimeter = { - enforced = [111111111, 222222222] - dry_run = [333333333] - } - } -} - # tftest:modules=1:resources=2 ``` +### Service perimeters + +Bridge and regulare service perimeters use two separate variables, as bridge perimeters only accept a limited number of arguments, and can leverage a much simpler interface. + +The regular perimeters variable exposes all the complexity of the underlying resource, use [its documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter) as a reference about the possible values and configurations. + +If you need to refer to access levels created by the same module in regular service perimeters, simply use the module's outputs in the provided variables. The example below shows how to do this in practice. + +Resources for both perimeters have a `lifecycle` block that ignores changes to `spec` and `status` resources (projects), to allow using the additive resource `google_access_context_manager_service_perimeter_resource` at project creation. If this is not needed, the `lifecycle` blocks can be safely commented in the code. + +#### Bridge type + +```hcl +module "test" { + source = "./modules/vpc-sc" + access_policy = "accessPolicies/12345678" + service_perimeters_bridge = { + b1 = { + status_resources = ["projects/111110", "projects/111111"] + spec_resources = null + use_explicit_dry_run_spec = false + } + b2 = { + status_resources = ["projects/222220", "projects/222221"] + spec_resources = ["projects/222220", "projects/222221"] + use_explicit_dry_run_spec = true + } + } +} +# tftest:modules=1:resources=2 +``` + +#### Regular type + +```hcl +module "test" { + source = "./modules/vpc-sc" + access_policy = "accessPolicies/12345678" + access_levels = { + a1 = { + combining_function = null + conditions = [{ + members = ["user:ludomagno@google.com"], + device_policy = null, ip_subnetworks = null, negate = null, + regions = null, required_access_levels = null + }] + } + } + service_perimeters_regular = { + r1 = { + spec = null + status = { + access_levels = [module.test.access_level_names["a1"]] + resources = ["projects/11111", "projects/111111"] + restricted_services = ["storage.googleapis.com"] + egress_policies = null + ingress_policies = null + vpc_accessible_services = { + allowed_services = ["compute.googleapis.com"] + enable_restriction = true + } + } + use_explicit_dry_run_spec = false + } + } +} +# tftest:modules=1:resources=2 +``` + +## TODO + +- [ ] implement support for the `google_access_context_manager_gcp_user_access_binding` resource + + + @@ -238,28 +150,24 @@ module "vpc-sc-first" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| organization_id | Organization id in organizations/nnnnnn format. | string | ✓ | | -| access_level_perimeters | Enforced mode -> Access Level -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | -| access_levels | Map of Access Levels to be created. For each Access Level you can specify 'ip_subnetworks, required_access_levels, members, negate or regions'. | map(object({…})) | | {} | -| access_policy_create | Enable autocreation of the Access Policy | bool | | true | -| access_policy_name | Referenced Access Policy name | string | | null | -| access_policy_title | Access Policy title to be created. | string | | null | -| egress_policies | List of EgressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#egress_policies) | | | null | -| egress_policies_perimeters | Enforced mode -> Egress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | -| ingress_policies | List of IngressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#ingress_policies) | | | null | -| ingress_policies_perimeters | Enforced mode -> Ingress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run' | map(map(list(string))) | | {} | -| perimeter_projects | Perimeter -> Enforced Mode -> Projects Number mapping. Enforced mode can be 'enforced' or 'dry_run'. | map(map(list(number))) | | {} | -| perimeters | Set of Perimeters. | map(object({…})) | | {} | +| access_policy | Access Policy name, leave null to use auto-created one. | string | ✓ | | +| access_levels | Map of access levels in name => [conditions] format. | map(object({…})) | | {} | +| access_policy_create | Access Policy configuration, fill in to create. Parent is in 'organizations/123456' format. | object({…}) | | null | +| service_perimeters_bridge | Bridge service perimeters. | map(object({…})) | | {} | +| service_perimeters_regular | Regular service perimeters. | map(object({…})) | | {} | ## Outputs | name | description | sensitive | |---|---|:---:| -| access_levels | Access Levels. | | -| access_policy_name | Access Policy resource | | -| organization_id | Organization id dependent on module resources. | | -| perimeters_bridge | VPC-SC bridge perimeter resources. | | -| perimeters_standard | VPC-SC standard perimeter resources. | | +| access_level_names | Access level resources. | | +| access_levels | Access level resources. | | +| access_policy | Access policy resource, if autocreated. | | +| access_policy_name | Access policy name. | | +| service_perimeters_bridge | Bridge service perimeter resources. | | +| service_perimeters_regular | Regular service perimeter resources. | | + + diff --git a/modules/vpc-sc/access_levels.tf b/modules/vpc-sc/access_levels.tf new file mode 100644 index 00000000..585570ee --- /dev/null +++ b/modules/vpc-sc/access_levels.tf @@ -0,0 +1,78 @@ +/** + * Copyright 2021 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. + */ + +# TODO(ludomagno): add a second variable and resource for custom access levels + +# this code implements "additive" access levels, if "authoritative" +# access levels are needed, switch to the +# google_access_context_manager_access_levels resource + +resource "google_access_context_manager_access_level" "basic" { + for_each = var.access_levels + parent = "accessPolicies/${local.access_policy}" + name = "accessPolicies/${local.access_policy}/accessLevels/${each.key}" + title = each.key + basic { + combining_function = each.value.combining_function + dynamic "conditions" { + for_each = toset( + each.value.conditions == null ? [] : each.value.conditions + ) + iterator = condition + content { + dynamic "device_policy" { + for_each = toset( + condition.key.device_policy == null ? [] : [condition.key.device_policy] + ) + iterator = device_policy + content { + dynamic "os_constraints" { + for_each = toset( + device_policy.key.os_constraints == null ? [] : device_policy.key.os_constraints + ) + iterator = os_constraint + content { + minimum_version = os_constraint.key.minimum_version + os_type = os_constraint.key.os_type + require_verified_chrome_os = os_constraint.key.require_verified_chrome_os + } + } + allowed_encryption_statuses = device_policy.key.allowed_encryption_statuses + allowed_device_management_levels = device_policy.key.allowed_device_management_levels + require_admin_approval = device_policy.key.require_admin_approval + require_corp_owned = device_policy.key.require_corp_owned + require_screen_lock = device_policy.key.require_screen_lock + } + } + ip_subnetworks = ( + condition.key.ip_subnetworks == null ? [] : condition.key.ip_subnetworks + ) + members = ( + condition.key.members == null ? [] : condition.key.members + ) + negate = condition.key.negate + regions = ( + condition.key.regions == null ? [] : condition.key.regions + ) + required_access_levels = ( + condition.key.required_access_levels == null + ? [] + : condition.key.required_access_levels + ) + } + } + } +} diff --git a/modules/vpc-sc/main.tf b/modules/vpc-sc/main.tf index 3e6c2801..73947036 100644 --- a/modules/vpc-sc/main.tf +++ b/modules/vpc-sc/main.tf @@ -15,340 +15,14 @@ */ locals { - access_policy_name = ( - var.access_policy_create - ? try(google_access_context_manager_access_policy.default[0].name, null) - : var.access_policy_name + access_policy = try( + google_access_context_manager_access_policy.default.0.name, + var.access_policy ) - - standard_perimeters = { - for key, value in var.perimeters : - key => value if value.type == "PERIMETER_TYPE_REGULAR" - } - - bridge_perimeters = { - for key, value in var.perimeters : - key => value if value.type == "PERIMETER_TYPE_BRIDGE" - } - - perimeter_access_levels_enforced = try(transpose(var.access_level_perimeters.enforced), null) - perimeter_access_levels_dry_run = try(transpose(var.access_level_perimeters.dry_run), null) - perimeter_ingress_policies_enforced = try(transpose(var.ingress_policies_perimeters.enforced), null) - perimeter_ingress_policies_dry_run = try(transpose(var.ingress_policies_perimeters.dry_run), null) - perimeter_egress_policies_enforced = try(transpose(var.egress_policies_perimeters.enforced), null) - perimeter_egress_policies_dry_run = try(transpose(var.egress_policies_perimeters.dry_run), null) } resource "google_access_context_manager_access_policy" "default" { - count = var.access_policy_create ? 1 : 0 - parent = var.organization_id - title = var.access_policy_title == null ? "${var.organization_id}-title" : var.access_policy_title -} - -resource "google_access_context_manager_access_level" "default" { - for_each = var.access_levels - parent = "accessPolicies/${local.access_policy_name}" - name = "accessPolicies/${local.access_policy_name}/accessLevels/${each.key}" - title = each.key - - dynamic "basic" { - for_each = try(toset(each.value.conditions), []) - iterator = condition - - content { - combining_function = try(each.value.combining_function, null) - conditions { - ip_subnetworks = try(condition.value.ip_subnetworks, null) - required_access_levels = try(condition.value.required_access_levels, null) - members = try(condition.value.members, null) - negate = try(condition.value.negate, null) - regions = try(condition.value.regions, null) - } - } - } -} - -resource "google_access_context_manager_service_perimeter" "standard" { - for_each = local.standard_perimeters - parent = "accessPolicies/${local.access_policy_name}" - description = "Terraform managed." - name = "accessPolicies/${local.access_policy_name}/servicePerimeters/${each.key}" - title = each.key - perimeter_type = each.value.type - - # Enforced mode configuration - dynamic "status" { - for_each = each.value.enforced_config != null ? [""] : [] - - content { - resources = formatlist( - "projects/%s", try(lookup(var.perimeter_projects, each.key, {}).enforced, []) - ) - restricted_services = each.value.enforced_config.restricted_services - access_levels = formatlist( - "accessPolicies/${local.access_policy_name}/accessLevels/%s", - try(lookup(local.perimeter_access_levels_enforced, each.key, []), []) - ) - - dynamic "vpc_accessible_services" { - for_each = try(length(each.value.enforced_config.vpc_accessible_services) != 0 ? [""] : [], []) - - content { - enable_restriction = true - allowed_services = each.value.enforced_config.vpc_accessible_services - } - } - - dynamic "egress_policies" { - for_each = try(local.perimeter_egress_policies_enforced[each.key] != null ? local.perimeter_egress_policies_enforced[each.key] : [], []) - - content { - dynamic "egress_from" { - for_each = try(var.egress_policies[egress_policies.value].egress_from != null ? [""] : [], []) - - content { - identity_type = try(var.egress_policies[egress_policies.value].egress_from.identity_type, null) - identities = try(var.egress_policies[egress_policies.value].egress_from.identities, null) - } - } - dynamic "egress_to" { - for_each = try(var.egress_policies[egress_policies.value].egress_to != null ? [""] : [], []) - - content { - resources = try(var.egress_policies[egress_policies.value].egress_to.resources, null) - - dynamic "operations" { - for_each = try(var.egress_policies[egress_policies.value].egress_to.operations, []) - - content { - service_name = try(operations.key, null) - - dynamic "method_selectors" { - for_each = try(operations.value, []) - - content { - method = try(method_selectors.value.method, null) - permission = try(method_selectors.value.permission, null) - } - } - } - } - } - } - } - } - - dynamic "ingress_policies" { - for_each = try(local.perimeter_ingress_policies_enforced[each.key] != null ? local.perimeter_ingress_policies_enforced[each.key] : [], []) - - content { - dynamic "ingress_from" { - for_each = try(var.ingress_policies[ingress_policies.value].ingress_from != null ? [""] : [], []) - - content { - identity_type = try(var.ingress_policies[ingress_policies.value].ingress_from.identity_type, null) - identities = try(var.ingress_policies[ingress_policies.value].ingress_from.identities, null) - - dynamic "sources" { - for_each = toset(try([var.ingress_policies[ingress_policies.value].ingress_from.sources], [])) - - content { - access_level = try(sources.value.access_level, null) - resource = try(sources.value.resource, null) - } - } - } - } - dynamic "ingress_to" { - for_each = try(var.ingress_policies[ingress_policies.value].ingress_to != null ? [""] : [], []) - - content { - resources = try(var.ingress_policies[ingress_policies.value].ingress_to.resources, null) - - dynamic "operations" { - for_each = try(var.ingress_policies[ingress_policies.value].ingress_to.operations, []) - - content { - service_name = try(operations.key, null) - - dynamic "method_selectors" { - for_each = try(operations.value, []) - - content { - method = try(method_selectors.value.method, null) - permission = try(method_selectors.value.permission, null) - } - } - } - } - } - } - } - } - } - } - - # Dry run mode configuration - use_explicit_dry_run_spec = each.value.dry_run_config != null ? true : false - dynamic "spec" { - for_each = each.value.dry_run_config != null ? [""] : [] - - content { - resources = formatlist( - "projects/%s", try(lookup(var.perimeter_projects, each.key, {}).dry_run, []) - ) - restricted_services = try(each.value.dry_run_config.restricted_services, null) - access_levels = formatlist( - "accessPolicies/${local.access_policy_name}/accessLevels/%s", - try(lookup(local.perimeter_access_levels_dry_run, each.key, []), []) - ) - - dynamic "vpc_accessible_services" { - for_each = try(length(each.value.dry_run_config.vpc_accessible_services) != 0 ? [""] : [], []) - - content { - enable_restriction = true - allowed_services = try(each.value.dry_run_config.vpc_accessible_services, null) - } - } - - dynamic "egress_policies" { - for_each = try(local.perimeter_egress_policies_dry_run[each.key] != null ? local.perimeter_egress_policies_dry_run[each.key] : [], []) - - content { - dynamic "egress_from" { - for_each = try(var.egress_policies[egress_policies.value].egress_from != null ? [""] : [], []) - - content { - identity_type = try(var.egress_policies[egress_policies.value].egress_from.identity_type, null) - identities = try(var.egress_policies[egress_policies.value].egress_from.identities, null) - } - } - dynamic "egress_to" { - for_each = try(var.egress_policies[egress_policies.value].egress_to != null ? [""] : [], []) - - content { - resources = try(var.egress_policies[egress_policies.value].egress_to.resources, null) - - dynamic "operations" { - for_each = try(var.egress_policies[egress_policies.value].egress_to.operations, []) - - content { - service_name = try(operations.key, null) - - dynamic "method_selectors" { - for_each = try(operations.value, []) - - content { - method = try(method_selectors.value.method, null) - permission = try(method_selectors.value.permission, null) - } - } - } - } - } - } - } - } - - dynamic "ingress_policies" { - for_each = try(local.perimeter_ingress_policies_dry_run[each.key] != null ? local.perimeter_ingress_policies_dry_run[each.key] : [], []) - - content { - dynamic "ingress_from" { - for_each = try(var.ingress_policies[ingress_policies.value].ingress_from != null ? [""] : [], []) - - content { - identity_type = try(var.ingress_policies[ingress_policies.value].ingress_from.identity_type, null) - identities = try(var.ingress_policies[ingress_policies.value].ingress_from.identities, null) - - dynamic "sources" { - for_each = toset(try([var.ingress_policies[ingress_policies.value].ingress_from.sources], [])) - - content { - access_level = try(sources.value.access_level, null) - resource = try(sources.value.resource, null) - } - } - } - } - dynamic "ingress_to" { - for_each = try(var.ingress_policies[ingress_policies.value].ingress_to != null ? [""] : [], []) - - content { - resources = try(var.ingress_policies[ingress_policies.value].ingress_to.resources, null) - - dynamic "operations" { - for_each = try(var.ingress_policies[ingress_policies.value].ingress_to.operations, []) - - content { - service_name = try(operations.key, null) - - dynamic "method_selectors" { - for_each = try(operations.value, []) - - content { - method = try(method_selectors.value.method, null) - permission = try(method_selectors.value.permission, null) - } - } - } - } - } - } - } - } - } - } - - # Uncomment if used alongside `google_access_context_manager_service_perimeter_resource`, - # so they don't fight over which resources should be in the policy. - # lifecycle { - # ignore_changes = [status[0].resources] - # } - - depends_on = [ - google_access_context_manager_access_level.default, - ] -} - -resource "google_access_context_manager_service_perimeter" "bridge" { - for_each = local.bridge_perimeters - parent = "accessPolicies/${local.access_policy_name}" - description = "Terraform managed." - name = "accessPolicies/${local.access_policy_name}/servicePerimeters/${each.key}" - title = each.key - perimeter_type = each.value.type - - # Enforced mode configuration - dynamic "status" { - for_each = try(lookup(var.perimeter_projects, each.key, {}).enforced, []) != null ? [""] : [] - - content { - resources = formatlist("projects/%s", try(lookup(var.perimeter_projects, each.key, {}).enforced, [])) - } - } - - # Dry run mode configuration - use_explicit_dry_run_spec = try(lookup(var.perimeter_projects, each.key, null).dry_run, null) != null ? true : null - dynamic "spec" { - for_each = try(lookup(var.perimeter_projects, each.key, null).dry_run, null) != null ? [""] : [] - - content { - resources = try(formatlist("projects/%s", lookup(var.perimeter_projects, each.key, {}).dry_run), null) - restricted_services = [] - access_levels = [] - } - } - - # Uncomment if used alongside `google_access_context_manager_service_perimeter_resource`, - # so they don't fight over which resources should be in the policy. - # lifecycle { - # ignore_changes = [status[0].resources] - # } - - depends_on = [ - google_access_context_manager_service_perimeter.standard, - google_access_context_manager_access_level.default, - ] + count = var.access_policy_create != null ? 1 : 0 + parent = var.access_policy_create.parent + title = var.access_policy_create.title } diff --git a/modules/vpc-sc/outputs.tf b/modules/vpc-sc/outputs.tf index 9a068b92..3412e234 100644 --- a/modules/vpc-sc/outputs.tf +++ b/modules/vpc-sc/outputs.tf @@ -14,39 +14,35 @@ * limitations under the License. */ -output "access_levels" { - description = "Access Levels." +output "access_level_names" { + description = "Access level resources." value = { - for key, value in google_access_context_manager_access_level.default : - key => value + for k, v in google_access_context_manager_access_level.basic : + k => v.name } } +output "access_levels" { + description = "Access level resources." + value = google_access_context_manager_access_level.basic +} + +output "access_policy" { + description = "Access policy resource, if autocreated." + value = try(google_access_context_manager_access_policy.default.0, null) +} + output "access_policy_name" { - description = "Access Policy resource" - value = local.access_policy_name + description = "Access policy name." + value = local.access_policy } -output "organization_id" { - description = "Organization id dependent on module resources." - value = var.organization_id - depends_on = [ - google_access_context_manager_access_policy.default - ] +output "service_perimeters_bridge" { + description = "Bridge service perimeter resources." + value = google_access_context_manager_service_perimeter.bridge } -output "perimeters_bridge" { - description = "VPC-SC bridge perimeter resources." - value = { - for key, value in google_access_context_manager_service_perimeter.bridge : - key => value - } -} - -output "perimeters_standard" { - description = "VPC-SC standard perimeter resources." - value = { - for key, value in google_access_context_manager_service_perimeter.standard : - key => value - } +output "service_perimeters_regular" { + description = "Regular service perimeter resources." + value = google_access_context_manager_service_perimeter.regular } diff --git a/modules/vpc-sc/service_perimeters_bridge.tf b/modules/vpc-sc/service_perimeters_bridge.tf new file mode 100644 index 00000000..c1d5130f --- /dev/null +++ b/modules/vpc-sc/service_perimeters_bridge.tf @@ -0,0 +1,41 @@ +/** + * Copyright 2021 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. + */ + +# this code implements "additive" service perimeters, if "authoritative" +# service perimeters are needed, switch to the +# google_access_context_manager_service_perimeters resource + +resource "google_access_context_manager_service_perimeter" "bridge" { + for_each = var.service_perimeters_bridge + parent = "accessPolicies/${local.access_policy}" + name = "accessPolicies/${local.access_policy}/servicePerimeters/${each.key}" + title = each.key + perimeter_type = "PERIMETER_TYPE_BRIDGE" + use_explicit_dry_run_spec = each.value.use_explicit_dry_run_spec + spec { + resources = each.value.spec_resources + } + status { + resources = each.value.status_resources + } + lifecycle { + ignore_changes = [spec[0].resources, status[0].resources] + } + depends_on = [ + google_access_context_manager_access_policy.default, + google_access_context_manager_access_level.basic + ] +} diff --git a/modules/vpc-sc/service_perimeters_regular.tf b/modules/vpc-sc/service_perimeters_regular.tf new file mode 100644 index 00000000..2f4eae3c --- /dev/null +++ b/modules/vpc-sc/service_perimeters_regular.tf @@ -0,0 +1,311 @@ +/** + * Copyright 2021 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. + */ + +# this code implements "additive" service perimeters, if "authoritative" +# service perimeters are needed, switch to the +# google_access_context_manager_service_perimeters resource + +resource "google_access_context_manager_service_perimeter" "regular" { + for_each = var.service_perimeters_regular + parent = "accessPolicies/${local.access_policy}" + name = "accessPolicies/${local.access_policy}/servicePerimeters/${each.key}" + title = each.key + perimeter_type = "PERIMETER_TYPE_REGULAR" + use_explicit_dry_run_spec = each.value.use_explicit_dry_run_spec + dynamic "spec" { + for_each = each.value.spec == null ? {} : { 1 = 1 } + content { + access_levels = each.value.spec.access_levels + resources = each.value.spec.resources + restricted_services = each.value.spec.restricted_services + # begin egress_policies + dynamic "egress_policies" { + for_each = toset( + each.value.spec.egress_policies == null + ? [] + : each.value.spec.egress_policies + ) + iterator = policy + content { + # begin egress_from + dynamic "egress_from" { + for_each = policy.key.egress_from == null ? {} : { 1 = 1 } + content { + identity_type = policy.key.egress_from.identity_type + identities = policy.key.egress_from.identities + } + } + # end egress_from + # begin egress_to + dynamic "egress_to" { + for_each = policy.key.egress_to == null ? {} : { 1 = 1 } + content { + resources = policy.key.egress_to.resources + dynamic "operations" { + for_each = toset( + policy.key.egress_to.operations == null + ? [] + : policy.key.egress_to.operations + ) + iterator = operation + content { + service_name = operation.service_name + dynamic "method_selectors" { + for_each = toset( + operation.key.method_selectors == null + ? [] + : operation.key.method_selectors + ) + content { + method = method_selectors.key + } + } + } + } + } + } + # end egress_to + } + } + # end egress_policies + # begin ingress_policies + dynamic "ingress_policies" { + for_each = toset( + each.value.spec.ingress_policies == null + ? [] + : each.value.spec.ingress_policies + ) + iterator = policy + content { + # begin ingress_from + dynamic "ingress_from" { + for_each = policy.key.ingress_from == null ? {} : { 1 = 1 } + content { + identity_type = policy.key.ingress_from.identity_type + identities = policy.key.ingress_from.identities + # begin sources + dynamic "sources" { + for_each = toset( + policy.key.ingress_from.source_access_levels == null + ? [] + : policy.key.ingress_from.source_access_levels + ) + content { + access_level = sources.key + } + } + dynamic "sources" { + for_each = toset( + policy.key.ingress_from.source_resources == null + ? [] + : policy.key.ingress_from.source_resources + ) + content { + resource = sources.key + } + } + # end sources + } + } + # end ingress_from + # begin ingress_to + dynamic "ingress_to" { + for_each = policy.key.ingress_to == null ? {} : { 1 = 1 } + content { + resources = policy.key.ingress_to.resources + dynamic "operations" { + for_each = toset( + policy.key.ingress_to.operations == null + ? [] + : policy.key.ingress_to.operations + ) + iterator = operation + content { + service_name = operation.service_name + dynamic "method_selectors" { + for_each = toset( + operation.key.method_selectors == null + ? [] + : operation.key.method_selectors + ) + content { + method = method_selectors.key + } + } + } + } + } + } + # end ingress_to + } + } + # end ingress_policies + # begin vpc_accessible_services + dynamic "vpc_accessible_services" { + for_each = each.value.spec.vpc_accessible_services == null ? {} : { 1 = 1 } + content { + allowed_services = each.value.spec.vpc_accessible_services.allowed_services + enable_restriction = each.value.spec.vpc_accessible_services.enable_restriction + } + } + # end vpc_accessible_services + } + } + dynamic "status" { + for_each = each.value.status == null ? {} : { 1 = 1 } + content { + access_levels = each.value.status.access_levels + resources = each.value.status.resources + restricted_services = each.value.status.restricted_services + # begin egress_policies + dynamic "egress_policies" { + for_each = toset( + each.value.status.egress_policies == null + ? [] + : each.value.status.egress_policies + ) + iterator = policy + content { + # begin egress_from + dynamic "egress_from" { + for_each = policy.key.egress_from == null ? {} : { 1 = 1 } + content { + identity_type = policy.key.egress_from.identity_type + identities = policy.key.egress_from.identities + } + } + # end egress_from + # begin egress_to + dynamic "egress_to" { + for_each = policy.key.egress_to == null ? {} : { 1 = 1 } + content { + resources = policy.key.egress_to.resources + dynamic "operations" { + for_each = toset( + policy.key.egress_to.operations == null + ? [] + : policy.key.egress_to.operations + ) + content { + service_name = operations.key.service_name + dynamic "method_selectors" { + for_each = toset( + operations.key.method_selectors == null + ? [] + : operations.key.method_selectors + ) + content { + method = method_selectors.key + } + } + } + } + } + } + # end egress_to + } + } + # end egress_policies + # begin ingress_policies + dynamic "ingress_policies" { + for_each = toset( + each.value.status.ingress_policies == null + ? [] + : each.value.status.ingress_policies + ) + iterator = policy + content { + # begin ingress_from + dynamic "ingress_from" { + for_each = policy.key.ingress_from == null ? {} : { 1 = 1 } + content { + identity_type = policy.key.ingress_from.identity_type + identities = policy.key.ingress_from.identities + # begin sources + dynamic "sources" { + for_each = toset( + policy.key.ingress_from.source_access_levels == null + ? [] + : policy.key.ingress_from.source_access_levels + ) + content { + access_level = sources.key + } + } + dynamic "sources" { + for_each = toset( + policy.key.ingress_from.source_resources == null + ? [] + : policy.key.ingress_from.source_resources + ) + content { + resource = sources.key + } + } + # end sources + } + } + # end ingress_from + # begin ingress_to + dynamic "ingress_to" { + for_each = policy.key.ingress_to == null ? {} : { 1 = 1 } + content { + resources = policy.key.ingress_to.resources + dynamic "operations" { + for_each = toset( + policy.key.ingress_to.operations == null + ? [] + : policy.key.ingress_to.operations + ) + content { + service_name = operations.key.service_name + dynamic "method_selectors" { + for_each = toset( + operations.key.method_selectors == null + ? [] + : operations.key.method_selectors + ) + content { + method = method_selectors.key + } + } + } + } + } + } + # end ingress_to + } + } + # end ingress_policies + # begin vpc_accessible_services + dynamic "vpc_accessible_services" { + for_each = each.value.status.vpc_accessible_services == null ? {} : { 1 = 1 } + content { + allowed_services = each.value.status.vpc_accessible_services.allowed_services + enable_restriction = each.value.status.vpc_accessible_services.enable_restriction + } + } + # end vpc_accessible_services + } + } + lifecycle { + ignore_changes = [spec[0].resources, status[0].resources] + } + depends_on = [ + google_access_context_manager_access_policy.default, + google_access_context_manager_access_level.basic + ] +} diff --git a/modules/vpc-sc/variables.tf b/modules/vpc-sc/variables.tf index 866f9ef2..fcfef359 100644 --- a/modules/vpc-sc/variables.tf +++ b/modules/vpc-sc/variables.tf @@ -14,90 +14,145 @@ * limitations under the License. */ -variable "access_level_perimeters" { - description = "Enforced mode -> Access Level -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run'" - type = map(map(list(string))) - default = {} -} - variable "access_levels" { - description = "Map of Access Levels to be created. For each Access Level you can specify 'ip_subnetworks, required_access_levels, members, negate or regions'." + description = "Map of access levels in name => [conditions] format." type = map(object({ combining_function = string conditions = list(object({ + device_policy = object({ + require_screen_lock = bool + allowed_encryption_statuses = list(string) + allowed_device_management_levels = list(string) + os_constraints = list(object({ + minimum_version = string + os_type = string + require_verified_chrome_os = bool + })) + require_admin_approval = bool + require_corp_owned = bool + }) ip_subnetworks = list(string) - required_access_levels = list(string) members = list(string) - negate = string + negate = bool regions = list(string) + required_access_levels = list(string) })) })) default = {} + validation { + condition = alltrue([ + for k, v in var.access_levels : ( + v.combining_function == null || + v.combining_function == "AND" || + v.combining_function == "OR" + ) + ]) + error_message = "Invalid `combining_function` value (null, \"AND\", \"OR\" accepted)." + } +} + +variable "access_policy" { + description = "Access Policy name, leave null to use auto-created one." + type = string } variable "access_policy_create" { - description = "Enable autocreation of the Access Policy" - type = bool - default = true + description = "Access Policy configuration, fill in to create. Parent is in 'organizations/123456' format." + type = object({ + parent = string + title = string + }) + default = null } -variable "access_policy_name" { - description = "Referenced Access Policy name" - type = string - default = null -} - -variable "access_policy_title" { - description = "Access Policy title to be created." - type = string - default = null -} - -variable "egress_policies" { - description = "List of EgressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#egress_policies)" - default = null -} - -variable "egress_policies_perimeters" { - description = "Enforced mode -> Egress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run'" - type = map(map(list(string))) - default = {} -} - -variable "ingress_policies" { - description = "List of IngressPolicies in the form described in the [documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter#ingress_policies)" - default = null -} - -variable "ingress_policies_perimeters" { - description = "Enforced mode -> Ingress Policy -> Perimeters mapping. Enforced mode can be 'enforced' or 'dry_run'" - type = map(map(list(string))) - default = {} -} - -variable "organization_id" { - description = "Organization id in organizations/nnnnnn format." - type = string -} - -variable "perimeter_projects" { - description = "Perimeter -> Enforced Mode -> Projects Number mapping. Enforced mode can be 'enforced' or 'dry_run'." - type = map(map(list(number))) - default = {} -} - -variable "perimeters" { - description = "Set of Perimeters." +variable "service_perimeters_bridge" { + description = "Bridge service perimeters." type = map(object({ - type = string - dry_run_config = object({ - restricted_services = list(string) - vpc_accessible_services = list(string) - }) - enforced_config = object({ - restricted_services = list(string) - vpc_accessible_services = list(string) - }) + spec_resources = list(string) + status_resources = list(string) + use_explicit_dry_run_spec = bool + })) + default = {} +} + +variable "service_perimeters_regular" { + description = "Regular service perimeters." + type = map(object({ + spec = object({ + access_levels = list(string) + resources = list(string) + restricted_services = list(string) + egress_policies = list(object({ + egress_from = object({ + identity_type = string + identities = list(string) + }) + egress_to = object({ + operations = list(object({ + method_selectors = list(string) + service_name = string + })) + resources = list(string) + }) + })) + ingress_policies = list(object({ + ingress_from = object({ + identity_type = string + identities = list(string) + source_access_levels = list(string) + source_resources = list(string) + }) + ingress_to = object({ + operations = list(object({ + method_selectors = list(string) + service_name = string + })) + resources = list(string) + }) + })) + vpc_accessible_services = object({ + allowed_services = list(string) + enable_restriction = bool + }) + }) + status = object({ + access_levels = list(string) + resources = list(string) + restricted_services = list(string) + egress_policies = list(object({ + egress_from = object({ + identity_type = string + identities = list(string) + }) + egress_to = object({ + operations = list(object({ + method_selectors = list(string) + service_name = string + })) + resources = list(string) + }) + })) + ingress_policies = list(object({ + ingress_from = object({ + identity_type = string + identities = list(string) + source_access_levels = list(string) + source_resources = list(string) + }) + ingress_to = object({ + operations = list(object({ + method_selectors = list(string) + service_name = string + })) + resources = list(string) + }) + })) + vpc_accessible_services = object({ + allowed_services = list(string) + enable_restriction = bool + }) + }) + use_explicit_dry_run_spec = bool })) default = {} } diff --git a/tests/conftest.py b/tests/conftest.py index 33c63596..ad8cc5ae 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -79,9 +79,10 @@ def example_plan_runner(_plan_runner): "Runs Terraform plan and returns count of modules and resources." plan = _plan_runner(fixture_path) # the fixture is the example we are testing + modules = plan.modules or {} return ( - len(plan.modules), - sum(len(m.resources) for m in plan.modules.values())) + len(modules), + sum(len(m.resources) for m in modules.values())) return run_plan diff --git a/modules/vpc-sc/versions.tf b/tests/modules/vpc_sc/__init__.py similarity index 63% rename from modules/vpc-sc/versions.tf rename to tests/modules/vpc_sc/__init__.py index 1cc6bf89..d46dbae5 100644 --- a/modules/vpc-sc/versions.tf +++ b/tests/modules/vpc_sc/__init__.py @@ -4,26 +4,10 @@ # 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 +# 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. - -terraform { - required_version = ">= 1.0.0" - required_providers { - google = { - source = "hashicorp/google" - version = ">= 4.0.0" - } - google-beta = { - source = "hashicorp/google-beta" - version = ">= 4.0.0" - } - } -} - - diff --git a/tests/modules/vpc_sc/fixture/main.tf b/tests/modules/vpc_sc/fixture/main.tf new file mode 100644 index 00000000..f13f5f82 --- /dev/null +++ b/tests/modules/vpc_sc/fixture/main.tf @@ -0,0 +1,146 @@ +/** + * Copyright 2021 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 "access_policy" { + description = "Access Policy name, leave null to use auto-created one." + type = string + default = null +} + +variable "access_policy_create" { + description = "Access Policy configuration, fill in to create. Parent is in 'organizations/123456' format." + type = object({ + parent = string + title = string + }) + default = { + parent = "organizations/123456" + title = "vpcsc-policy" + } +} + +module "test" { + source = "../../../../modules/vpc-sc" + access_policy = var.access_policy + access_policy_create = var.access_policy_create + access_levels = { + a1 = { + combining_function = null + conditions = [ + { + device_policy = null + ip_subnetworks = null + members = ["user:ludomagno@google.com"] + negate = null + regions = null + required_access_levels = null + } + ] + } + a2 = { + combining_function = "OR" + conditions = [ + { + device_policy = null + ip_subnetworks = null + members = null + negate = null + regions = ["IT", "FR"] + required_access_levels = null + }, + { + device_policy = null + ip_subnetworks = null + members = null + negate = null + regions = ["US"] + required_access_levels = null + } + ] + } + } + service_perimeters_bridge = { + b1 = { + status_resources = ["projects/111110", "projects/111111"] + spec_resources = null + use_explicit_dry_run_spec = false + } + b2 = { + status_resources = ["projects/111110", "projects/222220"] + spec_resources = ["projects/111110", "projects/222220"] + use_explicit_dry_run_spec = true + } + } + service_perimeters_regular = { + r1 = { + spec = null + status = { + access_levels = [module.test.access_level_names["a1"]] + resources = ["projects/11111", "projects/111111"] + restricted_services = ["storage.googleapis.com"] + egress_policies = null + ingress_policies = null + vpc_accessible_services = { + allowed_services = ["compute.googleapis.com"] + enable_restriction = true + } + } + use_explicit_dry_run_spec = false + } + r2 = { + spec = null + status = { + access_levels = [module.test.access_level_names["a1"]] + resources = ["projects/222220", "projects/222221"] + restricted_services = ["storage.googleapis.com"] + egress_policies = [ + { + egress_from = { + identity_type = null + identities = ["user:foo@example.com"] + } + egress_to = { + operations = null + resources = ["projects/333330"] + } + } + ] + ingress_policies = [ + { + ingress_from = { + identity_type = null + identities = null + source_access_levels = [module.test.access_level_names["a2"]] + source_resources = ["projects/333330"] + } + ingress_to = { + operations = [{ + method_selectors = null + service_name = "compute.googleapis.com" + }] + resources = ["projects/222220"] + } + } + ] + vpc_accessible_services = { + allowed_services = ["compute.googleapis.com"] + enable_restriction = true + } + } + use_explicit_dry_run_spec = false + } + } +} diff --git a/tests/modules/vpc_sc/test_plan.py b/tests/modules/vpc_sc/test_plan.py new file mode 100644 index 00000000..b4b88547 --- /dev/null +++ b/tests/modules/vpc_sc/test_plan.py @@ -0,0 +1,49 @@ +# Copyright 2021 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 + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_create_policy(plan_runner): + "Test with auto-created policy." + _, resources = plan_runner(FIXTURES_DIR) + counts = {} + for r in resources: + n = f'{r["type"]}.{r["name"]}' + counts[n] = counts.get(n, 0) + 1 + assert counts == { + 'google_access_context_manager_access_level.basic': 2, + 'google_access_context_manager_access_policy.default': 1, + 'google_access_context_manager_service_perimeter.bridge': 2, + 'google_access_context_manager_service_perimeter.regular': 2 + } + + +def test_use_policy(plan_runner): + "Test with existing policy." + _, resources = plan_runner(FIXTURES_DIR, access_policy_create="null", + access_policy="accessPolicies/foobar") + counts = {} + for r in resources: + n = f'{r["type"]}.{r["name"]}' + counts[n] = counts.get(n, 0) + 1 + assert counts == { + 'google_access_context_manager_access_level.basic': 2, + 'google_access_context_manager_service_perimeter.bridge': 2, + 'google_access_context_manager_service_perimeter.regular': 2 + } diff --git a/tests/requirements.txt b/tests/requirements.txt index 480dbeb5..b7c85499 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,4 +1,4 @@ pytest>=4.6.0 PyYAML>=5.3 -tftest>=1.6.1 +tftest>=1.6.2 marko>=0.9.1 From 5d264999b40ee3da1b04646ed75604ea04f045d8 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 31 Dec 2021 13:34:43 +0100 Subject: [PATCH 45/52] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ed14e18..c8510416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. - update hierarchical firewall resources to use the newer `google_compute_firewall_*` resources - **incompatible change** rename `firewall_policy_attachments` to `firewall_policy_association` in the `organization` and `folder` modules +- **incompatible change** updated API for the `net-vpc-sc` module ## [9.0.2] - 2021-12-22 From 5e4598ec976fbc04b672b86c22b45e23cce09153 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 31 Dec 2021 13:36:36 +0100 Subject: [PATCH 46/52] Update CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8510416..ab55a731 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [9.0.3] - 2021-12-31 + - update hierarchical firewall resources to use the newer `google_compute_firewall_*` resources - **incompatible change** rename `firewall_policy_attachments` to `firewall_policy_association` in the `organization` and `folder` modules - **incompatible change** updated API for the `net-vpc-sc` module @@ -394,7 +396,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/v9.0.2...HEAD +[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.3...HEAD +[9.0.3]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.2...v9.0.3 [9.0.2]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.0...v9.0.2 [9.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v8.0.0...v9.0.0 [8.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v7.0.0...v8.0.0 From ea2f92cfff473e140169a2b96b60f55dd9bf8f1b Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 31 Dec 2021 15:14:33 +0100 Subject: [PATCH 47/52] Update README.md --- modules/vpc-sc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vpc-sc/README.md b/modules/vpc-sc/README.md index 103ce001..02c7c406 100644 --- a/modules/vpc-sc/README.md +++ b/modules/vpc-sc/README.md @@ -70,7 +70,7 @@ module "test" { ### Service perimeters -Bridge and regulare service perimeters use two separate variables, as bridge perimeters only accept a limited number of arguments, and can leverage a much simpler interface. +Bridge and regular service perimeters use two separate variables, as bridge perimeters only accept a limited number of arguments, and can leverage a much simpler interface. The regular perimeters variable exposes all the complexity of the underlying resource, use [its documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter) as a reference about the possible values and configurations. From b9f00bf25950a315b52ce27f916f7489c4ddae6d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 31 Dec 2021 16:47:39 +0100 Subject: [PATCH 48/52] fix cases where bridge perimeter status resources are null (#408) --- modules/vpc-sc/README.md | 2 +- modules/vpc-sc/service_perimeters_bridge.tf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/vpc-sc/README.md b/modules/vpc-sc/README.md index 02c7c406..5fa2037a 100644 --- a/modules/vpc-sc/README.md +++ b/modules/vpc-sc/README.md @@ -91,7 +91,7 @@ module "test" { use_explicit_dry_run_spec = false } b2 = { - status_resources = ["projects/222220", "projects/222221"] + status_resources = null spec_resources = ["projects/222220", "projects/222221"] use_explicit_dry_run_spec = true } diff --git a/modules/vpc-sc/service_perimeters_bridge.tf b/modules/vpc-sc/service_perimeters_bridge.tf index c1d5130f..660736af 100644 --- a/modules/vpc-sc/service_perimeters_bridge.tf +++ b/modules/vpc-sc/service_perimeters_bridge.tf @@ -26,10 +26,10 @@ resource "google_access_context_manager_service_perimeter" "bridge" { perimeter_type = "PERIMETER_TYPE_BRIDGE" use_explicit_dry_run_spec = each.value.use_explicit_dry_run_spec spec { - resources = each.value.spec_resources + resources = each.value.spec_resources == null ? [] : each.value.spec_resources } status { - resources = each.value.status_resources + resources = each.value.status_resources == null ? [] : each.value.status_resources } lifecycle { ignore_changes = [spec[0].resources, status[0].resources] From 52bcf06b3d1a25f5a9e3d77de3864690d29259d0 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 31 Dec 2021 16:49:38 +0100 Subject: [PATCH 49/52] Update CHANGELOG.md --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab55a731..2140cf36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [10.0.0] - 2021-12-31 + +- fix cases where bridge perimeter status resources are `null` in `vpc-sc` module +- re-release 9.0.3 as a major release as it contains breaking changes + - update hierarchical firewall resources to use the newer `google_compute_firewall_*` resources + - **incompatible change** rename `firewall_policy_attachments` to `firewall_policy_association` in the `organization` and `folder` modules + - **incompatible change** updated API for the `net-vpc-sc` module + ## [9.0.3] - 2021-12-31 - update hierarchical firewall resources to use the newer `google_compute_firewall_*` resources @@ -396,7 +404,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/v9.0.3...HEAD +[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v10.0.0...HEAD +[10.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.3...v10.0.0 [9.0.3]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.2...v9.0.3 [9.0.2]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v9.0.0...v9.0.2 [9.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v8.0.0...v9.0.0 From ee25965c895aa4bc3f62090a08b797d45b82d178 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Sat, 1 Jan 2022 15:52:31 +0100 Subject: [PATCH 50/52] Copyright bump (#410) --- .ci/cloudbuild.lint.yaml | 4 +- .ci/cloudbuild.test.environments.yaml | 2 +- .ci/cloudbuild.test.examples.yaml | 2 +- .ci/cloudbuild.test.modules.yaml | 2 +- .github/workflows/daily-tag.yml | 6 +- .github/workflows/linting.yml | 2 +- .github/workflows/tests.yml | 12 +- .../backend.tf.sample | 2 +- .../cf/main.py | 2 +- .../asset-inventory-feed-remediation/main.tf | 2 +- .../outputs.tf | 2 +- .../variables.tf | 2 +- .../versions.tf | 2 +- .../dns-fine-grained-iam/backend.tf.sample | 2 +- cloud-operations/dns-fine-grained-iam/main.tf | 2 +- .../dns-fine-grained-iam/outputs.tf | 2 +- .../dns-fine-grained-iam/variables.tf | 2 +- .../dns-fine-grained-iam/versions.tf | 2 +- .../examples/shared-vpc-example/network.tf | 4 +- .../examples/shared-vpc-example/outputs.tf | 4 +- .../examples/shared-vpc-example/projects.tf | 4 +- .../examples/shared-vpc-example/test.example | 2 +- .../examples/shared-vpc-example/variables.tf | 4 +- cloud-operations/dns-shared-vpc/main.tf | 2 +- cloud-operations/dns-shared-vpc/outputs.tf | 2 +- cloud-operations/dns-shared-vpc/variables.tf | 2 +- cloud-operations/dns-shared-vpc/versions.tf | 2 +- .../iam-delegated-role-grants/audit.py | 70 ++-- .../iam-delegated-role-grants/main.tf | 2 +- .../iam-delegated-role-grants/outputs.tf | 2 +- .../iam-delegated-role-grants/variables.tf | 2 +- .../iam-delegated-role-grants/versions.tf | 2 +- .../backend.tf.sample | 2 +- .../onprem-sa-key-management/main.tf | 2 +- .../onprem-sa-key-management/outputs.tf | 2 +- .../onprem-sa-key-management/variables.tf | 2 +- .../onprem-sa-key-management/versions.tf | 2 +- cloud-operations/packer-image-builder/main.tf | 4 +- .../packer-image-builder/outputs.tf | 2 +- .../packer-image-builder/packer/build.pkr.hcl | 2 +- .../packer/install_httpd.sh | 2 +- .../packer/variables.pkr.hcl | 2 +- .../packer-image-builder/variables.tf | 2 +- .../packer-image-builder/versions.tf | 2 +- .../quota-monitoring/backend.tf.sample | 2 +- cloud-operations/quota-monitoring/cf/main.py | 2 +- cloud-operations/quota-monitoring/main.tf | 2 +- cloud-operations/quota-monitoring/outputs.tf | 2 +- .../quota-monitoring/variables.tf | 2 +- cloud-operations/quota-monitoring/versions.tf | 2 +- .../backend.tf.sample | 2 +- .../cf/main.py | 11 +- .../cffile/main.py | 12 +- .../main.tf | 2 +- .../outputs.tf | 2 +- .../variables.tf | 2 +- .../versions.tf | 2 +- .../backend.tf.sample | 2 +- .../cmek-via-centralized-kms/main.tf | 2 +- .../cmek-via-centralized-kms/outputs.tf | 2 +- .../cmek-via-centralized-kms/variables.tf | 2 +- .../cmek-via-centralized-kms/versions.tf | 2 +- .../01-environment/versions.tf | 2 +- .../02-resources/providers.tf | 2 +- .../02-resources/versions.tf | 2 +- .../gcs-to-bq-with-dataflow/backend.tf.sample | 2 +- .../gcs-to-bq-with-dataflow/main.tf | 2 +- .../gcs-to-bq-with-dataflow/outputs.tf | 2 +- .../scripts/data_ingestion/data_ingestion.py | 2 +- .../person_details_generator.py | 2 +- .../gcs-to-bq-with-dataflow/variables.tf | 2 +- .../gcs-to-bq-with-dataflow/versions.tf | 2 +- default-versions.tf | 2 +- factories/example-environments/dev/main.tf | 2 +- .../example-environments/dev/versions.tf | 2 +- factories/example-environments/prod/main.tf | 2 +- .../example-environments/prod/versions.tf | 2 +- .../firewall-hierarchical-policies/main.tf | 2 +- .../firewall-hierarchical-policies/outputs.tf | 2 +- .../variables.tf | 2 +- .../versions.tf | 2 +- factories/firewall-vpc-rules/flat/main.tf | 2 +- factories/firewall-vpc-rules/flat/outputs.tf | 2 +- .../firewall-vpc-rules/flat/variables.tf | 2 +- factories/firewall-vpc-rules/flat/versions.tf | 2 +- factories/firewall-vpc-rules/nested/main.tf | 2 +- .../firewall-vpc-rules/nested/outputs.tf | 2 +- .../firewall-vpc-rules/nested/variables.tf | 2 +- .../firewall-vpc-rules/nested/versions.tf | 2 +- factories/subnets/main.tf | 2 +- factories/subnets/outputs.tf | 2 +- factories/subnets/variables.tf | 2 +- factories/subnets/versions.tf | 2 +- foundations/business-units/backend.tf.sample | 2 +- foundations/business-units/main.tf | 2 +- foundations/business-units/outputs.tf | 2 +- .../business-units/terraform.tfvars.sample | 2 +- foundations/business-units/variables.tf | 2 +- foundations/business-units/versions.tf | 2 +- foundations/environments/backend.tf.sample | 2 +- foundations/environments/locals.tf | 2 +- foundations/environments/main.tf | 2 +- foundations/environments/outputs.tf | 2 +- foundations/environments/variables.tf | 2 +- foundations/environments/versions.tf | 2 +- modules/__experimental/net-neg/main.tf | 2 +- modules/__experimental/net-neg/outputs.tf | 2 +- modules/__experimental/net-neg/variables.tf | 2 +- modules/__experimental/net-neg/versions.tf | 2 +- modules/apigee-organization/main.tf | 4 +- modules/apigee-organization/outputs.tf | 2 +- modules/apigee-organization/variables.tf | 2 +- modules/apigee-organization/versions.tf | 2 +- modules/apigee-x-instance/main.tf | 2 +- modules/apigee-x-instance/outputs.tf | 2 +- modules/apigee-x-instance/variables.tf | 2 +- modules/apigee-x-instance/versions.tf | 2 +- modules/artifact-registry/main.tf | 2 +- modules/artifact-registry/outputs.tf | 2 +- modules/artifact-registry/variables.tf | 2 +- modules/artifact-registry/versions.tf | 2 +- modules/bigquery-dataset/main.tf | 2 +- modules/bigquery-dataset/outputs.tf | 2 +- modules/bigquery-dataset/variables.tf | 2 +- modules/bigquery-dataset/versions.tf | 2 +- modules/bigtable-instance/main.tf | 2 +- modules/bigtable-instance/outputs.tf | 2 +- modules/bigtable-instance/variables.tf | 2 +- modules/bigtable-instance/versions.tf | 2 +- modules/billing-budget/main.tf | 2 +- modules/billing-budget/outputs.tf | 2 +- modules/billing-budget/variables.tf | 2 +- modules/billing-budget/versions.tf | 2 +- .../coredns/cloud-config.yaml | 2 +- .../cloud-config-container/coredns/main.tf | 2 +- .../cloud-config-container/coredns/outputs.tf | 2 +- .../coredns/variables.tf | 2 +- .../coredns/versions.tf | 2 +- .../cos-generic-metadata/cloud-config.yaml | 2 +- .../cos-generic-metadata/main.tf | 2 +- .../cos-generic-metadata/outputs.tf | 2 +- .../cos-generic-metadata/variables.tf | 2 +- .../cos-generic-metadata/versions.tf | 2 +- .../envoy-traffic-director/files/customize.sh | 2 +- .../envoy-traffic-director/files/envoy.yaml | 2 +- .../envoy-traffic-director/main.tf | 2 +- .../envoy-traffic-director/outputs.tf | 2 +- .../envoy-traffic-director/variables.tf | 2 +- .../envoy-traffic-director/versions.tf | 2 +- modules/cloud-config-container/instance.tf | 2 +- .../mysql/cloud-config.yaml | 2 +- modules/cloud-config-container/mysql/main.tf | 2 +- .../cloud-config-container/mysql/outputs.tf | 2 +- .../cloud-config-container/mysql/variables.tf | 2 +- .../cloud-config-container/mysql/versions.tf | 2 +- .../nginx/cloud-config.yaml | 2 +- modules/cloud-config-container/nginx/main.tf | 2 +- .../cloud-config-container/nginx/outputs.tf | 2 +- .../cloud-config-container/nginx/variables.tf | 2 +- .../cloud-config-container/nginx/versions.tf | 2 +- .../onprem/cloud-config.yaml | 2 +- .../docker-images/strongswan/Dockerfile | 4 +- .../docker-images/strongswan/cloudbuild.yaml | 21 +- .../docker-images/strongswan/entrypoint.sh | 2 +- .../docker-images/strongswan/ipsec-vti.sh | 2 +- .../onprem/docker-images/toolbox/Dockerfile | 8 +- .../docker-images/toolbox/cloudbuild.yaml | 21 +- .../docker-images/toolbox/entrypoint.sh | 2 +- modules/cloud-config-container/onprem/main.tf | 2 +- .../cloud-config-container/onprem/outputs.tf | 2 +- .../onprem/static-vpn-gw-cloud-init.yaml | 383 +++++++++--------- .../onprem/variables.tf | 2 +- .../cloud-config-container/onprem/versions.tf | 2 +- .../outputs-instance.tf | 2 +- .../squid/cloud-config.yaml | 2 +- .../squid/docker/Dockerfile | 12 +- .../squid/docker/cloudbuild.yaml | 21 +- .../squid/docker/entrypoint.sh | 2 +- modules/cloud-config-container/squid/main.tf | 2 +- .../cloud-config-container/squid/outputs.tf | 2 +- .../cloud-config-container/squid/variables.tf | 2 +- .../cloud-config-container/squid/versions.tf | 2 +- .../variables-instance.tf | 2 +- modules/cloud-function/main.tf | 2 +- modules/cloud-function/outputs.tf | 2 +- modules/cloud-function/variables.tf | 2 +- modules/cloud-function/versions.tf | 2 +- modules/cloud-identity-group/main.tf | 2 +- modules/cloud-identity-group/outputs.tf | 2 +- modules/cloud-identity-group/variables.tf | 2 +- modules/cloud-identity-group/versions.tf | 2 +- modules/cloud-run/main.tf | 2 +- modules/cloud-run/outputs.tf | 4 +- modules/cloud-run/variables.tf | 2 +- modules/cloud-run/versions.tf | 4 +- modules/cloudsql-instance/main.tf | 2 +- modules/cloudsql-instance/outputs.tf | 2 +- modules/cloudsql-instance/variables.tf | 2 +- modules/cloudsql-instance/versions.tf | 2 +- modules/compute-mig/main.tf | 2 +- modules/compute-mig/outputs.tf | 2 +- modules/compute-mig/variables.tf | 2 +- modules/compute-mig/versions.tf | 2 +- modules/compute-vm/main.tf | 2 +- modules/compute-vm/outputs.tf | 2 +- modules/compute-vm/variables.tf | 2 +- modules/compute-vm/versions.tf | 2 +- modules/container-registry/main.tf | 2 +- modules/container-registry/outputs.tf | 2 +- modules/container-registry/variables.tf | 2 +- modules/container-registry/versions.tf | 2 +- modules/datafusion/main.tf | 2 +- modules/datafusion/outputs.tf | 2 +- modules/datafusion/variables.tf | 2 +- modules/datafusion/versions.tf | 2 +- modules/dns/main.tf | 2 +- modules/dns/outputs.tf | 2 +- modules/dns/variables.tf | 2 +- modules/dns/versions.tf | 2 +- modules/endpoints/main.tf | 2 +- modules/endpoints/outputs.tf | 2 +- modules/endpoints/variables.tf | 2 +- modules/endpoints/versions.tf | 2 +- modules/folder/firewall-policy.tf | 2 +- modules/folder/main.tf | 2 +- modules/folder/outputs.tf | 2 +- modules/folder/variables.tf | 2 +- modules/folder/versions.tf | 2 +- modules/folders-unit/locals.tf | 2 +- modules/folders-unit/main.tf | 2 +- modules/folders-unit/outputs.tf | 2 +- modules/folders-unit/variables.tf | 2 +- modules/folders-unit/versions.tf | 2 +- modules/gcs/main.tf | 4 +- modules/gcs/outputs.tf | 2 +- modules/gcs/variables.tf | 4 +- modules/gcs/versions.tf | 2 +- modules/gke-cluster/main.tf | 4 +- modules/gke-cluster/outputs.tf | 2 +- modules/gke-cluster/variables.tf | 2 +- modules/gke-cluster/versions.tf | 2 +- modules/gke-nodepool/main.tf | 2 +- modules/gke-nodepool/outputs.tf | 2 +- modules/gke-nodepool/variables.tf | 2 +- modules/gke-nodepool/versions.tf | 2 +- modules/iam-service-account/main.tf | 2 +- modules/iam-service-account/outputs.tf | 2 +- modules/iam-service-account/variables.tf | 2 +- modules/iam-service-account/versions.tf | 2 +- modules/kms/main.tf | 2 +- modules/kms/outputs.tf | 2 +- modules/kms/variables.tf | 2 +- modules/kms/versions.tf | 2 +- modules/logging-bucket/main.tf | 2 +- modules/logging-bucket/outputs.tf | 2 +- modules/logging-bucket/variables.tf | 2 +- modules/logging-bucket/versions.tf | 2 +- modules/naming-convention/main.tf | 2 +- modules/naming-convention/outputs.tf | 2 +- modules/naming-convention/variables.tf | 2 +- modules/naming-convention/versions.tf | 2 +- modules/net-address/main.tf | 2 +- modules/net-address/outputs.tf | 2 +- modules/net-address/variables.tf | 2 +- modules/net-address/versions.tf | 2 +- modules/net-cloudnat/main.tf | 2 +- modules/net-cloudnat/outputs.tf | 2 +- modules/net-cloudnat/variables.tf | 2 +- modules/net-cloudnat/versions.tf | 2 +- modules/net-ilb/main.tf | 2 +- modules/net-ilb/outputs.tf | 2 +- modules/net-ilb/variables.tf | 2 +- modules/net-ilb/versions.tf | 2 +- .../main.tf | 2 +- .../outputs.tf | 2 +- .../variables.tf | 2 +- .../versions.tf | 2 +- modules/net-vpc-firewall/main.tf | 2 +- modules/net-vpc-firewall/outputs.tf | 2 +- modules/net-vpc-firewall/variables.tf | 2 +- modules/net-vpc-firewall/versions.tf | 2 +- modules/net-vpc-peering/main.tf | 2 +- modules/net-vpc-peering/outputs.tf | 2 +- modules/net-vpc-peering/variables.tf | 2 +- modules/net-vpc-peering/versions.tf | 2 +- modules/net-vpc/main.tf | 2 +- modules/net-vpc/outputs.tf | 2 +- modules/net-vpc/variables.tf | 2 +- modules/net-vpc/versions.tf | 2 +- modules/net-vpn-dynamic/main.tf | 2 +- modules/net-vpn-dynamic/outputs.tf | 2 +- modules/net-vpn-dynamic/variables.tf | 2 +- modules/net-vpn-dynamic/versions.tf | 2 +- modules/net-vpn-ha/main.tf | 2 +- modules/net-vpn-ha/outputs.tf | 2 +- modules/net-vpn-ha/variables.tf | 2 +- modules/net-vpn-ha/versions.tf | 2 +- modules/net-vpn-static/main.tf | 2 +- modules/net-vpn-static/outputs.tf | 2 +- modules/net-vpn-static/variables.tf | 2 +- modules/net-vpn-static/versions.tf | 2 +- modules/organization/firewall-policy.tf | 2 +- modules/organization/iam.tf | 2 +- modules/organization/logging.tf | 2 +- modules/organization/main.tf | 2 +- modules/organization/outputs.tf | 2 +- modules/organization/variables.tf | 2 +- modules/organization/versions.tf | 2 +- modules/project/main.tf | 2 +- modules/project/outputs.tf | 2 +- modules/project/service_accounts.tf | 2 +- modules/project/variables.tf | 2 +- modules/project/versions.tf | 2 +- modules/pubsub/main.tf | 2 +- modules/pubsub/outputs.tf | 2 +- modules/pubsub/variables.tf | 2 +- modules/pubsub/versions.tf | 2 +- modules/secret-manager/main.tf | 2 +- modules/secret-manager/outputs.tf | 2 +- modules/secret-manager/variables.tf | 2 +- modules/secret-manager/versions.tf | 2 +- modules/service-directory/main.tf | 2 +- modules/service-directory/outputs.tf | 2 +- modules/service-directory/variables.tf | 2 +- modules/service-directory/versions.tf | 2 +- modules/source-repository/main.tf | 2 +- modules/source-repository/outputs.tf | 2 +- modules/source-repository/variables.tf | 2 +- modules/source-repository/versions.tf | 2 +- modules/vpc-sc/access_levels.tf | 2 +- modules/vpc-sc/main.tf | 2 +- modules/vpc-sc/outputs.tf | 2 +- modules/vpc-sc/service_perimeters_bridge.tf | 2 +- modules/vpc-sc/service_perimeters_regular.tf | 2 +- modules/vpc-sc/variables.tf | 2 +- .../decentralized-firewall/backend.tf.sample | 2 +- .../firewall/common/common-egress.yaml | 22 +- .../firewall/common/iap-access.yaml | 11 +- .../firewall/common/lb-access.yaml | 10 +- .../firewall/dev/app-1/app1-rules.yaml | 18 +- .../firewall/dev/app-2/app2-rules.yaml | 18 +- .../firewall/prod/app-1/app1-rules.yaml | 18 +- networking/decentralized-firewall/main.tf | 2 +- networking/decentralized-firewall/outputs.tf | 2 +- .../validator/Dockerfile | 2 +- .../validator/action.yml | 28 +- .../validator/firewallSchema.yaml | 2 +- .../validator/firewallSchemaAutoApprove.yaml | 2 +- .../validator/firewallSchemaSettings.yaml | 54 +-- .../validator/requirements.txt | 2 +- .../validator/validator.py | 5 +- .../decentralized-firewall/variables.tf | 2 +- networking/decentralized-firewall/versions.tf | 2 +- networking/filtering-proxy/main.tf | 2 +- networking/filtering-proxy/outputs.tf | 2 +- networking/filtering-proxy/variables.tf | 2 +- networking/filtering-proxy/versions.tf | 2 +- .../hub-and-spoke-peering/backend.tf.sample | 2 +- networking/hub-and-spoke-peering/main.tf | 2 +- networking/hub-and-spoke-peering/outputs.tf | 2 +- networking/hub-and-spoke-peering/variables.tf | 2 +- networking/hub-and-spoke-peering/versions.tf | 2 +- .../hub-and-spoke-vpn/backend.tf.sample | 2 +- networking/hub-and-spoke-vpn/main.tf | 2 +- networking/hub-and-spoke-vpn/outputs.tf | 2 +- networking/hub-and-spoke-vpn/provider.tf | 2 +- networking/hub-and-spoke-vpn/variables.tf | 2 +- networking/hub-and-spoke-vpn/versions.tf | 2 +- networking/ilb-next-hop/assets/gw.yaml | 2 +- networking/ilb-next-hop/backend.tf.sample | 2 +- networking/ilb-next-hop/gateways.tf | 2 +- networking/ilb-next-hop/main.tf | 2 +- networking/ilb-next-hop/outputs.tf | 2 +- networking/ilb-next-hop/variables.tf | 2 +- networking/ilb-next-hop/versions.tf | 2 +- networking/ilb-next-hop/vms.tf | 2 +- networking/ilb-next-hop/vpc-left.tf | 2 +- networking/ilb-next-hop/vpc-right.tf | 2 +- .../backend.tf.sample | 2 +- networking/onprem-google-access-dns/main.tf | 2 +- .../onprem-google-access-dns/outputs.tf | 2 +- .../onprem-google-access-dns/variables.tf | 2 +- .../onprem-google-access-dns/versions.tf | 2 +- .../assets/main.py | 16 +- .../main.tf | 2 +- .../outputs.tf | 4 +- .../variables.tf | 2 +- .../versions.tf | 2 +- networking/shared-vpc-gke/backend.tf.sample | 2 +- networking/shared-vpc-gke/main.tf | 2 +- networking/shared-vpc-gke/outputs.tf | 2 +- networking/shared-vpc-gke/variables.tf | 2 +- networking/shared-vpc-gke/versions.tf | 2 +- tests/__init__.py | 2 +- .../__init__.py | 2 +- .../fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../test_plan.py | 2 +- .../dns_fine_grained_iam/__init__.py | 2 +- .../dns_fine_grained_iam/fixture/main.tf | 2 +- .../dns_fine_grained_iam/fixture/variables.tf | 2 +- .../dns_fine_grained_iam/test_plan.py | 2 +- .../dns_shared_vpc/__init__.py | 2 +- .../dns_shared_vpc/fixture/main.tf | 2 +- .../dns_shared_vpc/fixture/variables.tf | 2 +- .../dns_shared_vpc/test_plan.py | 2 +- .../iam_delegated_role_grants/__init__.py | 2 +- .../iam_delegated_role_grants/fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../iam_delegated_role_grants/test_plan.py | 5 +- .../onprem_sa_key_management/__init__.py | 2 +- .../onprem_sa_key_management/fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../onprem_sa_key_management/test_plan.py | 2 +- .../packer_image_builder/__init__.py | 2 +- .../packer_image_builder/fixture/main.tf | 2 +- .../packer_image_builder/fixture/variables.tf | 2 +- .../packer_image_builder/test_plan.py | 8 +- .../quota_monitoring/__init__.py | 2 +- .../quota_monitoring/fixture/main.tf | 2 +- .../quota_monitoring/fixture/variables.tf | 2 +- .../quota_monitoring/test_plan.py | 2 +- .../__init__.py | 2 +- .../fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../test_plan.py | 2 +- tests/conftest.py | 5 +- .../cmek_via_centralized_kms/__init__.py | 2 +- .../cmek_via_centralized_kms/fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../cmek_via_centralized_kms/test_plan.py | 2 +- .../data_platform_foundations/__init__.py | 2 +- .../data_platform_foundations/fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../data_platform_foundations/test_plan.py | 2 +- .../gcs_to_bq_with_dataflow/__init__.py | 2 +- .../gcs_to_bq_with_dataflow/fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../gcs_to_bq_with_dataflow/test_plan.py | 2 +- tests/examples/conftest.py | 9 +- tests/examples/test_plan.py | 2 +- tests/examples/variables.tf | 2 +- tests/factories/__init__.py | 2 +- .../__init__.py | 2 +- .../fixture/main.tf | 2 +- .../test_plan.py | 2 +- tests/factories/subnets/__init__.py | 2 +- tests/factories/subnets/fixture/main.tf | 4 +- tests/factories/subnets/test_plan.py | 2 +- tests/factories/vpc_firewall/__init__.py | 2 +- tests/factories/vpc_firewall/flat/__init__.py | 2 +- .../vpc_firewall/flat/fixture/main.tf | 2 +- .../flat/fixture/rules/common.yaml | 17 +- .../vpc_firewall/flat/fixture/variables.tf | 2 +- .../factories/vpc_firewall/flat/test_plan.py | 44 +- .../factories/vpc_firewall/nested/__init__.py | 2 +- .../vpc_firewall/nested/fixture/main.tf | 2 +- .../vpc_firewall/nested/test_plan.py | 2 +- tests/foundations/__init__.py | 2 +- tests/foundations/business_units/__init__.py | 2 +- .../business_units/fixture/main.tf | 2 +- .../business_units/fixture/variables.tf | 2 +- tests/foundations/business_units/test_plan.py | 2 +- tests/foundations/environments/__init__.py | 2 +- .../foundations/environments/fixture/main.tf | 2 +- .../environments/fixture/variables.tf | 2 +- tests/foundations/environments/test_plan.py | 2 +- tests/modules/__init__.py | 4 +- tests/modules/apigee_organization/__init__.py | 2 +- .../apigee_organization/fixture/main.tf | 2 +- .../apigee_organization/fixture/variables.tf | 4 +- .../modules/apigee_organization/test_plan.py | 6 +- tests/modules/apigee_x_instance/__init__.py | 2 +- .../modules/apigee_x_instance/fixture/main.tf | 4 +- .../apigee_x_instance/fixture/variables.tf | 4 +- tests/modules/apigee_x_instance/test_plan.py | 7 +- tests/modules/bigquery_dataset/__init__.py | 2 +- .../modules/bigquery_dataset/fixture/main.tf | 2 +- tests/modules/bigquery_dataset/test_plan.py | 2 +- tests/modules/bigtable_instance/__init__.py | 2 +- .../modules/bigtable_instance/fixture/main.tf | 2 +- .../bigtable_instance/fixture/variables.tf | 2 +- tests/modules/bigtable_instance/test_plan.py | 2 +- tests/modules/billing_budget/__init__.py | 2 +- tests/modules/billing_budget/fixture/main.tf | 2 +- .../billing_budget/fixture/variables.tf | 2 +- tests/modules/billing_budget/test_plan.py | 34 +- .../__init__.py | 2 +- .../fixture/main.tf | 2 +- .../fixture/outputs.tf | 2 +- .../fixture/variables.tf | 2 +- .../test_apply.py | 2 +- .../cloud_config_container_mysql/__init__.py | 2 +- .../fixture/main.tf | 2 +- .../fixture/outputs.tf | 2 +- .../fixture/variables.tf | 2 +- .../test_apply.py | 2 +- tests/modules/cloud_function/__init__.py | 2 +- .../cloud_function/fixture/bundle/main.py | 2 +- tests/modules/cloud_function/fixture/main.tf | 2 +- .../cloud_function/fixture/variables.tf | 2 +- tests/modules/cloud_function/test_plan.py | 2 +- .../modules/cloud_identity_group/__init__.py | 2 +- .../cloud_identity_group/fixture/main.tf | 2 +- .../cloud_identity_group/fixture/variables.tf | 2 +- .../modules/cloud_identity_group/test_plan.py | 6 +- tests/modules/cloud_run/__init__.py | 4 +- .../modules/cloud_run/fixture/bundle/main.py | 4 +- tests/modules/cloud_run/fixture/main.tf | 2 +- tests/modules/cloud_run/fixture/variables.tf | 4 +- tests/modules/cloud_run/test_plan.py | 9 +- tests/modules/cloudsql_instance/__init__.py | 2 +- .../modules/cloudsql_instance/fixture/main.tf | 2 +- .../cloudsql_instance/fixture/variables.tf | 2 +- tests/modules/cloudsql_instance/test_plan.py | 2 +- tests/modules/compute_mig/__init__.py | 2 +- tests/modules/compute_mig/fixture/main.tf | 2 +- .../modules/compute_mig/fixture/variables.tf | 2 +- tests/modules/compute_mig/test_plan.py | 79 ++-- tests/modules/compute_vm/__init__.py | 2 +- tests/modules/compute_vm/fixture/main.tf | 2 +- tests/modules/compute_vm/fixture/variables.tf | 2 +- tests/modules/compute_vm/test_plan.py | 2 +- tests/modules/compute_vm/test_plan_disks.py | 2 +- .../compute_vm/test_plan_interfaces.py | 2 +- tests/modules/container_registry/__init__.py | 2 +- .../container_registry/fixture/main.tf | 2 +- .../container_registry/fixture/variables.tf | 2 +- tests/modules/container_registry/test_plan.py | 2 +- tests/modules/dns/__init__.py | 2 +- tests/modules/dns/fixture/main.tf | 2 +- tests/modules/dns/fixture/variables.tf | 2 +- tests/modules/dns/test_plan.py | 2 +- tests/modules/endpoints/__init__.py | 2 +- tests/modules/endpoints/fixture/main.tf | 2 +- tests/modules/endpoints/fixture/openapi.yaml | 2 +- tests/modules/endpoints/fixture/variables.tf | 2 +- tests/modules/endpoints/test_plan.py | 2 +- tests/modules/folder/__init__.py | 2 +- tests/modules/folder/fixture/main.tf | 2 +- tests/modules/folder/fixture/variables.tf | 2 +- tests/modules/folder/test_plan.py | 2 +- .../folder/test_plan_firewall_policy.py | 30 +- tests/modules/folder/test_plan_logging.py | 207 +++++----- .../modules/folder/test_plan_org_policies.py | 6 +- tests/modules/gcs/__init__.py | 2 +- tests/modules/gcs/fixture/main.tf | 2 +- tests/modules/gcs/fixture/variables.tf | 2 +- tests/modules/gcs/test_plan.py | 12 +- tests/modules/gke_nodepool/__init__.py | 2 +- tests/modules/gke_nodepool/fixture/main.tf | 2 +- .../modules/gke_nodepool/fixture/variables.tf | 2 +- tests/modules/gke_nodepool/test_plan.py | 2 +- tests/modules/iam_service_account/__init__.py | 2 +- .../iam_service_account/fixture/main.tf | 2 +- .../iam_service_account/fixture/variables.tf | 2 +- .../modules/iam_service_account/test_plan.py | 12 +- tests/modules/kms/__init__.py | 2 +- tests/modules/kms/fixture/main.tf | 2 +- tests/modules/kms/fixture/outputs.tf | 2 +- tests/modules/kms/fixture/variables.tf | 2 +- tests/modules/kms/test_plan.py | 2 +- tests/modules/logging_bucket/__init__.py | 2 +- tests/modules/logging_bucket/fixture/main.tf | 2 +- .../logging_bucket/fixture/variables.tf | 2 +- tests/modules/logging_bucket/test_plan.py | 103 ++--- tests/modules/naming_convention/__init__.py | 2 +- .../modules/naming_convention/fixture/main.tf | 2 +- tests/modules/naming_convention/test_plan.py | 2 +- tests/modules/net_address/__init__.py | 2 +- tests/modules/net_address/fixture/main.tf | 2 +- tests/modules/net_address/fixture/outputs.tf | 2 +- .../modules/net_address/fixture/variables.tf | 2 +- tests/modules/net_address/test_plan.py | 2 +- tests/modules/net_ilb/__init__.py | 2 +- tests/modules/net_ilb/fixture/main.tf | 2 +- tests/modules/net_ilb/fixture/variables.tf | 2 +- tests/modules/net_ilb/test_plan.py | 2 +- .../__init__.py | 2 +- .../fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../test_plan.py | 106 ++--- tests/modules/net_vpc/__init__.py | 2 +- .../net_vpc/fixture/data/factory-subnet.yaml | 24 +- tests/modules/net_vpc/fixture/main.tf | 2 +- tests/modules/net_vpc/fixture/variables.tf | 2 +- tests/modules/net_vpc/test_plan.py | 2 +- tests/modules/net_vpc/test_plan_psn.py | 2 +- tests/modules/net_vpc/test_plan_subnets.py | 2 +- tests/modules/net_vpc_firewall/__init__.py | 2 +- .../fixture/config/cidr_template.yaml | 2 +- .../config/firewall/load_balancers.yaml | 2 +- .../modules/net_vpc_firewall/fixture/main.tf | 2 +- .../net_vpc_firewall/fixture/variables.tf | 2 +- tests/modules/net_vpc_firewall/test_plan.py | 2 +- tests/modules/organization/__init__.py | 2 +- .../fixture/data/firewall-cidrs.yaml | 2 +- .../fixture/data/firewall-rules.yaml | 2 +- tests/modules/organization/fixture/main.tf | 2 +- .../modules/organization/fixture/variables.tf | 2 +- tests/modules/organization/test_plan.py | 2 +- .../organization/test_plan_firewall.py | 2 +- .../modules/organization/test_plan_logging.py | 2 +- tests/modules/project/__init__.py | 2 +- tests/modules/project/fixture/main.tf | 2 +- tests/modules/project/fixture/variables.tf | 2 +- tests/modules/project/test_iam.py | 2 +- tests/modules/project/test_plan.py | 2 +- tests/modules/project/test_plan_logging.py | 207 +++++----- .../modules/project/test_plan_org_policies.py | 2 +- tests/modules/pubsub/__init__.py | 2 +- tests/modules/pubsub/fixture/main.tf | 2 +- tests/modules/pubsub/fixture/variables.tf | 2 +- tests/modules/pubsub/test_plan.py | 2 +- tests/modules/secret_manager/__init__.py | 2 +- tests/modules/secret_manager/fixture/main.tf | 2 +- .../secret_manager/fixture/variables.tf | 2 +- tests/modules/secret_manager/test_plan.py | 2 +- tests/modules/service_directory/__init__.py | 2 +- .../modules/service_directory/fixture/main.tf | 2 +- .../service_directory/fixture/variables.tf | 2 +- tests/modules/service_directory/test_plan.py | 2 +- tests/modules/source_repository/__init__.py | 2 +- .../modules/source_repository/fixture/main.tf | 2 +- .../source_repository/fixture/variables.tf | 2 +- tests/modules/source_repository/test_plan.py | 2 +- tests/modules/vpc_sc/__init__.py | 2 +- tests/modules/vpc_sc/fixture/main.tf | 2 +- tests/modules/vpc_sc/test_plan.py | 2 +- tests/networking/__init__.py | 2 +- .../decentralized_firewall/__init__.py | 2 +- .../decentralized_firewall/fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../decentralized_firewall/test_plan.py | 10 +- tests/networking/filtering_proxy/__init__.py | 2 +- .../filtering_proxy/fixture/main.tf | 2 +- .../filtering_proxy/fixture/variables.tf | 2 +- tests/networking/filtering_proxy/test_plan.py | 2 +- .../hub_and_spoke_peering/__init__.py | 2 +- .../hub_and_spoke_peering/fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../hub_and_spoke_peering/test_plan.py | 2 +- .../networking/hub_and_spoke_vpn/__init__.py | 2 +- .../hub_and_spoke_vpn/fixture/main.tf | 2 +- .../hub_and_spoke_vpn/fixture/variables.tf | 2 +- .../networking/hub_and_spoke_vpn/test_plan.py | 2 +- tests/networking/ilb_next_hop/__init__.py | 2 +- tests/networking/ilb_next_hop/fixture/main.tf | 2 +- .../ilb_next_hop/fixture/variables.tf | 2 +- tests/networking/ilb_next_hop/test_plan.py | 2 +- .../onprem_google_access_dns/__init__.py | 2 +- .../onprem_google_access_dns/fixture/main.tf | 2 +- .../fixture/variables.tf | 2 +- .../onprem_google_access_dns/test_plan.py | 2 +- .../__init__.py | 2 +- .../fixture/main.tf | 2 +- .../test_plan.py | 2 +- tests/networking/shared_vpc_gke/__init__.py | 2 +- .../networking/shared_vpc_gke/fixture/main.tf | 2 +- .../shared_vpc_gke/fixture/variables.tf | 2 +- tests/networking/shared_vpc_gke/test_plan.py | 2 +- third-party-solutions/openshift/prepare.py | 2 +- .../openshift/tf/bootstrap.tf | 2 +- third-party-solutions/openshift/tf/dns.tf | 2 +- .../openshift/tf/firewall.tf | 2 +- third-party-solutions/openshift/tf/iam.tf | 2 +- third-party-solutions/openshift/tf/ilb.tf | 2 +- third-party-solutions/openshift/tf/main.tf | 2 +- third-party-solutions/openshift/tf/masters.tf | 2 +- third-party-solutions/openshift/tf/outputs.tf | 2 +- .../openshift/tf/variables.tf | 2 +- .../openshift/tf/versions.tf | 2 +- tools/__init__.py | 3 +- tools/check_boilerplate.py | 2 +- tools/check_documentation.py | 2 +- tools/tfutils.py | 2 +- 676 files changed, 1526 insertions(+), 1508 deletions(-) diff --git a/.ci/cloudbuild.lint.yaml b/.ci/cloudbuild.lint.yaml index a4fe27a4..1adfc9f1 100644 --- a/.ci/cloudbuild.lint.yaml +++ b/.ci/cloudbuild.lint.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -50,7 +50,7 @@ steps: "factories/firewall-vpc-rules", "foundations", "modules", - "networking" + "networking", ] substitutions: diff --git a/.ci/cloudbuild.test.environments.yaml b/.ci/cloudbuild.test.environments.yaml index 180f33de..8d8708f0 100644 --- a/.ci/cloudbuild.test.environments.yaml +++ b/.ci/cloudbuild.test.environments.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.ci/cloudbuild.test.examples.yaml b/.ci/cloudbuild.test.examples.yaml index 83c7cb94..22ce6016 100644 --- a/.ci/cloudbuild.test.examples.yaml +++ b/.ci/cloudbuild.test.examples.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.ci/cloudbuild.test.modules.yaml b/.ci/cloudbuild.test.modules.yaml index 1222f2a5..507aafe1 100644 --- a/.ci/cloudbuild.test.modules.yaml +++ b/.ci/cloudbuild.test.modules.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.github/workflows/daily-tag.yml b/.github/workflows/daily-tag.yml index 6f712a7a..7a0efc59 100644 --- a/.github/workflows/daily-tag.yml +++ b/.github/workflows/daily-tag.yml @@ -1,5 +1,4 @@ - -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +18,7 @@ name: | on: workflow_dispatch: schedule: - - cron: '0 2 * * *' + - cron: "0 2 * * *" permissions: contents: write @@ -44,4 +43,3 @@ jobs: else echo "No changes in last 24 hours" fi - \ No newline at end of file diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index aa042740..629ccaf1 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2ac6d252..588f6eaf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -name: 'Tests' +name: "Tests" on: schedule: - - cron: '45 2 * * *' + - cron: "45 2 * * *" pull_request: branches: - master @@ -35,7 +35,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: '3.9' + python-version: "3.9" - name: Set up Terraform uses: hashicorp/setup-terraform@v1 @@ -64,7 +64,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: '3.9' + python-version: "3.9" - name: Set up Terraform uses: hashicorp/setup-terraform@v1 @@ -93,7 +93,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: '3.9' + python-version: "3.9" - name: Set up Terraform uses: hashicorp/setup-terraform@v1 diff --git a/cloud-operations/asset-inventory-feed-remediation/backend.tf.sample b/cloud-operations/asset-inventory-feed-remediation/backend.tf.sample index ca06c47e..36f4e8b8 100644 --- a/cloud-operations/asset-inventory-feed-remediation/backend.tf.sample +++ b/cloud-operations/asset-inventory-feed-remediation/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/asset-inventory-feed-remediation/cf/main.py b/cloud-operations/asset-inventory-feed-remediation/cf/main.py index f363a37c..8a46eb13 100755 --- a/cloud-operations/asset-inventory-feed-remediation/cf/main.py +++ b/cloud-operations/asset-inventory-feed-remediation/cf/main.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/asset-inventory-feed-remediation/main.tf b/cloud-operations/asset-inventory-feed-remediation/main.tf index 88d3bfdb..95fdc46f 100644 --- a/cloud-operations/asset-inventory-feed-remediation/main.tf +++ b/cloud-operations/asset-inventory-feed-remediation/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/asset-inventory-feed-remediation/outputs.tf b/cloud-operations/asset-inventory-feed-remediation/outputs.tf index 812d69c7..64391813 100644 --- a/cloud-operations/asset-inventory-feed-remediation/outputs.tf +++ b/cloud-operations/asset-inventory-feed-remediation/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/asset-inventory-feed-remediation/variables.tf b/cloud-operations/asset-inventory-feed-remediation/variables.tf index c67ee645..ca1d4fc9 100644 --- a/cloud-operations/asset-inventory-feed-remediation/variables.tf +++ b/cloud-operations/asset-inventory-feed-remediation/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/asset-inventory-feed-remediation/versions.tf b/cloud-operations/asset-inventory-feed-remediation/versions.tf index 1cc6bf89..29041268 100644 --- a/cloud-operations/asset-inventory-feed-remediation/versions.tf +++ b/cloud-operations/asset-inventory-feed-remediation/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/dns-fine-grained-iam/backend.tf.sample b/cloud-operations/dns-fine-grained-iam/backend.tf.sample index 26629625..0b20c04a 100644 --- a/cloud-operations/dns-fine-grained-iam/backend.tf.sample +++ b/cloud-operations/dns-fine-grained-iam/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/dns-fine-grained-iam/main.tf b/cloud-operations/dns-fine-grained-iam/main.tf index 6876a2aa..56fa3936 100644 --- a/cloud-operations/dns-fine-grained-iam/main.tf +++ b/cloud-operations/dns-fine-grained-iam/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/dns-fine-grained-iam/outputs.tf b/cloud-operations/dns-fine-grained-iam/outputs.tf index 1f7f6612..d5b7aa11 100644 --- a/cloud-operations/dns-fine-grained-iam/outputs.tf +++ b/cloud-operations/dns-fine-grained-iam/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/dns-fine-grained-iam/variables.tf b/cloud-operations/dns-fine-grained-iam/variables.tf index 4604bfd5..008a48c7 100644 --- a/cloud-operations/dns-fine-grained-iam/variables.tf +++ b/cloud-operations/dns-fine-grained-iam/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/dns-fine-grained-iam/versions.tf b/cloud-operations/dns-fine-grained-iam/versions.tf index 1cc6bf89..29041268 100644 --- a/cloud-operations/dns-fine-grained-iam/versions.tf +++ b/cloud-operations/dns-fine-grained-iam/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/network.tf b/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/network.tf index 7864b3fd..725ef636 100644 --- a/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/network.tf +++ b/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/network.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,4 +40,4 @@ module "cloud-dns" { teams = var.teams dns_domain = var.dns_domain -} \ No newline at end of file +} diff --git a/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/outputs.tf b/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/outputs.tf index 1a097a71..fc145c2f 100644 --- a/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/outputs.tf +++ b/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,4 +22,4 @@ output "host_project_id" { output "shared_vpc_self_link" { description = "Shared VPC Self link" value = module.shared-vpc.self_link -} \ No newline at end of file +} diff --git a/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/projects.tf b/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/projects.tf index 5d7eef9d..66602cb1 100644 --- a/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/projects.tf +++ b/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/projects.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,4 +71,4 @@ module "project-service-2" { attach = true host_project = module.project-host.project_id } -} \ No newline at end of file +} diff --git a/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/test.example b/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/test.example index b00d2bb0..8a302df8 100644 --- a/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/test.example +++ b/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/test.example @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/variables.tf b/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/variables.tf index 1e79a9dd..77743bae 100644 --- a/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/variables.tf +++ b/cloud-operations/dns-shared-vpc/examples/shared-vpc-example/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,4 +62,4 @@ variable "dns_domain" { variable "teams" { description = "List of teams that require their own Cloud DNS instance" default = ["appteam1", "appteam2"] -} \ No newline at end of file +} diff --git a/cloud-operations/dns-shared-vpc/main.tf b/cloud-operations/dns-shared-vpc/main.tf index f7419b36..58a31bd3 100644 --- a/cloud-operations/dns-shared-vpc/main.tf +++ b/cloud-operations/dns-shared-vpc/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/dns-shared-vpc/outputs.tf b/cloud-operations/dns-shared-vpc/outputs.tf index dbe838a0..2c9021e7 100644 --- a/cloud-operations/dns-shared-vpc/outputs.tf +++ b/cloud-operations/dns-shared-vpc/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/dns-shared-vpc/variables.tf b/cloud-operations/dns-shared-vpc/variables.tf index 0cf5b95f..f74acfde 100644 --- a/cloud-operations/dns-shared-vpc/variables.tf +++ b/cloud-operations/dns-shared-vpc/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/dns-shared-vpc/versions.tf b/cloud-operations/dns-shared-vpc/versions.tf index 1cc6bf89..29041268 100644 --- a/cloud-operations/dns-shared-vpc/versions.tf +++ b/cloud-operations/dns-shared-vpc/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/iam-delegated-role-grants/audit.py b/cloud-operations/iam-delegated-role-grants/audit.py index abb042e8..75488c9b 100644 --- a/cloud-operations/iam-delegated-role-grants/audit.py +++ b/cloud-operations/iam-delegated-role-grants/audit.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,50 +28,50 @@ SENSITIVE_PERMISSIONS = { def get_role_permissions(role): - if role.startswith("roles/"): - endpoint = iam_service.roles() - elif role.startswith("projects/"): - endpoint = iam_service.projects().roles() - elif role.startswith("organizations/"): - endpoint = iam_service.organizations().roles() - else: - raise Exception(f"Invalid role {role}") + if role.startswith("roles/"): + endpoint = iam_service.roles() + elif role.startswith("projects/"): + endpoint = iam_service.projects().roles() + elif role.startswith("organizations/"): + endpoint = iam_service.organizations().roles() + else: + raise Exception(f"Invalid role {role}") - response = endpoint.get(name=role).execute() - permissions = response.get("includedPermissions") - return permissions + response = endpoint.get(name=role).execute() + permissions = response.get("includedPermissions") + return permissions @click.command() @click.argument("file", type=click.File("r")) def main(file): - """Verify that the set of GCP roles in FILE does not include the - permission setIamPolicy at project, folder or organization level + """Verify that the set of GCP roles in FILE does not include the + permission setIamPolicy at project, folder or organization level - This program authenticates against GCP using default application - credentials to query project and organization level roles. + This program authenticates against GCP using default application + credentials to query project and organization level roles. - """ - clean_roles = [x.rstrip(" \n") for x in file] - roles = (x for x in clean_roles if x) + """ + clean_roles = [x.rstrip(" \n") for x in file] + roles = (x for x in clean_roles if x) - allok = True - for role in roles: - try: - permissions = set(get_role_permissions(role)) - except Error as e: - print(f"WARNING: can't read {role}: {e}") - allok = False - else: - matched_sensitive_permissions = SENSITIVE_PERMISSIONS & permissions - if matched_sensitive_permissions: - print(f"WARNING: {role} contains {matched_sensitive_permissions}") - allok = False - else: - print(f"{role} ok") + allok = True + for role in roles: + try: + permissions = set(get_role_permissions(role)) + except Error as e: + print(f"WARNING: can't read {role}: {e}") + allok = False + else: + matched_sensitive_permissions = SENSITIVE_PERMISSIONS & permissions + if matched_sensitive_permissions: + print(f"WARNING: {role} contains {matched_sensitive_permissions}") + allok = False + else: + print(f"{role} ok") - exit(0 if allok else 1) + exit(0 if allok else 1) if __name__ == "__main__": - main() + main() diff --git a/cloud-operations/iam-delegated-role-grants/main.tf b/cloud-operations/iam-delegated-role-grants/main.tf index cdd8d35d..b9be3dad 100644 --- a/cloud-operations/iam-delegated-role-grants/main.tf +++ b/cloud-operations/iam-delegated-role-grants/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/iam-delegated-role-grants/outputs.tf b/cloud-operations/iam-delegated-role-grants/outputs.tf index d28a337a..11a2ddf1 100644 --- a/cloud-operations/iam-delegated-role-grants/outputs.tf +++ b/cloud-operations/iam-delegated-role-grants/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/iam-delegated-role-grants/variables.tf b/cloud-operations/iam-delegated-role-grants/variables.tf index e90100e9..6686f568 100644 --- a/cloud-operations/iam-delegated-role-grants/variables.tf +++ b/cloud-operations/iam-delegated-role-grants/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/iam-delegated-role-grants/versions.tf b/cloud-operations/iam-delegated-role-grants/versions.tf index 1cc6bf89..29041268 100644 --- a/cloud-operations/iam-delegated-role-grants/versions.tf +++ b/cloud-operations/iam-delegated-role-grants/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/onprem-sa-key-management/backend.tf.sample b/cloud-operations/onprem-sa-key-management/backend.tf.sample index 9eebc1ba..a22a87d8 100644 --- a/cloud-operations/onprem-sa-key-management/backend.tf.sample +++ b/cloud-operations/onprem-sa-key-management/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/onprem-sa-key-management/main.tf b/cloud-operations/onprem-sa-key-management/main.tf index e4ffe168..1fc53bbb 100644 --- a/cloud-operations/onprem-sa-key-management/main.tf +++ b/cloud-operations/onprem-sa-key-management/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/onprem-sa-key-management/outputs.tf b/cloud-operations/onprem-sa-key-management/outputs.tf index a8f9c16a..9174474c 100644 --- a/cloud-operations/onprem-sa-key-management/outputs.tf +++ b/cloud-operations/onprem-sa-key-management/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/onprem-sa-key-management/variables.tf b/cloud-operations/onprem-sa-key-management/variables.tf index 60f893c5..329c5deb 100644 --- a/cloud-operations/onprem-sa-key-management/variables.tf +++ b/cloud-operations/onprem-sa-key-management/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/onprem-sa-key-management/versions.tf b/cloud-operations/onprem-sa-key-management/versions.tf index 1cc6bf89..29041268 100644 --- a/cloud-operations/onprem-sa-key-management/versions.tf +++ b/cloud-operations/onprem-sa-key-management/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/packer-image-builder/main.tf b/cloud-operations/packer-image-builder/main.tf index 122ea104..4e073537 100644 --- a/cloud-operations/packer-image-builder/main.tf +++ b/cloud-operations/packer-image-builder/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -128,4 +128,4 @@ resource "local_file" "packer-vars" { USE_IAP = "${var.use_iap}" }) filename = local.packer_variables_file -} \ No newline at end of file +} diff --git a/cloud-operations/packer-image-builder/outputs.tf b/cloud-operations/packer-image-builder/outputs.tf index b19cac70..1ca7bfc0 100644 --- a/cloud-operations/packer-image-builder/outputs.tf +++ b/cloud-operations/packer-image-builder/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/packer-image-builder/packer/build.pkr.hcl b/cloud-operations/packer-image-builder/packer/build.pkr.hcl index 71c0c2d0..259a0b68 100644 --- a/cloud-operations/packer-image-builder/packer/build.pkr.hcl +++ b/cloud-operations/packer-image-builder/packer/build.pkr.hcl @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/packer-image-builder/packer/install_httpd.sh b/cloud-operations/packer-image-builder/packer/install_httpd.sh index 9a26b3aa..6d0c6a8e 100644 --- a/cloud-operations/packer-image-builder/packer/install_httpd.sh +++ b/cloud-operations/packer-image-builder/packer/install_httpd.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/packer-image-builder/packer/variables.pkr.hcl b/cloud-operations/packer-image-builder/packer/variables.pkr.hcl index 6686fd84..39ac9828 100644 --- a/cloud-operations/packer-image-builder/packer/variables.pkr.hcl +++ b/cloud-operations/packer-image-builder/packer/variables.pkr.hcl @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/packer-image-builder/variables.tf b/cloud-operations/packer-image-builder/variables.tf index 8ca8e4b3..d3b49a2f 100644 --- a/cloud-operations/packer-image-builder/variables.tf +++ b/cloud-operations/packer-image-builder/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/packer-image-builder/versions.tf b/cloud-operations/packer-image-builder/versions.tf index 1cc6bf89..29041268 100644 --- a/cloud-operations/packer-image-builder/versions.tf +++ b/cloud-operations/packer-image-builder/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/quota-monitoring/backend.tf.sample b/cloud-operations/quota-monitoring/backend.tf.sample index add43684..a0a18aed 100644 --- a/cloud-operations/quota-monitoring/backend.tf.sample +++ b/cloud-operations/quota-monitoring/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/quota-monitoring/cf/main.py b/cloud-operations/quota-monitoring/cf/main.py index ed26dc0b..622c2831 100755 --- a/cloud-operations/quota-monitoring/cf/main.py +++ b/cloud-operations/quota-monitoring/cf/main.py @@ -1,5 +1,5 @@ #! /usr/bin/env python3 -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/quota-monitoring/main.tf b/cloud-operations/quota-monitoring/main.tf index 01d03691..66b6185d 100644 --- a/cloud-operations/quota-monitoring/main.tf +++ b/cloud-operations/quota-monitoring/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/quota-monitoring/outputs.tf b/cloud-operations/quota-monitoring/outputs.tf index 9d277cce..3ea74b55 100644 --- a/cloud-operations/quota-monitoring/outputs.tf +++ b/cloud-operations/quota-monitoring/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/quota-monitoring/variables.tf b/cloud-operations/quota-monitoring/variables.tf index 02d3b1d7..12803af0 100644 --- a/cloud-operations/quota-monitoring/variables.tf +++ b/cloud-operations/quota-monitoring/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/quota-monitoring/versions.tf b/cloud-operations/quota-monitoring/versions.tf index 1cc6bf89..29041268 100644 --- a/cloud-operations/quota-monitoring/versions.tf +++ b/cloud-operations/quota-monitoring/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample b/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample index 12097d0a..fafd25d5 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample +++ b/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py b/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py index 9f9cfb3f..85bba465 100755 --- a/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -62,7 +62,8 @@ def main_cli(project=None, bq_project=None, bq_dataset=None, bq_table=None, bq_t the dataset specified on a dated table with the name specified. ''' try: - _main(project, bq_project, bq_dataset, bq_table, bq_table_overwrite, target_node, read_time, verbose) + _main(project, bq_project, bq_dataset, bq_table, + bq_table_overwrite, target_node, read_time, verbose) except RuntimeError: logging.exception('exception raised') @@ -89,10 +90,10 @@ def _main(project=None, bq_project=None, bq_dataset=None, bq_table=None, bq_tabl if bq_table_overwrite == False: read_time = datetime.datetime.now() output_config.bigquery_destination.table = '%s_%s' % ( - bq_table, read_time.strftime('%Y%m%d')) + bq_table, read_time.strftime('%Y%m%d')) else: - output_config.bigquery_destination.table = '%s_latest' % ( - bq_table) + output_config.bigquery_destination.table = '%s_latest' % ( + bq_table) content_type = asset_v1.ContentType.RESOURCE output_config.bigquery_destination.dataset = 'projects/%s/datasets/%s' % ( bq_project, bq_dataset) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py b/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py index cb54b0bc..ed2e54b8 100755 --- a/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cffile/main.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -44,6 +44,7 @@ def _configure_logging(verbose=True): logging.basicConfig(level=level) warnings.filterwarnings('ignore', r'.*end user credentials.*', UserWarning) + @click.command() @click.option('--bucket', required=True, help='GCS bucket for export') @click.option('--filename', required=True, help='Path and filename with extension to export e.g. folder/export.json .') @@ -60,6 +61,7 @@ def main_cli(bucket=None, filename=None, format=None, bq_dataset=None, bq_table= except RuntimeError: logging.exception('exception raised') + def main(event, context): 'Cloud Function entry point.' try: @@ -83,10 +85,10 @@ def _main(bucket=None, filename=None, format=None, bq_dataset=None, bq_table=Non table_ref = dataset_ref.table(bq_table) job_config = bigquery.job.ExtractJobConfig() job_config.destination_format = ( - getattr(bigquery.DestinationFormat, format) ) + getattr(bigquery.DestinationFormat, format)) extract_job = client.extract_table( - table_ref, destination_uri, job_config=job_config - ) + table_ref, destination_uri, job_config=job_config + ) try: extract_job.result() except (GoogleAPIError, googleapiclient.errors.HttpError) as e: @@ -96,4 +98,4 @@ def _main(bucket=None, filename=None, format=None, bq_dataset=None, bq_table=Non if __name__ == '__main__': - main_cli() \ No newline at end of file + main_cli() diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index 39c1e37d..a0a045be 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf b/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf index 8a4af39a..45d32170 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf index b31291b8..8c645447 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/versions.tf b/cloud-operations/scheduled-asset-inventory-export-bq/versions.tf index 1cc6bf89..29041268 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/versions.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/cmek-via-centralized-kms/backend.tf.sample b/data-solutions/cmek-via-centralized-kms/backend.tf.sample index 99f84b17..4f2bb336 100644 --- a/data-solutions/cmek-via-centralized-kms/backend.tf.sample +++ b/data-solutions/cmek-via-centralized-kms/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/cmek-via-centralized-kms/main.tf b/data-solutions/cmek-via-centralized-kms/main.tf index 66372b2c..2889d87f 100644 --- a/data-solutions/cmek-via-centralized-kms/main.tf +++ b/data-solutions/cmek-via-centralized-kms/main.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/cmek-via-centralized-kms/outputs.tf b/data-solutions/cmek-via-centralized-kms/outputs.tf index fdda13dc..1d7767a5 100644 --- a/data-solutions/cmek-via-centralized-kms/outputs.tf +++ b/data-solutions/cmek-via-centralized-kms/outputs.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/cmek-via-centralized-kms/variables.tf b/data-solutions/cmek-via-centralized-kms/variables.tf index f9f1ed52..737bde3d 100644 --- a/data-solutions/cmek-via-centralized-kms/variables.tf +++ b/data-solutions/cmek-via-centralized-kms/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/cmek-via-centralized-kms/versions.tf b/data-solutions/cmek-via-centralized-kms/versions.tf index 1cc6bf89..29041268 100644 --- a/data-solutions/cmek-via-centralized-kms/versions.tf +++ b/data-solutions/cmek-via-centralized-kms/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/data-platform-foundations/01-environment/versions.tf b/data-solutions/data-platform-foundations/01-environment/versions.tf index 1cc6bf89..29041268 100644 --- a/data-solutions/data-platform-foundations/01-environment/versions.tf +++ b/data-solutions/data-platform-foundations/01-environment/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/data-platform-foundations/02-resources/providers.tf b/data-solutions/data-platform-foundations/02-resources/providers.tf index c2ad682c..2907650f 100644 --- a/data-solutions/data-platform-foundations/02-resources/providers.tf +++ b/data-solutions/data-platform-foundations/02-resources/providers.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/data-solutions/data-platform-foundations/02-resources/versions.tf b/data-solutions/data-platform-foundations/02-resources/versions.tf index 1cc6bf89..29041268 100644 --- a/data-solutions/data-platform-foundations/02-resources/versions.tf +++ b/data-solutions/data-platform-foundations/02-resources/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/gcs-to-bq-with-dataflow/backend.tf.sample b/data-solutions/gcs-to-bq-with-dataflow/backend.tf.sample index 99f84b17..4f2bb336 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/backend.tf.sample +++ b/data-solutions/gcs-to-bq-with-dataflow/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/gcs-to-bq-with-dataflow/main.tf b/data-solutions/gcs-to-bq-with-dataflow/main.tf index d64789a6..f64c4696 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/main.tf +++ b/data-solutions/gcs-to-bq-with-dataflow/main.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/gcs-to-bq-with-dataflow/outputs.tf b/data-solutions/gcs-to-bq-with-dataflow/outputs.tf index 315fc909..0678de67 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/outputs.tf +++ b/data-solutions/gcs-to-bq-with-dataflow/outputs.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/gcs-to-bq-with-dataflow/scripts/data_ingestion/data_ingestion.py b/data-solutions/gcs-to-bq-with-dataflow/scripts/data_ingestion/data_ingestion.py index 4745e242..d31187d3 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/scripts/data_ingestion/data_ingestion.py +++ b/data-solutions/gcs-to-bq-with-dataflow/scripts/data_ingestion/data_ingestion.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/gcs-to-bq-with-dataflow/scripts/person_details_generator/person_details_generator.py b/data-solutions/gcs-to-bq-with-dataflow/scripts/person_details_generator/person_details_generator.py index 9292e4f1..84b6a262 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/scripts/person_details_generator/person_details_generator.py +++ b/data-solutions/gcs-to-bq-with-dataflow/scripts/person_details_generator/person_details_generator.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/gcs-to-bq-with-dataflow/variables.tf b/data-solutions/gcs-to-bq-with-dataflow/variables.tf index 1b4c4dff..8bb4aa66 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/variables.tf +++ b/data-solutions/gcs-to-bq-with-dataflow/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/data-solutions/gcs-to-bq-with-dataflow/versions.tf b/data-solutions/gcs-to-bq-with-dataflow/versions.tf index 1cc6bf89..29041268 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/versions.tf +++ b/data-solutions/gcs-to-bq-with-dataflow/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/default-versions.tf b/default-versions.tf index 1cc6bf89..29041268 100644 --- a/default-versions.tf +++ b/default-versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/factories/example-environments/dev/main.tf b/factories/example-environments/dev/main.tf index a7c58c1e..36233f7b 100644 --- a/factories/example-environments/dev/main.tf +++ b/factories/example-environments/dev/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/example-environments/dev/versions.tf b/factories/example-environments/dev/versions.tf index 1cc6bf89..29041268 100644 --- a/factories/example-environments/dev/versions.tf +++ b/factories/example-environments/dev/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/factories/example-environments/prod/main.tf b/factories/example-environments/prod/main.tf index a7c58c1e..36233f7b 100644 --- a/factories/example-environments/prod/main.tf +++ b/factories/example-environments/prod/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/example-environments/prod/versions.tf b/factories/example-environments/prod/versions.tf index 1cc6bf89..29041268 100644 --- a/factories/example-environments/prod/versions.tf +++ b/factories/example-environments/prod/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/factories/firewall-hierarchical-policies/main.tf b/factories/firewall-hierarchical-policies/main.tf index b63c9f5a..499a2a5b 100644 --- a/factories/firewall-hierarchical-policies/main.tf +++ b/factories/firewall-hierarchical-policies/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/firewall-hierarchical-policies/outputs.tf b/factories/firewall-hierarchical-policies/outputs.tf index 42d6738d..4ead20e3 100644 --- a/factories/firewall-hierarchical-policies/outputs.tf +++ b/factories/firewall-hierarchical-policies/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/firewall-hierarchical-policies/variables.tf b/factories/firewall-hierarchical-policies/variables.tf index 503a2e01..1fafc45f 100644 --- a/factories/firewall-hierarchical-policies/variables.tf +++ b/factories/firewall-hierarchical-policies/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/firewall-hierarchical-policies/versions.tf b/factories/firewall-hierarchical-policies/versions.tf index 1cc6bf89..29041268 100644 --- a/factories/firewall-hierarchical-policies/versions.tf +++ b/factories/firewall-hierarchical-policies/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/factories/firewall-vpc-rules/flat/main.tf b/factories/firewall-vpc-rules/flat/main.tf index ab19b23a..d478625c 100644 --- a/factories/firewall-vpc-rules/flat/main.tf +++ b/factories/firewall-vpc-rules/flat/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/firewall-vpc-rules/flat/outputs.tf b/factories/firewall-vpc-rules/flat/outputs.tf index ce49bff9..f60e9c92 100644 --- a/factories/firewall-vpc-rules/flat/outputs.tf +++ b/factories/firewall-vpc-rules/flat/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/firewall-vpc-rules/flat/variables.tf b/factories/firewall-vpc-rules/flat/variables.tf index 850c642f..04cb7a56 100644 --- a/factories/firewall-vpc-rules/flat/variables.tf +++ b/factories/firewall-vpc-rules/flat/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/firewall-vpc-rules/flat/versions.tf b/factories/firewall-vpc-rules/flat/versions.tf index 1cc6bf89..29041268 100644 --- a/factories/firewall-vpc-rules/flat/versions.tf +++ b/factories/firewall-vpc-rules/flat/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/factories/firewall-vpc-rules/nested/main.tf b/factories/firewall-vpc-rules/nested/main.tf index 62f535bc..afbf4473 100644 --- a/factories/firewall-vpc-rules/nested/main.tf +++ b/factories/firewall-vpc-rules/nested/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/firewall-vpc-rules/nested/outputs.tf b/factories/firewall-vpc-rules/nested/outputs.tf index 36b9e166..e6f5dc3f 100644 --- a/factories/firewall-vpc-rules/nested/outputs.tf +++ b/factories/firewall-vpc-rules/nested/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/firewall-vpc-rules/nested/variables.tf b/factories/firewall-vpc-rules/nested/variables.tf index 503a2e01..1fafc45f 100644 --- a/factories/firewall-vpc-rules/nested/variables.tf +++ b/factories/firewall-vpc-rules/nested/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/firewall-vpc-rules/nested/versions.tf b/factories/firewall-vpc-rules/nested/versions.tf index 1cc6bf89..29041268 100644 --- a/factories/firewall-vpc-rules/nested/versions.tf +++ b/factories/firewall-vpc-rules/nested/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/factories/subnets/main.tf b/factories/subnets/main.tf index 0bed8407..e837d807 100644 --- a/factories/subnets/main.tf +++ b/factories/subnets/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/subnets/outputs.tf b/factories/subnets/outputs.tf index 2cdecb2e..b068c1cc 100644 --- a/factories/subnets/outputs.tf +++ b/factories/subnets/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/subnets/variables.tf b/factories/subnets/variables.tf index fbea28bb..c6d3e731 100644 --- a/factories/subnets/variables.tf +++ b/factories/subnets/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/factories/subnets/versions.tf b/factories/subnets/versions.tf index 1cc6bf89..29041268 100644 --- a/factories/subnets/versions.tf +++ b/factories/subnets/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/foundations/business-units/backend.tf.sample b/foundations/business-units/backend.tf.sample index 1a4ffa12..065cd071 100644 --- a/foundations/business-units/backend.tf.sample +++ b/foundations/business-units/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/foundations/business-units/main.tf b/foundations/business-units/main.tf index 43782c69..01567682 100644 --- a/foundations/business-units/main.tf +++ b/foundations/business-units/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/foundations/business-units/outputs.tf b/foundations/business-units/outputs.tf index a828ef8c..4456123a 100644 --- a/foundations/business-units/outputs.tf +++ b/foundations/business-units/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/foundations/business-units/terraform.tfvars.sample b/foundations/business-units/terraform.tfvars.sample index b4f27c81..7036b969 100644 --- a/foundations/business-units/terraform.tfvars.sample +++ b/foundations/business-units/terraform.tfvars.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/foundations/business-units/variables.tf b/foundations/business-units/variables.tf index 88b9a777..69f5ac3b 100644 --- a/foundations/business-units/variables.tf +++ b/foundations/business-units/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/foundations/business-units/versions.tf b/foundations/business-units/versions.tf index 1cc6bf89..29041268 100644 --- a/foundations/business-units/versions.tf +++ b/foundations/business-units/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/foundations/environments/backend.tf.sample b/foundations/environments/backend.tf.sample index acf3b1be..4232a96e 100644 --- a/foundations/environments/backend.tf.sample +++ b/foundations/environments/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/foundations/environments/locals.tf b/foundations/environments/locals.tf index 9c501d71..f6c9ae0b 100644 --- a/foundations/environments/locals.tf +++ b/foundations/environments/locals.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/foundations/environments/main.tf b/foundations/environments/main.tf index 7174b2f5..a3cea472 100644 --- a/foundations/environments/main.tf +++ b/foundations/environments/main.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/foundations/environments/outputs.tf b/foundations/environments/outputs.tf index 50eb79a2..4d5f9d4c 100644 --- a/foundations/environments/outputs.tf +++ b/foundations/environments/outputs.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/foundations/environments/variables.tf b/foundations/environments/variables.tf index 2458b8f2..3b38a8ca 100644 --- a/foundations/environments/variables.tf +++ b/foundations/environments/variables.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/foundations/environments/versions.tf b/foundations/environments/versions.tf index 1cc6bf89..29041268 100644 --- a/foundations/environments/versions.tf +++ b/foundations/environments/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/__experimental/net-neg/main.tf b/modules/__experimental/net-neg/main.tf index dd5ef7e5..773a75c6 100644 --- a/modules/__experimental/net-neg/main.tf +++ b/modules/__experimental/net-neg/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/__experimental/net-neg/outputs.tf b/modules/__experimental/net-neg/outputs.tf index 6b5eac52..351a020c 100644 --- a/modules/__experimental/net-neg/outputs.tf +++ b/modules/__experimental/net-neg/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/__experimental/net-neg/variables.tf b/modules/__experimental/net-neg/variables.tf index 6594f11d..a3f872c6 100644 --- a/modules/__experimental/net-neg/variables.tf +++ b/modules/__experimental/net-neg/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/__experimental/net-neg/versions.tf b/modules/__experimental/net-neg/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/__experimental/net-neg/versions.tf +++ b/modules/__experimental/net-neg/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/apigee-organization/main.tf b/modules/apigee-organization/main.tf index b1c13481..fe798f92 100644 --- a/modules/apigee-organization/main.tf +++ b/modules/apigee-organization/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,4 +52,4 @@ resource "google_apigee_envgroup_attachment" "env_to_envgroup_attachment" { for_each = { for pair in local.env_envgroup_pairs : "${pair.envgroup}-${pair.env}" => pair } envgroup_id = google_apigee_envgroup.apigee_envgroup[each.value.envgroup].id environment = google_apigee_environment.apigee_env[each.value.env].name -} \ No newline at end of file +} diff --git a/modules/apigee-organization/outputs.tf b/modules/apigee-organization/outputs.tf index aa8b74ec..f53a4696 100644 --- a/modules/apigee-organization/outputs.tf +++ b/modules/apigee-organization/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/apigee-organization/variables.tf b/modules/apigee-organization/variables.tf index 7e855020..d7ab70da 100644 --- a/modules/apigee-organization/variables.tf +++ b/modules/apigee-organization/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/apigee-organization/versions.tf b/modules/apigee-organization/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/apigee-organization/versions.tf +++ b/modules/apigee-organization/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/apigee-x-instance/main.tf b/modules/apigee-x-instance/main.tf index 552d6d08..1d6f84e8 100644 --- a/modules/apigee-x-instance/main.tf +++ b/modules/apigee-x-instance/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/apigee-x-instance/outputs.tf b/modules/apigee-x-instance/outputs.tf index 0f2d5d6b..d21a422a 100644 --- a/modules/apigee-x-instance/outputs.tf +++ b/modules/apigee-x-instance/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/apigee-x-instance/variables.tf b/modules/apigee-x-instance/variables.tf index 219ee7d6..a1cfc954 100644 --- a/modules/apigee-x-instance/variables.tf +++ b/modules/apigee-x-instance/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/apigee-x-instance/versions.tf b/modules/apigee-x-instance/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/apigee-x-instance/versions.tf +++ b/modules/apigee-x-instance/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/artifact-registry/main.tf b/modules/artifact-registry/main.tf index 368ccc1c..8b01e096 100644 --- a/modules/artifact-registry/main.tf +++ b/modules/artifact-registry/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/artifact-registry/outputs.tf b/modules/artifact-registry/outputs.tf index e9cc69af..cb035b76 100644 --- a/modules/artifact-registry/outputs.tf +++ b/modules/artifact-registry/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/artifact-registry/variables.tf b/modules/artifact-registry/variables.tf index b350e824..1cb16b74 100644 --- a/modules/artifact-registry/variables.tf +++ b/modules/artifact-registry/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/artifact-registry/versions.tf b/modules/artifact-registry/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/artifact-registry/versions.tf +++ b/modules/artifact-registry/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/bigquery-dataset/main.tf b/modules/bigquery-dataset/main.tf index 24f72a23..fc8f8706 100644 --- a/modules/bigquery-dataset/main.tf +++ b/modules/bigquery-dataset/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/bigquery-dataset/outputs.tf b/modules/bigquery-dataset/outputs.tf index c8a533b3..dd2da22c 100644 --- a/modules/bigquery-dataset/outputs.tf +++ b/modules/bigquery-dataset/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/bigquery-dataset/variables.tf b/modules/bigquery-dataset/variables.tf index cadf5aeb..5f8028ab 100644 --- a/modules/bigquery-dataset/variables.tf +++ b/modules/bigquery-dataset/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/bigquery-dataset/versions.tf b/modules/bigquery-dataset/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/bigquery-dataset/versions.tf +++ b/modules/bigquery-dataset/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/bigtable-instance/main.tf b/modules/bigtable-instance/main.tf index f3081fca..49423d94 100644 --- a/modules/bigtable-instance/main.tf +++ b/modules/bigtable-instance/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/bigtable-instance/outputs.tf b/modules/bigtable-instance/outputs.tf index afbb1607..3cb46224 100644 --- a/modules/bigtable-instance/outputs.tf +++ b/modules/bigtable-instance/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/bigtable-instance/variables.tf b/modules/bigtable-instance/variables.tf index d8cf2dc8..d98cfab5 100644 --- a/modules/bigtable-instance/variables.tf +++ b/modules/bigtable-instance/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/bigtable-instance/versions.tf b/modules/bigtable-instance/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/bigtable-instance/versions.tf +++ b/modules/bigtable-instance/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/billing-budget/main.tf b/modules/billing-budget/main.tf index 739dcedd..2c6838dc 100644 --- a/modules/billing-budget/main.tf +++ b/modules/billing-budget/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/billing-budget/outputs.tf b/modules/billing-budget/outputs.tf index 9f2dd4ff..1d4e082f 100644 --- a/modules/billing-budget/outputs.tf +++ b/modules/billing-budget/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/billing-budget/variables.tf b/modules/billing-budget/variables.tf index e37301fa..bc836fce 100644 --- a/modules/billing-budget/variables.tf +++ b/modules/billing-budget/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/billing-budget/versions.tf b/modules/billing-budget/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/billing-budget/versions.tf +++ b/modules/billing-budget/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/coredns/cloud-config.yaml b/modules/cloud-config-container/coredns/cloud-config.yaml index 29616b43..0796fc6b 100644 --- a/modules/cloud-config-container/coredns/cloud-config.yaml +++ b/modules/cloud-config-container/coredns/cloud-config.yaml @@ -1,6 +1,6 @@ #cloud-config -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/coredns/main.tf b/modules/cloud-config-container/coredns/main.tf index 3d9079e7..789168ca 100644 --- a/modules/cloud-config-container/coredns/main.tf +++ b/modules/cloud-config-container/coredns/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/coredns/outputs.tf b/modules/cloud-config-container/coredns/outputs.tf index 33dcc551..7d8d4165 100644 --- a/modules/cloud-config-container/coredns/outputs.tf +++ b/modules/cloud-config-container/coredns/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/coredns/variables.tf b/modules/cloud-config-container/coredns/variables.tf index 33f6a319..c323017f 100644 --- a/modules/cloud-config-container/coredns/variables.tf +++ b/modules/cloud-config-container/coredns/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/coredns/versions.tf b/modules/cloud-config-container/coredns/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/cloud-config-container/coredns/versions.tf +++ b/modules/cloud-config-container/coredns/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/cos-generic-metadata/cloud-config.yaml b/modules/cloud-config-container/cos-generic-metadata/cloud-config.yaml index 535e3360..fc75616a 100644 --- a/modules/cloud-config-container/cos-generic-metadata/cloud-config.yaml +++ b/modules/cloud-config-container/cos-generic-metadata/cloud-config.yaml @@ -1,6 +1,6 @@ #cloud-config -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/cos-generic-metadata/main.tf b/modules/cloud-config-container/cos-generic-metadata/main.tf index db159650..5019fa09 100644 --- a/modules/cloud-config-container/cos-generic-metadata/main.tf +++ b/modules/cloud-config-container/cos-generic-metadata/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/cos-generic-metadata/outputs.tf b/modules/cloud-config-container/cos-generic-metadata/outputs.tf index 33dcc551..7d8d4165 100644 --- a/modules/cloud-config-container/cos-generic-metadata/outputs.tf +++ b/modules/cloud-config-container/cos-generic-metadata/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/cos-generic-metadata/variables.tf b/modules/cloud-config-container/cos-generic-metadata/variables.tf index 0f4a947d..41a11557 100644 --- a/modules/cloud-config-container/cos-generic-metadata/variables.tf +++ b/modules/cloud-config-container/cos-generic-metadata/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/cos-generic-metadata/versions.tf b/modules/cloud-config-container/cos-generic-metadata/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/cloud-config-container/cos-generic-metadata/versions.tf +++ b/modules/cloud-config-container/cos-generic-metadata/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/envoy-traffic-director/files/customize.sh b/modules/cloud-config-container/envoy-traffic-director/files/customize.sh index 5593826f..85c8746e 100644 --- a/modules/cloud-config-container/envoy-traffic-director/files/customize.sh +++ b/modules/cloud-config-container/envoy-traffic-director/files/customize.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/envoy-traffic-director/files/envoy.yaml b/modules/cloud-config-container/envoy-traffic-director/files/envoy.yaml index 009b7a70..2be4ef3c 100644 --- a/modules/cloud-config-container/envoy-traffic-director/files/envoy.yaml +++ b/modules/cloud-config-container/envoy-traffic-director/files/envoy.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/envoy-traffic-director/main.tf b/modules/cloud-config-container/envoy-traffic-director/main.tf index c208a728..cdb805c6 100644 --- a/modules/cloud-config-container/envoy-traffic-director/main.tf +++ b/modules/cloud-config-container/envoy-traffic-director/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/envoy-traffic-director/outputs.tf b/modules/cloud-config-container/envoy-traffic-director/outputs.tf index 2044eb46..4ce8d247 100644 --- a/modules/cloud-config-container/envoy-traffic-director/outputs.tf +++ b/modules/cloud-config-container/envoy-traffic-director/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/envoy-traffic-director/variables.tf b/modules/cloud-config-container/envoy-traffic-director/variables.tf index a3c4f1f5..6eb7dcc7 100644 --- a/modules/cloud-config-container/envoy-traffic-director/variables.tf +++ b/modules/cloud-config-container/envoy-traffic-director/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/envoy-traffic-director/versions.tf b/modules/cloud-config-container/envoy-traffic-director/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/cloud-config-container/envoy-traffic-director/versions.tf +++ b/modules/cloud-config-container/envoy-traffic-director/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/instance.tf b/modules/cloud-config-container/instance.tf index 447becf5..7f76f218 100644 --- a/modules/cloud-config-container/instance.tf +++ b/modules/cloud-config-container/instance.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/mysql/cloud-config.yaml b/modules/cloud-config-container/mysql/cloud-config.yaml index 0d5546b2..1c792a74 100644 --- a/modules/cloud-config-container/mysql/cloud-config.yaml +++ b/modules/cloud-config-container/mysql/cloud-config.yaml @@ -1,6 +1,6 @@ #cloud-config -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/mysql/main.tf b/modules/cloud-config-container/mysql/main.tf index 2323582d..4e44c469 100644 --- a/modules/cloud-config-container/mysql/main.tf +++ b/modules/cloud-config-container/mysql/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/mysql/outputs.tf b/modules/cloud-config-container/mysql/outputs.tf index 33dcc551..7d8d4165 100644 --- a/modules/cloud-config-container/mysql/outputs.tf +++ b/modules/cloud-config-container/mysql/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/mysql/variables.tf b/modules/cloud-config-container/mysql/variables.tf index 10720965..52bb3dbb 100644 --- a/modules/cloud-config-container/mysql/variables.tf +++ b/modules/cloud-config-container/mysql/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/mysql/versions.tf b/modules/cloud-config-container/mysql/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/cloud-config-container/mysql/versions.tf +++ b/modules/cloud-config-container/mysql/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/nginx/cloud-config.yaml b/modules/cloud-config-container/nginx/cloud-config.yaml index 16ee0efd..d061e037 100644 --- a/modules/cloud-config-container/nginx/cloud-config.yaml +++ b/modules/cloud-config-container/nginx/cloud-config.yaml @@ -1,6 +1,6 @@ #cloud-config -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/nginx/main.tf b/modules/cloud-config-container/nginx/main.tf index a748759e..a2fce41b 100644 --- a/modules/cloud-config-container/nginx/main.tf +++ b/modules/cloud-config-container/nginx/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/nginx/outputs.tf b/modules/cloud-config-container/nginx/outputs.tf index 33dcc551..7d8d4165 100644 --- a/modules/cloud-config-container/nginx/outputs.tf +++ b/modules/cloud-config-container/nginx/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/nginx/variables.tf b/modules/cloud-config-container/nginx/variables.tf index d38ca4c7..dec89cc2 100644 --- a/modules/cloud-config-container/nginx/variables.tf +++ b/modules/cloud-config-container/nginx/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/nginx/versions.tf b/modules/cloud-config-container/nginx/versions.tf index 1cc6bf89..29041268 100644 --- a/modules/cloud-config-container/nginx/versions.tf +++ b/modules/cloud-config-container/nginx/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/onprem/cloud-config.yaml b/modules/cloud-config-container/onprem/cloud-config.yaml index 47112722..ba27f84d 100644 --- a/modules/cloud-config-container/onprem/cloud-config.yaml +++ b/modules/cloud-config-container/onprem/cloud-config.yaml @@ -1,6 +1,6 @@ #cloud-config -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/onprem/docker-images/strongswan/Dockerfile b/modules/cloud-config-container/onprem/docker-images/strongswan/Dockerfile index 033173aa..7a22d943 100644 --- a/modules/cloud-config-container/onprem/docker-images/strongswan/Dockerfile +++ b/modules/cloud-config-container/onprem/docker-images/strongswan/Dockerfile @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ FROM alpine:latest RUN set -xe \ - && apk add --no-cache strongswan bash sudo + && apk add --no-cache strongswan bash sudo COPY entrypoint.sh /entrypoint.sh RUN chmod 0755 /entrypoint.sh diff --git a/modules/cloud-config-container/onprem/docker-images/strongswan/cloudbuild.yaml b/modules/cloud-config-container/onprem/docker-images/strongswan/cloudbuild.yaml index a24ab0bf..b451e79a 100644 --- a/modules/cloud-config-container/onprem/docker-images/strongswan/cloudbuild.yaml +++ b/modules/cloud-config-container/onprem/docker-images/strongswan/cloudbuild.yaml @@ -1,5 +1,4 @@ - -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,14 +16,14 @@ # $ gcloud builds submit . --config=cloudbuild.yaml steps: -- name: 'gcr.io/cloud-builders/docker' - args: - - build - - --tag=gcr.io/$PROJECT_ID/strongswan - - --tag=gcr.io/$PROJECT_ID/strongswan:latest - - . - + - name: "gcr.io/cloud-builders/docker" + args: + - build + - --tag=gcr.io/$PROJECT_ID/strongswan + - --tag=gcr.io/$PROJECT_ID/strongswan:latest + - . + images: - - 'gcr.io/$PROJECT_ID/strongswan:latest' - + - "gcr.io/$PROJECT_ID/strongswan:latest" + timeout: 1200s diff --git a/modules/cloud-config-container/onprem/docker-images/strongswan/entrypoint.sh b/modules/cloud-config-container/onprem/docker-images/strongswan/entrypoint.sh index 3ae54954..e99d1ec8 100644 --- a/modules/cloud-config-container/onprem/docker-images/strongswan/entrypoint.sh +++ b/modules/cloud-config-container/onprem/docker-images/strongswan/entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/onprem/docker-images/strongswan/ipsec-vti.sh b/modules/cloud-config-container/onprem/docker-images/strongswan/ipsec-vti.sh index cce90acd..399ff629 100644 --- a/modules/cloud-config-container/onprem/docker-images/strongswan/ipsec-vti.sh +++ b/modules/cloud-config-container/onprem/docker-images/strongswan/ipsec-vti.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/onprem/docker-images/toolbox/Dockerfile b/modules/cloud-config-container/onprem/docker-images/toolbox/Dockerfile index 87068a18..dfc8f6ec 100644 --- a/modules/cloud-config-container/onprem/docker-images/toolbox/Dockerfile +++ b/modules/cloud-config-container/onprem/docker-images/toolbox/Dockerfile @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,11 +19,11 @@ COPY entrypoint.sh /entrypoint.sh RUN chmod 0755 /entrypoint.sh RUN apk update && \ - apk add bash curl bind-tools busybox-extras netcat-openbsd && \ - rm /var/cache/apk/* + apk add bash curl bind-tools busybox-extras netcat-openbsd && \ + rm /var/cache/apk/* RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl && \ - chmod 755 kubectl && mv kubectl /usr/local/bin/ + chmod 755 kubectl && mv kubectl /usr/local/bin/ CMD ["/bin/bash"] diff --git a/modules/cloud-config-container/onprem/docker-images/toolbox/cloudbuild.yaml b/modules/cloud-config-container/onprem/docker-images/toolbox/cloudbuild.yaml index e3ef5f8e..6da9ed88 100644 --- a/modules/cloud-config-container/onprem/docker-images/toolbox/cloudbuild.yaml +++ b/modules/cloud-config-container/onprem/docker-images/toolbox/cloudbuild.yaml @@ -1,5 +1,4 @@ - -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,14 +16,14 @@ # $ gcloud builds submit . --config=cloudbuild.yaml steps: -- name: 'gcr.io/cloud-builders/docker' - args: - - build - - --tag=gcr.io/$PROJECT_ID/toolbox - - --tag=gcr.io/$PROJECT_ID/toolbox:latest - - . - + - name: "gcr.io/cloud-builders/docker" + args: + - build + - --tag=gcr.io/$PROJECT_ID/toolbox + - --tag=gcr.io/$PROJECT_ID/toolbox:latest + - . + images: - - 'gcr.io/$PROJECT_ID/toolbox:latest' - + - "gcr.io/$PROJECT_ID/toolbox:latest" + timeout: 1200s diff --git a/modules/cloud-config-container/onprem/docker-images/toolbox/entrypoint.sh b/modules/cloud-config-container/onprem/docker-images/toolbox/entrypoint.sh index ad8c4c4b..bee48ff6 100644 --- a/modules/cloud-config-container/onprem/docker-images/toolbox/entrypoint.sh +++ b/modules/cloud-config-container/onprem/docker-images/toolbox/entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/onprem/main.tf b/modules/cloud-config-container/onprem/main.tf index 6cff5bd0..2e616596 100644 --- a/modules/cloud-config-container/onprem/main.tf +++ b/modules/cloud-config-container/onprem/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/onprem/outputs.tf b/modules/cloud-config-container/onprem/outputs.tf index 33dcc551..7d8d4165 100644 --- a/modules/cloud-config-container/onprem/outputs.tf +++ b/modules/cloud-config-container/onprem/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2021 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cloud-config-container/onprem/static-vpn-gw-cloud-init.yaml b/modules/cloud-config-container/onprem/static-vpn-gw-cloud-init.yaml index 6a1e4e7a..36be78bc 100644 --- a/modules/cloud-config-container/onprem/static-vpn-gw-cloud-init.yaml +++ b/modules/cloud-config-container/onprem/static-vpn-gw-cloud-init.yaml @@ -1,6 +1,6 @@ #cloud-config -# Copyright 2021 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,200 +26,211 @@ packages: - software-properties-common write_files: - -# Docker daemon configuration -- path: /etc/docker/daemon.json - owner: root:root - permissions: '0644' - content: | - { - "log-driver": "json-file", - "log-opts": { - "max-size": "10m" + # Docker daemon configuration + - path: /etc/docker/daemon.json + owner: root:root + permissions: "0644" + content: | + { + "log-driver": "json-file", + "log-opts": { + "max-size": "10m" + } } - } -# Docker compose systemd unit for onprem -- path: /etc/systemd/system/docker-onprem.service - permissions: 0644 - owner: root - content: | - [Install] - WantedBy=multi-user.target - [Unit] - Description=Start Docker Compose onprem infrastructure - After=network-online.target docker.socket - Wants=network-online.target docker.socket - [Service] - ExecStart=/bin/sh -c "cd /var/lib/docker-compose/onprem && /usr/local/bin/docker-compose up" - ExecStop=/bin/sh -c "cd /var/lib/docker-compose/onprem && /usr/local/bin/docker-compose down" + # Docker compose systemd unit for onprem + - path: /etc/systemd/system/docker-onprem.service + permissions: 0644 + owner: root + content: | + [Install] + WantedBy=multi-user.target + [Unit] + Description=Start Docker Compose onprem infrastructure + After=network-online.target docker.socket + Wants=network-online.target docker.socket + [Service] + ExecStart=/bin/sh -c "cd /var/lib/docker-compose/onprem && /usr/local/bin/docker-compose up" + ExecStop=/bin/sh -c "cd /var/lib/docker-compose/onprem && /usr/local/bin/docker-compose down" -# Docker compose configuration file for onprem -- path: /var/lib/docker-compose/onprem/docker-compose.yaml - permissions: 0644 - owner: root - content: | - version: "3" - services: - vpn: - image: gcr.io/pso-cft-fabric/strongswan:latest - networks: - onprem: - ipv4_address: ${vpn_ip_address} - ports: - - "500:500/udp" - - "4500:4500/udp" - privileged: true - cap_add: - - NET_ADMIN - volumes: - - "/lib/modules:/lib/modules:ro" - - "/etc/localtime:/etc/localtime:ro" - - "/var/lib/docker-compose/onprem/ipsec/ipsec.conf:/etc/ipsec.conf:ro" - - "/var/lib/docker-compose/onprem/ipsec/ipsec.secrets:/etc/ipsec.secrets:ro" - environment: - - LAN_NETWORKS=${local_ip_cidr_range} - dns: - image: coredns/coredns - command: "-conf /etc/coredns/Corefile" - depends_on: - - "vpn" - networks: - onprem: - ipv4_address: ${dns_ip_address} - volumes: - - "/var/lib/docker-compose/onprem/coredns:/etc/coredns:ro" - routing_sidecar_dns: - image: alpine - network_mode: service:dns - command: | - /bin/sh -c "\ - ip route del default &&\ - ip route add default via ${vpn_ip_address}" - privileged: true - web: - image: nginx:stable-alpine - depends_on: - - "vpn" - - "dns" + # Docker compose configuration file for onprem + - path: /var/lib/docker-compose/onprem/docker-compose.yaml + permissions: 0644 + owner: root + content: | + version: "3" + services: + vpn: + image: gcr.io/pso-cft-fabric/strongswan:latest + networks: + onprem: + ipv4_address: ${vpn_ip_address} + ports: + - "500:500/udp" + - "4500:4500/udp" + privileged: true + cap_add: + - NET_ADMIN + volumes: + - "/lib/modules:/lib/modules:ro" + - "/etc/localtime:/etc/localtime:ro" + - "/var/lib/docker-compose/onprem/ipsec/ipsec.conf:/etc/ipsec.conf:ro" + - "/var/lib/docker-compose/onprem/ipsec/ipsec.secrets:/etc/ipsec.secrets:ro" + environment: + - LAN_NETWORKS=${local_ip_cidr_range} dns: - - ${dns_ip_address} - networks: - onprem: - ipv4_address: ${web_ip_address} - volumes: - - "/var/lib/docker-compose/onprem/nginx:/usr/share/nginx/html:ro" - routing_sidecar_web: - image: alpine - network_mode: service:web - command: | - /bin/sh -c "\ - ip route del default &&\ - ip route add default via ${vpn_ip_address}" - privileged: true - toolbox: - image: gcr.io/pso-cft-fabric/toolbox:latest - networks: - onprem: - ipv4_address: ${toolbox_ip_address} - depends_on: - - "vpn" - - "dns" - - "web" - dns: - - ${dns_ip_address} - routing_sidecar_toolbox: - image: alpine - network_mode: service:toolbox - command: | - /bin/sh -c "\ - ip route del default &&\ - ip route add default via ${vpn_ip_address}" - privileged: true - networks: - onprem: - ipam: - driver: default - config: - - subnet: ${local_ip_cidr_range} + image: coredns/coredns + command: "-conf /etc/coredns/Corefile" + depends_on: + - "vpn" + networks: + onprem: + ipv4_address: ${dns_ip_address} + volumes: + - "/var/lib/docker-compose/onprem/coredns:/etc/coredns:ro" + routing_sidecar_dns: + image: alpine + network_mode: service:dns + command: | + /bin/sh -c "\ + ip route del default &&\ + ip route add default via ${vpn_ip_address}" + privileged: true + web: + image: nginx:stable-alpine + depends_on: + - "vpn" + - "dns" + dns: + - ${dns_ip_address} + networks: + onprem: + ipv4_address: ${web_ip_address} + volumes: + - "/var/lib/docker-compose/onprem/nginx:/usr/share/nginx/html:ro" + routing_sidecar_web: + image: alpine + network_mode: service:web + command: | + /bin/sh -c "\ + ip route del default &&\ + ip route add default via ${vpn_ip_address}" + privileged: true + toolbox: + image: gcr.io/pso-cft-fabric/toolbox:latest + networks: + onprem: + ipv4_address: ${toolbox_ip_address} + depends_on: + - "vpn" + - "dns" + - "web" + dns: + - ${dns_ip_address} + routing_sidecar_toolbox: + image: alpine + network_mode: service:toolbox + command: | + /bin/sh -c "\ + ip route del default &&\ + ip route add default via ${vpn_ip_address}" + privileged: true + networks: + onprem: + ipam: + driver: default + config: + - subnet: ${local_ip_cidr_range} -# IPSEC tunnel secret -- path: /var/lib/docker-compose/onprem/ipsec/ipsec.secrets - owner: root:root - permissions: '0600' - content: | - : PSK "${shared_secret}" + # IPSEC tunnel secret + - path: /var/lib/docker-compose/onprem/ipsec/ipsec.secrets + owner: root:root + permissions: "0600" + content: | + : PSK "${shared_secret}" -# IPSEC tunnel configuration -- path: /var/lib/docker-compose/onprem/ipsec/ipsec.conf - owner: root:root - permissions: '0644' - content: | - conn %default - ikelifetime=600m - keylife=180m - rekeymargin=3m - keyingtries=3 - keyexchange=ikev2 - mobike=no - ike=aes256gcm16-sha512-modp2048 - esp=aes256gcm16-sha512-modp8192 - authby=psk + # IPSEC tunnel configuration + - path: /var/lib/docker-compose/onprem/ipsec/ipsec.conf + owner: root:root + permissions: "0644" + content: | + conn %default + ikelifetime=600m + keylife=180m + rekeymargin=3m + keyingtries=3 + keyexchange=ikev2 + mobike=no + ike=aes256gcm16-sha512-modp2048 + esp=aes256gcm16-sha512-modp8192 + authby=psk - conn gcp - left=%any - leftid=%any - leftsubnet=${local_ip_cidr_range} - leftauth=psk - right=${peer_ip_wildcard} - rightid=${peer_ip} - rightsubnet=199.36.153.4/30,35.199.192.0/19,${remote_ip_cidr_ranges} - rightauth=psk - type=tunnel - auto=start - dpdaction=restart - closeaction=restart + conn gcp + left=%any + leftid=%any + leftsubnet=${local_ip_cidr_range} + leftauth=psk + right=${peer_ip_wildcard} + rightid=${peer_ip} + rightsubnet=199.36.153.4/30,35.199.192.0/19,${remote_ip_cidr_ranges} + rightauth=psk + type=tunnel + auto=start + dpdaction=restart + closeaction=restart -# CoreDNS configuration -- path: /var/lib/docker-compose/onprem/coredns/Corefile - owner: root:root - permissions: '0644' - content: | - ${coredns_config} + # CoreDNS configuration + - path: /var/lib/docker-compose/onprem/coredns/Corefile + owner: root:root + permissions: "0644" + content: | + ${coredns_config} -# CoreDNS onprem hosts file -- path: /var/lib/docker-compose/onprem/coredns/onprem.hosts - owner: root:root - permissions: '0644' - content: | - ${vpn_ip_address} gw.${dns_domain} - ${dns_ip_address} ns.${dns_domain} - ${web_ip_address} www.${dns_domain} - ${toolbox_ip_address} toolbox.${dns_domain} + # CoreDNS onprem hosts file + - path: /var/lib/docker-compose/onprem/coredns/onprem.hosts + owner: root:root + permissions: "0644" + content: | + ${vpn_ip_address} gw.${dns_domain} + ${dns_ip_address} ns.${dns_domain} + ${web_ip_address} www.${dns_domain} + ${toolbox_ip_address} toolbox.${dns_domain} -# Minimal nginx index page -- path: /var/lib/docker-compose/onprem/nginx/index.html - owner: root:root - permissions: '0644' - content: | - - - - -