Added tag factory option for organization module (#3178)
* Added tag factory option for organization module * added tags-factory tests * added tag factory for project module * missing header * added lookup catch for org tag values and fixed incorrect link in doco * fixed factory locals from copy/paste * added full doco/tests for project tags factory. fixed failed test looking for ID in yamls * added context option for factories_config to configure existing K/Vs --------- Co-authored-by: Ludovico Magnocavallo <ludomagno@google.com>
This commit is contained in:
@@ -26,6 +26,7 @@ To manage organization policies, the `orgpolicy.googleapis.com` service should b
|
||||
- [Custom Roles](#custom-roles)
|
||||
- [Custom Roles Factory](#custom-roles-factory)
|
||||
- [Tags](#tags)
|
||||
- [Tags Factory](#tags-factory)
|
||||
- [Files](#files)
|
||||
- [Variables](#variables)
|
||||
- [Outputs](#outputs)
|
||||
@@ -134,7 +135,7 @@ Refer to the [project module](../project/README.md#iam) for examples of the IAM
|
||||
|
||||
### Organization Policy Factory
|
||||
|
||||
See the [organization policy factory in the project module](../project#organization-policy-factory).
|
||||
See the [organization policy factory in the project module](../project/README.md#organization-policy-factory).
|
||||
|
||||
### Organization Policy Custom Constraints
|
||||
|
||||
@@ -512,6 +513,41 @@ module "org" {
|
||||
# tftest modules=1 resources=5 inventory=network-tags.yaml e2e serial
|
||||
```
|
||||
|
||||
### Tags Factory
|
||||
|
||||
Tags can also be specified via a factory in a similar way to organization policies and policy constraints. Each file is mapped to tag key, where
|
||||
|
||||
- the key name defaults to the file name but can be overridden via a `name` attribute in the yaml
|
||||
- The structure of the YAML file allows defining the `description`, `iam` bindings, and a map of `values` for the tag key, including their own descriptions and IAM.
|
||||
- Tags defined via the `tags` and `network_tags` variables are merged with those from the factory, and will override factory definitions in case of duplicate names.
|
||||
|
||||
The example below deploys a `cost-center` tag key and its values from a YAML file.
|
||||
|
||||
```hcl
|
||||
module "org" {
|
||||
source = "./fabric/modules/organization"
|
||||
organization_id = var.organization_id
|
||||
factories_config = {
|
||||
tags = "data/tags"
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=4 files=cost-center inventory=tags-factory.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# tftest-file id=cost-center path=data/tags/cost-center.yaml
|
||||
|
||||
description: "Tag for internal cost allocation."
|
||||
iam:
|
||||
"roles/resourcemanager.tagViewer":
|
||||
- "group:finance-team@example.com"
|
||||
values:
|
||||
engineering:
|
||||
description: "Engineering department."
|
||||
marketing:
|
||||
description: "Marketing department."
|
||||
```
|
||||
|
||||
<!-- TFDOC OPTS files:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
## Files
|
||||
@@ -524,7 +560,7 @@ module "org" {
|
||||
| [org-policy-custom-constraints.tf](./org-policy-custom-constraints.tf) | None | <code>google_org_policy_custom_constraint</code> |
|
||||
| [organization-policies.tf](./organization-policies.tf) | Organization-level organization policies. | <code>google_org_policy_policy</code> |
|
||||
| [outputs.tf](./outputs.tf) | Module outputs. | |
|
||||
| [tags.tf](./tags.tf) | None | <code>google_tags_tag_binding</code> · <code>google_tags_tag_key</code> · <code>google_tags_tag_key_iam_binding</code> · <code>google_tags_tag_key_iam_member</code> · <code>google_tags_tag_value</code> · <code>google_tags_tag_value_iam_binding</code> · <code>google_tags_tag_value_iam_member</code> |
|
||||
| [tags.tf](./tags.tf) | Manages GCP Secure Tags, keys, values, and IAM. | <code>google_tags_tag_binding</code> · <code>google_tags_tag_key</code> · <code>google_tags_tag_key_iam_binding</code> · <code>google_tags_tag_key_iam_member</code> · <code>google_tags_tag_value</code> · <code>google_tags_tag_value_iam_binding</code> · <code>google_tags_tag_value_iam_member</code> |
|
||||
| [variables-iam.tf](./variables-iam.tf) | None | |
|
||||
| [variables-logging.tf](./variables-logging.tf) | None | |
|
||||
| [variables-tags.tf](./variables-tags.tf) | None | |
|
||||
@@ -535,11 +571,11 @@ module "org" {
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [organization_id](variables.tf#L96) | Organization id in organizations/nnnnnn format. | <code>string</code> | ✓ | |
|
||||
| [organization_id](variables.tf#L99) | Organization id in organizations/nnnnnn format. | <code>string</code> | ✓ | |
|
||||
| [contacts](variables.tf#L17) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [custom_roles](variables.tf#L24) | Map of role name => list of permissions to create in this project. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [factories_config](variables.tf#L31) | Paths to data files and folders that enable factory functionality. | <code title="object({ custom_roles = optional(string) org_policies = optional(string) org_policy_custom_constraints = optional(string) context = optional(object({ org_policies = optional(map(map(string)), {}) }), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [firewall_policy](variables.tf#L45) | Hierarchical firewall policies to associate to the organization. | <code title="object({ name = string policy = string })">object({…})</code> | | <code>null</code> |
|
||||
| [factories_config](variables.tf#L31) | Paths to data files and folders that enable factory functionality. | <code title="object({ custom_roles = optional(string) org_policies = optional(string) org_policy_custom_constraints = optional(string) tags = optional(string) context = optional(object({ org_policies = optional(map(map(string)), {}) tag_keys = optional(map(string), {}) tag_values = optional(map(string), {}) }), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [firewall_policy](variables.tf#L48) | Hierarchical firewall policies to associate to the organization. | <code title="object({ name = string policy = string })">object({…})</code> | | <code>null</code> |
|
||||
| [iam](variables-iam.tf#L17) | IAM bindings, in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables-iam.tf#L24) | 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-iam.tf#L39) | 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> |
|
||||
@@ -550,8 +586,8 @@ module "org" {
|
||||
| [logging_settings](variables-logging.tf#L35) | Default settings for logging resources. | <code title="object({ disable_default_sink = optional(bool) storage_location = optional(string) })">object({…})</code> | | <code>null</code> |
|
||||
| [logging_sinks](variables-logging.tf#L45) | Logging sinks to create for the organization. | <code title="map(object({ bq_partitioned_table = optional(bool, false) description = optional(string) destination = string disabled = optional(bool, false) exclusions = optional(map(string), {}) filter = optional(string) iam = optional(bool, true) include_children = optional(bool, true) intercept_children = optional(bool, false) type = string }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key 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 organization module.") iam = optional(map(list(string)), {}) iam_bindings = optional(map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) iam_bindings_additive = optional(map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) id = optional(string) network = string # project_id/vpc_name values = optional(map(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) iam_bindings = optional(map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) iam_bindings_additive = optional(map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policies](variables.tf#L54) | Organization policies applied to this organization keyed by policy name. | <code title="map(object({ inherit_from_parent = optional(bool) # for list policies only. reset = optional(bool) rules = optional(list(object({ allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool) # for boolean policies only. condition = optional(object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }), {}) parameters = optional(string) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policy_custom_constraints](variables.tf#L82) | Organization policy custom constraints keyed by constraint name. | <code title="map(object({ display_name = optional(string) description = optional(string) action_type = string condition = string method_types = list(string) resource_types = list(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policies](variables.tf#L57) | Organization policies applied to this organization keyed by policy name. | <code title="map(object({ inherit_from_parent = optional(bool) # for list policies only. reset = optional(bool) rules = optional(list(object({ allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool) # for boolean policies only. condition = optional(object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }), {}) parameters = optional(string) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policy_custom_constraints](variables.tf#L85) | Organization policy custom constraints keyed by constraint name. | <code title="map(object({ display_name = optional(string) description = optional(string) action_type = string condition = string method_types = list(string) resource_types = list(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [tag_bindings](variables-tags.tf#L81) | Tag bindings for this organization, in key => tag value id format. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [tags](variables-tags.tf#L88) | 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 organization module.") iam = optional(map(list(string)), {}) iam_bindings = optional(map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) iam_bindings_additive = optional(map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) id = optional(string) values = optional(map(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) iam_bindings = optional(map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) iam_bindings_additive = optional(map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) id = optional(string) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* 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,
|
||||
@@ -14,14 +14,65 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Manages GCP Secure Tags, keys, values, and IAM.
|
||||
|
||||
locals {
|
||||
_factory_tags_data_path = pathexpand(coalesce(var.factories_config.tags, "-"))
|
||||
_factory_tags_data_raw = {
|
||||
for f in try(fileset(local._factory_tags_data_path, "*.yaml"), []) :
|
||||
f => yamldecode(file("${local._factory_tags_data_path}/${f}"))
|
||||
}
|
||||
_factory_tags_data = {
|
||||
for f, v_raw in local._factory_tags_data_raw :
|
||||
coalesce(lookup(v_raw, "name", null), trimsuffix(f, ".yaml")) => {
|
||||
description = lookup(v_raw, "description", null)
|
||||
iam = lookup(v_raw, "iam", {})
|
||||
iam_bindings = lookup(v_raw, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(v_raw, "iam_bindings_additive", {})
|
||||
network = lookup(v_raw, "network", null)
|
||||
values = {
|
||||
for vk, vv_raw in lookup(v_raw, "values", {}) : vk => {
|
||||
description = lookup(vv_raw, "description", null)
|
||||
iam = lookup(vv_raw, "iam", {})
|
||||
iam_bindings = lookup(vv_raw, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(vv_raw, "iam_bindings_additive", {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_tags_merged = merge(local._factory_tags_data, var.tags, var.network_tags)
|
||||
tags = {
|
||||
for k, v in local._tags_merged : k => {
|
||||
description = v.description
|
||||
iam = v.iam
|
||||
iam_bindings = v.iam_bindings
|
||||
iam_bindings_additive = v.iam_bindings_additive
|
||||
network = lookup(v, "network", null)
|
||||
id = try(coalesce(
|
||||
lookup(v, "id", null),
|
||||
lookup(var.factories_config.context.tag_keys, k, null)
|
||||
), null)
|
||||
values = {
|
||||
for vk, vv in lookup(v, "values", {}) : vk => {
|
||||
description = vv.description
|
||||
iam = vv.iam
|
||||
iam_bindings = vv.iam_bindings
|
||||
iam_bindings_additive = vv.iam_bindings_additive
|
||||
id = try(coalesce(
|
||||
lookup(vv, "id", null),
|
||||
lookup(var.factories_config.context.tag_values, "${k}/${vk}", null)
|
||||
), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_tag_iam = flatten([
|
||||
for k, v in local.tags : [
|
||||
for role in keys(v.iam) : {
|
||||
# we cycle on keys here so we don't risk injecting dynamic values
|
||||
for role in keys(lookup(v, "iam", {})) : {
|
||||
# We cycle on keys here so we don't risk injecting dynamic values.
|
||||
role = role
|
||||
tag = k
|
||||
tag_id = v.id
|
||||
tag_id = lookup(v, "id", null)
|
||||
}
|
||||
]
|
||||
])
|
||||
@@ -38,18 +89,18 @@ locals {
|
||||
])
|
||||
_tag_values = flatten([
|
||||
for k, v in local.tags : [
|
||||
for vk, vv in v.values : {
|
||||
for vk, vv in lookup(v, "values", {}) : {
|
||||
description = vv.description,
|
||||
key = "${k}/${vk}"
|
||||
iam_bindings = keys(vv.iam_bindings)
|
||||
iam_bindings_additive = keys(vv.iam_bindings_additive)
|
||||
id = try(vv.id, null)
|
||||
iam_bindings = keys(lookup(vv, "iam_bindings", {}))
|
||||
iam_bindings_additive = keys(lookup(vv, "iam_bindings_additive", {}))
|
||||
id = lookup(vv, "id", null)
|
||||
name = vk
|
||||
# we only store keys here so we don't risk injecting dynamic values
|
||||
roles = keys(vv.iam)
|
||||
roles = keys(lookup(vv, "iam", {}))
|
||||
tag = k
|
||||
tag_id = v.id
|
||||
tag_network = try(v.network, null) != null
|
||||
tag_id = lookup(v, "id", null)
|
||||
tag_network = lookup(v, "network", null) != null
|
||||
}
|
||||
]
|
||||
])
|
||||
@@ -58,19 +109,19 @@ locals {
|
||||
}
|
||||
tag_iam_bindings = merge([
|
||||
for k, v in local.tags : {
|
||||
for bk in keys(v.iam_bindings) : "${k}:${bk}" => {
|
||||
for bk in keys(lookup(v, "iam_bindings", {})) : "${k}:${bk}" => {
|
||||
binding = bk
|
||||
tag = k
|
||||
tag_id = v.id
|
||||
tag_id = lookup(v, "id", null)
|
||||
}
|
||||
}
|
||||
]...)
|
||||
tag_iam_bindings_additive = merge([
|
||||
for k, v in local.tags : {
|
||||
for bk in keys(v.iam_bindings_additive) : "${k}:${bk}" => {
|
||||
for bk in keys(lookup(v, "iam_bindings_additive", {})) : "${k}:${bk}" => {
|
||||
binding = bk
|
||||
tag = k
|
||||
tag_id = v.id
|
||||
tag_id = lookup(v, "id", null)
|
||||
}
|
||||
}
|
||||
]...)
|
||||
@@ -104,13 +155,12 @@ locals {
|
||||
tag_values = {
|
||||
for v in local._tag_values : v.key => v
|
||||
}
|
||||
tags = merge(var.tags, var.network_tags)
|
||||
}
|
||||
|
||||
# keys
|
||||
|
||||
resource "google_tags_tag_key" "default" {
|
||||
for_each = { for k, v in local.tags : k => v if v.id == null }
|
||||
for_each = { for k, v in local.tags : k => v if lookup(v, "id", null) == null }
|
||||
parent = var.organization_id
|
||||
purpose = (
|
||||
lookup(each.value, "network", null) == null ? null : "GCE_FIREWALL"
|
||||
@@ -167,7 +217,7 @@ resource "google_tags_tag_key_iam_member" "bindings" {
|
||||
# values
|
||||
|
||||
resource "google_tags_tag_value" "default" {
|
||||
for_each = { for k, v in local.tag_values : k => v if v.id == null }
|
||||
for_each = { for k, v in local.tag_values : k => v if lookup(v, "id", null) == null }
|
||||
parent = (
|
||||
each.value.tag_id == null
|
||||
? google_tags_tag_key.default[each.value.tag].id
|
||||
|
||||
@@ -34,8 +34,11 @@ variable "factories_config" {
|
||||
custom_roles = optional(string)
|
||||
org_policies = optional(string)
|
||||
org_policy_custom_constraints = optional(string)
|
||||
tags = optional(string)
|
||||
context = optional(object({
|
||||
org_policies = optional(map(map(string)), {})
|
||||
tag_keys = optional(map(string), {})
|
||||
tag_values = optional(map(string), {})
|
||||
}), {})
|
||||
})
|
||||
nullable = false
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -14,14 +14,65 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Manages GCP Secure Tags, keys, values, and IAM.
|
||||
|
||||
locals {
|
||||
_factory_tags_data_path = pathexpand(coalesce(var.factories_config.tags, "-"))
|
||||
_factory_tags_data_raw = {
|
||||
for f in try(fileset(local._factory_tags_data_path, "*.yaml"), []) :
|
||||
f => yamldecode(file("${local._factory_tags_data_path}/${f}"))
|
||||
}
|
||||
_factory_tags_data = {
|
||||
for f, v_raw in local._factory_tags_data_raw :
|
||||
coalesce(lookup(v_raw, "name", null), trimsuffix(f, ".yaml")) => {
|
||||
description = lookup(v_raw, "description", null)
|
||||
iam = lookup(v_raw, "iam", {})
|
||||
iam_bindings = lookup(v_raw, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(v_raw, "iam_bindings_additive", {})
|
||||
network = lookup(v_raw, "network", null)
|
||||
values = {
|
||||
for vk, vv_raw in lookup(v_raw, "values", {}) : vk => {
|
||||
description = lookup(vv_raw, "description", null)
|
||||
iam = lookup(vv_raw, "iam", {})
|
||||
iam_bindings = lookup(vv_raw, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(vv_raw, "iam_bindings_additive", {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_tags_merged = merge(local._factory_tags_data, var.tags, var.network_tags)
|
||||
tags = {
|
||||
for k, v in local._tags_merged : k => {
|
||||
description = v.description
|
||||
iam = v.iam
|
||||
iam_bindings = v.iam_bindings
|
||||
iam_bindings_additive = v.iam_bindings_additive
|
||||
network = lookup(v, "network", null)
|
||||
id = try(coalesce(
|
||||
lookup(v, "id", null),
|
||||
lookup(var.factories_config.context.tag_keys, k, null)
|
||||
), null)
|
||||
values = {
|
||||
for vk, vv in lookup(v, "values", {}) : vk => {
|
||||
description = vv.description
|
||||
iam = vv.iam
|
||||
iam_bindings = vv.iam_bindings
|
||||
iam_bindings_additive = vv.iam_bindings_additive
|
||||
id = try(coalesce(
|
||||
lookup(vv, "id", null),
|
||||
lookup(var.factories_config.context.tag_values, "${k}/${vk}", null)
|
||||
), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_tag_iam = flatten([
|
||||
for k, v in local.tags : [
|
||||
for role in keys(v.iam) : {
|
||||
for role in keys(lookup(v, "iam", {})) : {
|
||||
# we cycle on keys here so we don't risk injecting dynamic values
|
||||
role = role
|
||||
tag = k
|
||||
tag_id = v.id
|
||||
tag_id = lookup(v, "id", null)
|
||||
}
|
||||
]
|
||||
])
|
||||
@@ -38,18 +89,18 @@ locals {
|
||||
])
|
||||
_tag_values = flatten([
|
||||
for k, v in local.tags : [
|
||||
for vk, vv in v.values : {
|
||||
for vk, vv in lookup(v, "values", {}) : {
|
||||
description = vv.description,
|
||||
key = "${k}/${vk}"
|
||||
iam_bindings = keys(vv.iam_bindings)
|
||||
iam_bindings_additive = keys(vv.iam_bindings_additive)
|
||||
id = try(vv.id, null)
|
||||
iam_bindings = keys(lookup(vv, "iam_bindings", {}))
|
||||
iam_bindings_additive = keys(lookup(vv, "iam_bindings_additive", {}))
|
||||
id = lookup(vv, "id", null)
|
||||
name = vk
|
||||
# we only store keys here so we don't risk injecting dynamic values
|
||||
roles = keys(vv.iam)
|
||||
roles = keys(lookup(vv, "iam", {}))
|
||||
tag = k
|
||||
tag_id = v.id
|
||||
tag_network = try(v.network, null) != null
|
||||
tag_id = lookup(v, "id", null)
|
||||
tag_network = lookup(v, "network", null) != null
|
||||
}
|
||||
]
|
||||
])
|
||||
@@ -58,19 +109,19 @@ locals {
|
||||
}
|
||||
tag_iam_bindings = merge([
|
||||
for k, v in local.tags : {
|
||||
for bk in keys(v.iam_bindings) : "${k}:${bk}" => {
|
||||
for bk in keys(lookup(v, "iam_bindings", {})) : "${k}:${bk}" => {
|
||||
binding = bk
|
||||
tag = k
|
||||
tag_id = v.id
|
||||
tag_id = lookup(v, "id", null)
|
||||
}
|
||||
}
|
||||
]...)
|
||||
tag_iam_bindings_additive = merge([
|
||||
for k, v in local.tags : {
|
||||
for bk in keys(v.iam_bindings_additive) : "${k}:${bk}" => {
|
||||
for bk in keys(lookup(v, "iam_bindings_additive", {})) : "${k}:${bk}" => {
|
||||
binding = bk
|
||||
tag = k
|
||||
tag_id = v.id
|
||||
tag_id = lookup(v, "id", null)
|
||||
}
|
||||
}
|
||||
]...)
|
||||
@@ -104,13 +155,12 @@ locals {
|
||||
tag_values = {
|
||||
for v in local._tag_values : v.key => v
|
||||
}
|
||||
tags = merge(var.tags, var.network_tags)
|
||||
}
|
||||
|
||||
# keys
|
||||
|
||||
resource "google_tags_tag_key" "default" {
|
||||
for_each = { for k, v in local.tags : k => v if v.id == null }
|
||||
for_each = { for k, v in local.tags : k => v if lookup(v, "id", null) == null }
|
||||
parent = "projects/${local.project.project_id}"
|
||||
purpose = (
|
||||
lookup(each.value, "network", null) == null ? null : "GCE_FIREWALL"
|
||||
@@ -167,7 +217,7 @@ resource "google_tags_tag_key_iam_member" "bindings" {
|
||||
# values
|
||||
|
||||
resource "google_tags_tag_value" "default" {
|
||||
for_each = { for k, v in local.tag_values : k => v if v.id == null }
|
||||
for_each = { for k, v in local.tag_values : k => v if lookup(v, "id", null) == null }
|
||||
parent = (
|
||||
each.value.tag_id == null
|
||||
? google_tags_tag_key.default[each.value.tag].id
|
||||
|
||||
@@ -90,9 +90,12 @@ variable "factories_config" {
|
||||
observability = optional(string)
|
||||
org_policies = optional(string)
|
||||
quotas = optional(string)
|
||||
tags = optional(string)
|
||||
context = optional(object({
|
||||
notification_channels = optional(map(string), {})
|
||||
org_policies = optional(map(map(string)), {})
|
||||
tag_keys = optional(map(string), {})
|
||||
tag_values = optional(map(string), {})
|
||||
}), {})
|
||||
})
|
||||
nullable = false
|
||||
|
||||
42
tests/modules/organization/examples/tags-factory.yaml
Normal file
42
tests/modules/organization/examples/tags-factory.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
# Copyright 2023 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.org.google_tags_tag_key.default["cost-center"]:
|
||||
description: Tag for internal cost allocation.
|
||||
parent: organizations/1122334455
|
||||
purpose: null
|
||||
purpose_data: null
|
||||
short_name: cost-center
|
||||
timeouts: null
|
||||
module.org.google_tags_tag_key_iam_binding.default["cost-center:roles/resourcemanager.tagViewer"]:
|
||||
condition: []
|
||||
members:
|
||||
- group:finance-team@example.com
|
||||
role: roles/resourcemanager.tagViewer
|
||||
module.org.google_tags_tag_value.default["cost-center/engineering"]:
|
||||
description: Engineering department.
|
||||
short_name: engineering
|
||||
timeouts: null
|
||||
module.org.google_tags_tag_value.default["cost-center/marketing"]:
|
||||
description: "Marketing department."
|
||||
short_name: marketing
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_tags_tag_key: 1
|
||||
google_tags_tag_key_iam_binding: 1
|
||||
google_tags_tag_value: 2
|
||||
modules: 1
|
||||
resources: 4
|
||||
53
tests/modules/project/examples/tags-factory.yaml
Normal file
53
tests/modules/project/examples/tags-factory.yaml
Normal file
@@ -0,0 +1,53 @@
|
||||
# Copyright 2023 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_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: 123456-123456-123456
|
||||
deletion_policy: 'DELETE'
|
||||
folder_id: '1122334455'
|
||||
labels: null
|
||||
name: test-project
|
||||
org_id: null
|
||||
project_id: test-project
|
||||
timeouts: null
|
||||
module.project.google_tags_tag_key.default["workloads"]:
|
||||
description: Tag for workload classifications.
|
||||
parent: projects/test-project
|
||||
purpose: null
|
||||
purpose_data: null
|
||||
short_name: workloads
|
||||
timeouts: null
|
||||
module.project.google_tags_tag_key_iam_binding.default["workloads:roles/resourcemanager.tagViewer"]:
|
||||
condition: []
|
||||
members:
|
||||
- group:devops@example.com
|
||||
role: roles/resourcemanager.tagViewer
|
||||
module.project.google_tags_tag_value.default["workloads/frontend"]:
|
||||
description: "Frontend workloads."
|
||||
short_name: frontend
|
||||
timeouts: null
|
||||
module.project.google_tags_tag_value.default["workloads/backend"]:
|
||||
description: "Backend workloads."
|
||||
short_name: backend
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_project: 1
|
||||
google_tags_tag_key: 1
|
||||
google_tags_tag_key_iam_binding: 1
|
||||
google_tags_tag_value: 2
|
||||
modules: 1
|
||||
resources: 5
|
||||
Reference in New Issue
Block a user