From aae6ab132c456381cdcfdb671d3bac0184b11710 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Tue, 8 Nov 2022 18:10:13 +0100 Subject: [PATCH] Add tests for org policy custom constraints --- .../org-policy-custom-constraints.tf | 62 +++++++++++++++++++ modules/organization/organization-policies.tf | 45 -------------- tests/modules/organization/fixture/main.tf | 36 ++++++----- .../test.orgpolicy-custom-constraints.tfvars | 18 ++++++ .../modules/organization/fixture/variables.tf | 10 +++ .../organization/test_plan_org_policies.py | 16 ++++- .../test_plan_org_policies_modules.py | 8 +-- .../modules/organization/validate_policies.py | 22 +++++++ 8 files changed, 150 insertions(+), 67 deletions(-) create mode 100644 modules/organization/org-policy-custom-constraints.tf create mode 100644 tests/modules/organization/fixture/test.orgpolicy-custom-constraints.tfvars diff --git a/modules/organization/org-policy-custom-constraints.tf b/modules/organization/org-policy-custom-constraints.tf new file mode 100644 index 00000000..bfc2de1b --- /dev/null +++ b/modules/organization/org-policy-custom-constraints.tf @@ -0,0 +1,62 @@ +/** + * Copyright 2022 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 { + _custom_constraints_factory_data_raw = ( + var.org_policy_custom_constraints_data_path == null + ? tomap({}) + : tomap(merge([ + for f in fileset(var.org_policy_custom_constraints_data_path, "*.yaml") : + yamldecode(file("${var.org_policy_custom_constraints_data_path}/${f}")) + ]...)) + ) + + _custom_constraints_factory_data = { + for k, v in local._custom_constraints_factory_data_raw : + k => { + display_name = try(v.display_name, null) + description = try(v.description, null) + action_type = v.action_type + condition = v.condition + method_types = v.method_types + resource_types = v.resource_types + } + } + + _custom_constraints = merge(local._custom_constraints_factory_data, var.org_policy_custom_constraints) + + custom_constraints = { + for k, v in local._custom_constraints : + k => merge(v, { + name = k + parent = var.organization_id + }) + } +} + +resource "google_org_policy_custom_constraint" "constraint" { + provider = google-beta + + for_each = local.custom_constraints + name = each.value.name + parent = each.value.parent + display_name = each.value.display_name + description = each.value.description + action_type = each.value.action_type + condition = each.value.condition + method_types = each.value.method_types + resource_types = each.value.resource_types +} diff --git a/modules/organization/organization-policies.tf b/modules/organization/organization-policies.tf index 4e82467b..425e8f52 100644 --- a/modules/organization/organization-policies.tf +++ b/modules/organization/organization-policies.tf @@ -88,37 +88,6 @@ locals { ] }) } - - _custom_constraints_factory_data_raw = ( - var.org_policy_custom_constraints_data_path == null - ? tomap({}) - : merge([ - for f in fileset(var.org_policy_custom_constraints_data_path, "*.yaml") : - yamldecode(file("${var.org_policy_custom_constraints_data_path}/${f}")) - ]...) - ) - - _custom_constraints_factory_data = { - for k, v in local._custom_constraints_factory_data_raw : - k => { - display_name = try(v.display_name, null) - description = try(v.description, null) - action_type = v.action_type - condition = v.condition - method_types = v.method_types - resource_types = v.resource_types - } - } - - _custom_constraints = merge(local._custom_constraints_factory_data, var.org_policy_custom_constraints) - - custom_constraints = { - for k, v in local._custom_constraints : - k => merge(v, { - name = k - parent = var.organization_id - }) - } } resource "google_org_policy_policy" "default" { @@ -184,17 +153,3 @@ resource "google_org_policy_policy" "default" { google_org_policy_custom_constraint.constraint, ] } - -resource "google_org_policy_custom_constraint" "constraint" { - provider = google-beta - - for_each = local.custom_constraints - name = each.value.name - parent = each.value.parent - display_name = each.value.display_name - description = each.value.description - action_type = each.value.action_type - condition = each.value.condition - method_types = each.value.method_types - resource_types = each.value.resource_types -} diff --git a/tests/modules/organization/fixture/main.tf b/tests/modules/organization/fixture/main.tf index 4f5df9e2..a620542e 100644 --- a/tests/modules/organization/fixture/main.tf +++ b/tests/modules/organization/fixture/main.tf @@ -15,21 +15,23 @@ */ module "test" { - source = "../../../../modules/organization" - organization_id = "organizations/1234567890" - custom_roles = var.custom_roles - firewall_policies = var.firewall_policies - firewall_policy_association = var.firewall_policy_association - firewall_policy_factory = var.firewall_policy_factory - group_iam = var.group_iam - iam = var.iam - iam_additive = var.iam_additive - iam_additive_members = var.iam_additive_members - iam_audit_config = var.iam_audit_config - logging_sinks = var.logging_sinks - logging_exclusions = var.logging_exclusions - org_policies = var.org_policies - org_policies_data_path = var.org_policies_data_path - tag_bindings = var.tag_bindings - tags = var.tags + source = "../../../../modules/organization" + organization_id = "organizations/1234567890" + custom_roles = var.custom_roles + firewall_policies = var.firewall_policies + firewall_policy_association = var.firewall_policy_association + firewall_policy_factory = var.firewall_policy_factory + group_iam = var.group_iam + iam = var.iam + iam_additive = var.iam_additive + iam_additive_members = var.iam_additive_members + iam_audit_config = var.iam_audit_config + logging_sinks = var.logging_sinks + logging_exclusions = var.logging_exclusions + org_policies = var.org_policies + org_policies_data_path = var.org_policies_data_path + org_policy_custom_constraints = var.org_policy_custom_constraints + org_policy_custom_constraints_data_path = var.org_policy_custom_constraints_data_path + tag_bindings = var.tag_bindings + tags = var.tags } diff --git a/tests/modules/organization/fixture/test.orgpolicy-custom-constraints.tfvars b/tests/modules/organization/fixture/test.orgpolicy-custom-constraints.tfvars new file mode 100644 index 00000000..a02f97c4 --- /dev/null +++ b/tests/modules/organization/fixture/test.orgpolicy-custom-constraints.tfvars @@ -0,0 +1,18 @@ +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." + } +} diff --git a/tests/modules/organization/fixture/variables.tf b/tests/modules/organization/fixture/variables.tf index 2508fb06..c4efa8fb 100644 --- a/tests/modules/organization/fixture/variables.tf +++ b/tests/modules/organization/fixture/variables.tf @@ -79,6 +79,16 @@ variable "org_policies_data_path" { default = null } +variable "org_policy_custom_constraints" { + type = any + default = {} +} + +variable "org_policy_custom_constraints_data_path" { + type = any + default = null +} + variable "tag_bindings" { type = any default = null diff --git a/tests/modules/organization/test_plan_org_policies.py b/tests/modules/organization/test_plan_org_policies.py index 8267106f..05550832 100644 --- a/tests/modules/organization/test_plan_org_policies.py +++ b/tests/modules/organization/test_plan_org_policies.py @@ -14,7 +14,7 @@ import pathlib -from .validate_policies import validate_policy_boolean, validate_policy_list +from .validate_policies import validate_policy_boolean, validate_policy_list, validate_policy_custom_constraints def test_policy_boolean(plan_runner): @@ -31,6 +31,13 @@ def test_policy_list(plan_runner): validate_policy_list(resources) +def test_policy_custom_constraints(plan_runner): + "Test org policy custom constraints." + tfvars = 'test.orgpolicy-custom-constraints.tfvars' + _, resources = plan_runner(tf_var_file=tfvars) + validate_policy_custom_constraints(resources) + + def test_factory_policy_boolean(plan_runner, tfvars_to_yaml, tmp_path): dest = tmp_path / 'policies.yaml' tfvars_to_yaml('test.orgpolicies-boolean.tfvars', dest, 'org_policies') @@ -43,3 +50,10 @@ def test_factory_policy_list(plan_runner, tfvars_to_yaml, tmp_path): tfvars_to_yaml('test.orgpolicies-list.tfvars', dest, 'org_policies') _, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"') validate_policy_list(resources) + + +def test_factory_policy_custom_constraints(plan_runner, tfvars_to_yaml, tmp_path): + dest = tmp_path / 'constraints.yaml' + tfvars_to_yaml('test.orgpolicy-custom-constraints.tfvars', dest, 'org_policy_custom_constraints') + _, resources = plan_runner(org_policy_custom_constraints_data_path=f'"{tmp_path}"') + validate_policy_custom_constraints(resources) diff --git a/tests/modules/organization/test_plan_org_policies_modules.py b/tests/modules/organization/test_plan_org_policies_modules.py index 81030035..d2a5e097 100644 --- a/tests/modules/organization/test_plan_org_policies_modules.py +++ b/tests/modules/organization/test_plan_org_policies_modules.py @@ -72,11 +72,10 @@ def test_policy_implementation(): '- name = "${local.folder.name}/policies/${k}"\n', '- parent = local.folder.name\n', '+ name = "${var.organization_id}/policies/${k}"\n', - '+ parent = var.organization_id\n', - ' \n', + '+ parent = var.organization_id\n', ' \n', ' is_boolean_policy = v.allow == null && v.deny == null\n', ' has_values = (\n', - '@@ -143,4 +143,12 @@\n', + '@@ -143,4 +143,13 @@\n', ' }\n', ' }\n', ' }\n', @@ -87,6 +86,7 @@ def test_policy_implementation(): '+ google_organization_iam_custom_role.roles,\n', '+ google_organization_iam_member.additive,\n', '+ google_organization_iam_policy.authoritative,\n', + '+ google_org_policy_custom_constraint.constraint,\n', '+ ]\n', - ' }\n', + ' }\n' ] diff --git a/tests/modules/organization/validate_policies.py b/tests/modules/organization/validate_policies.py index 73acb088..51844b15 100644 --- a/tests/modules/organization/validate_policies.py +++ b/tests/modules/organization/validate_policies.py @@ -138,3 +138,25 @@ def validate_policy_list(resources): 'enforce': None, 'values': [] } + +def validate_policy_custom_constraints(resources): + assert len(resources) == 2 + assert all( + r['values']['parent'] == 'organizations/1234567890' for r in resources) + constraints = { + r['index']: r['values'] + for r in resources + if r['type'] == 'google_org_policy_custom_constraint' + } + assert len(constraints) == 2 + c1 = constraints['custom.gkeEnableAutoUpgrade'] + assert c1['resource_types'][0] == 'container.googleapis.com/NodePool' + assert c1['method_types'] == ['CREATE'] + assert c1['condition'] == 'resource.management.autoUpgrade == true' + assert c1['action_type'] == 'ALLOW' + + c2 = constraints['custom.dataprocNoMoreThan10Workers'] + assert c2['resource_types'][0] == 'dataproc.googleapis.com/Cluster' + assert c2['method_types'] == ['CREATE', 'UPDATE'] + assert c2['condition'] == 'resource.config.workerConfig.numInstances + resource.config.secondaryWorkerConfig.numInstances > 10' + assert c2['action_type'] == 'DENY'