Remove hcl2 python dependency (#3836)

* Migrate organization policy tests to standard tftest.yaml.

Remove python-hcl2 dependency and the custom python test file.
Consolidate the boolean, list, and custom constraint tests into a single `org_policies` test with a factory equivalent.
Restructure factory files into a unified `factory/` directory.

* Migrate project and folder org policy tests to standard tftest.yaml.

Replicate the organization module changes for project and folder modules:
- Remove python-hcl2 dependency usages and conftest.py.
- Remove custom python test files for org policies.
- Consolidate org policy tests into a single `org_policies` test with a factory equivalent.
- Unify factory files into a `factory/` directory.
- Remove redundant common.tfvars in folder module.

* Add factory policies directory to duplicate-diff checks.

Ensure the YAML factory files for org policies remain perfectly identical across the organization, folder, and project modules.

* Remove unused deepdiff dependency from requirements and pre-commit config.

* Add boilerplate

* fix broken link
This commit is contained in:
Julio Castillo
2026-04-08 08:14:16 +02:00
committed by GitHub
parent 5d407f4df8
commit 15c7951f97
34 changed files with 733 additions and 328 deletions

View File

@@ -31,7 +31,6 @@ repos:
language: python
additional_dependencies:
- click
- deepdiff
- ghapi
- iso8601
- marko
@@ -49,7 +48,6 @@ repos:
entry: tools/check_boilerplate.py
additional_dependencies:
- click
- deepdiff
- ghapi
- iso8601
- marko
@@ -66,7 +64,6 @@ repos:
language: python
additional_dependencies:
- click
- deepdiff
- ghapi
- iso8601
- marko
@@ -89,7 +86,6 @@ repos:
language: python
additional_dependencies:
- click
- deepdiff
- ghapi
- iso8601
- marko
@@ -105,7 +101,6 @@ repos:
language: python
additional_dependencies:
- click
- deepdiff
- ghapi
- iso8601
- marko
@@ -121,7 +116,6 @@ repos:
language: python
additional_dependencies:
- click
- deepdiff
- ghapi
- iso8601
- marko

View File

@@ -1331,7 +1331,7 @@ def test_name(plan_summary, tfvars_to_yaml, tmp_path):
assert s.values[address]['project'] == 'my-project'
```
For more examples on how to write python tests, check the tests for the [`organization`](./tests/modules/organization/test_plan_org_policies.py) module.
For more examples on how to write python tests, check the tests for the [`organization`](./tests/modules/organization/) module.
### Running tests from a temporary directory

View File

@@ -1,51 +0,0 @@
# Copyright 2023 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 inspect
import pathlib
import hcl2
import pytest
import yaml
@pytest.fixture()
def tfvars_to_yaml(request):
def converter(source, dest, from_var, to_var=None):
p_fixture = pathlib.Path(request.path).parent
p_source = p_fixture / source
if not p_source.exists():
raise ValueError(f"tfvars '{source}' not found")
try:
with p_source.open() as f:
data = hcl2.load(f)
except Exception as e:
raise ValueError(f'error decoding tfvars: {e.args[0]}')
if from_var not in data:
raise ValueError(f"variable '{from_var}' not in tfvars")
if to_var is None:
data_yaml = data[from_var]
else:
data_yaml = {to_var: data[from_var]}
p_dest = pathlib.Path(dest) if not isinstance(dest, pathlib.Path) else dest
try:
with p_dest.open('w') as f:
data_yaml = yaml.dump(data_yaml, f)
except yaml.YAMLError as e:
raise ValueError(f'error encoding data to yaml: {e.args[0]}')
except (IOError, OSError) as e:
raise ValueError(f"error writing '{dest}': {e.args[0]}")
return converter

View File

@@ -1,2 +0,0 @@
parent = "organizations/12345678"
name = "folder-a"

View File

@@ -0,0 +1,28 @@
# Copyright 2026 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.
# yamllint disable rule:line-length
iam.disableServiceAccountKeyCreation:
rules:
- enforce: true
iam.disableServiceAccountKeyUpload:
rules:
- condition:
expression: resource.matchTagId(aa, bb)
title: condition
description: test condition
location: xxx
enforce: true
- enforce: false

View File

@@ -0,0 +1,47 @@
# Copyright 2026 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.
# yamllint disable rule:line-length
compute.vmExternalIpAccess:
rules:
- deny:
all: true
iam.allowedPolicyMemberDomains:
inherit_from_parent: true
rules:
- allow:
values:
- C0xxxxxxx
- C0yyyyyyy
compute.restrictLoadBalancerCreationForTypes:
rules:
- condition:
expression: resource.matchTagId(aa, bb)
title: condition
description: test condition
location: xxx
allow:
values:
- EXTERNAL_1
- condition:
expression: resource.matchTagId(cc, dd)
title: condition2
description: test condition2
location: xxx
allow:
all: true
- deny:
values:
- in:EXTERNAL

View File

@@ -1,8 +1,30 @@
parent = "organizations/12345678"
name = "folder-a"
org_policies = {
"iam.disableServiceAccountKeyCreation" = {
rules = [{ enforce = true }]
}
"iam.disableServiceAccountKeyUpload" = {
rules = [
{
condition = {
expression = "resource.matchTagId(aa, bb)"
title = "condition"
description = "test condition"
location = "xxx"
}
enforce = true
},
{
enforce = false
}
]
}
"compute.vmExternalIpAccess" = {
rules = [{ deny = { all = true } }]
}
"iam.allowedPolicyMemberDomains" = {
inherit_from_parent = true
rules = [{
allow = {
values = ["C0xxxxxxx", "C0yyyyyyy"]

View File

@@ -0,0 +1,132 @@
# Copyright 2026 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.
values:
google_folder.folder[0]:
deletion_protection: false
display_name: folder-a
parent: organizations/12345678
tags: null
timeouts: null
google_org_policy_policy.default["compute.restrictLoadBalancerCreationForTypes"]:
dry_run_spec: []
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition:
- description: test condition
expression: resource.matchTagId(aa, bb)
location: xxx
title: condition
deny_all: null
enforce: null
parameters: null
values:
- allowed_values:
- EXTERNAL_1
denied_values: null
- allow_all: 'TRUE'
condition:
- description: test condition2
expression: resource.matchTagId(cc, dd)
location: xxx
title: condition2
deny_all: null
enforce: null
parameters: null
values: []
- allow_all: null
condition: []
deny_all: null
enforce: null
parameters: null
values:
- allowed_values: null
denied_values:
- in:EXTERNAL
timeouts: null
google_org_policy_policy.default["compute.vmExternalIpAccess"]:
dry_run_spec: []
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: 'TRUE'
enforce: null
parameters: null
values: []
timeouts: null
google_org_policy_policy.default["iam.allowedPolicyMemberDomains"]:
dry_run_spec: []
spec:
- inherit_from_parent: true
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: null
parameters: null
values:
- allowed_values:
- C0xxxxxxx
- C0yyyyyyy
denied_values: null
timeouts: null
google_org_policy_policy.default["iam.disableServiceAccountKeyCreation"]:
dry_run_spec: []
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: 'TRUE'
parameters: null
values: []
timeouts: null
google_org_policy_policy.default["iam.disableServiceAccountKeyUpload"]:
dry_run_spec: []
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition:
- description: test condition
expression: resource.matchTagId(aa, bb)
location: xxx
title: condition
deny_all: null
enforce: 'TRUE'
parameters: null
values: []
- allow_all: null
condition: []
deny_all: null
enforce: 'FALSE'
parameters: null
values: []
timeouts: null
counts:
google_folder: 1
google_org_policy_policy: 5
modules: 0
resources: 6

View File

@@ -1,21 +0,0 @@
org_policies = {
"iam.disableServiceAccountKeyCreation" = {
rules = [{ enforce = true }]
}
"iam.disableServiceAccountKeyUpload" = {
rules = [
{
condition = {
expression = "resource.matchTagId(aa, bb)"
title = "condition"
description = "test condition"
location = "xxx"
}
enforce = true
},
{
enforce = false
}
]
}
}

View File

@@ -0,0 +1,5 @@
parent = "organizations/12345678"
name = "folder-a"
factories_config = {
org_policies = "factory/policies"
}

View File

@@ -1,29 +0,0 @@
# Copyright 2023 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 pytest
_params = ['boolean', 'list']
@pytest.mark.parametrize('policy_type', _params)
def test_policy_factory(plan_summary, tfvars_to_yaml, tmp_path, policy_type):
dest = tmp_path / 'policies.yaml'
tfvars_to_yaml(f'org_policies_{policy_type}.tfvars', dest, 'org_policies')
tfvars_plan = plan_summary(
'modules/folder',
tf_var_files=['common.tfvars', f'org_policies_{policy_type}.tfvars'])
yaml_plan = plan_summary('modules/folder', tf_var_files=['common.tfvars'],
factories_config=f'{{org_policies="{tmp_path}"}}')
assert tfvars_plan.values == yaml_plan.values

View File

@@ -18,3 +18,9 @@ tests:
context:
iam_by_principals_additive:
iam_by_principals_conditional:
org_policies:
org_policies_factory:
inventory:
- org_policies.yaml
extra_dirs:
- ../../tests/modules/folder/factory

View File

@@ -0,0 +1,35 @@
# Copyright 2026 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.
# yamllint disable rule:line-length
custom.gkeEnableAutoUpgrade:
resource_types:
- container.googleapis.com/NodePool
method_types:
- CREATE
condition: resource.management.autoUpgrade == true
action_type: ALLOW
display_name: Enable node auto-upgrade
description: All node pools must have node auto-upgrade enabled.
custom.dataprocNoMoreThan10Workers:
resource_types:
- dataproc.googleapis.com/Cluster
method_types:
- CREATE
- UPDATE
condition: resource.config.workerConfig.numInstances + resource.config.secondaryWorkerConfig.numInstances > 10
action_type: DENY
display_name: Total number of worker instances cannot be larger than 10
description: Cluster cannot have more than 10 workers, including primary and secondary workers.

View File

@@ -0,0 +1,28 @@
# Copyright 2026 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.
# yamllint disable rule:line-length
iam.disableServiceAccountKeyCreation:
rules:
- enforce: true
iam.disableServiceAccountKeyUpload:
rules:
- condition:
expression: resource.matchTagId(aa, bb)
title: condition
description: test condition
location: xxx
enforce: true
- enforce: false

View File

@@ -0,0 +1,47 @@
# Copyright 2026 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.
# yamllint disable rule:line-length
compute.vmExternalIpAccess:
rules:
- deny:
all: true
iam.allowedPolicyMemberDomains:
inherit_from_parent: true
rules:
- allow:
values:
- C0xxxxxxx
- C0yyyyyyy
compute.restrictLoadBalancerCreationForTypes:
rules:
- condition:
expression: resource.matchTagId(aa, bb)
title: condition
description: test condition
location: xxx
allow:
values:
- EXTERNAL_1
- condition:
expression: resource.matchTagId(cc, dd)
title: condition2
description: test condition2
location: xxx
allow:
all: true
- deny:
values:
- in:EXTERNAL

View File

@@ -0,0 +1,80 @@
org_policies = {
"iam.disableServiceAccountKeyCreation" = {
rules = [{ enforce = true }]
}
"iam.disableServiceAccountKeyUpload" = {
rules = [
{
condition = {
expression = "resource.matchTagId(aa, bb)"
title = "condition"
description = "test condition"
location = "xxx"
}
enforce = true
},
{
enforce = false
}
]
}
"compute.vmExternalIpAccess" = {
rules = [{ deny = { all = true } }]
}
"iam.allowedPolicyMemberDomains" = {
inherit_from_parent = true
rules = [{
allow = {
values = ["C0xxxxxxx", "C0yyyyyyy"]
}
}]
}
"compute.restrictLoadBalancerCreationForTypes" = {
rules = [
{
condition = {
expression = "resource.matchTagId(aa, bb)"
title = "condition"
description = "test condition"
location = "xxx"
}
allow = {
values = ["EXTERNAL_1"]
}
},
{
condition = {
expression = "resource.matchTagId(cc, dd)"
title = "condition2"
description = "test condition2"
location = "xxx"
}
allow = {
all = true
}
},
{
deny = { values = ["in:EXTERNAL"] }
}
]
}
}
org_policy_custom_constraints = {
"custom.gkeEnableAutoUpgrade" = {
resource_types = ["container.googleapis.com/NodePool"]
method_types = ["CREATE"]
condition = "resource.management.autoUpgrade == true"
action_type = "ALLOW"
display_name = "Enable node auto-upgrade"
description = "All node pools must have node auto-upgrade enabled."
},
"custom.dataprocNoMoreThan10Workers" = {
resource_types = ["dataproc.googleapis.com/Cluster"]
method_types = ["CREATE", "UPDATE"]
condition = "resource.config.workerConfig.numInstances + resource.config.secondaryWorkerConfig.numInstances > 10"
action_type = "DENY"
display_name = "Total number of worker instances cannot be larger than 10"
description = "Cluster cannot have more than 10 workers, including primary and secondary workers."
}
}

View File

@@ -0,0 +1,140 @@
# Copyright 2023 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.
values:
google_org_policy_policy.default["iam.disableServiceAccountKeyCreation"]:
name: organizations/1234567890/policies/iam.disableServiceAccountKeyCreation
parent: organizations/1234567890
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: 'TRUE'
values: []
timeouts: null
google_org_policy_policy.default["iam.disableServiceAccountKeyUpload"]:
name: organizations/1234567890/policies/iam.disableServiceAccountKeyUpload
parent: organizations/1234567890
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition:
- description: test condition
expression: resource.matchTagId(aa, bb)
location: xxx
title: condition
deny_all: null
enforce: 'TRUE'
values: []
- allow_all: null
condition: []
deny_all: null
enforce: 'FALSE'
values: []
timeouts: null
google_org_policy_policy.default["compute.restrictLoadBalancerCreationForTypes"]:
name: organizations/1234567890/policies/compute.restrictLoadBalancerCreationForTypes
parent: organizations/1234567890
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition:
- description: test condition
expression: resource.matchTagId(aa, bb)
location: xxx
title: condition
deny_all: null
enforce: null
values:
- allowed_values:
- EXTERNAL_1
denied_values: null
- allow_all: 'TRUE'
condition:
- description: test condition2
expression: resource.matchTagId(cc, dd)
location: xxx
title: condition2
deny_all: null
enforce: null
values: []
- allow_all: null
condition: []
deny_all: null
enforce: null
values:
- allowed_values: null
denied_values:
- in:EXTERNAL
timeouts: null
google_org_policy_policy.default["compute.vmExternalIpAccess"]:
name: organizations/1234567890/policies/compute.vmExternalIpAccess
parent: organizations/1234567890
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: 'TRUE'
enforce: null
values: []
timeouts: null
google_org_policy_policy.default["iam.allowedPolicyMemberDomains"]:
name: organizations/1234567890/policies/iam.allowedPolicyMemberDomains
parent: organizations/1234567890
spec:
- inherit_from_parent: true
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: null
values:
- allowed_values:
- C0xxxxxxx
- C0yyyyyyy
denied_values: null
timeouts: null
google_org_policy_custom_constraint.constraint["custom.dataprocNoMoreThan10Workers"]:
action_type: DENY
condition: resource.config.workerConfig.numInstances + resource.config.secondaryWorkerConfig.numInstances
> 10
method_types:
- CREATE
- UPDATE
name: custom.dataprocNoMoreThan10Workers
parent: organizations/1234567890
resource_types:
- dataproc.googleapis.com/Cluster
google_org_policy_custom_constraint.constraint["custom.gkeEnableAutoUpgrade"]:
action_type: ALLOW
condition: resource.management.autoUpgrade == true
method_types:
- CREATE
name: custom.gkeEnableAutoUpgrade
parent: organizations/1234567890
resource_types:
- container.googleapis.com/NodePool
counts:
google_org_policy_policy: 5
google_org_policy_custom_constraint: 2

View File

@@ -1,21 +0,0 @@
org_policies = {
"iam.disableServiceAccountKeyCreation" = {
rules = [{ enforce = true }]
}
"iam.disableServiceAccountKeyUpload" = {
rules = [
{
condition = {
expression = "resource.matchTagId(aa, bb)"
title = "condition"
description = "test condition"
location = "xxx"
}
enforce = true
},
{
enforce = false
}
]
}
}

View File

@@ -1,53 +0,0 @@
# Copyright 2023 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.
values:
google_org_policy_policy.default["iam.disableServiceAccountKeyCreation"]:
name: organizations/1234567890/policies/iam.disableServiceAccountKeyCreation
parent: organizations/1234567890
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: 'TRUE'
values: []
timeouts: null
google_org_policy_policy.default["iam.disableServiceAccountKeyUpload"]:
name: organizations/1234567890/policies/iam.disableServiceAccountKeyUpload
parent: organizations/1234567890
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition:
- description: test condition
expression: resource.matchTagId(aa, bb)
location: xxx
title: condition
deny_all: null
enforce: 'TRUE'
values: []
- allow_all: null
condition: []
deny_all: null
enforce: 'FALSE'
values: []
timeouts: null
counts:
google_org_policy_policy: 2

View File

@@ -1,18 +0,0 @@
org_policy_custom_constraints = {
"custom.gkeEnableAutoUpgrade" = {
resource_types = ["container.googleapis.com/NodePool"]
method_types = ["CREATE"]
condition = "resource.management.autoUpgrade == true"
action_type = "ALLOW"
display_name = "Enable node auto-upgrade"
description = "All node pools must have node auto-upgrade enabled."
},
"custom.dataprocNoMoreThan10Workers" = {
resource_types = ["dataproc.googleapis.com/Cluster"]
method_types = ["CREATE", "UPDATE"]
condition = "resource.config.workerConfig.numInstances + resource.config.secondaryWorkerConfig.numInstances > 10"
action_type = "DENY"
display_name = "Total number of worker instances cannot be larger than 10"
description = "Cluster cannot have more than 10 workers, including primary and secondary workers."
}
}

View File

@@ -1,37 +0,0 @@
# Copyright 2023 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.
values:
google_org_policy_custom_constraint.constraint["custom.dataprocNoMoreThan10Workers"]:
action_type: DENY
condition: resource.config.workerConfig.numInstances + resource.config.secondaryWorkerConfig.numInstances > 10
method_types:
- CREATE
- UPDATE
name: custom.dataprocNoMoreThan10Workers
parent: organizations/1234567890
resource_types:
- dataproc.googleapis.com/Cluster
google_org_policy_custom_constraint.constraint["custom.gkeEnableAutoUpgrade"]:
action_type: ALLOW
condition: resource.management.autoUpgrade == true
method_types:
- CREATE
name: custom.gkeEnableAutoUpgrade
parent: organizations/1234567890
resource_types:
- container.googleapis.com/NodePool
counts:
google_org_policy_custom_constraint: 2

View File

@@ -0,0 +1,4 @@
factories_config = {
org_policies = "factory/policies"
org_policy_custom_constraints = "factory/custom_constraints"
}

View File

@@ -1,43 +0,0 @@
# Copyright 2023 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 pytest
_params = ['boolean', 'list']
@pytest.mark.parametrize('policy_type', _params)
def test_policy_factory(plan_summary, tfvars_to_yaml, tmp_path, policy_type):
dest = tmp_path / 'policies.yaml'
tfvars_to_yaml(f'org_policies_{policy_type}.tfvars', dest, 'org_policies')
tfvars_plan = plan_summary(
'modules/organization',
tf_var_files=['common.tfvars', f'org_policies_{policy_type}.tfvars'])
yaml_plan = plan_summary('modules/organization',
tf_var_files=['common.tfvars'],
factories_config=f'{{org_policies="{tmp_path}"}}')
assert tfvars_plan.values == yaml_plan.values
def test_custom_constraint_factory(plan_summary, tfvars_to_yaml, tmp_path):
dest = tmp_path / 'constraints.yaml'
tfvars_to_yaml(f'org_policies_custom_constraints.tfvars', dest,
'org_policy_custom_constraints')
tfvars_plan = plan_summary(
'modules/organization',
tf_var_files=['common.tfvars', f'org_policies_custom_constraints.tfvars'])
yaml_plan = plan_summary(
'modules/organization', tf_var_files=['common.tfvars'],
factories_config=f'{{org_policy_custom_constraints="{tmp_path}"}}')
assert tfvars_plan.values == yaml_plan.values

View File

@@ -21,9 +21,12 @@ tests:
context:
iam_by_principals_additive:
iam_by_principals_conditional:
org_policies_list:
org_policies_boolean:
org_policies_custom_constraints:
org_policies:
org_policies_factory:
inventory:
- org_policies.yaml
extra_dirs:
- ../../tests/modules/organization/factory
tags:
tags_force_context:
tags_skip_iam:

View File

@@ -0,0 +1,28 @@
# Copyright 2026 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.
# yamllint disable rule:line-length
iam.disableServiceAccountKeyCreation:
rules:
- enforce: true
iam.disableServiceAccountKeyUpload:
rules:
- condition:
expression: resource.matchTagId(aa, bb)
title: condition
description: test condition
location: xxx
enforce: true
- enforce: false

View File

@@ -0,0 +1,47 @@
# Copyright 2026 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.
# yamllint disable rule:line-length
compute.vmExternalIpAccess:
rules:
- deny:
all: true
iam.allowedPolicyMemberDomains:
inherit_from_parent: true
rules:
- allow:
values:
- C0xxxxxxx
- C0yyyyyyy
compute.restrictLoadBalancerCreationForTypes:
rules:
- condition:
expression: resource.matchTagId(aa, bb)
title: condition
description: test condition
location: xxx
allow:
values:
- EXTERNAL_1
- condition:
expression: resource.matchTagId(cc, dd)
title: condition2
description: test condition2
location: xxx
allow:
all: true
- deny:
values:
- in:EXTERNAL

View File

@@ -1,4 +1,23 @@
org_policies = {
"iam.disableServiceAccountKeyCreation" = {
rules = [{ enforce = true }]
}
"iam.disableServiceAccountKeyUpload" = {
rules = [
{
condition = {
expression = "resource.matchTagId(aa, bb)"
title = "condition"
description = "test condition"
location = "xxx"
}
enforce = true
},
{
enforce = false
}
]
}
"compute.vmExternalIpAccess" = {
rules = [{ deny = { all = true } }]
}
@@ -9,7 +28,6 @@ org_policies = {
values = ["C0xxxxxxx", "C0yyyyyyy"]
}
}]
}
"compute.restrictLoadBalancerCreationForTypes" = {
rules = [

View File

@@ -13,9 +13,44 @@
# limitations under the License.
values:
google_org_policy_policy.default["iam.disableServiceAccountKeyCreation"]:
name: projects/my-project/policies/iam.disableServiceAccountKeyCreation
parent: projects/my-project
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: 'TRUE'
values: []
timeouts: null
google_org_policy_policy.default["iam.disableServiceAccountKeyUpload"]:
name: projects/my-project/policies/iam.disableServiceAccountKeyUpload
parent: projects/my-project
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition:
- description: test condition
expression: resource.matchTagId(aa, bb)
location: xxx
title: condition
deny_all: null
enforce: 'TRUE'
values: []
- allow_all: null
condition: []
deny_all: null
enforce: 'FALSE'
values: []
timeouts: null
google_org_policy_policy.default["compute.restrictLoadBalancerCreationForTypes"]:
name: organizations/1234567890/policies/compute.restrictLoadBalancerCreationForTypes
parent: organizations/1234567890
name: projects/my-project/policies/compute.restrictLoadBalancerCreationForTypes
parent: projects/my-project
spec:
- inherit_from_parent: null
reset: null
@@ -51,8 +86,8 @@ values:
- in:EXTERNAL
timeouts: null
google_org_policy_policy.default["compute.vmExternalIpAccess"]:
name: organizations/1234567890/policies/compute.vmExternalIpAccess
parent: organizations/1234567890
name: projects/my-project/policies/compute.vmExternalIpAccess
parent: projects/my-project
spec:
- inherit_from_parent: null
reset: null
@@ -64,8 +99,8 @@ values:
values: []
timeouts: null
google_org_policy_policy.default["iam.allowedPolicyMemberDomains"]:
name: organizations/1234567890/policies/iam.allowedPolicyMemberDomains
parent: organizations/1234567890
name: projects/my-project/policies/iam.allowedPolicyMemberDomains
parent: projects/my-project
spec:
- inherit_from_parent: true
reset: null
@@ -80,6 +115,5 @@ values:
- C0yyyyyyy
denied_values: null
timeouts: null
counts:
google_org_policy_policy: 3
google_org_policy_policy: 5

View File

@@ -0,0 +1,3 @@
factories_config = {
org_policies = "factory/policies"
}

View File

@@ -1,29 +0,0 @@
# Copyright 2023 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 pytest
_params = ['boolean', 'list']
@pytest.mark.parametrize('policy_type', _params)
def test_policy_factory(plan_summary, tfvars_to_yaml, tmp_path, policy_type):
dest = tmp_path / 'policies.yaml'
tfvars_to_yaml(f'org_policies_{policy_type}.tfvars', dest, 'org_policies')
tfvars_plan = plan_summary(
'modules/project',
tf_var_files=['common.tfvars', f'org_policies_{policy_type}.tfvars'])
yaml_plan = plan_summary('modules/project', tf_var_files=['common.tfvars'],
factories_config=f'{{org_policies="{tmp_path}"}}')
assert tfvars_plan.values == yaml_plan.values

View File

@@ -23,8 +23,12 @@ tests:
iam_by_principals_conditional:
no_parent:
no_prefix:
org_policies_boolean:
org_policies_list:
org_policies:
org_policies_factory:
inventory:
- org_policies.yaml
extra_dirs:
- ../../tests/modules/project/factory
parent_folder:
parent_org:
prefix:

View File

@@ -3,8 +3,7 @@ pytest>=7.2.1
PyYAML>=6.0
tftest>=1.8.7
marko>=1.2.2
deepdiff>=6.2.3
python-hcl2>=4.3.0
pytest-xdist>=3.1.0
jsonschema>=4.22.0
yamllint>=1.37.1

View File

@@ -21,6 +21,12 @@ import os
# List of folders and files that are expected to have same content
duplicates = [
# factory policies
[
"tests/modules/folder/factory/policies",
"tests/modules/organization/factory/policies",
"tests/modules/project/factory/policies",
],
# schemas
[
"fast/stages/1-vpcsc/schemas/access-level.schema.json",

View File

@@ -1,6 +1,5 @@
click
cloudpickle
deepdiff
ghapi
iso8601
marko