Merge branch 'fast-dev'
This commit is contained in:
@@ -254,7 +254,7 @@ module "cloud_run" {
|
||||
}
|
||||
deletion_protection = false
|
||||
}
|
||||
# tftest modules=4 resources=56 fixtures=fixtures/shared-vpc.tf inventory=service-vpc-access-connector-create-sharedvpc.yaml e2e
|
||||
# tftest modules=4 resources=59 fixtures=fixtures/shared-vpc.tf inventory=service-vpc-access-connector-create-sharedvpc.yaml e2e
|
||||
```
|
||||
|
||||
## Using Customer-Managed Encryption Key
|
||||
@@ -420,7 +420,7 @@ module "cloud_run" {
|
||||
|
||||
## Cloud Run Invoker IAM Disable
|
||||
|
||||
To disables IAM permission check for `run.routes.invoke` for callers of this service set the `invoker_iam_disabled` variable of the module to `true` (default `false`). There should be no requirement to pass the `roles/run.invoker` to the IAM block to enable public access. This allows for the org policy `domain restricted sharing` org policy remain enabled.
|
||||
To disables IAM permission check for `run.routes.invoke` for callers of this service set the `invoker_iam_disabled` variable of the module to `true` (default `false`). There should be no requirement to pass the `roles/run.invoker` to the IAM block to enable public access. This allows for the org policy `domain restricted sharing` org policy remain enabled.
|
||||
|
||||
```hcl
|
||||
module "cloud_run" {
|
||||
|
||||
@@ -249,7 +249,8 @@ module "addresses" {
|
||||
| [ipsec_interconnect_addresses](outputs.tf#L41) | Allocated internal addresses for HA VPN over Cloud Interconnect. | |
|
||||
| [network_attachment_ids](outputs.tf#L49) | IDs of network attachments. | |
|
||||
| [psa_addresses](outputs.tf#L57) | Allocated internal addresses for PSA endpoints. | |
|
||||
| [psc_addresses](outputs.tf#L65) | Allocated internal addresses for PSC endpoints. | |
|
||||
| [psc](outputs.tf#L65) | Allocated resources for PSC endpoints. | |
|
||||
| [psc_addresses](outputs.tf#L99) | Allocated internal addresses for PSC endpoints. | |
|
||||
|
||||
## Fixtures
|
||||
|
||||
|
||||
@@ -62,6 +62,40 @@ output "psa_addresses" {
|
||||
}
|
||||
}
|
||||
|
||||
output "psc" {
|
||||
description = "Allocated resources for PSC endpoints."
|
||||
value = merge(
|
||||
{
|
||||
for k, v in local.global_psc :
|
||||
k => {
|
||||
address = {
|
||||
address = google_compute_global_address.psc[k].address
|
||||
id = google_compute_global_address.psc[k].id
|
||||
name = google_compute_global_address.psc[k].name
|
||||
}
|
||||
forwarding_rule = {
|
||||
id = try(google_compute_global_forwarding_rule.psc_consumer[k].id, null)
|
||||
name = try(google_compute_global_forwarding_rule.psc_consumer[k].name, null)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
for k, v in local.regional_psc :
|
||||
k => {
|
||||
address = {
|
||||
address = google_compute_address.psc[k].address
|
||||
id = google_compute_address.psc[k].id
|
||||
name = google_compute_address.psc[k].name
|
||||
}
|
||||
forwarding_rule = {
|
||||
id = try(google_compute_forwarding_rule.psc_consumer[k].id, null)
|
||||
name = try(google_compute_forwarding_rule.psc_consumer[k].name, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
output "psc_addresses" {
|
||||
description = "Allocated internal addresses for PSC endpoints."
|
||||
value = merge(
|
||||
|
||||
@@ -234,7 +234,25 @@ locals {
|
||||
try(v.tag_bindings, null),
|
||||
local.__projects_config.data_defaults.tag_bindings
|
||||
)
|
||||
vpc_sc = ( # type: object
|
||||
tags = {
|
||||
for tag_name, tag_data in try(v.tags, {}) : tag_name => {
|
||||
description = try(tag_data.description, "Managed by the Terraform project-factory module.")
|
||||
id = try(tag_data.id, null)
|
||||
iam = try(tag_data.iam, {})
|
||||
iam_bindings = try(tag_data.iam_bindings, {})
|
||||
iam_bindings_additive = try(tag_data.iam_bindings_additive, {})
|
||||
values = {
|
||||
for value_name, value_data in try(tag_data.values, {}) : value_name => {
|
||||
description = try(value_data.description, "Managed by the Terraform project-factory module.")
|
||||
id = try(value_data.id, null)
|
||||
iam = try(value_data.iam, {})
|
||||
iam_bindings = try(value_data.iam_bindings, {})
|
||||
iam_bindings_additive = try(value_data.iam_bindings_additive, {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vpc_sc = (
|
||||
local.__projects_config.data_overrides.vpc_sc != null
|
||||
? local.__projects_config.data_overrides.vpc_sc
|
||||
: (
|
||||
@@ -255,10 +273,10 @@ locals {
|
||||
})
|
||||
}
|
||||
# tflint-ignore: terraform_unused_declarations
|
||||
_projects_uniqunees_validation = {
|
||||
_projects_uniqueness_validation = {
|
||||
# will raise error, if the same project (derived from file name, or provided in the YAML file)
|
||||
# is sued more than once
|
||||
# is used more than once
|
||||
for k, v in local._projects_output :
|
||||
"${v.prefix}-${v.name}" => k
|
||||
"${v.prefix != null ? v.prefix : ""}-${v.name}" => k
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,10 +95,17 @@ module "vpc" {
|
||||
flow_sampling = 0.5
|
||||
aggregation_interval = "INTERVAL_10_MIN"
|
||||
}
|
||||
},
|
||||
# hybrid subnet
|
||||
{
|
||||
name = "hybrid"
|
||||
region = "europe-west1"
|
||||
ip_cidr_range = "10.0.4.0/24"
|
||||
allow_subnet_cidr_routes_overlap = true
|
||||
}
|
||||
]
|
||||
}
|
||||
# tftest modules=1 resources=8 inventory=subnet-options.yaml e2e
|
||||
# tftest modules=1 resources=9 inventory=subnet-options.yaml e2e
|
||||
```
|
||||
|
||||
### Subnet IAM
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
"enable_private_access": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"allow_subnet_cidr_routes_overlap": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"flow_logs_config": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -196,8 +196,8 @@ Interpolations leverage contexts from two separate sources: an internal set for
|
||||
|
||||
The following table lists the available context interpolations. External contexts are passed in via the `factories_config.contexts` variable. IAM principals are interpolated in all IAM attributes except `iam_by_principal`. First two columns show for which attribute of which resource context is interpolated. `external contexts` column show in which map passed as `var.factories_config.context` key will be looked up.
|
||||
|
||||
* Internally created folders creates keys under `${folder_name_1}[/${folder_name_2}/${folder_name_3}]`
|
||||
* IAM principals are resolved within context of managed project or use `${project}/${service_account}` to refer service account from other projects managed by the same project factory instance.
|
||||
- Internally created folders creates keys under `${folder_name_1}[/${folder_name_2}/${folder_name_3}]`
|
||||
- IAM principals are resolved within context of managed project or use `${project}/${service_account}` to refer service account from other projects managed by the same project factory instance.
|
||||
|
||||
| resource | attribute | external contexts | internal contexts |
|
||||
|---------------------|-----------------|---------------------|------------------------------------|
|
||||
@@ -378,6 +378,16 @@ shared_vpc_service_config:
|
||||
- container-engine
|
||||
billing_budgets:
|
||||
- test-100
|
||||
tags:
|
||||
my-tag-key-1:
|
||||
values:
|
||||
my-value-1:
|
||||
description: My value 1
|
||||
my-value-2:
|
||||
description: My value 3
|
||||
iam:
|
||||
roles/resourcemanager.tagUser:
|
||||
- user:user@example.com
|
||||
# tftest-file id=6 path=data/projects/dev-ta-app0-be.yaml schema=project.schema.json
|
||||
```
|
||||
|
||||
@@ -390,9 +400,9 @@ services:
|
||||
- storage.googleapis.com
|
||||
iam:
|
||||
"roles/owner":
|
||||
- rw
|
||||
- automation/rw
|
||||
"roles/viewer":
|
||||
- ro
|
||||
- automation/ro
|
||||
shared_vpc_host_config:
|
||||
enabled: true
|
||||
automation:
|
||||
@@ -406,12 +416,12 @@ automation:
|
||||
description: Team B app 0 Terraform state bucket.
|
||||
iam:
|
||||
roles/storage.objectCreator:
|
||||
- rw
|
||||
- automation/rw
|
||||
roles/storage.objectViewer:
|
||||
- gcp-devops
|
||||
- group:team-b-admins@example.org
|
||||
- rw
|
||||
- ro
|
||||
- automation/rw
|
||||
- automation/ro
|
||||
|
||||
# tftest-file id=7 path=data/projects/dev-tb-app0-0.yaml schema=project.schema.json
|
||||
```
|
||||
@@ -484,7 +494,7 @@ service_accounts:
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [factories_config](variables.tf#L121) | Path to folder with YAML resource description data files. | <code title="object({ budgets = optional(object({ billing_account = string budgets_data_path = string notification_channels = optional(map(any), {}) })) context = optional(object({ folder_ids = optional(map(string), {}) iam_principals = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) notification_channels = optional(map(string), {}) }), {}) folders_data_path = optional(string) projects_data_path = optional(string) })">object({…})</code> | ✓ | |
|
||||
| [factories_config](variables.tf#L121) | Path to folder with YAML resource description data files. | <code title="object({ budgets = optional(object({ billing_account = string budgets_data_path = string notification_channels = optional(map(any), {}) })) context = optional(object({ folder_ids = optional(map(string), {}) iam_principals = optional(map(string), {}) perimeters = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) notification_channels = optional(map(string), {}) }), {}) folders_data_path = optional(string) projects_data_path = optional(string) })">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)), {}) factories_config = optional(object({ custom_roles = optional(string) observability = optional(string) org_policies = optional(string) quotas = optional(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_agent_iam = optional(map(list(string)), {}) service_agent_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) }), { host_project = null }) storage_location = optional(string) 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) })) logging_data_access = optional(map(object({ ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_WRITE = optional(object({ exempted_members = optional(list(string)) })) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [data_merges](variables.tf#L64) | 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#L83) | 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))) factories_config = optional(object({ custom_roles = optional(string) observability = optional(string) org_policies = optional(string) quotas = optional(string) }), {}) parent = optional(string) prefix = optional(string) service_encryption_key_ids = optional(map(list(string))) storage_location = 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)) }))) vpc_sc = optional(object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })) logging_data_access = optional(map(object({ ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_WRITE = optional(object({ exempted_members = optional(list(string)) })) }))) })">object({…})</code> | | <code>{}</code> |
|
||||
|
||||
@@ -22,6 +22,7 @@ locals {
|
||||
k => merge(try(v.automation.bucket, {}), {
|
||||
automation_project = v.automation.project
|
||||
prefix = v.prefix
|
||||
project_name = v.name
|
||||
}) if try(v.automation.bucket, null) != null
|
||||
}
|
||||
automation_sa = flatten([
|
||||
@@ -31,6 +32,7 @@ locals {
|
||||
name = ks
|
||||
prefix = v.prefix
|
||||
project = k
|
||||
project_name = v.name
|
||||
})
|
||||
]
|
||||
])
|
||||
@@ -43,7 +45,7 @@ module "automation-bucket" {
|
||||
# from the IAM dependency in the outputs of the main project
|
||||
project_id = each.value.automation_project
|
||||
prefix = each.value.prefix
|
||||
name = "${each.key}-tf-state"
|
||||
name = "${each.value.project_name}-tf-state"
|
||||
encryption_key = lookup(each.value, "encryption_key", null)
|
||||
iam = {
|
||||
for k, v in lookup(each.value, "iam", {}) : k => [
|
||||
@@ -58,9 +60,20 @@ module "automation-bucket" {
|
||||
for k, v in lookup(each.value, "iam_bindings", {}) : k => merge(v, {
|
||||
members = [
|
||||
for vv in v.members : try(
|
||||
# rw (infer local project and automation prefix)
|
||||
module.automation-service-accounts["${each.key}/automation/${vv}"].iam_email,
|
||||
# automation/rw or sa (infer local project)
|
||||
module.automation-service-accounts["${each.key}/${vv}"].iam_email,
|
||||
# project/automation/rw project/sa
|
||||
var.factories_config.context.iam_principals[vv],
|
||||
vv
|
||||
# fully specified principal
|
||||
vv,
|
||||
# passthrough + error handling using tonumber until Terraform gets fail/raise function
|
||||
(
|
||||
strcontains(vv, ":")
|
||||
? vv
|
||||
: tonumber("[Error] Invalid member: '${vv}' in automation bucket '${each.key}'")
|
||||
)
|
||||
)
|
||||
]
|
||||
})
|
||||
@@ -94,19 +107,20 @@ module "automation-bucket" {
|
||||
module "automation-service-accounts" {
|
||||
source = "../iam-service-account"
|
||||
for_each = {
|
||||
for k in local.automation_sa : "${k.project}/${k.name}" => k
|
||||
for k in local.automation_sa : "${k.project}/automation/${k.name}" => k
|
||||
}
|
||||
# we cannot use interpolation here as we would get a cycle
|
||||
# from the IAM dependency in the outputs of the main project
|
||||
project_id = each.value.automation_project
|
||||
prefix = each.value.prefix
|
||||
name = "${each.value.project}-${each.value.name}"
|
||||
name = "${each.value.project_name}-${each.value.name}"
|
||||
description = lookup(each.value, "description", null)
|
||||
display_name = lookup(
|
||||
each.value,
|
||||
"display_name",
|
||||
"Service account ${each.value.name} for ${each.value.project}."
|
||||
)
|
||||
# TODO: also support short form for service accounts in this project
|
||||
iam = {
|
||||
for k, v in lookup(each.value, "iam", {}) : k => [
|
||||
for vv in v : lookup(
|
||||
|
||||
@@ -234,7 +234,25 @@ locals {
|
||||
try(v.tag_bindings, null),
|
||||
local.__projects_config.data_defaults.tag_bindings
|
||||
)
|
||||
vpc_sc = ( # type: object
|
||||
tags = {
|
||||
for tag_name, tag_data in try(v.tags, {}) : tag_name => {
|
||||
description = try(tag_data.description, "Managed by the Terraform project-factory module.")
|
||||
id = try(tag_data.id, null)
|
||||
iam = try(tag_data.iam, {})
|
||||
iam_bindings = try(tag_data.iam_bindings, {})
|
||||
iam_bindings_additive = try(tag_data.iam_bindings_additive, {})
|
||||
values = {
|
||||
for value_name, value_data in try(tag_data.values, {}) : value_name => {
|
||||
description = try(value_data.description, "Managed by the Terraform project-factory module.")
|
||||
id = try(value_data.id, null)
|
||||
iam = try(value_data.iam, {})
|
||||
iam_bindings = try(value_data.iam_bindings, {})
|
||||
iam_bindings_additive = try(value_data.iam_bindings_additive, {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vpc_sc = (
|
||||
local.__projects_config.data_overrides.vpc_sc != null
|
||||
? local.__projects_config.data_overrides.vpc_sc
|
||||
: (
|
||||
@@ -255,10 +273,10 @@ locals {
|
||||
})
|
||||
}
|
||||
# tflint-ignore: terraform_unused_declarations
|
||||
_projects_uniqunees_validation = {
|
||||
_projects_uniqueness_validation = {
|
||||
# will raise error, if the same project (derived from file name, or provided in the YAML file)
|
||||
# is sued more than once
|
||||
# is used more than once
|
||||
for k, v in local._projects_output :
|
||||
"${v.prefix}-${v.name}" => k
|
||||
"${v.prefix != null ? v.prefix : ""}-${v.name}" => k
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ locals {
|
||||
trimsuffix(f, ".yaml") => merge(
|
||||
{ parent = dirname(f) == "." ? "default" : dirname(f) },
|
||||
yamldecode(file("${local._folders_path}/${f}"))
|
||||
)
|
||||
if !endswith(f, "/_config.yaml")
|
||||
) if !endswith(f, "/_config.yaml")
|
||||
}
|
||||
)
|
||||
_project_path = try(pathexpand(var.factories_config.projects_data_path), null)
|
||||
@@ -32,8 +31,10 @@ locals {
|
||||
for f in try(fileset(local._project_path, "**/*.yaml"), []) :
|
||||
trimsuffix(f, ".yaml") => yamldecode(file("${local._project_path}/${f}"))
|
||||
}
|
||||
|
||||
_projects_input = merge(local._hierarchy_projects_full_path, local._projects_full_path)
|
||||
_projects_input = merge(
|
||||
local._hierarchy_projects_full_path,
|
||||
local._projects_full_path
|
||||
)
|
||||
_project_budgets = flatten([
|
||||
for k, v in local._projects_input : [
|
||||
for b in try(v.billing_budgets, []) : {
|
||||
@@ -55,7 +56,6 @@ locals {
|
||||
project_budgets = {
|
||||
for v in local._project_budgets : v.budget => v.project...
|
||||
}
|
||||
|
||||
buckets = flatten([
|
||||
for k, v in local.projects : [
|
||||
for name, opts in v.buckets : {
|
||||
@@ -98,6 +98,7 @@ locals {
|
||||
try(var.data_defaults.service_accounts.display_name, null),
|
||||
"Terraform-managed."
|
||||
)
|
||||
iam = try(opts.iam, {})
|
||||
iam_billing_roles = try(opts.iam_billing_roles, {})
|
||||
iam_organization_roles = try(opts.iam_organization_roles, {})
|
||||
iam_sa_roles = try(opts.iam_sa_roles, {})
|
||||
|
||||
@@ -87,7 +87,20 @@ module "projects" {
|
||||
for k, v in merge(each.value.tag_bindings, var.data_merges.tag_bindings) :
|
||||
k => lookup(var.factories_config.context.tag_values, v, v)
|
||||
}
|
||||
vpc_sc = each.value.vpc_sc
|
||||
tags = each.value.tags
|
||||
vpc_sc = each.value.vpc_sc == null ? null : {
|
||||
perimeter_name = (
|
||||
each.value.vpc_sc.perimeter_name == null
|
||||
? null
|
||||
: lookup(
|
||||
var.factories_config.context.perimeters,
|
||||
each.value.vpc_sc.perimeter_name,
|
||||
each.value.vpc_sc.perimeter_name
|
||||
)
|
||||
)
|
||||
perimeter_bridges = each.value.vpc_sc.perimeter_bridges
|
||||
is_dry_run = each.value.vpc_sc.is_dry_run
|
||||
}
|
||||
}
|
||||
|
||||
module "projects-iam" {
|
||||
@@ -105,16 +118,22 @@ module "projects-iam" {
|
||||
iam = {
|
||||
for k, v in lookup(each.value, "iam", {}) : k => [
|
||||
for vv in v : try(
|
||||
# project service accounts
|
||||
# project service accounts (sa)
|
||||
module.service-accounts["${each.key}/${vv}"].iam_email,
|
||||
# automation service account
|
||||
# automation service account (rw)
|
||||
local.context.iam_principals["${each.key}/automation/${vv}"],
|
||||
# automation service account (automation/rw)
|
||||
local.context.iam_principals["${each.key}/${vv}"],
|
||||
# other projects service accounts
|
||||
# other projects service accounts (project/sa)
|
||||
module.service-accounts[vv].iam_email,
|
||||
# other automation service account
|
||||
# other automation service account (project/automation/rw)
|
||||
local.context.iam_principals[vv],
|
||||
# passthrough + error handling using tonumber until Terraform gets fail/raise function
|
||||
strcontains(vv, ":") ? vv : tonumber("[Error] Invalid member: '${vv}' in project '${each.key}'")
|
||||
(
|
||||
strcontains(vv, ":")
|
||||
? vv
|
||||
: tonumber("[Error] Invalid member: '${vv}' in project '${each.key}'")
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -122,16 +141,22 @@ module "projects-iam" {
|
||||
for k, v in lookup(each.value, "iam_bindings", {}) : k => merge(v, {
|
||||
members = [
|
||||
for vv in v.members : try(
|
||||
# project service accounts
|
||||
# project service accounts (sa)
|
||||
module.service-accounts["${each.key}/${vv}"].iam_email,
|
||||
# automation service account
|
||||
# automation service account (rw)
|
||||
local.context.iam_principals["${each.key}/automation/${vv}"],
|
||||
# automation service account (automation/rw)
|
||||
local.context.iam_principals["${each.key}/${vv}"],
|
||||
# other projects service accounts
|
||||
# other projects service accounts (project/sa)
|
||||
module.service-accounts[vv].iam_email,
|
||||
# other automation service account
|
||||
# other automation service account (project/automation/rw)
|
||||
local.context.iam_principals[vv],
|
||||
# passthrough + error handling using tonumber until Terraform gets fail/raise function
|
||||
strcontains(vv, ":") ? vv : tonumber("[Error] Invalid member: '${vv}' in project '${each.key}'")
|
||||
(
|
||||
strcontains(vv, ":")
|
||||
? vv
|
||||
: tonumber("[Error] Invalid member: '${vv}' in project '${each.key}'")
|
||||
)
|
||||
)
|
||||
]
|
||||
})
|
||||
@@ -139,16 +164,22 @@ module "projects-iam" {
|
||||
iam_bindings_additive = {
|
||||
for k, v in lookup(each.value, "iam_bindings_additive", {}) : k => merge(v, {
|
||||
member = try(
|
||||
# project service accounts
|
||||
# project service accounts (sa)
|
||||
module.service-accounts["${each.key}/${v.member}"].iam_email,
|
||||
# automation service account
|
||||
# automation service account (rw)
|
||||
local.context.iam_principals["${each.key}/automation/${v.member}"],
|
||||
# automation service account (automation/rw)
|
||||
local.context.iam_principals["${each.key}/${v.member}"],
|
||||
# other projects service accounts
|
||||
# other projects service accounts (project/sa)
|
||||
module.service-accounts[v.member].iam_email,
|
||||
# other automation service account
|
||||
# other automation service account (project/automation/rw)
|
||||
local.context.iam_principals[v.member],
|
||||
# passthrough + error handling using tonumber until Terraform gets fail/raise function
|
||||
strcontains(v.member, ":") ? v.member : tonumber("[Error] Invalid member: '${v.member}' in project '${each.key}'")
|
||||
(
|
||||
strcontains(v.member, ":")
|
||||
? v.member
|
||||
: tonumber("[Error] Invalid member: '${v.member}' in project '${each.key}'")
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -163,32 +194,38 @@ module "projects-iam" {
|
||||
try(each.value.shared_vpc_service_config.host_project, null) == null
|
||||
? null
|
||||
: merge(each.value.shared_vpc_service_config, {
|
||||
host_project = lookup(
|
||||
var.factories_config.context.vpc_host_projects,
|
||||
each.value.shared_vpc_service_config.host_project,
|
||||
host_project = try(
|
||||
var.factories_config.context.vpc_host_projects[each.value.shared_vpc_service_config.host_project],
|
||||
module.projects[each.value.shared_vpc_service_config.host_project].project_id,
|
||||
each.value.shared_vpc_service_config.host_project
|
||||
)
|
||||
network_users = [
|
||||
for v in try(each.value.shared_vpc_service_config.network_users, []) :
|
||||
for vv in try(each.value.shared_vpc_service_config.network_users, []) :
|
||||
try(
|
||||
# project service accounts
|
||||
module.service-accounts["${each.key}/${v}"].iam_email,
|
||||
# automation service account
|
||||
local.context.iam_principals["${each.key}/${v}"],
|
||||
# other projects service accounts
|
||||
module.service-accounts[v].iam_email,
|
||||
# other automation service account
|
||||
local.context.iam_principals[v],
|
||||
# project service accounts (sa)
|
||||
module.service-accounts["${each.key}/${vv}"].iam_email,
|
||||
# automation service account (rw)
|
||||
local.context.iam_principals["${each.key}/automation/${vv}"],
|
||||
# automation service account (automation/rw)
|
||||
local.context.iam_principals["${each.key}/${vv}"],
|
||||
# other projects service accounts (project/sa)
|
||||
module.service-accounts[vv].iam_email,
|
||||
# other automation service account (project/automation/rw)
|
||||
local.context.iam_principals[vv],
|
||||
# passthrough + error handling using tonumber until Terraform gets fail/raise function
|
||||
strcontains(v, ":") ? v : tonumber("[Error] Invalid member: '${v}' in project '${each.key}'")
|
||||
(
|
||||
strcontains(vv, ":")
|
||||
? vv
|
||||
: tonumber("[Error] Invalid member: '${vv}' in project '${each.key}'")
|
||||
)
|
||||
)
|
||||
]
|
||||
# TODO: network subnet users
|
||||
})
|
||||
)
|
||||
# add service agents config, so Service Agents can be referred in the IAM grants
|
||||
service_agents_config = {
|
||||
grant_default_roles = false # Default roles are granted in module.project
|
||||
# default roles are granted in module.project
|
||||
grant_default_roles = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,16 +241,22 @@ module "buckets" {
|
||||
iam = {
|
||||
for k, v in each.value.iam : k => [
|
||||
for vv in v : try(
|
||||
# project service accounts
|
||||
# project service accounts (sa)
|
||||
module.service-accounts["${each.value.project_key}/${vv}"].iam_email,
|
||||
# automation service account
|
||||
# automation service account (rw)
|
||||
local.context.iam_principals["${each.value.project_key}/automation/${vv}"],
|
||||
# automation service account (automation/rw)
|
||||
local.context.iam_principals["${each.value.project_key}/${vv}"],
|
||||
# other projects service accounts
|
||||
# other projects service accounts (project/sa)
|
||||
module.service-accounts[vv].iam_email,
|
||||
# other automation service account
|
||||
# other automation service account (project/automation/rw)
|
||||
local.context.iam_principals[vv],
|
||||
# passthrough + error handling using tonumber until Terraform gets fail/raise function
|
||||
strcontains(vv, ":") ? vv : tonumber("[Error] Invalid member: '${vv}' for bucket '${each.key}' in project '${each.value.project}'")
|
||||
(
|
||||
strcontains(vv, ":")
|
||||
? vv
|
||||
: tonumber("[Error] Invalid member: '${vv}' in project '${each.value.project_key}'")
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -221,16 +264,22 @@ module "buckets" {
|
||||
for k, v in each.value.iam_bindings : k => merge(v, {
|
||||
members = [
|
||||
for vv in v.members : try(
|
||||
# project service accounts
|
||||
# project service accounts (sa)
|
||||
module.service-accounts["${each.value.project}/${vv}"].iam_email,
|
||||
# automation service account
|
||||
# automation service account (rw)
|
||||
local.context.iam_principals["${each.value.project}/automation/${vv}"],
|
||||
# automation service account (automation/rw)
|
||||
local.context.iam_principals["${each.value.project}/${vv}"],
|
||||
# other projects service accounts
|
||||
# other projects service accounts (project/sa)
|
||||
module.service-accounts[vv].iam_email,
|
||||
# other automation service account
|
||||
# other automation service account (project/automation/rw)
|
||||
local.context.iam_principals[vv],
|
||||
# passthrough + error handling using tonumber until Terraform gets fail/raise function
|
||||
strcontains(vv, ":") ? vv : tonumber("[Error] Invalid member: '${vv}' for bucket '${each.key}' in project '${each.value.project}'")
|
||||
(
|
||||
strcontains(vv, ":")
|
||||
? vv
|
||||
: tonumber("[Error] Invalid member: '${vv}' in project '${each.value.project}'")
|
||||
)
|
||||
)
|
||||
]
|
||||
})
|
||||
@@ -238,16 +287,22 @@ module "buckets" {
|
||||
iam_bindings_additive = {
|
||||
for k, v in each.value.iam_bindings_additive : k => merge(v, {
|
||||
member = try(
|
||||
# project service accounts
|
||||
# project service accounts (sa)
|
||||
module.service-accounts["${each.value.project}/${v.member}"].iam_email,
|
||||
# automation service account
|
||||
# automation service account (rw)
|
||||
local.context.iam_principals["${each.value.project}/automation/${v.member}"],
|
||||
# automation service account (automation/rw)
|
||||
local.context.iam_principals["${each.value.project}/${v.member}"],
|
||||
# other projects service accounts
|
||||
# other projects service accounts (project/sa)
|
||||
module.service-accounts[v.member].iam_email,
|
||||
# other automation service account
|
||||
# other automation service account (project/automation/rw)
|
||||
local.context.iam_principals[v.member],
|
||||
# passthrough + error handling using tonumber until Terraform gets fail/raise function
|
||||
strcontains(v.member, ":") ? v.member : tonumber("[Error] Invalid member: '${v.member}' for bucket '${each.key}' in project '${each.value.project}'")
|
||||
(
|
||||
strcontains(v.member, ":")
|
||||
? v.member
|
||||
: tonumber("[Error] Invalid member: '${v.member}' in project '${each.value.project}'")
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -270,6 +325,24 @@ module "service-accounts" {
|
||||
project_id = module.projects[each.value.project_key].project_id
|
||||
name = each.value.name
|
||||
display_name = each.value.display_name
|
||||
iam = {
|
||||
for k, v in lookup(each.value, "iam", {}) : k => [
|
||||
for vv in v : try(
|
||||
# automation service account (rw)
|
||||
local.context.iam_principals["${each.key}/automation/${vv}"],
|
||||
# automation service account (automation/rw)
|
||||
local.context.iam_principals["${each.key}/${vv}"],
|
||||
# other automation service account (project/automation/rw)
|
||||
local.context.iam_principals[vv],
|
||||
# passthrough + error handling using tonumber until Terraform gets fail/raise function
|
||||
(
|
||||
strcontains(vv, ":")
|
||||
? vv
|
||||
: tonumber("[Error] Invalid member: '${vv}' in project '${each.key}'")
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
||||
iam_project_roles = merge(
|
||||
{
|
||||
for k, v in each.value.iam_project_roles :
|
||||
|
||||
@@ -200,6 +200,9 @@
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"iam": {
|
||||
"$ref": "#/$defs/iam"
|
||||
},
|
||||
"iam_self_roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -319,6 +322,54 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"iam": {
|
||||
"$ref": "#/$defs/iam"
|
||||
},
|
||||
"iam_bindings": {
|
||||
"$ref": "#/$defs/iam_bindings"
|
||||
},
|
||||
"iam_bindings_additive": {
|
||||
"$ref": "#/$defs/iam_bindings_additive"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"values": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"iam": {
|
||||
"$ref": "#/$defs/iam"
|
||||
},
|
||||
"iam_bindings": {
|
||||
"$ref": "#/$defs/iam_bindings"
|
||||
},
|
||||
"iam_bindings_additive": {
|
||||
"$ref": "#/$defs/iam_bindings_additive"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"vpc_sc": {
|
||||
"type": "object",
|
||||
"additionalItems": false,
|
||||
|
||||
@@ -131,6 +131,7 @@ variable "factories_config" {
|
||||
# TODO: add KMS keys
|
||||
folder_ids = optional(map(string), {})
|
||||
iam_principals = optional(map(string), {})
|
||||
perimeters = optional(map(string), {})
|
||||
tag_values = optional(map(string), {})
|
||||
vpc_host_projects = optional(map(string), {})
|
||||
notification_channels = optional(map(string), {})
|
||||
|
||||
@@ -37,9 +37,16 @@ locals {
|
||||
for agent in lookup(local._service_agents_by_api, api, []) :
|
||||
(agent.name) => merge(agent, {
|
||||
email = (
|
||||
var.universe == null || api != "cloudservices"
|
||||
? templatestring(agent.identity, { project_number = local.project.number, universe_domain = local._universe_domain })
|
||||
: format("%s@cloudservices.%siam.gserviceaccount.com", local.project.number, local._universe_domain)
|
||||
# If universe variable is set, enfore the use of the service-PROJECT_NUMBER@gcp-sa-ekms.UNVIVERSE-system.iam.gserviceaccount.com
|
||||
# instead of service-PROJECT_NUMBER@gcp-sa-kms.UNVIVERSE-system.iam.gserviceaccount.com
|
||||
# as in the TPC universes, the partner KMS is enforced by design
|
||||
var.universe != null && api == "cloudkms.googleapis.com"
|
||||
? format("service-%s@gcp-sa-ekms.%siam.gserviceaccount.com", local.project.number, local._universe_domain)
|
||||
: (
|
||||
var.universe == null || api != "cloudservices"
|
||||
? templatestring(agent.identity, { project_number = local.project.number, universe_domain = local._universe_domain })
|
||||
: format("%s@cloudservices.%siam.gserviceaccount.com", local.project.number, local._universe_domain)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2023 Google LLC
|
||||
* Copyright 2025 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -209,7 +209,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", "sa-roles"]
|
||||
@@ -226,11 +226,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" {
|
||||
@@ -240,12 +240,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"]
|
||||
@@ -319,9 +325,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.
|
||||
@@ -350,17 +402,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(object({ combining_function = optional(string) conditions = optional(list(object({ device_policy = optional(object({ allowed_device_management_levels = optional(list(string)) allowed_encryption_statuses = optional(list(string)) require_admin_approval = bool require_corp_owned = bool require_screen_lock = optional(bool) os_constraints = optional(list(object({ os_type = string minimum_version = optional(string) require_verified_chrome_os = optional(bool) }))) })) ip_subnetworks = optional(list(string), []) members = optional(list(string), []) negate = optional(bool) regions = optional(list(string), []) required_access_levels = optional(list(string), []) vpc_subnets = optional(map(list(string)), {}) })), []) description = optional(string) }))">map(object({…}))</code> | | <code>{}</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({ parent = string title = string scopes = optional(list(string), null) })">object({…})</code> | | <code>null</code> |
|
||||
| [egress_policies](variables.tf#L82) | Egress policy definitions that can be referenced in perimeters. | <code title="map(object({ title = optional(string) from = object({ access_levels = optional(list(string), []) identity_type = optional(string) identities = optional(list(string)) resources = optional(list(string), []) }) to = object({ external_resources = optional(list(string)) operations = optional(list(object({ method_selectors = optional(list(string)) permission_selectors = optional(list(string)) service_name = string })), []) resources = optional(list(string)) roles = optional(list(string)) }) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [factories_config](variables.tf#L125) | Paths to folders that enable factory functionality. | <code title="object({ access_levels = optional(string) egress_policies = optional(string) ingress_policies = optional(string) restricted_services = optional(string, "data/restricted-services.yaml") })">object({…})</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L137) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables.tf#L143) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [iam_bindings_additive](variables.tf#L158) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [ingress_policies](variables.tf#L173) | Ingress policy definitions that can be referenced in perimeters. | <code title="map(object({ title = optional(string) from = object({ access_levels = optional(list(string), []) identity_type = optional(string) identities = optional(list(string)) resources = optional(list(string), []) }) to = object({ operations = optional(list(object({ method_selectors = optional(list(string)) permission_selectors = optional(list(string)) service_name = string })), []) resources = optional(list(string)) roles = optional(list(string)) }) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [service_perimeters_bridge](variables.tf#L215) | Bridge service perimeters. | <code title="map(object({ spec_resources = optional(list(string)) status_resources = optional(list(string)) use_explicit_dry_run_spec = optional(bool, false) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [service_perimeters_regular](variables.tf#L225) | Regular service perimeters. | <code title="map(object({ description = optional(string) spec = optional(object({ access_levels = optional(list(string)) resources = optional(list(string)) restricted_services = optional(list(string)) egress_policies = optional(list(string)) ingress_policies = optional(list(string)) vpc_accessible_services = optional(object({ allowed_services = list(string) enable_restriction = optional(bool, true) })) })) status = optional(object({ access_levels = optional(list(string)) resources = optional(list(string)) restricted_services = optional(list(string)) egress_policies = optional(list(string)) ingress_policies = optional(list(string)) vpc_accessible_services = optional(object({ allowed_services = list(string) enable_restriction = bool })) })) use_explicit_dry_run_spec = optional(bool, false) }))">map(object({…}))</code> | | <code>{}</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(object({ combining_function = optional(string) conditions = optional(list(object({ device_policy = optional(object({ allowed_device_management_levels = optional(list(string)) allowed_encryption_statuses = optional(list(string)) require_admin_approval = bool require_corp_owned = bool require_screen_lock = optional(bool) os_constraints = optional(list(object({ os_type = string minimum_version = optional(string) require_verified_chrome_os = optional(bool) }))) })) ip_subnetworks = optional(list(string), []) members = optional(list(string), []) negate = optional(bool) regions = optional(list(string), []) required_access_levels = optional(list(string), []) vpc_subnets = optional(map(list(string)), {}) })), []) description = optional(string) title = optional(string) }))">map(object({…}))</code> | | <code>{}</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({ parent = string title = string scopes = optional(list(string), null) })">object({…})</code> | | <code>null</code> |
|
||||
| [egress_policies](variables.tf#L83) | Egress policy definitions that can be referenced in perimeters. | <code title="map(object({ title = optional(string) from = object({ access_levels = optional(list(string), []) identity_type = optional(string) identities = optional(list(string)) resources = optional(list(string), []) }) to = object({ external_resources = optional(list(string)) operations = optional(list(object({ method_selectors = optional(list(string)) permission_selectors = optional(list(string)) service_name = string })), []) resources = optional(list(string)) roles = optional(list(string)) }) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [factories_config](variables.tf#L126) | Paths to folders that enable factory functionality. | <code title="object({ access_levels = optional(string) bridges = optional(string) egress_policies = optional(string) ingress_policies = optional(string) perimeters = optional(string) context = optional(object({ resource_sets = optional(map(list(string)), {}) service_sets = optional(map(list(string)), {}) identity_sets = optional(map(list(string)), {}) }), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L144) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables.tf#L150) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [iam_bindings_additive](variables.tf#L165) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [ingress_policies](variables.tf#L180) | Ingress policy definitions that can be referenced in perimeters. | <code title="map(object({ title = optional(string) from = object({ access_levels = optional(list(string), []) identity_type = optional(string) identities = optional(list(string)) resources = optional(list(string), []) }) to = object({ operations = optional(list(object({ method_selectors = optional(list(string)) permission_selectors = optional(list(string)) service_name = string })), []) resources = optional(list(string)) roles = optional(list(string)) }) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [service_perimeters_bridge](variables.tf#L222) | Bridge service perimeters. | <code title="map(object({ description = optional(string) title = optional(string) spec_resources = optional(list(string)) status_resources = optional(list(string)) use_explicit_dry_run_spec = optional(bool, false) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [service_perimeters_regular](variables.tf#L234) | Regular service perimeters. | <code title="map(object({ description = optional(string) title = optional(string) spec = optional(object({ access_levels = optional(list(string)) egress_policies = optional(list(string)) ingress_policies = optional(list(string)) restricted_services = optional(list(string)) resources = optional(list(string)) vpc_accessible_services = optional(object({ allowed_services = list(string) enable_restriction = optional(bool, true) })) })) status = optional(object({ access_levels = optional(list(string)) egress_policies = optional(list(string)) ingress_policies = optional(list(string)) resources = optional(list(string)) restricted_services = optional(list(string)) vpc_accessible_services = optional(object({ allowed_services = list(string) enable_restriction = optional(bool, true) })) })) use_explicit_dry_run_spec = optional(bool, false) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
@@ -403,7 +455,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"]
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ resource "google_access_context_manager_access_level" "basic" {
|
||||
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
|
||||
title = coalesce(each.value.title, each.key)
|
||||
description = each.value.description
|
||||
|
||||
basic {
|
||||
@@ -34,8 +34,11 @@ resource "google_access_context_manager_access_level" "basic" {
|
||||
for_each = toset(each.value.conditions)
|
||||
iterator = c
|
||||
content {
|
||||
ip_subnetworks = c.value.ip_subnetworks
|
||||
members = c.value.members
|
||||
ip_subnetworks = c.value.ip_subnetworks
|
||||
members = flatten([
|
||||
for i in c.value.members :
|
||||
lookup(var.factories_config.context.identity_sets, i, [i])
|
||||
])
|
||||
negate = c.value.negate
|
||||
regions = c.value.regions
|
||||
required_access_levels = coalesce(c.value.required_access_levels, [])
|
||||
|
||||
@@ -22,7 +22,7 @@ locals {
|
||||
}
|
||||
}
|
||||
_data_paths = {
|
||||
for k in ["access_levels", "egress_policies", "ingress_policies"] : k => (
|
||||
for k in ["access_levels", "bridges", "egress_policies", "ingress_policies", "perimeters"] : k => (
|
||||
var.factories_config[k] == null
|
||||
? null
|
||||
: pathexpand(var.factories_config[k])
|
||||
@@ -32,7 +32,6 @@ locals {
|
||||
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
|
||||
@@ -44,6 +43,8 @@ locals {
|
||||
vpc_subnets = {}
|
||||
}, c)
|
||||
]
|
||||
description = try(v.description, null)
|
||||
title = try(v.title, null)
|
||||
}
|
||||
}
|
||||
egress_policies = {
|
||||
@@ -91,6 +92,40 @@ locals {
|
||||
}
|
||||
}
|
||||
}
|
||||
bridges = {
|
||||
for k, v in local._data.bridges :
|
||||
k => {
|
||||
description = try(v.description, null)
|
||||
title = try(v.title, null)
|
||||
spec_resources = try(v.spec_resources, null)
|
||||
status_resources = try(v.resources, null)
|
||||
use_explicit_dry_run_spec = try(v.use_explicit_dry_run_spec, false)
|
||||
}
|
||||
}
|
||||
perimeters = {
|
||||
for k, v in local._data.perimeters :
|
||||
k => {
|
||||
description = try(v.description, null)
|
||||
title = try(v.title, null)
|
||||
spec = !can(v.spec) ? null : merge(v.spec, {
|
||||
access_levels = try(v.spec.access_levels, [])
|
||||
egress_policies = try(v.spec.egress_policies, [])
|
||||
ingress_policies = try(v.spec.ingress_policies, [])
|
||||
restricted_services = try(v.spec.restricted_services, [])
|
||||
resources = try(v.spec.resources, [])
|
||||
vpc_accessible_services = try(v.spec.vpc_accessible_services, null)
|
||||
})
|
||||
status = !can(v.status) ? null : merge(v.status, {
|
||||
access_levels = try(v.status.access_levels, [])
|
||||
egress_policies = try(v.status.egress_policies, [])
|
||||
ingress_policies = try(v.status.ingress_policies, [])
|
||||
restricted_services = try(v.status.restricted_services, [])
|
||||
resources = try(v.status.resources, [])
|
||||
vpc_accessible_services = try(v.status.vpc_accessible_services, null)
|
||||
})
|
||||
use_explicit_dry_run_spec = try(v.use_explicit_dry_run_spec, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
# TODO: add checks that emulate the variable validations
|
||||
}
|
||||
|
||||
@@ -74,8 +74,7 @@
|
||||
"members": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^(?:serviceAccount:|user:)"
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"negate": {
|
||||
|
||||
@@ -33,8 +33,7 @@
|
||||
"identities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^(?:serviceAccount:|user:|group:|principal:)"
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
|
||||
116
modules/vpc-sc/schemas/perimeters.schema.json
Normal file
116
modules/vpc-sc/schemas/perimeters.schema.json
Normal file
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "perimeters",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"spec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_levels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"egress_policies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"ingress_policies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"restricted_services": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"vpc_accessible_services": {
|
||||
"$ref": "#/$defs/VpcAccessibleServices"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"status": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_levels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"egress_policies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"ingress_policies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"restricted_services": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"vpc_accessible_services": {
|
||||
"$ref": "#/$defs/VpcAccessibleServices"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"use_explicit_dry_run_spec": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"VpcAccessibleServices": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"allowed_services": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"enable_restriction": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"allowed_services"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2023 Google LLC
|
||||
* Copyright 2025 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,8 +20,12 @@
|
||||
# service perimeters are needed, switch to the
|
||||
# google_access_context_manager_service_perimeters resource
|
||||
|
||||
locals {
|
||||
bridge_perimeters = merge(local.data.bridges, var.service_perimeters_bridge)
|
||||
}
|
||||
|
||||
resource "google_access_context_manager_service_perimeter" "bridge" {
|
||||
for_each = var.service_perimeters_bridge
|
||||
for_each = local.bridge_perimeters
|
||||
parent = "accessPolicies/${local.access_policy}"
|
||||
name = "accessPolicies/${local.access_policy}/servicePerimeters/${each.key}"
|
||||
title = each.key
|
||||
@@ -31,12 +35,21 @@ resource "google_access_context_manager_service_perimeter" "bridge" {
|
||||
dynamic "spec" {
|
||||
for_each = each.value.spec_resources == null ? [] : [""]
|
||||
content {
|
||||
resources = each.value.spec_resources
|
||||
resources = flatten([
|
||||
for r in each.value.spec_resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
status {
|
||||
resources = each.value.status_resources == null ? [] : each.value.status_resources
|
||||
dynamic "status" {
|
||||
for_each = each.value.status_resources == null ? [] : [""]
|
||||
content {
|
||||
resources = flatten([
|
||||
for r in each.value.status_resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
# lifecycle {
|
||||
|
||||
@@ -21,16 +21,17 @@
|
||||
# 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)
|
||||
egress_policies = merge(local.data.egress_policies, var.egress_policies)
|
||||
ingress_policies = merge(local.data.ingress_policies, var.ingress_policies)
|
||||
regular_perimeters = merge(local.data.perimeters, var.service_perimeters_regular)
|
||||
}
|
||||
|
||||
resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
for_each = var.service_perimeters_regular
|
||||
for_each = local.regular_perimeters
|
||||
parent = "accessPolicies/${local.access_policy}"
|
||||
name = "accessPolicies/${local.access_policy}/servicePerimeters/${each.key}"
|
||||
description = each.value.description
|
||||
title = each.key
|
||||
title = coalesce(each.value.title, each.key)
|
||||
perimeter_type = "PERIMETER_TYPE_REGULAR"
|
||||
use_explicit_dry_run_spec = each.value.use_explicit_dry_run_spec
|
||||
dynamic "spec" {
|
||||
@@ -43,8 +44,14 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
try(google_access_context_manager_access_level.basic[k].id, k)
|
||||
]
|
||||
)
|
||||
resources = spec.value.resources
|
||||
restricted_services = spec.value.restricted_services
|
||||
resources = flatten([
|
||||
for r in spec.value.resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
restricted_services = flatten([
|
||||
for r in coalesce(spec.value.restricted_services, []) :
|
||||
lookup(var.factories_config.context.service_sets, r, [r])
|
||||
])
|
||||
|
||||
dynamic "egress_policies" {
|
||||
for_each = spec.value.egress_policies == null ? {} : {
|
||||
@@ -58,7 +65,10 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
for_each = policy.value.from == null ? [] : [""]
|
||||
content {
|
||||
identity_type = policy.value.from.identity_type
|
||||
identities = policy.value.from.identities
|
||||
identities = flatten([
|
||||
for i in policy.value.from.identities :
|
||||
lookup(var.factories_config.context.identity_sets, i, [i])
|
||||
])
|
||||
source_restriction = (
|
||||
length(policy.value.from.access_levels) > 0 || length(policy.value.from.resources) > 0
|
||||
? "SOURCE_RESTRICTION_ENABLED"
|
||||
@@ -75,7 +85,10 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
}
|
||||
}
|
||||
dynamic "sources" {
|
||||
for_each = policy.value.from.resources
|
||||
for_each = flatten([
|
||||
for r in policy.value.from.resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
iterator = resource
|
||||
content {
|
||||
resource = resource.value
|
||||
@@ -87,8 +100,12 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
for_each = policy.value.to == null ? [] : [""]
|
||||
content {
|
||||
external_resources = policy.value.to.external_resources
|
||||
resources = policy.value.to.resources
|
||||
roles = policy.value.to.roles
|
||||
resources = flatten([
|
||||
for r in policy.value.to.resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
|
||||
roles = policy.value.to.roles
|
||||
dynamic "operations" {
|
||||
for_each = toset(policy.value.to.operations)
|
||||
iterator = o
|
||||
@@ -125,7 +142,10 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
for_each = policy.value.from == null ? [] : [""]
|
||||
content {
|
||||
identity_type = policy.value.from.identity_type
|
||||
identities = policy.value.from.identities
|
||||
identities = flatten([
|
||||
for i in policy.value.from.identities :
|
||||
lookup(var.factories_config.context.identity_sets, i, [i])
|
||||
])
|
||||
dynamic "sources" {
|
||||
for_each = toset(policy.value.from.access_levels)
|
||||
iterator = s
|
||||
@@ -136,9 +156,12 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
}
|
||||
}
|
||||
dynamic "sources" {
|
||||
for_each = toset(policy.value.from.resources)
|
||||
for_each = flatten([
|
||||
for r in policy.value.from.resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
content {
|
||||
resource = sources.key
|
||||
resource = sources.value
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,8 +169,11 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
dynamic "ingress_to" {
|
||||
for_each = policy.value.to == null ? [] : [""]
|
||||
content {
|
||||
resources = policy.value.to.resources
|
||||
roles = policy.value.to.roles
|
||||
resources = flatten([
|
||||
for r in policy.value.to.resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
roles = policy.value.to.roles
|
||||
dynamic "operations" {
|
||||
for_each = toset(policy.value.to.operations)
|
||||
iterator = o
|
||||
@@ -175,7 +201,10 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
dynamic "vpc_accessible_services" {
|
||||
for_each = spec.value.vpc_accessible_services == null ? {} : { 1 = 1 }
|
||||
content {
|
||||
allowed_services = spec.value.vpc_accessible_services.allowed_services
|
||||
allowed_services = flatten([
|
||||
for r in spec.value.vpc_accessible_services.allowed_services :
|
||||
lookup(var.factories_config.context.service_sets, r, [r])
|
||||
])
|
||||
enable_restriction = spec.value.vpc_accessible_services.enable_restriction
|
||||
}
|
||||
}
|
||||
@@ -192,8 +221,14 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
try(google_access_context_manager_access_level.basic[k].id, k)
|
||||
]
|
||||
)
|
||||
resources = status.value.resources
|
||||
restricted_services = status.value.restricted_services
|
||||
resources = flatten([
|
||||
for r in status.value.resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
restricted_services = flatten([
|
||||
for r in coalesce(status.value.restricted_services, []) :
|
||||
lookup(var.factories_config.context.service_sets, r, [r])
|
||||
])
|
||||
|
||||
dynamic "egress_policies" {
|
||||
for_each = status.value.egress_policies == null ? {} : {
|
||||
@@ -207,7 +242,10 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
for_each = policy.value.from == null ? [] : [""]
|
||||
content {
|
||||
identity_type = policy.value.from.identity_type
|
||||
identities = policy.value.from.identities
|
||||
identities = flatten([
|
||||
for i in policy.value.from.identities :
|
||||
lookup(var.factories_config.context.identity_sets, i, [i])
|
||||
])
|
||||
source_restriction = (
|
||||
length(policy.value.from.access_levels) > 0 || length(policy.value.from.resources) > 0
|
||||
? "SOURCE_RESTRICTION_ENABLED"
|
||||
@@ -224,7 +262,10 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
}
|
||||
}
|
||||
dynamic "sources" {
|
||||
for_each = policy.value.from.resources
|
||||
for_each = flatten([
|
||||
for r in policy.value.from.resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
iterator = resource
|
||||
content {
|
||||
resource = resource.value
|
||||
@@ -274,7 +315,10 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
for_each = policy.value.from == null ? [] : [""]
|
||||
content {
|
||||
identity_type = policy.value.from.identity_type
|
||||
identities = policy.value.from.identities
|
||||
identities = flatten([
|
||||
for i in policy.value.from.identities :
|
||||
lookup(var.factories_config.context.identity_sets, i, [i])
|
||||
])
|
||||
dynamic "sources" {
|
||||
for_each = toset(policy.value.from.access_levels)
|
||||
iterator = s
|
||||
@@ -286,9 +330,12 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
}
|
||||
}
|
||||
dynamic "sources" {
|
||||
for_each = toset(policy.value.from.resources)
|
||||
for_each = flatten([
|
||||
for r in policy.value.from.resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
content {
|
||||
resource = sources.key
|
||||
resource = sources.value
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,8 +343,11 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
dynamic "ingress_to" {
|
||||
for_each = policy.value.to == null ? [] : [""]
|
||||
content {
|
||||
resources = policy.value.to.resources
|
||||
roles = policy.value.to.roles
|
||||
resources = flatten([
|
||||
for r in policy.value.to.resources :
|
||||
lookup(var.factories_config.context.resource_sets, r, [r])
|
||||
])
|
||||
roles = policy.value.to.roles
|
||||
dynamic "operations" {
|
||||
for_each = toset(policy.value.to.operations)
|
||||
iterator = o
|
||||
@@ -325,7 +375,10 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
||||
dynamic "vpc_accessible_services" {
|
||||
for_each = status.value.vpc_accessible_services == null ? {} : { 1 = 1 }
|
||||
content {
|
||||
allowed_services = status.value.vpc_accessible_services.allowed_services
|
||||
allowed_services = flatten([
|
||||
for r in status.value.vpc_accessible_services.allowed_services :
|
||||
lookup(var.factories_config.context.service_sets, r, [r])
|
||||
])
|
||||
enable_restriction = status.value.vpc_accessible_services.enable_restriction
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ variable "access_levels" {
|
||||
vpc_subnets = optional(map(list(string)), {})
|
||||
})), [])
|
||||
description = optional(string)
|
||||
title = optional(string)
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
@@ -125,10 +126,16 @@ 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)
|
||||
restricted_services = optional(string, "data/restricted-services.yaml")
|
||||
access_levels = optional(string)
|
||||
bridges = optional(string)
|
||||
egress_policies = optional(string)
|
||||
ingress_policies = optional(string)
|
||||
perimeters = optional(string)
|
||||
context = optional(object({
|
||||
resource_sets = optional(map(list(string)), {})
|
||||
service_sets = optional(map(list(string)), {})
|
||||
identity_sets = optional(map(list(string)), {})
|
||||
}), {})
|
||||
})
|
||||
nullable = false
|
||||
default = {}
|
||||
@@ -215,6 +222,8 @@ variable "ingress_policies" {
|
||||
variable "service_perimeters_bridge" {
|
||||
description = "Bridge service perimeters."
|
||||
type = map(object({
|
||||
description = optional(string)
|
||||
title = optional(string)
|
||||
spec_resources = optional(list(string))
|
||||
status_resources = optional(list(string))
|
||||
use_explicit_dry_run_spec = optional(bool, false)
|
||||
@@ -226,12 +235,13 @@ variable "service_perimeters_regular" {
|
||||
description = "Regular service perimeters."
|
||||
type = map(object({
|
||||
description = optional(string)
|
||||
title = optional(string)
|
||||
spec = optional(object({
|
||||
access_levels = optional(list(string))
|
||||
resources = optional(list(string))
|
||||
restricted_services = optional(list(string))
|
||||
egress_policies = optional(list(string))
|
||||
ingress_policies = optional(list(string))
|
||||
restricted_services = optional(list(string))
|
||||
resources = optional(list(string))
|
||||
vpc_accessible_services = optional(object({
|
||||
allowed_services = list(string)
|
||||
enable_restriction = optional(bool, true)
|
||||
@@ -239,13 +249,13 @@ variable "service_perimeters_regular" {
|
||||
}))
|
||||
status = optional(object({
|
||||
access_levels = optional(list(string))
|
||||
resources = optional(list(string))
|
||||
restricted_services = optional(list(string))
|
||||
egress_policies = optional(list(string))
|
||||
ingress_policies = optional(list(string))
|
||||
resources = optional(list(string))
|
||||
restricted_services = optional(list(string))
|
||||
vpc_accessible_services = optional(object({
|
||||
allowed_services = list(string)
|
||||
enable_restriction = bool
|
||||
enable_restriction = optional(bool, true)
|
||||
}))
|
||||
}))
|
||||
use_explicit_dry_run_spec = optional(bool, false)
|
||||
|
||||
Reference in New Issue
Block a user