Add network tags support to the organization module (#979)
This commit is contained in:
parent
dea1a778f6
commit
4124ef41fc
|
@ -375,7 +375,7 @@ module "org" {
|
|||
"roles/resourcemanager.tagAdmin" = ["group:admins@example.com"]
|
||||
}
|
||||
values = {
|
||||
dev = null
|
||||
dev = {}
|
||||
prod = {
|
||||
description = "Environment: production."
|
||||
iam = {
|
||||
|
@ -393,6 +393,34 @@ module "org" {
|
|||
# tftest modules=1 resources=7
|
||||
```
|
||||
|
||||
You can also define network tags, through a dedicated variable *network_tags*:
|
||||
|
||||
```hcl
|
||||
module "org" {
|
||||
source = "./fabric/modules/organization"
|
||||
organization_id = var.organization_id
|
||||
network_tags = {
|
||||
net-environment = {
|
||||
description = "This is a network tag."
|
||||
network = "my_project/my_vpc"
|
||||
iam = {
|
||||
"roles/resourcemanager.tagAdmin" = ["group:admins@example.com"]
|
||||
}
|
||||
values = {
|
||||
dev = null
|
||||
prod = {
|
||||
description = "Environment: production."
|
||||
iam = {
|
||||
"roles/resourcemanager.tagUser" = ["user:user1@example.com"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=5
|
||||
```
|
||||
|
||||
<!-- TFDOC OPTS files:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
|
@ -415,7 +443,7 @@ module "org" {
|
|||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [organization_id](variables.tf#L225) | Organization id in organizations/nnnnnn format. | <code>string</code> | ✓ | |
|
||||
| [organization_id](variables.tf#L246) | 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> |
|
||||
| [firewall_policies](variables.tf#L31) | Hierarchical firewall policy rules created in the organization. | <code title="map(map(object({ action = string description = string direction = string logging = bool ports = map(list(string)) priority = number ranges = list(string) target_resources = list(string) target_service_accounts = list(string) })))">map(map(object({…})))</code> | | <code>{}</code> |
|
||||
|
@ -430,24 +458,27 @@ module "org" {
|
|||
| [iam_bindings_authoritative](variables.tf#L116) | IAM authoritative bindings, in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared. Bindings should also be authoritative when using authoritative audit config. Use with caution. | <code>map(list(string))</code> | | <code>null</code> |
|
||||
| [logging_exclusions](variables.tf#L122) | Logging exclusions for this organization in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L129) | Logging sinks to create for the organization. | <code title="map(object({ bq_partitioned_table = optional(bool) description = optional(string) destination = string disabled = optional(bool, false) exclusions = optional(map(string), {}) filter = string include_children = optional(bool, true) type = string }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policies](variables.tf#L159) | 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) allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool, true) # for boolean policies only. 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, true) # for boolean policies only. condition = object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policies_data_path](variables.tf#L199) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |
|
||||
| [org_policy_custom_constraints](variables.tf#L205) | Organization policiy 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_policy_custom_constraints_data_path](variables.tf#L219) | Path containing org policy custom constraints in YAML format. | <code>string</code> | | <code>null</code> |
|
||||
| [tag_bindings](variables.tf#L235) | Tag bindings for this organization, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [tags](variables.tf#L241) | Tags by key name. The `iam` attribute behaves like the similarly named one at module level. | <code title="map(object({ description = string iam = map(list(string)) values = map(object({ description = string iam = map(list(string)) })) }))">map(object({…}))</code> | | <code>null</code> |
|
||||
| [network_tags](variables.tf#L159) | Network tags by key name. 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)), {}) network = string # project_id/vpc_name values = optional(map(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policies](variables.tf#L180) | 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) allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool, true) # for boolean policies only. 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, true) # for boolean policies only. condition = object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policies_data_path](variables.tf#L220) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |
|
||||
| [org_policy_custom_constraints](variables.tf#L226) | Organization policiy 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_policy_custom_constraints_data_path](variables.tf#L240) | Path containing org policy custom constraints in YAML format. | <code>string</code> | | <code>null</code> |
|
||||
| [tag_bindings](variables.tf#L275) | Tag bindings for this organization, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [tags](variables.tf#L255) | Tags by key name. 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)), {}) values = optional(map(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [custom_role_id](outputs.tf#L18) | Map of custom role IDs created in the organization. | |
|
||||
| [custom_roles](outputs.tf#L31) | Map of custom roles resources created in the organization. | |
|
||||
| [firewall_policies](outputs.tf#L36) | Map of firewall policy resources created in the organization. | |
|
||||
| [firewall_policy_id](outputs.tf#L41) | Map of firewall policy ids created in the organization. | |
|
||||
| [organization_id](outputs.tf#L46) | Organization id dependent on module resources. | |
|
||||
| [sink_writer_identities](outputs.tf#L63) | Writer identities created for each sink. | |
|
||||
| [tag_keys](outputs.tf#L71) | Tag key resources. | |
|
||||
| [tag_values](outputs.tf#L78) | Tag value resources. | |
|
||||
| [custom_role_id](outputs.tf#L17) | Map of custom role IDs created in the organization. | |
|
||||
| [custom_roles](outputs.tf#L30) | Map of custom roles resources created in the organization. | |
|
||||
| [firewall_policies](outputs.tf#L35) | Map of firewall policy resources created in the organization. | |
|
||||
| [firewall_policy_id](outputs.tf#L40) | Map of firewall policy ids created in the organization. | |
|
||||
| [network_tag_keys](outputs.tf#L45) | Tag key resources. | |
|
||||
| [network_tag_values](outputs.tf#L52) | Tag value resources. | |
|
||||
| [organization_id](outputs.tf#L60) | Organization id dependent on module resources. | |
|
||||
| [sink_writer_identities](outputs.tf#L77) | Writer identities created for each sink. | |
|
||||
| [tag_keys](outputs.tf#L85) | Tag key resources. | |
|
||||
| [tag_values](outputs.tf#L92) | Tag value resources. | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
output "custom_role_id" {
|
||||
description = "Map of custom role IDs created in the organization."
|
||||
value = {
|
||||
|
@ -43,6 +42,21 @@ output "firewall_policy_id" {
|
|||
value = { for k, v in google_compute_firewall_policy.policy : k => v.id }
|
||||
}
|
||||
|
||||
output "network_tag_keys" {
|
||||
description = "Tag key resources."
|
||||
value = {
|
||||
for k, v in google_tags_tag_key.default : k => v if v.purpose != null
|
||||
}
|
||||
}
|
||||
|
||||
output "network_tag_values" {
|
||||
description = "Tag value resources."
|
||||
value = {
|
||||
for k, v in google_tags_tag_value.default
|
||||
: k => v if google_tags_tag_key.default[split("/", k)[0]].purpose != null
|
||||
}
|
||||
}
|
||||
|
||||
output "organization_id" {
|
||||
description = "Organization id dependent on module resources."
|
||||
value = var.organization_id
|
||||
|
@ -71,13 +85,14 @@ output "sink_writer_identities" {
|
|||
output "tag_keys" {
|
||||
description = "Tag key resources."
|
||||
value = {
|
||||
for k, v in google_tags_tag_key.default : k => v
|
||||
for k, v in google_tags_tag_key.default : k => v if v.purpose == null
|
||||
}
|
||||
}
|
||||
|
||||
output "tag_values" {
|
||||
description = "Tag value resources."
|
||||
value = {
|
||||
for k, v in google_tags_tag_value.default : k => v
|
||||
for k, v in google_tags_tag_value.default
|
||||
: k => v if google_tags_tag_key.default[split("/", k)[0]].purpose == null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,10 +55,7 @@ locals {
|
|||
tag_values_iam = {
|
||||
for t in local._tag_values_iam : "${t.key}:${t.role}" => t
|
||||
}
|
||||
tags = {
|
||||
for k, v in coalesce(var.tags, {}) :
|
||||
k => v == null ? { description = null, iam = {}, values = null } : v
|
||||
}
|
||||
tags = merge(var.tags, var.network_tags)
|
||||
tags_iam = {
|
||||
for t in local._tags_iam : "${t.tag}:${t.role}" => t
|
||||
}
|
||||
|
@ -69,11 +66,14 @@ locals {
|
|||
resource "google_tags_tag_key" "default" {
|
||||
for_each = local.tags
|
||||
parent = var.organization_id
|
||||
short_name = each.key
|
||||
description = coalesce(
|
||||
each.value.description,
|
||||
"Managed by the Terraform organization module."
|
||||
purpose = (
|
||||
lookup(each.value, "network", null) == null ? null : "GCE_FIREWALL"
|
||||
)
|
||||
purpose_data = (
|
||||
lookup(each.value, "network", null) == null ? null : { network = each.value.network }
|
||||
)
|
||||
short_name = each.key
|
||||
description = each.value.description
|
||||
depends_on = [
|
||||
google_organization_iam_binding.authoritative,
|
||||
google_organization_iam_member.additive,
|
||||
|
@ -96,10 +96,7 @@ resource "google_tags_tag_value" "default" {
|
|||
for_each = local.tag_values
|
||||
parent = google_tags_tag_key.default[each.value.tag].id
|
||||
short_name = each.value.name
|
||||
description = coalesce(
|
||||
each.value.description,
|
||||
"Managed by the Terraform organization module."
|
||||
)
|
||||
description = each.value.description
|
||||
}
|
||||
|
||||
resource "google_tags_tag_value_iam_binding" "default" {
|
||||
|
|
|
@ -156,6 +156,27 @@ variable "logging_sinks" {
|
|||
}
|
||||
}
|
||||
|
||||
variable "network_tags" {
|
||||
description = "Network tags by key name. The `iam` attribute behaves like the similarly named one at module level."
|
||||
type = map(object({
|
||||
description = optional(string, "Managed by the Terraform organization module.")
|
||||
iam = optional(map(list(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)), {})
|
||||
})), {})
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for k, v in var.network_tags : v != null
|
||||
])
|
||||
error_message = "Use an empty map instead of null as value."
|
||||
}
|
||||
}
|
||||
|
||||
variable "org_policies" {
|
||||
description = "Organization policies applied to this organization keyed by policy name."
|
||||
type = map(object({
|
||||
|
@ -231,22 +252,28 @@ variable "organization_id" {
|
|||
}
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Tags by key name. The `iam` attribute behaves like the similarly named one at module level."
|
||||
type = map(object({
|
||||
description = optional(string, "Managed by the Terraform organization module.")
|
||||
iam = optional(map(list(string)), {})
|
||||
values = optional(map(object({
|
||||
description = optional(string, "Managed by the Terraform organization module.")
|
||||
iam = optional(map(list(string)), {})
|
||||
})), {})
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for k, v in var.tags : v != null
|
||||
])
|
||||
error_message = "Use an empty map instead of null as value."
|
||||
}
|
||||
}
|
||||
|
||||
variable "tag_bindings" {
|
||||
description = "Tag bindings for this organization, in key => tag value id format."
|
||||
type = map(string)
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Tags by key name. The `iam` attribute behaves like the similarly named one at module level."
|
||||
type = map(object({
|
||||
description = string
|
||||
iam = map(list(string))
|
||||
values = map(object({
|
||||
description = string
|
||||
iam = map(list(string))
|
||||
}))
|
||||
}))
|
||||
default = null
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ module "test" {
|
|||
iam_audit_config = var.iam_audit_config
|
||||
logging_sinks = var.logging_sinks
|
||||
logging_exclusions = var.logging_exclusions
|
||||
network_tags = var.network_tags
|
||||
org_policies = var.org_policies
|
||||
org_policies_data_path = var.org_policies_data_path
|
||||
org_policy_custom_constraints = var.org_policy_custom_constraints
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
network_tags = {
|
||||
net_environment = {
|
||||
network = "foobar"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
tags = {
|
||||
foo = {}
|
||||
bar = {
|
||||
description = null
|
||||
iam = null
|
||||
values = null
|
||||
}
|
||||
foobar = {
|
||||
description = "Foobar tag."
|
||||
iam = {
|
||||
"roles/resourcemanager.tagAdmin" = [
|
||||
"user:user1@example.com", "user:user2@example.com"
|
||||
]
|
||||
}
|
||||
values = {
|
||||
one = null
|
||||
two = {
|
||||
description = "Foobar 2."
|
||||
iam = {
|
||||
"roles/resourcemanager.tagViewer" = [
|
||||
"user:user3@example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
three = {
|
||||
description = "Foobar 3."
|
||||
iam = {
|
||||
"roles/resourcemanager.tagViewer" = [
|
||||
"user:user3@example.com"
|
||||
]
|
||||
"roles/resourcemanager.tagAdmin" = [
|
||||
"user:user4@example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -69,6 +69,11 @@ variable "logging_exclusions" {
|
|||
default = {}
|
||||
}
|
||||
|
||||
variable "network_tags" {
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "org_policies" {
|
||||
type = any
|
||||
default = {}
|
||||
|
|
|
@ -13,47 +13,9 @@
|
|||
# limitations under the License.
|
||||
|
||||
|
||||
def test_keys(plan_runner):
|
||||
'Test tag keys.'
|
||||
tags = '''{
|
||||
foo = null
|
||||
bar = {
|
||||
description = null
|
||||
iam = null
|
||||
values = null
|
||||
}
|
||||
foobar = {
|
||||
description = "Foobar tag."
|
||||
iam = {
|
||||
"roles/resourcemanager.tagAdmin" = [
|
||||
"user:user1@example.com", "user:user2@example.com"
|
||||
]
|
||||
}
|
||||
values = {
|
||||
one = null
|
||||
two = {
|
||||
description = "Foobar 2."
|
||||
iam = {
|
||||
"roles/resourcemanager.tagViewer" = [
|
||||
"user:user3@example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
three = {
|
||||
description = "Foobar 3."
|
||||
iam = {
|
||||
"roles/resourcemanager.tagViewer" = [
|
||||
"user:user3@example.com"
|
||||
]
|
||||
"roles/resourcemanager.tagAdmin" = [
|
||||
"user:user4@example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}'''
|
||||
_, resources = plan_runner(tags=tags)
|
||||
def test_resource_tags(plan_runner):
|
||||
'Test resource tags.'
|
||||
_, resources = plan_runner(tf_var_file='test.resource_tags.tfvars')
|
||||
assert len(resources) == 10
|
||||
resource_values = {}
|
||||
for r in resources:
|
||||
|
@ -64,12 +26,25 @@ def test_keys(plan_runner):
|
|||
r['role'] for r in resource_values['google_tags_tag_value_iam_binding']
|
||||
]
|
||||
expected = [
|
||||
'roles/resourcemanager.tagAdmin', 'roles/resourcemanager.tagViewer',
|
||||
'roles/resourcemanager.tagAdmin',
|
||||
'roles/resourcemanager.tagViewer',
|
||||
'roles/resourcemanager.tagViewer'
|
||||
]
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_network_tags(plan_runner):
|
||||
'Test network tags.'
|
||||
_, resources = plan_runner(tf_var_file='test.network_tags.tfvars')
|
||||
assert len(resources) == 1
|
||||
resource_values = {}
|
||||
for r in resources:
|
||||
resource_values.setdefault(r['type'], []).append(r['values'])
|
||||
google_tags_tag_key = resource_values['google_tags_tag_key'][0]
|
||||
assert google_tags_tag_key['purpose'] == "GCE_FIREWALL"
|
||||
assert google_tags_tag_key['purpose_data']['network'] == "foobar"
|
||||
|
||||
|
||||
def test_bindings(plan_runner):
|
||||
'Test tag bindings.'
|
||||
tag_bindings = '{foo = "tagValues/123456789012"}'
|
||||
|
|
Loading…
Reference in New Issue