Add perimeter factory to modules/vpc-sc (#2919)

* Add perimeter factory

Proposal to allow the management of perimeters in yaml factory.
Project discovery is extended to multiple perimeters with query filtering.

Doc clean uo

* default perimeters desc to null

* linting

* Revert changes to FAST

* Remove test and schema from FAST

* Align vars and perimeter factory

* Interpolate resource_sets in more places

* Silence linter

* Update README.md

---------

Co-authored-by: Julio Castillo <jccb@google.com>
Co-authored-by: Ludovico Magnocavallo <ludo@qix.it>
This commit is contained in:
karpok78
2025-02-22 07:49:05 +01:00
committed by GitHub
parent 16727ded25
commit 07a70eedb5
10 changed files with 326 additions and 67 deletions

View File

@@ -198,7 +198,7 @@ module "test" {
r1 = {
status = {
access_levels = ["a1", "a2"]
resources = ["projects/11111", "projects/111111"]
resources = ["projects/1111", "projects/2222"]
restricted_services = ["storage.googleapis.com"]
egress_policies = ["gcs-sa-foo"]
ingress_policies = ["sa-tf-test"]
@@ -215,11 +215,11 @@ module "test" {
## Factories
This module implements support for three distinct factories, used to create and manage access levels, egress policies and ingress policies via YAML files. The YAML files syntax is a 1:1 match for the corresponding variables, and the factory data is merged at runtime with any data set in variables, which take precedence in case of key overlaps.
This module implements support for five distinct factories, used to create and manage perimeters, bridges, access levels, egress policies, and ingress policies via YAML files.
JSON Schema files for each factory object are available in the [`schemas`](./schemas/) folder, and can be used to validate input YAML data with [`validate-yaml`](https://github.com/gerald1248/validate-yaml) or any of the available tools and libraries.
This is an example that uses all three factories. Note that the factory configuration points to folders, where each file represents one resource.
This is an example that uses only three factories and leave perimeter management in tfvars. Note that the factory configuration points to folders, where each file represents one resource.
```hcl
module "test" {
@@ -229,12 +229,18 @@ module "test" {
access_levels = "data/access-levels"
egress_policies = "data/egress-policies"
ingress_policies = "data/ingress-policies"
context = {
resource_sets = {
foo_projects = ["projects/321", "projects/654"]
}
}
}
service_perimeters_regular = {
r1 = {
perimeter-north = {
description = "Main perimeter"
status = {
access_levels = ["geo-it", "identity-user1"]
resources = ["projects/11111", "projects/111111"]
resources = ["projects/1111", "projects/2222"]
restricted_services = ["storage.googleapis.com"]
egress_policies = ["gcs-sa-foo"]
ingress_policies = ["sa-tf-test-geo", "sa-tf-test"]
@@ -308,9 +314,55 @@ to:
- service_name: "*"
resources:
- projects/1234567890
- foo_projects
# tftest-file id=i2 path=data/ingress-policies/sa-tf-test-geo.yaml schema=ingress-policy.schema.json
```
But perimeters could also defined in a yaml file.
```hcl
module "test" {
source = "./fabric/modules/vpc-sc"
access_policy = "12345678"
factories_config = {
access_levels = "data/access-levels"
egress_policies = "data/egress-policies"
ingress_policies = "data/ingress-policies"
perimeters = "data/perimeters"
context = {
resource_sets = {
foo_projects = ["projects/321", "projects/654"]
}
}
}
}
# tftest modules=1 resources=3 files=a1,a2,e1,i1,i2,r1 inventory=factory.yaml
```
```yaml
description: Main perimeter
status:
access_levels:
- "geo-it"
- "identity-user1"
resources:
- "projects/1111"
- "projects/2222"
restricted_services:
- "storage.googleapis.com"
egress_policies:
- "gcs-sa-foo"
ingress_policies:
- "sa-tf-test-geo"
- "sa-tf-test"
vpc_accessible_services:
allowed_services:
- "storage.googleapis.com"
enable_restriction: yes
# tftest-file id=r1 path=data/perimeters/perimeter-north.yaml schema=perimeters.schema.json
````
## Notes
- To remove an access level, first remove the binding between perimeter and the access level in `status` and/or `spec` without removing the access level itself. Once you have run `terraform apply`, you'll then be able to remove the access level and run `terraform apply` again.
@@ -339,17 +391,17 @@ to:
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [access_policy](variables.tf#L67) | Access Policy name, set to null if creating one. | <code>string</code> | ✓ | |
| [access_levels](variables.tf#L17) | Access level definitions. | <code title="map&#40;object&#40;&#123;&#10; combining_function &#61; optional&#40;string&#41;&#10; conditions &#61; optional&#40;list&#40;object&#40;&#123;&#10; device_policy &#61; optional&#40;object&#40;&#123;&#10; allowed_device_management_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; allowed_encryption_statuses &#61; optional&#40;list&#40;string&#41;&#41;&#10; require_admin_approval &#61; bool&#10; require_corp_owned &#61; bool&#10; require_screen_lock &#61; optional&#40;bool&#41;&#10; os_constraints &#61; optional&#40;list&#40;object&#40;&#123;&#10; os_type &#61; string&#10; minimum_version &#61; optional&#40;string&#41;&#10; require_verified_chrome_os &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#41;&#10; &#125;&#41;&#41;&#10; ip_subnetworks &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; members &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; negate &#61; optional&#40;bool&#41;&#10; regions &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; required_access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; vpc_subnets &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; description &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [access_policy_create](variables.tf#L72) | Access Policy configuration, fill in to create. Parent is in 'organizations/123456' format, scopes are in 'folders/456789' or 'projects/project_id' format. | <code title="object&#40;&#123;&#10; parent &#61; string&#10; title &#61; string&#10; scopes &#61; optional&#40;list&#40;string&#41;, null&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [egress_policies](variables.tf#L82) | Egress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#10; title &#61; optional&#40;string&#41;&#10; from &#61; object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; identity_type &#61; optional&#40;string&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; to &#61; object&#40;&#123;&#10; external_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; operations &#61; optional&#40;list&#40;object&#40;&#123;&#10; method_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; permission_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [factories_config](variables.tf#L124) | Paths to folders that enable factory functionality. | <code title="object&#40;&#123;&#10; access_levels &#61; optional&#40;string&#41;&#10; egress_policies &#61; optional&#40;string&#41;&#10; ingress_policies &#61; optional&#40;string&#41;&#10; restricted_services &#61; optional&#40;string, &#34;data&#47;restricted-services.yaml&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L136) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L142) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L157) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [ingress_policies](variables.tf#L172) | Ingress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#10; title &#61; optional&#40;string&#41;&#10; from &#61; object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; identity_type &#61; optional&#40;string&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; to &#61; object&#40;&#123;&#10; operations &#61; optional&#40;list&#40;object&#40;&#123;&#10; method_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; permission_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeters_bridge](variables.tf#L213) | Bridge service perimeters. | <code title="map&#40;object&#40;&#123;&#10; spec_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; status_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_explicit_dry_run_spec &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeters_regular](variables.tf#L223) | Regular service perimeters. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; spec &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; restricted_services &#61; optional&#40;list&#40;string&#41;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; vpc_accessible_services &#61; optional&#40;object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; status &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; restricted_services &#61; optional&#40;list&#40;string&#41;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; vpc_accessible_services &#61; optional&#40;object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; bool&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; use_explicit_dry_run_spec &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [access_policy](variables.tf#L68) | Access Policy name, set to null if creating one. | <code>string</code> | ✓ | |
| [access_levels](variables.tf#L17) | Access level definitions. | <code title="map&#40;object&#40;&#123;&#10; combining_function &#61; optional&#40;string&#41;&#10; conditions &#61; optional&#40;list&#40;object&#40;&#123;&#10; device_policy &#61; optional&#40;object&#40;&#123;&#10; allowed_device_management_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; allowed_encryption_statuses &#61; optional&#40;list&#40;string&#41;&#41;&#10; require_admin_approval &#61; bool&#10; require_corp_owned &#61; bool&#10; require_screen_lock &#61; optional&#40;bool&#41;&#10; os_constraints &#61; optional&#40;list&#40;object&#40;&#123;&#10; os_type &#61; string&#10; minimum_version &#61; optional&#40;string&#41;&#10; require_verified_chrome_os &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#41;&#10; &#125;&#41;&#41;&#10; ip_subnetworks &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; members &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; negate &#61; optional&#40;bool&#41;&#10; regions &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; required_access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; vpc_subnets &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; description &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [access_policy_create](variables.tf#L73) | Access Policy configuration, fill in to create. Parent is in 'organizations/123456' format, scopes are in 'folders/456789' or 'projects/project_id' format. | <code title="object&#40;&#123;&#10; parent &#61; string&#10; title &#61; string&#10; scopes &#61; optional&#40;list&#40;string&#41;, null&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [egress_policies](variables.tf#L83) | Egress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#10; title &#61; optional&#40;string&#41;&#10; from &#61; object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; identity_type &#61; optional&#40;string&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; to &#61; object&#40;&#123;&#10; external_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; operations &#61; optional&#40;list&#40;object&#40;&#123;&#10; method_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; permission_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [factories_config](variables.tf#L125) | Paths to folders that enable factory functionality. | <code title="object&#40;&#123;&#10; access_levels &#61; optional&#40;string&#41;&#10; bridges &#61; optional&#40;string&#41;&#10; egress_policies &#61; optional&#40;string&#41;&#10; ingress_policies &#61; optional&#40;string&#41;&#10; perimeters &#61; optional&#40;string&#41;&#10; context &#61; optional&#40;object&#40;&#123;&#10; resource_sets &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_sets &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L142) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L148) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L163) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [ingress_policies](variables.tf#L178) | Ingress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#10; title &#61; optional&#40;string&#41;&#10; from &#61; object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; identity_type &#61; optional&#40;string&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; to &#61; object&#40;&#123;&#10; operations &#61; optional&#40;list&#40;object&#40;&#123;&#10; method_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; permission_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeters_bridge](variables.tf#L219) | Bridge service perimeters. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; spec_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; status_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_explicit_dry_run_spec &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeters_regular](variables.tf#L231) | Regular service perimeters. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; spec &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; restricted_services &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; vpc_accessible_services &#61; optional&#40;object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; status &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; restricted_services &#61; optional&#40;list&#40;string&#41;&#41;&#10; vpc_accessible_services &#61; optional&#40;object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; use_explicit_dry_run_spec &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs
@@ -392,7 +444,7 @@ module "test" {
default = {
status = {
access_levels = ["geo-it"]
resources = ["projects/11111"]
resources = ["projects/1111"]
egress_policies = ["factory-egress-policy"]
ingress_policies = ["variable-policy", "factory-ingress-policy"]
}