From a34983b2e960ec5d2e1539f6c781248068d22ef3 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Wed, 6 Jul 2022 19:41:18 +0200 Subject: [PATCH 01/10] OrgPolicy module (factory) using new org-policy API, #698 --- .../factories/net-vpc-firewall-yaml/README.md | 4 +- modules/organization-policy/README.md | 166 ++++++++++++++++++ modules/organization-policy/main.tf | 106 +++++++++++ modules/organization-policy/outputs.tf | 20 +++ modules/organization-policy/variables.tf | 30 ++++ modules/organization-policy/versions.tf | 29 +++ tests/modules/organization-policy/__init__.py | 13 ++ .../organization-policy/fixture/main.tf | 22 +++ .../fixture/policies/test.yaml | 41 +++++ .../organization-policy/fixture/variables.tf | 31 ++++ .../modules/organization-policy/test_plan.py | 91 ++++++++++ 11 files changed, 551 insertions(+), 2 deletions(-) create mode 100644 modules/organization-policy/README.md create mode 100644 modules/organization-policy/main.tf create mode 100644 modules/organization-policy/outputs.tf create mode 100644 modules/organization-policy/variables.tf create mode 100644 modules/organization-policy/versions.tf create mode 100644 tests/modules/organization-policy/__init__.py create mode 100644 tests/modules/organization-policy/fixture/main.tf create mode 100644 tests/modules/organization-policy/fixture/policies/test.yaml create mode 100644 tests/modules/organization-policy/fixture/variables.tf create mode 100644 tests/modules/organization-policy/test_plan.py diff --git a/examples/factories/net-vpc-firewall-yaml/README.md b/examples/factories/net-vpc-firewall-yaml/README.md index 0cf7af5c82..8bee90df76 100644 --- a/examples/factories/net-vpc-firewall-yaml/README.md +++ b/examples/factories/net-vpc-firewall-yaml/README.md @@ -12,7 +12,7 @@ Nested folder structure for yaml configurations is optionally supported, which a ```hcl module "prod-firewall" { - source = "./modules/net-vpc-firewall-yaml" + source = "./examples/factories/net-vpc-firewall-yaml" project_id = "my-prod-project" network = "my-prod-network" @@ -27,7 +27,7 @@ module "prod-firewall" { } module "dev-firewall" { - source = "./modules/net-vpc-firewall-yaml" + source = "./examples/factories/net-vpc-firewall-yaml" project_id = "my-dev-project" network = "my-dev-network" diff --git a/modules/organization-policy/README.md b/modules/organization-policy/README.md new file mode 100644 index 0000000000..95bafece82 --- /dev/null +++ b/modules/organization-policy/README.md @@ -0,0 +1,166 @@ +# Google Cloud Organization Policy + +This module allows creation and management of [GCP Organization Policies](https://cloud.google.com/resource-manager/docs/organization-policy/org-policy-constraints) by defining them in a well formatted `yaml` files or with HCL. + +Yaml based factory can simplify centralized management of Org Policies for a DevSecOps team by providing a simple way to define/structure policies and exclusions. + +## Example + +### Terraform code + +```hcl +# using configuration provided in a set of yaml files +module "org-policy-factory" { + source = "./modules/organization-policy" + + config_directory = "./policies" +} + +# using configuration provided in the module variable +module "org-policy" { + source = "./modules/organization-policy" + + organization_policies = { + "folders/1234567890" = { + "constraints/iam.disableServiceAccountKeyUpload" = { + rules = [ + { + enforce = true + } + ] + } + }, + "organizations/1234567890" = { + "run.allowedIngress" = { + rules = [ + { + condition = { + description= "allow ingress" + expression = "resource.matchTag('123456789/environment', 'prod')" + title = "allow-for-prod-org" + }, + values = { + allowed_values = ["internal"] + } + } + ] + } + } + } +} +# tftest skip +``` + +## Org Policy definition format and structure + +### Structure of `organization_policies` variable + +```hcl +organization_policies = { + "parent_id" = { # parent id in format projects/project-id, folders/1234567890 or organizations/1234567890. + "policy_name" = { # policy constraint id, for example compute.vmExternalIpAccess. + inherit_from_parent = true|false # (Optional) Only for list constraints. Determines the inheritance behavior for this policy. + reset = true|false # (Optional) Ignores policies set above this resource and restores the constraint_default enforcement behavior. + rules = [ # Up to 10 PolicyRules are allowed. + { + allow_all = true|false # (Optional) Only for list constraints. Setting this to true means that all values are allowed. + deny_all = true|false # (Optional) Only for list constraints. Setting this to true means that all values are denied. + enforce = true|false # (Optional) Only for boolean constraints. If true, then the Policy is enforced. + condition = { # (Optional) A condition which determines whether this rule is used in the evaluation of the policy. + description = "Condition description" # (Optional) + expression = "Condition expression" # (Optional) For example "resource.matchTag('123456789/environment', 'prod')". + location = "policy-error.log" # (Optional) String indicating the location of the expression for error reporting. + title = "condition-title" # (Optional) + } + values = { # (Optional) Only for list constraints. List of values to be used for this PolicyRule. + allowed_values = ["value1", "value2"] # (Optional) List of values allowed at this resource. + denied_values = ["value3", "value4"] # (Optional) List of values denied at this resource. + } + } + ] + } + } +} +# tftest skip +``` + +### Structure of configuration provided in a yaml file/s + +Configuration should be placed in a set of yaml files in the config directory. Policy entry structure as follows: + +```yaml +parent_id: # parent id in format projects/project-id, folders/1234567890 or organizations/1234567890. + policy_name1: # policy constraint id, for example compute.vmExternalIpAccess. + inherit_from_parent: true|false # (Optional) Only for list constraints. Determines the inheritance behavior for this policy. + reset: true|false # (Optional) Ignores policies set above this resource and restores the constraint_default enforcement behavior. + rules: + - allow_all: true|false # (Optional) Only for list constraints. Setting this to true means that all values are allowed. + deny_all: true|false # (Optional) Only for list constraints. Setting this to true means that all values are denied. + enforce: true|false # (Optional) Only for boolean constraints. If true, then the Policy is enforced. + condition: # (Optional) A condition which determines whether this rule is used in the evaluation of the policy. + description: Condition description # (Optional) + expression: Condition expression # (Optional) For example resource.matchTag("123456789/environment", "prod") + location: policy-error.log # (Optional) String indicating the location of the expression for error reporting. + title: condition-title # (Optional) + values: # (Optional) Only for list constraints. List of values to be used for this PolicyRule. + allowed_values: ['value1', 'value2'] # (Optional) List of values allowed at this resource. + denied_values: ['value3', 'value4'] # (Optional) List of values denied at this resource. + +``` + +Module allows policies to be distributed into multiple yaml files for a better management and navigation. + +```bash +├── org-policies +│ ├── baseline.yaml +│   ├── image-import-projects.yaml +│   └── exclusions.yaml +``` + +Organization policies example yaml configuration + +```bash +cat ./policies/baseline.yaml +organizations/1234567890: + constraints/compute.vmExternalIpAccess: + rules: + - deny_all: true +folders/1234567890: + compute.vmCanIpForward: + inherit_from_parent: false + reset: false + rules: + - allow_all: true +projects/my-project-id: + run.allowedIngress: + inherit_from_parent: true + rules: + - condition: + description: allow internal ingress + expression: resource.matchTag("123456789/environment", "prod") + location: test.log + title: allow-for-prod + values: + allowed_values: ['internal'] + iam.allowServiceAccountCredentialLifetimeExtension: + rules: + - allow_all: true + compute.disableGlobalLoadBalancing: + reset: true +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [config_directory](variables.tf#L17) | Paths to a folder where organization policy configs are stored in yaml format. Files suffix must be `.yaml`. | string | | null | +| [organization_policies](variables.tf#L25) | Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`. | any | | {} | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [organization_policies](outputs.tf#L17) | Organization policies. | | + + diff --git a/modules/organization-policy/main.tf b/modules/organization-policy/main.tf new file mode 100644 index 0000000000..6c7c153769 --- /dev/null +++ b/modules/organization-policy/main.tf @@ -0,0 +1,106 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +locals { + org_policy_files = var.config_directory == null ? [] : concat( + [ + for config_file in fileset("${path.root}/${var.config_directory}", "**/*.yaml") : + "${path.root}/${var.config_directory}/${config_file}" + ] + ) + + org_policies_raw = merge( + merge( + [ + for config_file in local.org_policy_files : + try(yamldecode(file(config_file)), {}) + ]... + ), var.organization_policies) + + org_policies_list = flatten([ + for parent, policies in local.org_policies_raw : [ + for policy_name, policy in policies : { + parent = parent, + policy_name = policy_name, + inherit_from_parent = try(policy["inherit_from_parent"], null), + reset = try(policy["reset"], null), + rules = [ + for rule in try(policy["rules"], []) : { + allow_all = try( + rule["allow_all"], null) == true ? "TRUE" : try( + rule["allow_all"], null) == false ? "FALSE" : null, + deny_all = try( + rule["deny_all"], null) == true ? "TRUE" : try( + rule["deny_all"], null) == false ? "FALSE" : null, + enforce = try(rule["enforce"], null) == true ? "TRUE" : try( + rule["enforce"], null) == false ? "FALSE" : null, + condition = try(rule["condition"], null) != null ? { + description = try(rule["condition"]["description"], null), + expression = try(rule["condition"]["expression"], null), + location = try(rule["condition"]["location"], null), + title = try(rule["condition"]["title"], null) + } : null, + values = try(rule["values"], null) != null ? { + allowed_values = try(rule["values"]["allowed_values"], null), + denied_values = try(rule["values"]["denied_values"], null) + } : null + } + ] + } + ] + ]) + + org_policies_map = { + for item in local.org_policies_list : + format("%s-%s", item["parent"], item["policy_name"]) => item + } +} + +resource "google_org_policy_policy" "primary" { + for_each = local.org_policies_map + name = format("%s/policies/%s", each.value.parent, each.value.policy_name) + parent = each.value.parent + + spec { + inherit_from_parent = each.value.inherit_from_parent + reset = each.value.reset + dynamic "rules" { + for_each = each.value.rules + content { + allow_all = rules.value.allow_all + deny_all = rules.value.deny_all + enforce = rules.value.enforce + dynamic "condition" { + for_each = rules.value.condition != null ? [""] : [] + content { + description = rules.value.condition.description + expression = rules.value.condition.expression + location = rules.value.condition.location + title = rules.value.condition.title + } + } + dynamic "values" { + for_each = rules.value.values != null ? [""] : [] + content { + allowed_values = rules.value.values.allowed_values + denied_values = rules.value.values.denied_values + } + } + } + } + } +} diff --git a/modules/organization-policy/outputs.tf b/modules/organization-policy/outputs.tf new file mode 100644 index 0000000000..5f545fd2c0 --- /dev/null +++ b/modules/organization-policy/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2022 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 "organization_policies" { + description = "Organization policies." + value = google_org_policy_policy.primary +} diff --git a/modules/organization-policy/variables.tf b/modules/organization-policy/variables.tf new file mode 100644 index 0000000000..97284f9b7c --- /dev/null +++ b/modules/organization-policy/variables.tf @@ -0,0 +1,30 @@ +/** + * Copyright 2022 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 "config_directory" { + description = "Paths to a folder where organization policy configs are stored in yaml format. Files suffix must be `.yaml`." + type = string + default = null +} + +# TODO: convert to a proper data structure map(map(object({...}))) once tf1.3 is released and optional object keys are avaliable, +# for now it will cause multiple keys to be set to null for every policy definition +# https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 +variable "organization_policies" { + description = "Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`." + type = any + default = {} +} diff --git a/modules/organization-policy/versions.tf b/modules/organization-policy/versions.tf new file mode 100644 index 0000000000..c01e5da095 --- /dev/null +++ b/modules/organization-policy/versions.tf @@ -0,0 +1,29 @@ +# Copyright 2022 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.1.0" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.20.0" # tftest + } + google-beta = { + source = "hashicorp/google-beta" + version = ">= 4.20.0" # tftest + } + } +} + + diff --git a/tests/modules/organization-policy/__init__.py b/tests/modules/organization-policy/__init__.py new file mode 100644 index 0000000000..6d6d1266c3 --- /dev/null +++ b/tests/modules/organization-policy/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/modules/organization-policy/fixture/main.tf b/tests/modules/organization-policy/fixture/main.tf new file mode 100644 index 0000000000..addb391163 --- /dev/null +++ b/tests/modules/organization-policy/fixture/main.tf @@ -0,0 +1,22 @@ +/** + * Copyright 2022 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 "org-policy" { + source = "../../../../modules/organization-policy" + + config_directory = var.config_directory + organization_policies = var.organization_policies +} diff --git a/tests/modules/organization-policy/fixture/policies/test.yaml b/tests/modules/organization-policy/fixture/policies/test.yaml new file mode 100644 index 0000000000..2630cd6bb3 --- /dev/null +++ b/tests/modules/organization-policy/fixture/policies/test.yaml @@ -0,0 +1,41 @@ +# Copyright 2022 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. + + +organizations/1234567890: + constraints/compute.vmExternalIpAccess: + rules: + - deny_all: true +folders/1234567890: + compute.vmCanIpForward: + inherit_from_parent: false + reset: false + rules: + - allow_all: true +projects/my-project-id: + run.allowedIngress: + inherit_from_parent: true + rules: + - condition: + description: allow internal ingress + expression: resource.matchTag("123456789/environment", "prod") + location: test.log + title: allow-for-prod + values: + allowed_values: ['internal'] + iam.allowServiceAccountCredentialLifetimeExtension: + rules: + - allow_all: true + compute.disableGlobalLoadBalancing: + reset: true diff --git a/tests/modules/organization-policy/fixture/variables.tf b/tests/modules/organization-policy/fixture/variables.tf new file mode 100644 index 0000000000..6e36bdecbc --- /dev/null +++ b/tests/modules/organization-policy/fixture/variables.tf @@ -0,0 +1,31 @@ +/** + * Copyright 2022 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 "config_directory" { + description = "Paths to a folder where organization policy configs are stored in yaml format. Files suffix must be `.yaml`." + type = string + default = null +} + +# TODO: convert to a proper data structure map(map(object({...}))) once tf1.3 is released and optional object keys are avaliable, +# for now it will cause multiple keys to be set to null for every policy definition +# https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 +variable "organization_policies" { + description = "Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`." + type = any + default = {} +} diff --git a/tests/modules/organization-policy/test_plan.py b/tests/modules/organization-policy/test_plan.py new file mode 100644 index 0000000000..bb7879d779 --- /dev/null +++ b/tests/modules/organization-policy/test_plan.py @@ -0,0 +1,91 @@ +# Copyright 2022 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. + +def test_org_policy_simple(plan_runner): + "Test vpc with no extra options." + org_policies = ( + '{' + '"folders/1234567890" = {' + ' "constraints/iam.disableServiceAccountKeyUpload" = {' + ' rules = [' + ' {' + ' enforce = true' + ' }' + ' ]' + ' }' + ' },' + ' "organizations/1234567890" = {' + ' "run.allowedIngress" = {' + ' rules = [' + ' {' + ' condition = {' + ' description= "allow ingress",' + ' expression = "resource.matchTag(\'123456789/environment\', \'prod\')",' + ' title = "allow-for-prod-org",' + ' },' + ' values = {' + ' allowed_values = ["internal"]' + ' }' + ' }' + ' ]' + ' }' + ' }' + '}' + ) + _, resources = plan_runner( + organization_policies = org_policies + ) + assert len(resources) == 2 + + org_policy = [r for r in resources if r["values"] + ["name"].endswith('iam.disableServiceAccountKeyUpload')][0]["values"] + assert org_policy["parent"] == "folders/1234567890" + assert org_policy["spec"][0]["rules"][0]["enforce"] == "TRUE" + + +def test_org_policy_factory(plan_runner): + "Test yaml based configuration" + _, resources = plan_runner( + config_directory="./policies", + ) + assert len(resources) == 5 + + org_policy = [r for r in resources if r["values"] + ["name"].endswith('run.allowedIngress')][0]["values"]["spec"][0] + assert org_policy["inherit_from_parent"] == True + assert org_policy["rules"][0]["condition"][0]["title"] == "allow-for-prod" + assert set(org_policy["rules"][0]["values"][0]["allowed_values"]) == set(["internal"]) + + +def test_combined_org_policy_config(plan_runner): + "Test combined (yaml, hcl) policy configuration" + org_policies = ( + '{' + '"folders/3456789012" = {' + ' "constraints/iam.disableServiceAccountKeyUpload" = {' + ' rules = [' + ' {' + ' enforce = true' + ' }' + ' ]' + ' }' + ' }' + '}' + ) + _, resources = plan_runner( + config_directory="./policies", + organization_policies = org_policies + ) + + assert len(resources) == 6 From adac90d1bb51da78cba4f7ee66760308afb83806 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Wed, 6 Jul 2022 19:49:12 +0200 Subject: [PATCH 02/10] Fix docs --- modules/organization-policy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/organization-policy/README.md b/modules/organization-policy/README.md index 95bafece82..0b352699fe 100644 --- a/modules/organization-policy/README.md +++ b/modules/organization-policy/README.md @@ -155,7 +155,7 @@ projects/my-project-id: | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [config_directory](variables.tf#L17) | Paths to a folder where organization policy configs are stored in yaml format. Files suffix must be `.yaml`. | string | | null | -| [organization_policies](variables.tf#L25) | Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`. | any | | {} | +| [organization_policies](variables.tf#L26) | Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`. | any | | {} | ## Outputs From e6d558e416daddfa4b2e7e44f8a60fc2107723c3 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Wed, 6 Jul 2022 20:05:38 +0200 Subject: [PATCH 03/10] Add refs to the readme files. --- README.md | 2 +- modules/README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1378b67452..509138b6e9 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ The current list of modules supports most of the core foundational and networkin Currently available modules: -- **foundational** - [folder](./modules/folder), [organization](./modules/organization), [project](./modules/project), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [billing budget](./modules/billing-budget), [naming convention](./modules/naming-convention), [projects-data-source](./modules/projects-data-source) +- **foundational** - [folder](./modules/folder), [organization](./modules/organization), [project](./modules/project), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [billing budget](./modules/billing-budget), [naming convention](./modules/naming-convention), [projects-data-source](./modules/projects-data-source), [organization-policy](./module/organization-policy) - **networking** - [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), [VPN static](./modules/net-vpn-static), [VPN dynamic](./modules/net-vpn-dynamic), [HA VPN](./modules/net-vpn-ha), [NAT](./modules/net-cloudnat), [address reservation](./modules/net-address), [DNS](./modules/dns), [L4 ILB](./modules/net-ilb), [L7 ILB](./modules/net-ilb-l7), [Service Directory](./modules/service-directory), [Cloud Endpoints](./modules/endpoints) - **compute** - [VM/VM group](./modules/compute-vm), [MIG](./modules/compute-mig), [GKE cluster](./modules/gke-cluster), [GKE nodepool](./modules/gke-nodepool), [GKE hub](./modules/gke-hub), [COS container](./modules/cloud-config-container/cos-generic-metadata/) (coredns, mysql, onprem, squid) - **data** - [GCS](./modules/gcs), [BigQuery dataset](./modules/bigquery-dataset), [Pub/Sub](./modules/pubsub), [Datafusion](./modules/datafusion), [Bigtable instance](./modules/bigtable-instance), [Cloud SQL instance](./modules/cloudsql-instance), [Data Catalog Policy Tag](./modules/data-catalog-policy-tag) diff --git a/modules/README.md b/modules/README.md index 6303f01556..3280a68285 100644 --- a/modules/README.md +++ b/modules/README.md @@ -37,6 +37,7 @@ These modules are used in the examples included in this repository. If you are u - [project](./project) - [projects-data-source](./projects-data-source) - [service account](./iam-service-account) +- [organization policy](./organization-policy) ## Networking modules From 3c593f92208ec2fb0450710ec0c88f454abde32e Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Wed, 6 Jul 2022 20:11:24 +0200 Subject: [PATCH 04/10] Fix module ref --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 509138b6e9..57f766248a 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ The current list of modules supports most of the core foundational and networkin Currently available modules: -- **foundational** - [folder](./modules/folder), [organization](./modules/organization), [project](./modules/project), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [billing budget](./modules/billing-budget), [naming convention](./modules/naming-convention), [projects-data-source](./modules/projects-data-source), [organization-policy](./module/organization-policy) +- **foundational** - [folder](./modules/folder), [organization](./modules/organization), [project](./modules/project), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [billing budget](./modules/billing-budget), [naming convention](./modules/naming-convention), [projects-data-source](./modules/projects-data-source), [organization-policy](./modules/organization-policy) - **networking** - [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), [VPN static](./modules/net-vpn-static), [VPN dynamic](./modules/net-vpn-dynamic), [HA VPN](./modules/net-vpn-ha), [NAT](./modules/net-cloudnat), [address reservation](./modules/net-address), [DNS](./modules/dns), [L4 ILB](./modules/net-ilb), [L7 ILB](./modules/net-ilb-l7), [Service Directory](./modules/service-directory), [Cloud Endpoints](./modules/endpoints) - **compute** - [VM/VM group](./modules/compute-vm), [MIG](./modules/compute-mig), [GKE cluster](./modules/gke-cluster), [GKE nodepool](./modules/gke-nodepool), [GKE hub](./modules/gke-hub), [COS container](./modules/cloud-config-container/cos-generic-metadata/) (coredns, mysql, onprem, squid) - **data** - [GCS](./modules/gcs), [BigQuery dataset](./modules/bigquery-dataset), [Pub/Sub](./modules/pubsub), [Datafusion](./modules/datafusion), [Bigtable instance](./modules/bigtable-instance), [Cloud SQL instance](./modules/cloudsql-instance), [Data Catalog Policy Tag](./modules/data-catalog-policy-tag) From 63ac1420057ae7b06304c50b798507d97ac75670 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Fri, 8 Jul 2022 12:24:18 +0200 Subject: [PATCH 05/10] Simplified module interface, proper data structure for policies --- modules/organization-policy/README.md | 61 +++++++++---------- modules/organization-policy/main.tf | 32 +++++----- modules/organization-policy/outputs.tf | 2 +- modules/organization-policy/variables.tf | 27 ++++++-- modules/organization-policy/versions.tf | 4 ++ .../organization-policy/fixture/main.tf | 4 +- .../fixture/policies/test.yaml | 9 ++- .../organization-policy/fixture/variables.tf | 2 +- .../modules/organization-policy/test_plan.py | 24 ++++---- 9 files changed, 88 insertions(+), 77 deletions(-) diff --git a/modules/organization-policy/README.md b/modules/organization-policy/README.md index 0b352699fe..f26357e4fd 100644 --- a/modules/organization-policy/README.md +++ b/modules/organization-policy/README.md @@ -20,27 +20,34 @@ module "org-policy-factory" { module "org-policy" { source = "./modules/organization-policy" - organization_policies = { + policies = { "folders/1234567890" = { - "constraints/iam.disableServiceAccountKeyUpload" = { - rules = [ - { - enforce = true - } - ] - } + # enforce boolean policy with no conditions + "iam.disableServiceAccountKeyUpload" = { + rules = [ + { + enforce = true + } + ] + }, + # Deny All for compute.vmCanIpForward policy + "compute.vmCanIpForward" = { + inherit_from_parent = false + rules = [ + deny = [] # stands for deny_all + ] + } }, "organizations/1234567890" = { + # allow only internal ingress when match condition env=prod "run.allowedIngress" = { rules = [ { + allow = ["internal"] condition = { description= "allow ingress" expression = "resource.matchTag('123456789/environment', 'prod')" title = "allow-for-prod-org" - }, - values = { - allowed_values = ["internal"] } } ] @@ -53,29 +60,25 @@ module "org-policy" { ## Org Policy definition format and structure -### Structure of `organization_policies` variable +### Structure of `policies` variable ```hcl -organization_policies = { +policies = { "parent_id" = { # parent id in format projects/project-id, folders/1234567890 or organizations/1234567890. "policy_name" = { # policy constraint id, for example compute.vmExternalIpAccess. inherit_from_parent = true|false # (Optional) Only for list constraints. Determines the inheritance behavior for this policy. reset = true|false # (Optional) Ignores policies set above this resource and restores the constraint_default enforcement behavior. rules = [ # Up to 10 PolicyRules are allowed. { - allow_all = true|false # (Optional) Only for list constraints. Setting this to true means that all values are allowed. - deny_all = true|false # (Optional) Only for list constraints. Setting this to true means that all values are denied. - enforce = true|false # (Optional) Only for boolean constraints. If true, then the Policy is enforced. - condition = { # (Optional) A condition which determines whether this rule is used in the evaluation of the policy. + allow = ["value1", "value2"] # (Optional) Only for list constraints. Stands for `allow_all` if set to empty list `[]` or to `values.allowed_values` if set to a list of values + denyl = ["value3", "value4"] # (Optional) Only for list constraints. Stands for `deny_all` if set to empty list `[]` or to `values.denied_values` if set to a list of values + enforce = true|false # (Optional) Only for boolean constraints. If true, then the Policy is enforced. + condition = { # (Optional) A condition which determines whether this rule is used in the evaluation of the policy. description = "Condition description" # (Optional) expression = "Condition expression" # (Optional) For example "resource.matchTag('123456789/environment', 'prod')". location = "policy-error.log" # (Optional) String indicating the location of the expression for error reporting. title = "condition-title" # (Optional) } - values = { # (Optional) Only for list constraints. List of values to be used for this PolicyRule. - allowed_values = ["value1", "value2"] # (Optional) List of values allowed at this resource. - denied_values = ["value3", "value4"] # (Optional) List of values denied at this resource. - } } ] } @@ -94,18 +97,14 @@ parent_id: # parent id in format projects/project-id, folders/1234567890 or orga inherit_from_parent: true|false # (Optional) Only for list constraints. Determines the inheritance behavior for this policy. reset: true|false # (Optional) Ignores policies set above this resource and restores the constraint_default enforcement behavior. rules: - - allow_all: true|false # (Optional) Only for list constraints. Setting this to true means that all values are allowed. - deny_all: true|false # (Optional) Only for list constraints. Setting this to true means that all values are denied. + - allow: ["value1", "value2"] # (Optional) Only for list constraints. Stands for `allow_all` if set to empty list `[]` or to `values.allowed_values` if set to a list of values + deny: ["value3", "value4"] # (Optional) Only for list constraints. Stands for `deny_all` if set to empty list `[]` or to `values.denied_values` if set to a list of values enforce: true|false # (Optional) Only for boolean constraints. If true, then the Policy is enforced. condition: # (Optional) A condition which determines whether this rule is used in the evaluation of the policy. description: Condition description # (Optional) expression: Condition expression # (Optional) For example resource.matchTag("123456789/environment", "prod") location: policy-error.log # (Optional) String indicating the location of the expression for error reporting. title: condition-title # (Optional) - values: # (Optional) Only for list constraints. List of values to be used for this PolicyRule. - allowed_values: ['value1', 'value2'] # (Optional) List of values allowed at this resource. - denied_values: ['value3', 'value4'] # (Optional) List of values denied at this resource. - ``` Module allows policies to be distributed into multiple yaml files for a better management and navigation. @@ -130,7 +129,7 @@ folders/1234567890: inherit_from_parent: false reset: false rules: - - allow_all: true + - allow: [] # Stands for allow_all = true projects/my-project-id: run.allowedIngress: inherit_from_parent: true @@ -144,7 +143,7 @@ projects/my-project-id: allowed_values: ['internal'] iam.allowServiceAccountCredentialLifetimeExtension: rules: - - allow_all: true + - deny: [] # Stands for deny_all = true compute.disableGlobalLoadBalancing: reset: true ``` @@ -155,12 +154,12 @@ projects/my-project-id: | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [config_directory](variables.tf#L17) | Paths to a folder where organization policy configs are stored in yaml format. Files suffix must be `.yaml`. | string | | null | -| [organization_policies](variables.tf#L26) | Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`. | any | | {} | +| [policies](variables.tf#L23) | Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`. | map(map(object({…}))) | | {} | ## Outputs | name | description | sensitive | |---|---|:---:| -| [organization_policies](outputs.tf#L17) | Organization policies. | | +| [policies](outputs.tf#L17) | Organization policies. | | diff --git a/modules/organization-policy/main.tf b/modules/organization-policy/main.tf index 6c7c153769..960a8462eb 100644 --- a/modules/organization-policy/main.tf +++ b/modules/organization-policy/main.tf @@ -16,23 +16,23 @@ locals { - org_policy_files = var.config_directory == null ? [] : concat( + policy_files = var.config_directory == null ? [] : concat( [ for config_file in fileset("${path.root}/${var.config_directory}", "**/*.yaml") : "${path.root}/${var.config_directory}/${config_file}" ] ) - org_policies_raw = merge( + policies_raw = merge( merge( [ - for config_file in local.org_policy_files : + for config_file in local.policy_files : try(yamldecode(file(config_file)), {}) ]... - ), var.organization_policies) + ), var.policies) - org_policies_list = flatten([ - for parent, policies in local.org_policies_raw : [ + policies_list = flatten([ + for parent, policies in local.policies_raw : [ for policy_name, policy in policies : { parent = parent, policy_name = policy_name, @@ -40,12 +40,8 @@ locals { reset = try(policy["reset"], null), rules = [ for rule in try(policy["rules"], []) : { - allow_all = try( - rule["allow_all"], null) == true ? "TRUE" : try( - rule["allow_all"], null) == false ? "FALSE" : null, - deny_all = try( - rule["deny_all"], null) == true ? "TRUE" : try( - rule["deny_all"], null) == false ? "FALSE" : null, + allow_all = try(length(rule["allow"]), -1) == 0 ? "TRUE" : null + deny_all = try(length(rule["deny"]), -1) == 0 ? "TRUE" : null enforce = try(rule["enforce"], null) == true ? "TRUE" : try( rule["enforce"], null) == false ? "FALSE" : null, condition = try(rule["condition"], null) != null ? { @@ -54,9 +50,9 @@ locals { location = try(rule["condition"]["location"], null), title = try(rule["condition"]["title"], null) } : null, - values = try(rule["values"], null) != null ? { - allowed_values = try(rule["values"]["allowed_values"], null), - denied_values = try(rule["values"]["denied_values"], null) + values = try(length(rule["allow"]), 0) > 0 || try(length(rule["deny"]), 0) > 0 ? { + allowed_values = try(length(rule["allow"]), 0) > 0 ? rule["allow"] : null + denied_values = try(length(rule["deny"]), 0) > 0 ? rule["deny"] : null } : null } ] @@ -64,14 +60,14 @@ locals { ] ]) - org_policies_map = { - for item in local.org_policies_list : + policies_map = { + for item in local.policies_list : format("%s-%s", item["parent"], item["policy_name"]) => item } } resource "google_org_policy_policy" "primary" { - for_each = local.org_policies_map + for_each = local.policies_map name = format("%s/policies/%s", each.value.parent, each.value.policy_name) parent = each.value.parent diff --git a/modules/organization-policy/outputs.tf b/modules/organization-policy/outputs.tf index 5f545fd2c0..6134d87607 100644 --- a/modules/organization-policy/outputs.tf +++ b/modules/organization-policy/outputs.tf @@ -14,7 +14,7 @@ * limitations under the License. */ -output "organization_policies" { +output "policies" { description = "Organization policies." value = google_org_policy_policy.primary } diff --git a/modules/organization-policy/variables.tf b/modules/organization-policy/variables.tf index 97284f9b7c..ff842dd98f 100644 --- a/modules/organization-policy/variables.tf +++ b/modules/organization-policy/variables.tf @@ -20,11 +20,26 @@ variable "config_directory" { default = null } -# TODO: convert to a proper data structure map(map(object({...}))) once tf1.3 is released and optional object keys are avaliable, -# for now it will cause multiple keys to be set to null for every policy definition -# https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 -variable "organization_policies" { +variable "policies" { description = "Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`." - type = any - default = {} + type = map(map(object({ + inherit_from_parent = optional(bool) # List policy only. + reset = optional(bool) + rules = optional( + list(object({ + allow = optional(list(string)) # List policy only. Stands for `allow_all` if set to empty list `[]` or to `values.allowed_values` if set to a list of values + deny = optional(list(string)) # List policy only. Stands for `deny_all` if set to empty list `[]` or to `values.denied_values` if set to a list of values + enforce = optional(bool) # Boolean policy only. + condition = optional( + object({ + description = optional(string) + expression = optional(string) + location = optional(string) + title = optional(string) + }) + ) + })) + ) + }))) + default = {} } diff --git a/modules/organization-policy/versions.tf b/modules/organization-policy/versions.tf index c01e5da095..52c88cef15 100644 --- a/modules/organization-policy/versions.tf +++ b/modules/organization-policy/versions.tf @@ -14,6 +14,10 @@ terraform { required_version = ">= 1.1.0" + + # TODO: Remove once Terraform 1.3 is released https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 + experiments = [module_variable_optional_attrs] + required_providers { google = { source = "hashicorp/google" diff --git a/tests/modules/organization-policy/fixture/main.tf b/tests/modules/organization-policy/fixture/main.tf index addb391163..09a09267ce 100644 --- a/tests/modules/organization-policy/fixture/main.tf +++ b/tests/modules/organization-policy/fixture/main.tf @@ -17,6 +17,6 @@ module "org-policy" { source = "../../../../modules/organization-policy" - config_directory = var.config_directory - organization_policies = var.organization_policies + config_directory = var.config_directory + policies = var.policies } diff --git a/tests/modules/organization-policy/fixture/policies/test.yaml b/tests/modules/organization-policy/fixture/policies/test.yaml index 2630cd6bb3..4b81e524cf 100644 --- a/tests/modules/organization-policy/fixture/policies/test.yaml +++ b/tests/modules/organization-policy/fixture/policies/test.yaml @@ -22,20 +22,19 @@ folders/1234567890: inherit_from_parent: false reset: false rules: - - allow_all: true + - allow: [] projects/my-project-id: run.allowedIngress: inherit_from_parent: true rules: - - condition: + - allow: ['internal'] + condition: description: allow internal ingress expression: resource.matchTag("123456789/environment", "prod") location: test.log title: allow-for-prod - values: - allowed_values: ['internal'] iam.allowServiceAccountCredentialLifetimeExtension: rules: - - allow_all: true + - deny: [] compute.disableGlobalLoadBalancing: reset: true diff --git a/tests/modules/organization-policy/fixture/variables.tf b/tests/modules/organization-policy/fixture/variables.tf index 6e36bdecbc..709a9f98ba 100644 --- a/tests/modules/organization-policy/fixture/variables.tf +++ b/tests/modules/organization-policy/fixture/variables.tf @@ -24,7 +24,7 @@ variable "config_directory" { # TODO: convert to a proper data structure map(map(object({...}))) once tf1.3 is released and optional object keys are avaliable, # for now it will cause multiple keys to be set to null for every policy definition # https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 -variable "organization_policies" { +variable "policies" { description = "Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`." type = any default = {} diff --git a/tests/modules/organization-policy/test_plan.py b/tests/modules/organization-policy/test_plan.py index bb7879d779..fa7e5cd7a7 100644 --- a/tests/modules/organization-policy/test_plan.py +++ b/tests/modules/organization-policy/test_plan.py @@ -17,25 +17,23 @@ def test_org_policy_simple(plan_runner): org_policies = ( '{' '"folders/1234567890" = {' - ' "constraints/iam.disableServiceAccountKeyUpload" = {' - ' rules = [' - ' {' - ' enforce = true' - ' }' - ' ]' - ' }' + ' "constraints/iam.disableServiceAccountKeyUpload" = {' + ' rules = [' + ' {' + ' enforce = true,' + ' }' + ' ]' + ' }' ' },' ' "organizations/1234567890" = {' ' "run.allowedIngress" = {' ' rules = [' ' {' + ' allow = ["internal"],' ' condition = {' ' description= "allow ingress",' ' expression = "resource.matchTag(\'123456789/environment\', \'prod\')",' - ' title = "allow-for-prod-org",' - ' },' - ' values = {' - ' allowed_values = ["internal"]' + ' title = "allow-for-prod-org"' ' }' ' }' ' ]' @@ -44,7 +42,7 @@ def test_org_policy_simple(plan_runner): '}' ) _, resources = plan_runner( - organization_policies = org_policies + policies = org_policies ) assert len(resources) == 2 @@ -85,7 +83,7 @@ def test_combined_org_policy_config(plan_runner): ) _, resources = plan_runner( config_directory="./policies", - organization_policies = org_policies + policies = org_policies ) assert len(resources) == 6 From 9ec644d524e19c53154a0238c5003ecda4d1899e Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Fri, 8 Jul 2022 12:27:43 +0200 Subject: [PATCH 06/10] Mention of experimental feature in the readme file --- modules/organization-policy/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/organization-policy/README.md b/modules/organization-policy/README.md index f26357e4fd..ab94832b7a 100644 --- a/modules/organization-policy/README.md +++ b/modules/organization-policy/README.md @@ -4,6 +4,8 @@ This module allows creation and management of [GCP Organization Policies](https: Yaml based factory can simplify centralized management of Org Policies for a DevSecOps team by providing a simple way to define/structure policies and exclusions. +> **_NOTE:_** This module uses experimental feature `module_variable_optional_attrs` which will be included into [terraform release 1.3](https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220706). + ## Example ### Terraform code From 9d7ce78df2e711f3c04df89c884379bddde16ccc Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Fri, 8 Jul 2022 12:47:05 +0200 Subject: [PATCH 07/10] Enable experimental feature for the test fixture --- .../organization-policy/fixture/versions.tf | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/modules/organization-policy/fixture/versions.tf diff --git a/tests/modules/organization-policy/fixture/versions.tf b/tests/modules/organization-policy/fixture/versions.tf new file mode 100644 index 0000000000..beba019027 --- /dev/null +++ b/tests/modules/organization-policy/fixture/versions.tf @@ -0,0 +1,20 @@ +# Copyright 2022 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.1.0" + + # TODO: Remove once Terraform 1.3 is released https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 + experiments = [module_variable_optional_attrs] +} From b8fae0fbf064d881e70b7958685006c3c084ff10 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Fri, 8 Jul 2022 14:55:28 +0200 Subject: [PATCH 08/10] Update fixtures variable type to follow the module experimental feature. --- .../fixture/{versions.tf => experimental.tf} | 1 - .../organization-policy/fixture/variables.tf | 25 +++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) rename tests/modules/organization-policy/fixture/{versions.tf => experimental.tf} (84%) diff --git a/tests/modules/organization-policy/fixture/versions.tf b/tests/modules/organization-policy/fixture/experimental.tf similarity index 84% rename from tests/modules/organization-policy/fixture/versions.tf rename to tests/modules/organization-policy/fixture/experimental.tf index beba019027..c6434f966c 100644 --- a/tests/modules/organization-policy/fixture/versions.tf +++ b/tests/modules/organization-policy/fixture/experimental.tf @@ -15,6 +15,5 @@ terraform { required_version = ">= 1.1.0" - # TODO: Remove once Terraform 1.3 is released https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 experiments = [module_variable_optional_attrs] } diff --git a/tests/modules/organization-policy/fixture/variables.tf b/tests/modules/organization-policy/fixture/variables.tf index 709a9f98ba..8196bcff34 100644 --- a/tests/modules/organization-policy/fixture/variables.tf +++ b/tests/modules/organization-policy/fixture/variables.tf @@ -21,11 +21,26 @@ variable "config_directory" { default = null } -# TODO: convert to a proper data structure map(map(object({...}))) once tf1.3 is released and optional object keys are avaliable, -# for now it will cause multiple keys to be set to null for every policy definition -# https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 variable "policies" { description = "Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`." - type = any - default = {} + type = map(map(object({ + inherit_from_parent = optional(bool) # List policy only. + reset = optional(bool) + rules = optional( + list(object({ + allow = optional(list(string)) # List policy only. Stands for `allow_all` if set to empty list `[]` or to `values.allowed_values` if set to a list of values + deny = optional(list(string)) # List policy only. Stands for `deny_all` if set to empty list `[]` or to `values.denied_values` if set to a list of values + enforce = optional(bool) # Boolean policy only. + condition = optional( + object({ + description = optional(string) + expression = optional(string) + location = optional(string) + title = optional(string) + }) + ) + })) + ) + }))) + default = {} } From 9c942a68d6511184dae15cb2c9711de398db3867 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Fri, 8 Jul 2022 15:19:47 +0200 Subject: [PATCH 09/10] More experimental definition to a separate file, so CICD does not rewrite it with default-versions.tf --- modules/organization-policy/experimental.tf | 19 +++++++++++++++++++ modules/organization-policy/versions.tf | 4 ---- .../fixture/experimental.tf | 3 +-- 3 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 modules/organization-policy/experimental.tf diff --git a/modules/organization-policy/experimental.tf b/modules/organization-policy/experimental.tf new file mode 100644 index 0000000000..30f36f312e --- /dev/null +++ b/modules/organization-policy/experimental.tf @@ -0,0 +1,19 @@ +# Copyright 2022 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 { + # TODO: Remove once Terraform 1.3 is released https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 + experiments = [module_variable_optional_attrs] +} diff --git a/modules/organization-policy/versions.tf b/modules/organization-policy/versions.tf index 52c88cef15..c01e5da095 100644 --- a/modules/organization-policy/versions.tf +++ b/modules/organization-policy/versions.tf @@ -14,10 +14,6 @@ terraform { required_version = ">= 1.1.0" - - # TODO: Remove once Terraform 1.3 is released https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 - experiments = [module_variable_optional_attrs] - required_providers { google = { source = "hashicorp/google" diff --git a/tests/modules/organization-policy/fixture/experimental.tf b/tests/modules/organization-policy/fixture/experimental.tf index c6434f966c..6ed0c9340c 100644 --- a/tests/modules/organization-policy/fixture/experimental.tf +++ b/tests/modules/organization-policy/fixture/experimental.tf @@ -13,7 +13,6 @@ # limitations under the License. terraform { - required_version = ">= 1.1.0" - + # TODO: Remove once Terraform 1.3 is released https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 experiments = [module_variable_optional_attrs] } From a1e34234ae03c25d3f419bc3b0a2df7a6c85a76c Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Fri, 8 Jul 2022 15:25:35 +0200 Subject: [PATCH 10/10] Tf fmt for the org-policy module --- modules/organization-policy/experimental.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/organization-policy/experimental.tf b/modules/organization-policy/experimental.tf index 30f36f312e..5fc3fc44b7 100644 --- a/modules/organization-policy/experimental.tf +++ b/modules/organization-policy/experimental.tf @@ -15,5 +15,5 @@ terraform { # TODO: Remove once Terraform 1.3 is released https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622 - experiments = [module_variable_optional_attrs] + experiments = [module_variable_optional_attrs] }