VPC-SC module factories (#2081)

* factory untested

* factory example test
This commit is contained in:
Ludovico Magnocavallo 2024-02-17 10:02:16 +03:00 committed by GitHub
parent b408494a74
commit 91615e0140
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 322 additions and 15 deletions

View File

@ -6,6 +6,23 @@ Given the complexity of the underlying resources, the module intentionally mimic
If you are using [Application Default Credentials](https://cloud.google.com/sdk/gcloud/reference/auth/application-default) with Terraform and run into permissions issues, make sure to check out the recommended provider configuration in the [VPC SC resources documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_access_level).
<!-- BEGIN TOC -->
- [Examples](#examples)
- [Access policy](#access-policy)
- [Scoped policy](#scoped-policy)
- [Access policy IAM](#access-policy-iam)
- [Access levels](#access-levels)
- [Service perimeters](#service-perimeters)
- [Bridge type](#bridge-type)
- [Regular type](#regular-type)
- [Factories](#factories)
- [Notes](#notes)
- [TODO](#todo)
- [Files](#files)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
## Examples
### Access policy
@ -194,6 +211,83 @@ module "test" {
# tftest modules=1 resources=3 inventory=regular.yaml
```
## 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 is an example that uses all three factories. Note that the factory configuration points to folders, where each file represents one resource.
```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"
}
service_perimeters_regular = {
r1 = {
status = {
access_levels = ["geo-it", "identity-user1"]
resources = ["projects/11111", "projects/111111"]
restricted_services = ["storage.googleapis.com"]
egress_policies = ["gcs-sa-foo"]
ingress_policies = ["sa-tf-test"]
vpc_accessible_services = {
allowed_services = ["storage.googleapis.com"]
enable_restriction = true
}
}
}
}
}
# tftest modules=1 resources=3 files=a1,a2,e1,i1 inventory=factory.yaml
```
```yaml
conditions:
- members:
- user:user1@example.com
# tftest-file id=a1 path=data/access-levels/identity-user1.yaml
```
```yaml
conditions:
- regions:
- IT
# tftest-file id=a2 path=data/access-levels/geo-it.yaml
```
```yaml
from:
identities:
- serviceAccount:foo@myproject.iam.gserviceaccount.com
to:
operations:
- method_selectors:
- "*"
service_name: storage.googleapis.com
resources:
- projects/123456789
# tftest-file id=e1 path=data/egress-policies/gcs-sa-foo.yaml
```
```yaml
from:
access_levels:
- "*"
identities:
- serviceAccount:test-tf@myproject.iam.gserviceaccount.com
to:
operations:
- service_name: "*"
resources:
- "*"
# tftest-file id=i1 path=data/ingress-policies/sa-tf-test.yaml
```
## 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.
@ -209,6 +303,7 @@ module "test" {
| name | description | resources |
|---|---|---|
| [access-levels.tf](./access-levels.tf) | Access level resources. | <code>google_access_context_manager_access_level</code> |
| [factory.tf](./factory.tf) | None | |
| [iam.tf](./iam.tf) | IAM bindings | <code>google_access_context_manager_access_policy_iam_binding</code> · <code>google_access_context_manager_access_policy_iam_member</code> |
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_access_context_manager_access_policy</code> |
| [outputs.tf](./outputs.tf) | Module outputs. | |
@ -225,12 +320,13 @@ module "test" {
| [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; &#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#L61) | 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#L71) | Egress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#10; from &#61; object&#40;&#123;&#10; identity_type &#61; optional&#40;string&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#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; resource_type_external &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L102) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L108) | 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#L123) | 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#L138) | Ingress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#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#L170) | 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#L180) | Regular service perimeters. | <code title="map&#40;object&#40;&#123;&#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; bool&#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> |
| [factories_config](variables.tf#L102) | 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;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L113) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L119) | 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#L134) | 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#L149) | Ingress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#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#L181) | 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#L191) | Regular service perimeters. | <code title="map&#40;object&#40;&#123;&#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; bool&#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> |
## Outputs

View File

@ -21,7 +21,7 @@
# google_access_context_manager_access_levels resource
resource "google_access_context_manager_access_level" "basic" {
for_each = var.access_levels
for_each = merge(local.data.access_levels, var.access_levels)
parent = "accessPolicies/${local.access_policy}"
name = "accessPolicies/${local.access_policy}/accessLevels/${each.key}"
title = each.key

89
modules/vpc-sc/factory.tf Normal file
View File

@ -0,0 +1,89 @@
/**
* Copyright 2024 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 {
_data = {
for k, v in local._data_paths : k => {
for f in try(fileset(v, "**/*.yaml"), []) :
trimsuffix(f, ".yaml") => yamldecode(file("${v}/${f}"))
}
}
_data_paths = {
for k in ["access_levels", "egress_policies", "ingress_policies"] : k => (
var.factories_config[k] == null
? null
: pathexpand(var.factories_config[k])
)
}
data = {
access_levels = {
for k, v in local._data.access_levels : k => {
combining_function = try(v.combining_function, null)
description = try(v.description, null)
conditions = [
for c in try(v.conditions, []) : merge({
device_policy = null
ip_subnetworks = []
members = []
negate = null
regions = []
required_access_levels = []
}, c)
]
}
}
egress_policies = {
for k, v in local._data.egress_policies : k => {
from = merge({
identity_type = null
identities = null
}, try(v.from, {}))
to = {
operations = [
for o in try(v.to.operations, []) : merge({
method_selectors = []
permission_selectors = []
service_name = null
}, o)
]
resources = try(v.to.resources, [])
resource_type_external = try(v.to.resource_type_external, false)
}
}
}
ingress_policies = {
for k, v in local._data.ingress_policies : k => {
from = merge({
access_levels = []
identity_type = null
identities = null
resources = []
}, try(v.from, {}))
to = {
operations = [
for o in try(v.operations, []) : merge({
method_selectors = []
permission_selectors = []
service_name = null
}, o)
]
resources = try(v.to.resources, [])
}
}
}
}
# TODO: add checks that emulate the variable validations
}

View File

@ -20,6 +20,11 @@
# service perimeters are needed, switch to the
# google_access_context_manager_service_perimeters resource
locals {
egress_policies = merge(local.data.egress_policies, var.egress_policies)
ingress_policies = merge(local.data.ingress_policies, var.ingress_policies)
}
resource "google_access_context_manager_service_perimeter" "regular" {
for_each = var.service_perimeters_regular
parent = "accessPolicies/${local.access_policy}"
@ -43,8 +48,8 @@ resource "google_access_context_manager_service_perimeter" "regular" {
dynamic "egress_policies" {
for_each = spec.value.egress_policies == null ? {} : {
for k in spec.value.egress_policies :
k => lookup(var.egress_policies, k, null)
if contains(keys(var.egress_policies), k)
k => lookup(local.egress_policies, k, null)
if contains(keys(local.egress_policies), k)
}
iterator = policy
content {
@ -86,8 +91,8 @@ resource "google_access_context_manager_service_perimeter" "regular" {
dynamic "ingress_policies" {
for_each = spec.value.ingress_policies == null ? {} : {
for k in spec.value.ingress_policies :
k => lookup(var.ingress_policies, k, null)
if contains(keys(var.ingress_policies), k)
k => lookup(local.ingress_policies, k, null)
if contains(keys(local.ingress_policies), k)
}
iterator = policy
content {
@ -167,8 +172,8 @@ resource "google_access_context_manager_service_perimeter" "regular" {
dynamic "egress_policies" {
for_each = status.value.egress_policies == null ? {} : {
for k in status.value.egress_policies :
k => lookup(var.egress_policies, k, null)
if contains(keys(var.egress_policies), k)
k => lookup(local.egress_policies, k, null)
if contains(keys(local.egress_policies), k)
}
iterator = policy
content {
@ -210,8 +215,8 @@ resource "google_access_context_manager_service_perimeter" "regular" {
dynamic "ingress_policies" {
for_each = status.value.ingress_policies == null ? {} : {
for k in status.value.ingress_policies :
k => lookup(var.ingress_policies, k, null)
if contains(keys(var.ingress_policies), k)
k => lookup(local.ingress_policies, k, null)
if contains(keys(local.ingress_policies), k)
}
iterator = policy
content {

View File

@ -99,6 +99,17 @@ variable "egress_policies" {
}
}
variable "factories_config" {
description = "Paths to folders that enable factory functionality."
type = object({
access_levels = optional(string)
egress_policies = optional(string)
ingress_policies = optional(string)
})
nullable = false
default = {}
}
variable "iam" {
description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(list(string))

View File

@ -0,0 +1,106 @@
# Copyright 2024 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.test.google_access_context_manager_access_level.basic["geo-it"]:
basic:
- combining_function: AND
conditions:
- device_policy: []
ip_subnetworks: []
members: []
negate: null
regions:
- IT
required_access_levels: []
vpc_network_sources: []
custom: []
description: null
name: accessPolicies/12345678/accessLevels/geo-it
parent: accessPolicies/12345678
timeouts: null
title: geo-it
module.test.google_access_context_manager_access_level.basic["identity-user1"]:
basic:
- combining_function: AND
conditions:
- device_policy: []
ip_subnetworks: []
members:
- user:user1@example.com
negate: null
regions: []
required_access_levels: []
vpc_network_sources: []
custom: []
description: null
name: accessPolicies/12345678/accessLevels/identity-user1
parent: accessPolicies/12345678
timeouts: null
title: identity-user1
module.test.google_access_context_manager_service_perimeter.regular["r1"]:
description: null
name: accessPolicies/12345678/servicePerimeters/r1
parent: accessPolicies/12345678
perimeter_type: PERIMETER_TYPE_REGULAR
spec: []
status:
- egress_policies:
- egress_from:
- identities:
- serviceAccount:foo@myproject.iam.gserviceaccount.com
identity_type: null
source_restriction: null
sources: []
egress_to:
- external_resources: null
operations:
- method_selectors:
- method: '*'
permission: null
service_name: storage.googleapis.com
resources:
- projects/123456789
ingress_policies:
- ingress_from:
- identities:
- serviceAccount:test-tf@myproject.iam.gserviceaccount.com
identity_type: null
sources:
- access_level: '*'
resource: null
ingress_to:
- operations: []
resources:
- '*'
resources:
- projects/11111
- projects/111111
restricted_services:
- storage.googleapis.com
vpc_accessible_services:
- allowed_services:
- storage.googleapis.com
enable_restriction: true
timeouts: null
title: r1
use_explicit_dry_run_spec: false
counts:
google_access_context_manager_access_level: 2
google_access_context_manager_service_perimeter: 1
modules: 1
resources: 3
outputs: {}