Refactor vpc-sc support in project module, add support for dry run (#2229)
This commit is contained in:
parent
0454fd681d
commit
309792c559
|
@ -390,10 +390,10 @@ update_rules:
|
|||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [factories_config](variables.tf#L91) | Path to folder with YAML resource description data files. | <code title="object({ hierarchy = optional(object({ folders_data_path = string parent_ids = optional(map(string), {}) })) projects_data_path = optional(string) budgets = optional(object({ billing_account = string budgets_data_path = string notification_channels = optional(map(any), {}) })) })">object({…})</code> | ✓ | |
|
||||
| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | <code title="object({ billing_account = optional(string) contacts = optional(map(list(string)), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) parent = optional(string) prefix = optional(string) service_encryption_key_ids = optional(map(list(string)), {}) service_perimeter_bridges = optional(list(string), []) service_perimeter_standard = optional(string) services = optional(list(string), []) shared_vpc_service_config = optional(object({ host_project = string network_users = optional(list(string), []) service_identity_iam = optional(map(list(string)), {}) service_identity_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) }), { host_project = null }) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [data_merges](variables.tf#L49) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code title="object({ contacts = optional(map(list(string)), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) service_encryption_key_ids = optional(map(list(string)), {}) service_perimeter_bridges = optional(list(string), []) services = optional(list(string), []) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [data_overrides](variables.tf#L69) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object({ billing_account = optional(string) contacts = optional(map(list(string))) parent = optional(string) prefix = optional(string) service_encryption_key_ids = optional(map(list(string))) service_perimeter_bridges = optional(list(string)) service_perimeter_standard = optional(string) tag_bindings = optional(map(string)) services = optional(list(string)) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) }))) })">object({…})</code> | | <code>{}</code> |
|
||||
| [factories_config](variables.tf#L96) | Path to folder with YAML resource description data files. | <code title="object({ hierarchy = optional(object({ folders_data_path = string parent_ids = optional(map(string), {}) })) projects_data_path = optional(string) budgets = optional(object({ billing_account = string budgets_data_path = string notification_channels = optional(map(any), {}) })) })">object({…})</code> | ✓ | |
|
||||
| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | <code title="object({ billing_account = optional(string) contacts = optional(map(list(string)), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) parent = optional(string) prefix = optional(string) service_encryption_key_ids = optional(map(list(string)), {}) services = optional(list(string), []) shared_vpc_service_config = optional(object({ host_project = string network_users = optional(list(string), []) service_identity_iam = optional(map(list(string)), {}) service_identity_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) }), { host_project = null }) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) vpc_sc = optional(object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })) })">object({…})</code> | | <code>{}</code> |
|
||||
| [data_merges](variables.tf#L52) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code title="object({ contacts = optional(map(list(string)), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) service_encryption_key_ids = optional(map(list(string)), {}) services = optional(list(string), []) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [data_overrides](variables.tf#L71) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object({ billing_account = optional(string) contacts = optional(map(list(string))) parent = optional(string) prefix = optional(string) service_encryption_key_ids = optional(map(list(string))) tag_bindings = optional(map(string)) services = optional(list(string)) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) }))) vpc_sc = optional(object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })) })">object({…})</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -82,16 +82,6 @@ locals {
|
|||
try(v.service_encryption_key_ids, null),
|
||||
var.data_defaults.service_encryption_key_ids
|
||||
)
|
||||
service_perimeter_bridges = coalesce(
|
||||
var.data_overrides.service_perimeter_bridges,
|
||||
try(v.service_perimeter_bridges, null),
|
||||
var.data_defaults.service_perimeter_bridges
|
||||
)
|
||||
service_perimeter_standard = try(coalesce(
|
||||
var.data_overrides.service_perimeter_standard,
|
||||
try(v.service_perimeter_standard, null),
|
||||
var.data_defaults.service_perimeter_standard
|
||||
), null)
|
||||
services = coalesce(
|
||||
var.data_overrides.services,
|
||||
try(v.services, null),
|
||||
|
@ -116,6 +106,11 @@ locals {
|
|||
try(v.tag_bindings, null),
|
||||
var.data_defaults.tag_bindings
|
||||
)
|
||||
vpc_sc = (
|
||||
var.data_overrides.vpc_sc != null
|
||||
? var.data_overrides.vpc_sc
|
||||
: try(v.vpc_sc, var.data_defaults.vpc_sc, null)
|
||||
)
|
||||
# non-project resources
|
||||
service_accounts = try(v.service_accounts, {})
|
||||
})
|
||||
|
|
|
@ -77,11 +77,6 @@ module "projects" {
|
|||
each.value.service_encryption_key_ids,
|
||||
var.data_merges.service_encryption_key_ids
|
||||
)
|
||||
service_perimeter_bridges = distinct(concat(
|
||||
each.value.service_perimeter_bridges,
|
||||
var.data_merges.service_perimeter_bridges
|
||||
))
|
||||
service_perimeter_standard = each.value.service_perimeter_standard
|
||||
services = distinct(concat(
|
||||
each.value.services,
|
||||
var.data_merges.services
|
||||
|
@ -91,6 +86,7 @@ module "projects" {
|
|||
each.value.tag_bindings,
|
||||
var.data_merges.tag_bindings
|
||||
)
|
||||
vpc_sc = each.value.vpc_sc
|
||||
}
|
||||
|
||||
module "service-accounts" {
|
||||
|
|
|
@ -24,8 +24,6 @@ variable "data_defaults" {
|
|||
parent = optional(string)
|
||||
prefix = optional(string)
|
||||
service_encryption_key_ids = optional(map(list(string)), {})
|
||||
service_perimeter_bridges = optional(list(string), [])
|
||||
service_perimeter_standard = optional(string)
|
||||
services = optional(list(string), [])
|
||||
shared_vpc_service_config = optional(object({
|
||||
host_project = string
|
||||
|
@ -41,6 +39,11 @@ variable "data_defaults" {
|
|||
display_name = optional(string, "Terraform-managed.")
|
||||
iam_self_roles = optional(list(string))
|
||||
})), {})
|
||||
vpc_sc = optional(object({
|
||||
perimeter_name = string
|
||||
perimeter_bridges = optional(list(string), [])
|
||||
is_dry_run = optional(bool, false)
|
||||
}))
|
||||
})
|
||||
nullable = false
|
||||
default = {}
|
||||
|
@ -53,7 +56,6 @@ variable "data_merges" {
|
|||
labels = optional(map(string), {})
|
||||
metric_scopes = optional(list(string), [])
|
||||
service_encryption_key_ids = optional(map(list(string)), {})
|
||||
service_perimeter_bridges = optional(list(string), [])
|
||||
services = optional(list(string), [])
|
||||
tag_bindings = optional(map(string), {})
|
||||
# non-project resources
|
||||
|
@ -74,8 +76,6 @@ variable "data_overrides" {
|
|||
parent = optional(string)
|
||||
prefix = optional(string)
|
||||
service_encryption_key_ids = optional(map(list(string)))
|
||||
service_perimeter_bridges = optional(list(string))
|
||||
service_perimeter_standard = optional(string)
|
||||
tag_bindings = optional(map(string))
|
||||
services = optional(list(string))
|
||||
# non-project resources
|
||||
|
@ -83,6 +83,11 @@ variable "data_overrides" {
|
|||
display_name = optional(string, "Terraform-managed.")
|
||||
iam_self_roles = optional(list(string))
|
||||
})))
|
||||
vpc_sc = optional(object({
|
||||
perimeter_name = string
|
||||
perimeter_bridges = optional(list(string), [])
|
||||
is_dry_run = optional(bool, false)
|
||||
}))
|
||||
})
|
||||
nullable = false
|
||||
default = {}
|
||||
|
|
|
@ -24,6 +24,7 @@ This module implements the creation and management of one GCP project including
|
|||
- [Custom Roles Factory](#custom-roles-factory)
|
||||
- [Quotas](#quotas)
|
||||
- [Quotas factory](#quotas-factory)
|
||||
- [VPC Service Controls](#vpc-service-controls)
|
||||
- [Outputs](#outputs)
|
||||
- [Managing project related configuration without creating it](#managing-project-related-configuration-without-creating-it)
|
||||
- [Files](#files)
|
||||
|
@ -802,6 +803,7 @@ includedPermissions:
|
|||
## Quotas
|
||||
|
||||
Project and regional quotas can be managed via the `quotas` variable. Keep in mind, that metrics returned by `gcloud compute regions describe` do not match `quota_id`s. To get a list of quotas in the project use the API call, for example to get quotas for `compute.googleapis.com` use:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
|
@ -874,6 +876,52 @@ cpus-ew8:
|
|||
region: europe-west8
|
||||
```
|
||||
|
||||
## VPC Service Controls
|
||||
|
||||
This module also allows managing project membership in VPC Service Controls perimeters. When using this functionality care should be taken so that perimeter management (e.g. via the `vpc-sc` module) does not try reconciling resources, to avoid permadiffs and related violations.
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./fabric/modules/project"
|
||||
billing_account = var.billing_account_id
|
||||
name = "project"
|
||||
parent = var.folder_id
|
||||
prefix = var.prefix
|
||||
services = [
|
||||
"container.googleapis.com",
|
||||
"stackdriver.googleapis.com"
|
||||
]
|
||||
vpc_sc = {
|
||||
perimeter_name = "accessPolicies/1234567890/servicePerimeters/default"
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=4 inventory=vpc-sc.yaml
|
||||
```
|
||||
|
||||
Perimeter bridges and dry run configuration are also supported.
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./fabric/modules/project"
|
||||
billing_account = var.billing_account_id
|
||||
name = "project"
|
||||
parent = var.folder_id
|
||||
prefix = var.prefix
|
||||
services = [
|
||||
"container.googleapis.com",
|
||||
"stackdriver.googleapis.com"
|
||||
]
|
||||
vpc_sc = {
|
||||
perimeter_name = "accessPolicies/1234567890/servicePerimeters/default"
|
||||
perimeter_bridges = [
|
||||
"accessPolicies/1234567890/servicePerimeters/b1",
|
||||
"accessPolicies/1234567890/servicePerimeters/b2",
|
||||
]
|
||||
is_dry_run = true
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=6
|
||||
```
|
||||
|
||||
## Outputs
|
||||
|
||||
|
@ -1131,7 +1179,7 @@ module "bucket" {
|
|||
| [variables-tags.tf](./variables-tags.tf) | None | |
|
||||
| [variables.tf](./variables.tf) | Module variables. | |
|
||||
| [versions.tf](./versions.tf) | Version pins. | |
|
||||
| [vpc-sc.tf](./vpc-sc.tf) | VPC-SC project-level perimeter configuration. | <code>google_access_context_manager_service_perimeter_resource</code> |
|
||||
| [vpc-sc.tf](./vpc-sc.tf) | VPC-SC project-level perimeter configuration. | <code>google_access_context_manager_service_perimeter_dry_run_resource</code> · <code>google_access_context_manager_service_perimeter_resource</code> |
|
||||
|
||||
## Variables
|
||||
|
||||
|
@ -1164,14 +1212,13 @@ module "bucket" {
|
|||
| [quotas](variables-quotas.tf#L17) | Service quota configuration. | <code title="map(object({ service = string quota_id = string preferred_value = number dimensions = optional(map(string), {}) justification = optional(string) contact_email = optional(string) annotations = optional(map(string)) ignore_safety_checks = optional(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [service_config](variables.tf#L211) | Configure service API activation. | <code title="object({ disable_on_destroy = bool disable_dependent_services = bool })">object({…})</code> | | <code title="{ disable_on_destroy = false disable_dependent_services = false }">{…}</code> |
|
||||
| [service_encryption_key_ids](variables.tf#L223) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [service_perimeter_bridges](variables.tf#L230) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list(string)</code> | | <code>null</code> |
|
||||
| [service_perimeter_standard](variables.tf#L237) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | <code>string</code> | | <code>null</code> |
|
||||
| [services](variables.tf#L243) | Service APIs to enable. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [shared_vpc_host_config](variables.tf#L249) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object({ enabled = bool service_projects = optional(list(string), []) })">object({…})</code> | | <code>null</code> |
|
||||
| [shared_vpc_service_config](variables.tf#L258) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object({ host_project = string network_users = optional(list(string), []) service_identity_iam = optional(map(list(string)), {}) service_identity_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) })">object({…})</code> | | <code title="{ host_project = null }">{…}</code> |
|
||||
| [skip_delete](variables.tf#L286) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
|
||||
| [services](variables.tf#L229) | Service APIs to enable. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [shared_vpc_host_config](variables.tf#L235) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object({ enabled = bool service_projects = optional(list(string), []) })">object({…})</code> | | <code>null</code> |
|
||||
| [shared_vpc_service_config](variables.tf#L244) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object({ host_project = string network_users = optional(list(string), []) service_identity_iam = optional(map(list(string)), {}) service_identity_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) })">object({…})</code> | | <code title="{ host_project = null }">{…}</code> |
|
||||
| [skip_delete](variables.tf#L272) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
|
||||
| [tag_bindings](variables-tags.tf#L45) | Tag bindings for this project, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [tags](variables-tags.tf#L51) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | <code title="map(object({ description = optional(string, "Managed by the Terraform project module.") iam = optional(map(list(string)), {}) id = optional(string) values = optional(map(object({ description = optional(string, "Managed by the Terraform project module.") iam = optional(map(list(string)), {}) id = optional(string) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [vpc_sc](variables.tf#L278) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | <code title="object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })">object({…})</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -226,20 +226,6 @@ variable "service_encryption_key_ids" {
|
|||
default = {}
|
||||
}
|
||||
|
||||
# accessPolicies/ACCESS_POLICY_NAME/servicePerimeters/PERIMETER_NAME
|
||||
variable "service_perimeter_bridges" {
|
||||
description = "Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format."
|
||||
type = list(string)
|
||||
default = null
|
||||
}
|
||||
|
||||
# accessPolicies/ACCESS_POLICY_NAME/servicePerimeters/PERIMETER_NAME
|
||||
variable "service_perimeter_standard" {
|
||||
description = "Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "services" {
|
||||
description = "Service APIs to enable."
|
||||
type = list(string)
|
||||
|
@ -288,3 +274,13 @@ variable "skip_delete" {
|
|||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "vpc_sc" {
|
||||
description = "VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module."
|
||||
type = object({
|
||||
perimeter_name = string
|
||||
perimeter_bridges = optional(list(string), [])
|
||||
is_dry_run = optional(bool, false)
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
|
|
@ -16,30 +16,28 @@
|
|||
|
||||
# tfdoc:file:description VPC-SC project-level perimeter configuration.
|
||||
|
||||
moved {
|
||||
from = google_access_context_manager_service_perimeter_resource.service-perimeter-resource-standard
|
||||
to = google_access_context_manager_service_perimeter_resource.standard
|
||||
locals {
|
||||
vpc_sc_perimeters = compact(concat(
|
||||
[try(var.vpc_sc.perimeter_name, null)],
|
||||
try(var.vpc_sc.perimeter_bridges, [])
|
||||
))
|
||||
vpc_sc_dry_run = try(var.vpc_sc.is_dry_run, false) == true
|
||||
}
|
||||
|
||||
resource "google_access_context_manager_service_perimeter_resource" "standard" {
|
||||
count = var.service_perimeter_standard != null ? 1 : 0
|
||||
# this needs an additional lifecycle block in the vpc module on the
|
||||
# google_access_context_manager_service_perimeter resource
|
||||
perimeter_name = var.service_perimeter_standard
|
||||
resource = "projects/${local.project.number}"
|
||||
}
|
||||
# use only if the vpc-sc module has a lifecycle block to ignore resources
|
||||
|
||||
moved {
|
||||
from = google_access_context_manager_service_perimeter_resource.service-perimeter-resource-bridges
|
||||
to = google_access_context_manager_service_perimeter_resource.bridge
|
||||
}
|
||||
|
||||
resource "google_access_context_manager_service_perimeter_resource" "bridge" {
|
||||
resource "google_access_context_manager_service_perimeter_resource" "default" {
|
||||
for_each = toset(
|
||||
var.service_perimeter_bridges != null ? var.service_perimeter_bridges : []
|
||||
local.vpc_sc_dry_run ? [] : local.vpc_sc_perimeters
|
||||
)
|
||||
# this needs an additional lifecycle block in the vpc module on the
|
||||
# google_access_context_manager_service_perimeter resource
|
||||
perimeter_name = each.value
|
||||
perimeter_name = each.key
|
||||
resource = "projects/${local.project.number}"
|
||||
}
|
||||
|
||||
resource "google_access_context_manager_service_perimeter_dry_run_resource" "default" {
|
||||
for_each = toset(
|
||||
local.vpc_sc_dry_run ? local.vpc_sc_perimeters : []
|
||||
)
|
||||
perimeter_name = each.key
|
||||
resource = "projects/${local.project.number}"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# 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.project.google_access_context_manager_service_perimeter_dry_run_resource.default["accessPolicies/1234567890/servicePerimeters/b1"]
|
||||
: perimeter_name: accessPolicies/1234567890/servicePerimeters/b1
|
||||
timeouts: null
|
||||
? module.project.google_access_context_manager_service_perimeter_dry_run_resource.default["accessPolicies/1234567890/servicePerimeters/b2"]
|
||||
: perimeter_name: accessPolicies/1234567890/servicePerimeters/b2
|
||||
timeouts: null
|
||||
? module.project.google_access_context_manager_service_perimeter_dry_run_resource.default["accessPolicies/1234567890/servicePerimeters/default"]
|
||||
: perimeter_name: accessPolicies/1234567890/servicePerimeters/default
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_access_context_manager_service_perimeter_dry_run_resource: 3
|
||||
google_project: 1
|
||||
google_project_service: 2
|
||||
modules: 1
|
||||
resources: 6
|
||||
|
||||
outputs: {}
|
|
@ -0,0 +1,27 @@
|
|||
# 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.project.google_access_context_manager_service_perimeter_resource.default["accessPolicies/1234567890/servicePerimeters/default"]
|
||||
: perimeter_name: accessPolicies/1234567890/servicePerimeters/default
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_access_context_manager_service_perimeter_resource: 1
|
||||
google_project: 1
|
||||
google_project_service: 2
|
||||
modules: 1
|
||||
resources: 4
|
||||
|
||||
outputs: {}
|
Loading…
Reference in New Issue