Update folder tests

This commit is contained in:
Julio Castillo 2023-01-03 16:37:50 +01:00
parent 065b1471a8
commit c8bfe892a6
18 changed files with 472 additions and 655 deletions

View File

@ -2,9 +2,7 @@
This module allows the creation and management of folders, including support for IAM bindings, organization policies, and hierarchical firewall rules. This module allows the creation and management of folders, including support for IAM bindings, organization policies, and hierarchical firewall rules.
## Examples ## Basic example with IAM bindings
### IAM bindings
```hcl ```hcl
module "folder" { module "folder" {
@ -14,17 +12,26 @@ module "folder" {
group_iam = { group_iam = {
"cloud-owners@example.org" = [ "cloud-owners@example.org" = [
"roles/owner", "roles/owner",
"roles/resourcemanager.folderAdmin",
"roles/resourcemanager.projectCreator" "roles/resourcemanager.projectCreator"
] ]
} }
iam = { iam = {
"roles/owner" = ["user:one@example.com"] "roles/owner" = ["user:one@example.org"]
}
iam_additive = {
"roles/compute.admin" = ["user:a1@example.org", "user:a2@example.org"]
"roles/compute.viewer" = ["user:a2@example.org"]
}
iam_additive_members = {
"user:am1@example.org" = ["roles/storage.admin"]
"user:am2@example.org" = ["roles/storage.objectViewer"]
} }
} }
# tftest modules=1 resources=3 # tftest modules=1 resources=9 inventory=iam.yaml
``` ```
### Organization policies ## Organization policies
To manage organization policies, the `orgpolicy.googleapis.com` service should be enabled in the quota project. To manage organization policies, the `orgpolicy.googleapis.com` service should be enabled in the quota project.
@ -72,70 +79,14 @@ module "folder" {
} }
} }
} }
# tftest modules=1 resources=8 # tftest modules=1 resources=8 inventory=org-policies.yaml
``` ```
### Organization policy factory ### Organization policy factory
See the [organization policy factory in the project module](../project#organization-policy-factory). See the [organization policy factory in the project module](../project#organization-policy-factory).
### Firewall policy factory ## Logging Sinks
In the same way as for the [organization](../organization) module, the in-built factory allows you to define a single policy, using one file for rules, and an optional file for CIDR range substitution variables. Remember that non-absolute paths are relative to the root module (the folder where you run `terraform`).
```hcl
module "folder" {
source = "./fabric/modules/folder"
parent = "organizations/1234567890"
name = "Folder name"
firewall_policy_factory = {
cidr_file = "configs/firewall-policies/cidrs.yaml"
policy_name = null
rules_file = "configs/firewall-policies/rules.yaml"
}
firewall_policy_association = {
factory-policy = module.folder.firewall_policy_id["factory"]
}
}
# tftest modules=1 resources=5 files=cidrs,rules
```
```yaml
# tftest-file id=cidrs path=configs/firewall-policies/cidrs.yaml
rfc1918:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
```
```yaml
# tftest-file id=rules path=configs/firewall-policies/rules.yaml
allow-admins:
description: Access from the admin subnet to all subnets
direction: INGRESS
action: allow
priority: 1000
ranges:
- $rfc1918
ports:
all: []
target_resources: null
enable_logging: false
allow-ssh-from-iap:
description: Enable SSH from IAP
direction: INGRESS
action: allow
priority: 1002
ranges:
- 35.235.240.0/20
ports:
tcp: ["22"]
target_resources: null
enable_logging: false
```
### Logging Sinks
```hcl ```hcl
module "gcs" { module "gcs" {
@ -164,7 +115,6 @@ module "bucket" {
id = "bucket" id = "bucket"
} }
module "folder-sink" { module "folder-sink" {
source = "./fabric/modules/folder" source = "./fabric/modules/folder"
parent = "folders/657104291943" parent = "folders/657104291943"
@ -198,10 +148,19 @@ module "folder-sink" {
no-gce-instances = "resource.type=gce_instance" no-gce-instances = "resource.type=gce_instance"
} }
} }
# tftest modules=5 resources=14 # tftest modules=5 resources=14 inventory=logging.yaml
``` ```
### Hierarchical firewall policies ## Hierarchical firewall policies
Hierarchical firewall policies can be managed in two ways:
- via the `firewall_policies` variable, to directly define policies and rules in Terraform
- via the `firewall_policy_factory` variable, to leverage external YaML files via a simple "factory" embedded in the module ([see here](../../blueprints/factories) for more context on factories)
Once you have policies (either created via the module or externally), you can associate them using the `firewall_policy_association` variable.
### Directly defined firewall policies
```hcl ```hcl
module "folder1" { module "folder1" {
@ -211,6 +170,17 @@ module "folder1" {
firewall_policies = { firewall_policies = {
iap-policy = { iap-policy = {
allow-admins = {
description = "Access from the admin subnet to all subnets"
direction = "INGRESS"
action = "allow"
priority = 1000
ranges = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
ports = { all = [] }
target_service_accounts = null
target_resources = null
logging = false
}
allow-iap-ssh = { allow-iap-ssh = {
description = "Always allow ssh from IAP" description = "Always allow ssh from IAP"
direction = "INGRESS" direction = "INGRESS"
@ -237,7 +207,71 @@ module "folder2" {
iap-policy = module.folder1.firewall_policy_id["iap-policy"] iap-policy = module.folder1.firewall_policy_id["iap-policy"]
} }
} }
# tftest modules=2 resources=6 # tftest modules=2 resources=7 inventory=hfw.yaml
```
### Firewall policy factory
The in-built factory allows you to define a single policy, using one file for rules, and an optional file for CIDR range substitution variables. Remember that non-absolute paths are relative to the root module (the folder where you run `terraform`).
```hcl
module "folder1" {
source = "./fabric/modules/folder"
parent = var.organization_id
name = "policy-container"
firewall_policy_factory = {
cidr_file = "configs/firewall-policies/cidrs.yaml"
policy_name = "iap-policy"
rules_file = "configs/firewall-policies/rules.yaml"
}
firewall_policy_association = {
iap-policy = "iap-policy"
}
}
module "folder2" {
source = "./fabric/modules/folder"
parent = var.organization_id
name = "hf2"
firewall_policy_association = {
iap-policy = module.folder1.firewall_policy_id["iap-policy"]
}
}
# tftest modules=2 resources=7 files=cidrs,rules inventory=hfw.yaml
```
```yaml
# tftest-file id=cidrs path=configs/firewall-policies/cidrs.yaml
rfc1918:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
```
```yaml
# tftest-file id=rules path=configs/firewall-policies/rules.yaml
allow-admins:
description: Access from the admin subnet to all subnets
direction: INGRESS
action: allow
priority: 1000
ranges:
- $rfc1918
ports:
all: []
target_resources: null
logging: false
allow-iap-ssh:
description: "Always allow ssh from IAP"
direction: INGRESS
action: allow
priority: 100
ranges:
- 35.235.240.0/20
ports:
tcp: ["22"]
target_resources: null
logging: false
``` ```
## Tags ## Tags
@ -269,7 +303,7 @@ module "folder" {
foo = "tagValues/12345678" foo = "tagValues/12345678"
} }
} }
# tftest modules=2 resources=6 # tftest modules=2 resources=6 inventory=tags.yaml
``` ```
<!-- TFDOC OPTS files:1 --> <!-- TFDOC OPTS files:1 -->

View File

@ -49,6 +49,11 @@ def test_example(plan_validator, tmp_path, example):
summary = plan_validator(module_path=tmp_path, inventory_paths=inventory, summary = plan_validator(module_path=tmp_path, inventory_paths=inventory,
tf_var_files=[]) tf_var_files=[])
import yaml
print(yaml.dump({"values": summary.values}))
print(yaml.dump({"counts": summary.counts}))
print(yaml.dump({"outputs": summary.outputs}))
counts = summary.counts counts = summary.counts
num_modules, num_resources = counts['modules'], counts['resources'] num_modules, num_resources = counts['modules'], counts['resources']
assert expected_modules == num_modules, 'wrong number of modules' assert expected_modules == num_modules, 'wrong number of modules'

View File

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

View File

@ -0,0 +1,67 @@
# Copyright 2022 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:
module.folder1.google_compute_firewall_policy.policy["iap-policy"]:
description: null
short_name: iap-policy
module.folder1.google_compute_firewall_policy_association.association["iap-policy"]: {}
module.folder1.google_compute_firewall_policy_rule.rule["iap-policy-allow-admins"]:
action: allow
description: Access from the admin subnet to all subnets
direction: INGRESS
disabled: null
enable_logging: false
match:
- dest_ip_ranges: null
layer4_configs:
- ip_protocol: all
ports: []
src_ip_ranges:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
priority: 1000
target_resources: null
target_service_accounts: null
module.folder1.google_compute_firewall_policy_rule.rule["iap-policy-allow-iap-ssh"]:
action: allow
description: Always allow ssh from IAP
direction: INGRESS
disabled: null
enable_logging: false
match:
- dest_ip_ranges: null
layer4_configs:
- ip_protocol: tcp
ports:
- '22'
src_ip_ranges:
- 35.235.240.0/20
priority: 100
target_resources: null
target_service_accounts: null
module.folder1.google_folder.folder[0]:
display_name: policy-container
parent: organizations/1122334455
module.folder2.google_compute_firewall_policy_association.association["iap-policy"]: {}
module.folder2.google_folder.folder[0]:
display_name: hf2
parent: organizations/1122334455
counts:
google_compute_firewall_policy: 1
google_compute_firewall_policy_association: 2
google_compute_firewall_policy_rule: 2
google_folder: 2

View File

@ -0,0 +1,59 @@
# Copyright 2022 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:
module.folder.google_folder.folder[0]:
display_name: Folder name
parent: organizations/1234567890
module.folder.google_folder_iam_binding.authoritative["roles/owner"]:
condition: []
members:
- group:cloud-owners@example.org
- user:one@example.org
role: roles/owner
module.folder.google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]:
condition: []
members:
- group:cloud-owners@example.org
role: roles/resourcemanager.folderAdmin
module.folder.google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]:
condition: []
members:
- group:cloud-owners@example.org
role: roles/resourcemanager.projectCreator
module.folder.google_folder_iam_member.additive["roles/compute.admin-user:a1@example.org"]:
condition: []
member: user:a1@example.org
role: roles/compute.admin
module.folder.google_folder_iam_member.additive["roles/compute.admin-user:a2@example.org"]:
condition: []
member: user:a2@example.org
role: roles/compute.admin
module.folder.google_folder_iam_member.additive["roles/compute.viewer-user:a2@example.org"]:
condition: []
member: user:a2@example.org
role: roles/compute.viewer
module.folder.google_folder_iam_member.additive["roles/storage.admin-user:am1@example.org"]:
condition: []
member: user:am1@example.org
role: roles/storage.admin
module.folder.google_folder_iam_member.additive["roles/storage.objectViewer-user:am2@example.org"]:
condition: []
member: user:am2@example.org
role: roles/storage.objectViewer
counts:
google_folder: 1
google_folder_iam_binding: 3
google_folder_iam_member: 5

View File

@ -0,0 +1,75 @@
# Copyright 2022 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:
module.folder-sink.google_bigquery_dataset_iam_member.bq-sinks-binding["info"]:
role: roles/bigquery.dataEditor
module.folder-sink.google_folder.folder[0]:
display_name: my-folder
parent: folders/657104291943
module.folder-sink.google_logging_folder_exclusion.logging-exclusion["no-gce-instances"]:
description: no-gce-instances (Terraform-managed).
filter: resource.type=gce_instance
name: no-gce-instances
module.folder-sink.google_logging_folder_sink.sink["debug"]:
disabled: false
exclusions:
- description: null
disabled: false
filter: logName:compute
name: no-compute
filter: severity=DEBUG
include_children: true
name: debug
module.folder-sink.google_logging_folder_sink.sink["info"]:
disabled: false
exclusions: []
filter: severity=INFO
include_children: true
name: info
module.folder-sink.google_logging_folder_sink.sink["notice"]:
disabled: false
exclusions: []
filter: severity=NOTICE
include_children: true
name: notice
module.folder-sink.google_logging_folder_sink.sink["warnings"]:
description: warnings (Terraform-managed).
destination: storage.googleapis.com/gcs_sink
disabled: false
exclusions: []
filter: severity=WARNING
include_children: true
name: warnings
module.folder-sink.google_project_iam_member.bucket-sinks-binding["debug"]:
condition:
- title: debug bucket writer
role: roles/logging.bucketWriter
module.folder-sink.google_pubsub_topic_iam_member.pubsub-sinks-binding["notice"]:
condition: []
role: roles/pubsub.publisher
module.folder-sink.google_storage_bucket_iam_member.gcs-sinks-binding["warnings"]:
bucket: gcs_sink
condition: []
role: roles/storage.objectCreator
counts:
google_bigquery_dataset_iam_member: 1
google_folder: 1
google_logging_folder_exclusion: 1
google_logging_folder_sink: 4
google_logging_project_bucket_config: 1
google_project_iam_member: 1
google_pubsub_topic_iam_member: 1
google_storage_bucket_iam_member: 1

View File

@ -0,0 +1,108 @@
# Copyright 2022 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:
module.folder.google_folder.folder[0]:
display_name: Folder name
parent: organizations/1234567890
module.folder.google_org_policy_policy.default["compute.disableGuestAttributesAccess"]:
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: 'TRUE'
values: []
module.folder.google_org_policy_policy.default["constraints/compute.skipDefaultNetworkCreation"]:
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: 'TRUE'
values: []
module.folder.google_org_policy_policy.default["constraints/compute.trustedImageProjects"]:
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: null
values:
- allowed_values:
- projects/my-project
denied_values: null
module.folder.google_org_policy_policy.default["constraints/compute.vmExternalIpAccess"]:
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: 'TRUE'
enforce: null
values: []
module.folder.google_org_policy_policy.default["constraints/iam.allowedPolicyMemberDomains"]:
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: null
values:
- allowed_values:
- C0xxxxxxx
- C0yyyyyyy
denied_values: null
module.folder.google_org_policy_policy.default["iam.disableServiceAccountKeyCreation"]:
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: 'TRUE'
values: []
module.folder.google_org_policy_policy.default["iam.disableServiceAccountKeyUpload"]:
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition:
- description: test condition
expression: resource.matchTagId("tagKeys/1234", "tagValues/1234")
location: somewhere
title: condition
deny_all: null
enforce: 'TRUE'
values: []
- allow_all: null
condition: []
deny_all: null
enforce: 'FALSE'
values: []
counts:
google_folder: 1
google_org_policy_policy: 7

View File

@ -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.
tests/examples/test_plan.py::test_example[modules/folder:Tags] values:
module.folder.google_folder.folder[0]:
display_name: Test
parent: organizations/1122334455
module.folder.google_tags_tag_binding.binding["env-prod"]: {}
module.folder.google_tags_tag_binding.binding["foo"]:
tag_value: tagValues/12345678
module.org.google_tags_tag_key.default["environment"]:
description: Environment specification.
parent: organizations/1122334455
purpose: null
purpose_data: null
short_name: environment
timeouts: null
module.org.google_tags_tag_value.default["environment/dev"]:
description: Managed by the Terraform organization module.
short_name: dev
module.org.google_tags_tag_value.default["environment/prod"]:
description: Managed by the Terraform organization module.
short_name: prod
counts:
google_folder: 1
google_tags_tag_binding: 2
google_tags_tag_key: 1
google_tags_tag_value: 2

View File

@ -1,31 +0,0 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module "test" {
source = "../../../../modules/folder"
parent = "organizations/12345678"
name = "folder-a"
group_iam = var.group_iam
iam = var.iam
iam_additive = var.iam_additive
iam_additive_members = var.iam_additive_members
firewall_policies = var.firewall_policies
firewall_policy_association = var.firewall_policy_association
logging_sinks = var.logging_sinks
logging_exclusions = var.logging_exclusions
org_policies = var.org_policies
org_policies_data_path = var.org_policies_data_path
}

View File

@ -1,29 +0,0 @@
logging_sinks = {
warning = {
destination = "mybucket"
type = "storage"
filter = "severity=WARNING"
}
info = {
destination = "projects/myproject/datasets/mydataset"
type = "bigquery"
filter = "severity=INFO"
disabled = true
}
notice = {
destination = "projects/myproject/topics/mytopic"
type = "pubsub"
filter = "severity=NOTICE"
include_children = false
}
debug = {
destination = "projects/myproject/locations/global/buckets/mybucket"
type = "logging"
filter = "severity=DEBUG"
include_children = false
exclusions = {
no-compute = "logName:compute"
no-container = "logName:container"
}
}
}

View File

@ -1,65 +0,0 @@
/**
* Copyright 2022 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 "group_iam" {
type = any
default = {}
}
variable "iam" {
type = any
default = {}
}
variable "iam_additive" {
type = any
default = {}
}
variable "iam_additive_members" {
type = any
default = {}
}
variable "firewall_policies" {
type = any
default = {}
}
variable "firewall_policy_association" {
type = any
default = {}
}
variable "logging_sinks" {
type = any
default = {}
}
variable "logging_exclusions" {
type = any
default = {}
}
variable "org_policies" {
type = any
default = {}
}
variable "org_policies_data_path" {
type = any
default = null
}

View File

@ -1,72 +0,0 @@
# Copyright 2022 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_folder(plan_runner):
"Test folder resources."
_, resources = plan_runner()
assert len(resources) == 1
resource = resources[0]
assert resource['type'] == 'google_folder'
assert resource['values']['display_name'] == 'folder-a'
assert resource['values']['parent'] == 'organizations/12345678'
def test_iam(plan_runner):
"Test IAM."
group_iam = (
'{'
'"owners@example.org" = ["roles/owner", "roles/resourcemanager.folderAdmin"],'
'"viewers@example.org" = ["roles/viewer"]'
'}')
iam = ('{'
'"roles/owner" = ["user:one@example.org", "user:two@example.org"],'
'"roles/browser" = ["domain:example.org"]'
'}')
_, resources = plan_runner(group_iam=group_iam, iam=iam)
roles = sorted([(r['values']['role'], sorted(r['values']['members']))
for r in resources
if r['type'] == 'google_folder_iam_binding'])
assert roles == [
('roles/browser', ['domain:example.org']),
('roles/owner', [
'group:owners@example.org', 'user:one@example.org',
'user:two@example.org'
]),
('roles/resourcemanager.folderAdmin', ['group:owners@example.org']),
('roles/viewer', ['group:viewers@example.org']),
]
def test_iam_multiple_roles(plan_runner):
"Test folder resources with multiple iam roles."
iam = ('{ '
'"roles/owner" = ["user:a@b.com"], '
'"roles/viewer" = ["user:c@d.com"] '
'} ')
_, resources = plan_runner(iam=iam)
assert len(resources) == 3
def test_iam_additive_members(plan_runner):
"Test IAM additive members."
iam = ('{"user:one@example.org" = ["roles/owner"],'
'"user:two@example.org" = ["roles/owner", "roles/editor"]}')
_, resources = plan_runner(iam_additive_members=iam)
roles = set((r['values']['role'], r['values']['member'])
for r in resources
if r['type'] == 'google_folder_iam_member')
assert roles == set([('roles/owner', 'user:one@example.org'),
('roles/owner', 'user:two@example.org'),
('roles/editor', 'user:two@example.org')])

View File

@ -1,85 +0,0 @@
# Copyright 2022 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_firweall_policy(plan_runner):
"Test boolean folder policy."
policy = """
{
policy1 = {
allow-ingress = {
description = ""
direction = "INGRESS"
action = "allow"
priority = 100
ranges = ["10.0.0.0/8"]
ports = {
tcp = ["22"]
}
target_service_accounts = null
target_resources = null
logging = false
}
deny-egress = {
description = ""
direction = "EGRESS"
action = "deny"
priority = 200
ranges = ["192.168.0.0/24"]
ports = {
tcp = ["443"]
}
target_service_accounts = null
target_resources = null
logging = false
}
}
}
"""
association = '{policy1="policy1"}'
_, resources = plan_runner(firewall_policies=policy,
firewall_policy_association=association)
assert len(resources) == 5
policies = [r for r in resources
if r['type'] == 'google_compute_firewall_policy']
assert len(policies) == 1
rules = [r for r in resources
if r['type'] == 'google_compute_firewall_policy_rule']
assert len(rules) == 2
rule_values = []
for rule in rules:
name = rule['name']
index = rule['index']
action = rule['values']['action']
direction = rule['values']['direction']
priority = rule['values']['priority']
match = rule['values']['match']
rule_values.append((name, index, action, direction, priority, match))
assert sorted(rule_values) == sorted([
('rule', 'policy1-allow-ingress', 'allow', 'INGRESS', 100, [
{
'dest_ip_ranges': None,
'layer4_configs': [{'ip_protocol': 'tcp', 'ports': ['22']}],
'src_ip_ranges': ['10.0.0.0/8']
}]),
('rule', 'policy1-deny-egress', 'deny', 'EGRESS', 200, [
{
'dest_ip_ranges': ['192.168.0.0/24'],
'layer4_configs': [{'ip_protocol': 'tcp', 'ports': ['443']}],
'src_ip_ranges': None
}])
])

View File

@ -1,119 +0,0 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from collections import Counter
def test_sinks(plan_runner):
"Test folder-level sinks."
tfvars = 'test.logging-sinks.tfvars'
_, resources = plan_runner(tf_var_file=tfvars)
assert len(resources) == 9
resource_types = Counter([r["type"] for r in resources])
assert resource_types == {
"google_logging_folder_sink": 4,
"google_folder": 1,
"google_bigquery_dataset_iam_member": 1,
"google_project_iam_member": 1,
"google_pubsub_topic_iam_member": 1,
"google_storage_bucket_iam_member": 1,
}
sinks = [r for r in resources if r["type"] == "google_logging_folder_sink"]
assert sorted([r["index"] for r in sinks]) == [
"debug",
"info",
"notice",
"warning",
]
values = [(
r["index"],
r["values"]["filter"],
r["values"]["destination"],
r["values"]["description"],
r["values"]["include_children"],
r["values"]["disabled"],
) for r in sinks]
assert sorted(values) == [
("debug", "severity=DEBUG",
"logging.googleapis.com/projects/myproject/locations/global/buckets/mybucket",
"debug (Terraform-managed).", False, False),
("info", "severity=INFO",
"bigquery.googleapis.com/projects/myproject/datasets/mydataset",
"info (Terraform-managed).", True, True),
("notice", "severity=NOTICE",
"pubsub.googleapis.com/projects/myproject/topics/mytopic",
"notice (Terraform-managed).", False, False),
("warning", "severity=WARNING", "storage.googleapis.com/mybucket",
"warning (Terraform-managed).", True, False),
]
bindings = [r for r in resources if "member" in r["type"]]
values = [(r["index"], r["type"], r["values"]["role"],
r["values"]["condition"]) for r in bindings]
assert sorted(values) == [
("debug", "google_project_iam_member", "roles/logging.bucketWriter", [{
'expression':
"resource.name.endsWith('projects/myproject/locations/global/buckets/mybucket')",
'title':
'debug bucket writer'
}]),
("info", "google_bigquery_dataset_iam_member",
"roles/bigquery.dataEditor", []),
("notice", "google_pubsub_topic_iam_member", "roles/pubsub.publisher",
[]),
("warning", "google_storage_bucket_iam_member",
"roles/storage.objectCreator", []),
]
exclusions = [(r["index"], r["values"]["exclusions"]) for r in sinks]
assert sorted(exclusions) == [
("debug", [{
"description": None,
"disabled": False,
"filter": "logName:compute",
"name": "no-compute"
}, {
"description": None,
"disabled": False,
"filter": "logName:container",
"name": "no-container"
}]),
("info", []),
("notice", []),
("warning", []),
]
def test_exclusions(plan_runner):
"Test folder-level logging exclusions."
logging_exclusions = ("{"
'exclusion1 = "resource.type=gce_instance", '
'exclusion2 = "severity=NOTICE", '
"}")
_, resources = plan_runner(logging_exclusions=logging_exclusions)
assert len(resources) == 3
exclusions = [
r for r in resources if r["type"] == "google_logging_folder_exclusion"
]
assert sorted([r["index"] for r in exclusions]) == [
"exclusion1",
"exclusion2",
]
values = [(r["index"], r["values"]["filter"]) for r in exclusions]
assert sorted(values) == [
("exclusion1", "resource.type=gce_instance"),
("exclusion2", "severity=NOTICE"),
]

View File

@ -12,33 +12,18 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from .validate_policies import validate_policy_boolean, validate_policy_list import pytest
_params = ['boolean', 'list']
def test_policy_boolean(plan_runner): @pytest.mark.parametrize('policy_type', _params)
"Test boolean org policy." def test_policy_factory(plan_summary, tfvars_to_yaml, tmp_path, policy_type):
tfvars = 'test.orgpolicies-boolean.tfvars'
_, resources = plan_runner(tf_var_file=tfvars)
validate_policy_boolean(resources)
def test_policy_list(plan_runner):
"Test list org policy."
tfvars = 'test.orgpolicies-list.tfvars'
_, resources = plan_runner(tf_var_file=tfvars)
validate_policy_list(resources)
def test_factory_policy_boolean(plan_runner, tfvars_to_yaml, tmp_path):
dest = tmp_path / 'policies.yaml' dest = tmp_path / 'policies.yaml'
tfvars_to_yaml('fixture/test.orgpolicies-boolean.tfvars', dest, tfvars_to_yaml(f'org_policies_{policy_type}.tfvars', dest, 'org_policies')
'org_policies') tfvars_plan = plan_summary(
_, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"') 'modules/folder',
validate_policy_boolean(resources) tf_var_files=['common.tfvars', f'org_policies_{policy_type}.tfvars'])
yaml_plan = plan_summary('modules/folder', tf_var_files=['common.tfvars'],
org_policies_data_path=f'{tmp_path}')
def test_factory_policy_list(plan_runner, tfvars_to_yaml, tmp_path): assert tfvars_plan.values == yaml_plan.values
dest = tmp_path / 'policies.yaml'
tfvars_to_yaml('fixture/test.orgpolicies-list.tfvars', dest, 'org_policies')
_, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"')
validate_policy_list(resources)

View File

@ -1,158 +0,0 @@
# Copyright 2022 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 validate_policy_boolean(resources):
assert len(resources) == 3
policies = [r for r in resources if r['type'] == 'google_org_policy_policy']
assert len(policies) == 2
p1 = [
r['values']['spec'][0]
for r in policies
if r['index'] == 'iam.disableServiceAccountKeyCreation'
][0]
assert p1['inherit_from_parent'] is None
assert p1['reset'] is None
assert p1['rules'] == [{
'allow_all': None,
'condition': [],
'deny_all': None,
'enforce': 'TRUE',
'values': []
}]
p2 = [
r['values']['spec'][0]
for r in policies
if r['index'] == 'iam.disableServiceAccountKeyUpload'
][0]
assert p2['inherit_from_parent'] is None
assert p2['reset'] is None
assert len(p2['rules']) == 2
assert p2['rules'][0] == {
'allow_all': None,
'condition': [],
'deny_all': None,
'enforce': 'FALSE',
'values': []
}
assert p2['rules'][1] == {
'allow_all': None,
'condition': [{
'description': 'test condition',
'expression': 'resource.matchTagId(aa, bb)',
'location': 'xxx',
'title': 'condition'
}],
'deny_all': None,
'enforce': 'TRUE',
'values': []
}
def validate_policy_list(resources):
assert len(resources) == 4
policies = [r for r in resources if r['type'] == 'google_org_policy_policy']
assert len(policies) == 3
p1 = [
r['values']['spec'][0]
for r in policies
if r['index'] == 'compute.vmExternalIpAccess'
][0]
assert p1['inherit_from_parent'] is None
assert p1['reset'] is None
assert p1['rules'] == [{
'allow_all': None,
'condition': [],
'deny_all': 'TRUE',
'enforce': None,
'values': []
}]
p2 = [
r['values']['spec'][0]
for r in policies
if r['index'] == 'iam.allowedPolicyMemberDomains'
][0]
assert p2['inherit_from_parent'] is None
assert p2['reset'] is None
assert p2['rules'] == [{
'allow_all':
None,
'condition': [],
'deny_all':
None,
'enforce':
None,
'values': [{
'allowed_values': [
'C0xxxxxxx',
'C0yyyyyyy',
],
'denied_values': None
}]
}]
p3 = [
r['values']['spec'][0]
for r in policies
if r['index'] == 'compute.restrictLoadBalancerCreationForTypes'
][0]
assert p3['inherit_from_parent'] is None
assert p3['reset'] is None
assert len(p3['rules']) == 3
assert p3['rules'][0] == {
'allow_all': None,
'condition': [],
'deny_all': None,
'enforce': None,
'values': [{
'allowed_values': None,
'denied_values': ['in:EXTERNAL']
}]
}
assert p3['rules'][1] == {
'allow_all': None,
'condition': [{
'description': 'test condition',
'expression': 'resource.matchTagId(aa, bb)',
'location': 'xxx',
'title': 'condition'
}],
'deny_all': None,
'enforce': None,
'values': [{
'allowed_values': ['EXTERNAL_1'],
'denied_values': None
}]
}
assert p3['rules'][2] == {
'allow_all': 'TRUE',
'condition': [{
'description': 'test condition2',
'expression': 'resource.matchTagId(cc, dd)',
'location': 'xxx',
'title': 'condition2'
}],
'deny_all': None,
'enforce': None,
'values': []
}