Update resman modules (#475)

* Make logging sinks in different resources use the same API

* Split resman modules in multiple files. Add nullables where applicable
This commit is contained in:
Julio Castillo
2022-01-29 19:35:33 +01:00
committed by GitHub
parent eb0ab2c1ec
commit e2abd772f2
20 changed files with 340 additions and 243 deletions

View File

@@ -140,7 +140,7 @@ module "folder-sink" {
name = "my-folder"
logging_sinks = {
warnings = {
type = "gcs"
type = "storage"
destination = module.gcs.name
filter = "severity=WARNING"
include_children = true
@@ -215,26 +215,41 @@ module "folder2" {
}
# tftest modules=2 resources=6
```
<!-- TFDOC OPTS files:1 -->
<!-- BEGIN TFDOC -->
## Files
| name | description | resources |
|---|---|---|
| [firewal_policies.tf](./firewal_policies.tf) | None | <code>google_compute_firewall_policy</code> · <code>google_compute_firewall_policy_association</code> · <code>google_compute_firewall_policy_rule</code> |
| [iam.tf](./iam.tf) | IAM bindings, roles and audit logging resources. | <code>google_folder_iam_binding</code> |
| [logging.tf](./logging.tf) | Log sinks and supporting resources. | <code>google_bigquery_dataset_iam_member</code> · <code>google_logging_folder_exclusion</code> · <code>google_logging_folder_sink</code> · <code>google_project_iam_member</code> · <code>google_pubsub_topic_iam_member</code> · <code>google_storage_bucket_iam_member</code> |
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_essential_contacts_contact</code> · <code>google_folder</code> |
| [organization_policies.tf](./organization_policies.tf) | Folder-level organization policies. | <code>google_folder_organization_policy</code> |
| [outputs.tf](./outputs.tf) | Module outputs. | |
| [variables.tf](./variables.tf) | Module variables. | |
| [versions.tf](./versions.tf) | Version pins. | |
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [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&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policies](variables.tf#L23) | Hierarchical firewall policies created in this folder. | <code title="map&#40;map&#40;object&#40;&#123;&#10; action &#61; string&#10; description &#61; string&#10; direction &#61; string&#10; logging &#61; bool&#10; ports &#61; map&#40;list&#40;string&#41;&#41;&#10; priority &#61; number&#10; ranges &#61; list&#40;string&#41;&#10; target_resources &#61; list&#40;string&#41;&#10; target_service_accounts &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;&#41;">map&#40;map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy_association](variables.tf#L39) | The hierarchical firewall policy to associate to this folder. Must be either a key in the `firewall_policies` map or the id of a policy defined somewhere else. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy_factory](variables.tf#L45) | Configuration for the firewall policy factory. | <code title="object&#40;&#123;&#10; cidr_file &#61; string&#10; policy_name &#61; string&#10; rules_file &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [folder_create](variables.tf#L55) | Create folder. When set to false, uses id to reference an existing folder. | <code>bool</code> | | <code>true</code> |
| [group_iam](variables.tf#L61) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L67) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [id](variables.tf#L73) | Folder ID in case you use folder_create=false | <code>string</code> | | <code>null</code> |
| [logging_exclusions](variables.tf#L79) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L85) | Logging sinks to create for this folder. | <code title="map&#40;object&#40;&#123;&#10; destination &#61; string&#10; type &#61; string&#10; filter &#61; string&#10; include_children &#61; bool&#10; exclusions &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [name](variables.tf#L98) | Folder name. | <code>string</code> | | <code>null</code> |
| [parent](variables.tf#L104) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> |
| [policy_boolean](variables.tf#L114) | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code>map&#40;bool&#41;</code> | | <code>&#123;&#125;</code> |
| [policy_list](variables.tf#L120) | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; bool&#10; suggested_value &#61; string&#10; status &#61; bool&#10; values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policies](variables.tf#L24) | Hierarchical firewall policies created in this folder. | <code title="map&#40;map&#40;object&#40;&#123;&#10; action &#61; string&#10; description &#61; string&#10; direction &#61; string&#10; logging &#61; bool&#10; ports &#61; map&#40;list&#40;string&#41;&#41;&#10; priority &#61; number&#10; ranges &#61; list&#40;string&#41;&#10; target_resources &#61; list&#40;string&#41;&#10; target_service_accounts &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;&#41;">map&#40;map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy_association](variables.tf#L41) | The hierarchical firewall policy to associate to this folder. Must be either a key in the `firewall_policies` map or the id of a policy defined somewhere else. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy_factory](variables.tf#L48) | Configuration for the firewall policy factory. | <code title="object&#40;&#123;&#10; cidr_file &#61; string&#10; policy_name &#61; string&#10; rules_file &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [folder_create](variables.tf#L58) | Create folder. When set to false, uses id to reference an existing folder. | <code>bool</code> | | <code>true</code> |
| [group_iam](variables.tf#L64) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L71) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [id](variables.tf#L78) | Folder ID in case you use folder_create=false | <code>string</code> | | <code>null</code> |
| [logging_exclusions](variables.tf#L84) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L91) | Logging sinks to create for this folder. | <code title="map&#40;object&#40;&#123;&#10; destination &#61; string&#10; type &#61; string&#10; filter &#61; string&#10; include_children &#61; bool&#10; exclusions &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [name](variables.tf#L112) | Folder name. | <code>string</code> | | <code>null</code> |
| [parent](variables.tf#L118) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> |
| [policy_boolean](variables.tf#L128) | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code>map&#40;bool&#41;</code> | | <code>&#123;&#125;</code> |
| [policy_list](variables.tf#L135) | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; bool&#10; suggested_value &#61; string&#10; status &#61; bool&#10; values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs

40
modules/folder/iam.tf Normal file
View File

@@ -0,0 +1,40 @@
/**
* Copyright 2022 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.
*/
# tfdoc:file:description IAM bindings, roles and audit logging resources.
locals {
group_iam_roles = distinct(flatten(values(var.group_iam)))
group_iam = {
for r in local.group_iam_roles : r => [
for k, v in var.group_iam : "group:${k}" if try(index(v, r), null) != null
]
}
iam = {
for role in distinct(concat(keys(var.iam), keys(local.group_iam))) :
role => concat(
try(var.iam[role], []),
try(local.group_iam[role], [])
)
}
}
resource "google_folder_iam_binding" "authoritative" {
for_each = local.iam
folder = local.folder.name
role = each.key
members = each.value
}

91
modules/folder/logging.tf Normal file
View File

@@ -0,0 +1,91 @@
/**
* Copyright 2022 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.
*/
# tfdoc:file:description Log sinks and supporting resources.
locals {
sink_bindings = {
for type in ["bigquery", "pubsub", "logging", "storage"] :
type => {
for name, sink in var.logging_sinks :
name => sink
if sink.type == type
}
}
}
resource "google_logging_folder_sink" "sink" {
for_each = var.logging_sinks
name = each.key
#description = "${each.key} (Terraform-managed)"
folder = local.folder.name
destination = "${each.value.type}.googleapis.com/${each.value.destination}"
filter = each.value.filter
include_children = each.value.include_children
dynamic "exclusions" {
for_each = each.value.exclusions
iterator = exclusion
content {
name = exclusion.key
filter = exclusion.value
}
}
depends_on = [
google_folder_iam_binding.authoritative
]
}
resource "google_storage_bucket_iam_member" "gcs-sinks-binding" {
for_each = local.sink_bindings["storage"]
bucket = each.value.destination
role = "roles/storage.objectCreator"
member = google_logging_folder_sink.sink[each.key].writer_identity
}
resource "google_bigquery_dataset_iam_member" "bq-sinks-binding" {
for_each = local.sink_bindings["bigquery"]
project = split("/", each.value.destination)[1]
dataset_id = split("/", each.value.destination)[3]
role = "roles/bigquery.dataEditor"
member = google_logging_folder_sink.sink[each.key].writer_identity
}
resource "google_pubsub_topic_iam_member" "pubsub-sinks-binding" {
for_each = local.sink_bindings["pubsub"]
project = split("/", each.value.destination)[1]
topic = split("/", each.value.destination)[3]
role = "roles/pubsub.publisher"
member = google_logging_folder_sink.sink[each.key].writer_identity
}
resource "google_project_iam_member" "bucket-sinks-binding" {
for_each = local.sink_bindings["logging"]
project = split("/", each.value.destination)[1]
role = "roles/logging.bucketWriter"
member = google_logging_folder_sink.sink[each.key].writer_identity
# TODO(jccb): use a condition to limit writer-identity only to this
# bucket
}
resource "google_logging_folder_exclusion" "logging-exclusion" {
for_each = var.logging_exclusions
name = each.key
folder = local.folder.name
description = "${each.key} (Terraform-managed)"
filter = each.value
}

View File

@@ -15,34 +15,6 @@
*/
locals {
group_iam_roles = distinct(flatten(values(var.group_iam)))
group_iam = {
for r in local.group_iam_roles : r => [
for k, v in var.group_iam : "group:${k}" if try(index(v, r), null) != null
]
}
iam = {
for role in distinct(concat(keys(var.iam), keys(local.group_iam))) :
role => concat(
try(var.iam[role], []),
try(local.group_iam[role], [])
)
}
logging_sinks = coalesce(var.logging_sinks, {})
sink_type_destination = {
gcs = "storage.googleapis.com"
bigquery = "bigquery.googleapis.com"
pubsub = "pubsub.googleapis.com"
logging = "logging.googleapis.com"
}
sink_bindings = {
for type in ["gcs", "bigquery", "pubsub", "logging"] :
type => {
for name, sink in local.logging_sinks :
name => sink
if sink.type == type
}
}
folder = (
var.folder_create
? try(google_folder.folder.0, null)
@@ -61,149 +33,6 @@ resource "google_folder" "folder" {
parent = var.parent
}
resource "google_folder_iam_binding" "authoritative" {
for_each = local.iam
folder = local.folder.name
role = each.key
members = each.value
}
resource "google_folder_organization_policy" "boolean" {
for_each = var.policy_boolean
folder = local.folder.name
constraint = each.key
dynamic "boolean_policy" {
for_each = each.value == null ? [] : [each.value]
iterator = policy
content {
enforced = policy.value
}
}
dynamic "restore_policy" {
for_each = each.value == null ? [""] : []
content {
default = true
}
}
}
resource "google_folder_organization_policy" "list" {
for_each = var.policy_list
folder = local.folder.name
constraint = each.key
dynamic "list_policy" {
for_each = each.value.status == null ? [] : [each.value]
iterator = policy
content {
inherit_from_parent = policy.value.inherit_from_parent
suggested_value = policy.value.suggested_value
dynamic "allow" {
for_each = policy.value.status ? [""] : []
content {
values = (
try(length(policy.value.values) > 0, false)
? policy.value.values
: null
)
all = (
try(length(policy.value.values) > 0, false)
? null
: true
)
}
}
dynamic "deny" {
for_each = policy.value.status ? [] : [""]
content {
values = (
try(length(policy.value.values) > 0, false)
? policy.value.values
: null
)
all = (
try(length(policy.value.values) > 0, false)
? null
: true
)
}
}
}
}
dynamic "restore_policy" {
for_each = each.value.status == null ? [true] : []
content {
default = true
}
}
}
resource "google_logging_folder_sink" "sink" {
for_each = local.logging_sinks
name = each.key
#description = "${each.key} (Terraform-managed)"
folder = local.folder.name
destination = "${local.sink_type_destination[each.value.type]}/${each.value.destination}"
filter = each.value.filter
include_children = each.value.include_children
dynamic "exclusions" {
for_each = each.value.exclusions
iterator = exclusion
content {
name = exclusion.key
filter = exclusion.value
}
}
depends_on = [
google_folder_iam_binding.authoritative
]
}
resource "google_storage_bucket_iam_member" "gcs-sinks-binding" {
for_each = local.sink_bindings["gcs"]
bucket = each.value.destination
role = "roles/storage.objectCreator"
member = google_logging_folder_sink.sink[each.key].writer_identity
}
resource "google_bigquery_dataset_iam_member" "bq-sinks-binding" {
for_each = local.sink_bindings["bigquery"]
project = split("/", each.value.destination)[1]
dataset_id = split("/", each.value.destination)[3]
role = "roles/bigquery.dataEditor"
member = google_logging_folder_sink.sink[each.key].writer_identity
}
resource "google_pubsub_topic_iam_member" "pubsub-sinks-binding" {
for_each = local.sink_bindings["pubsub"]
project = split("/", each.value.destination)[1]
topic = split("/", each.value.destination)[3]
role = "roles/pubsub.publisher"
member = google_logging_folder_sink.sink[each.key].writer_identity
}
resource "google_project_iam_member" "bucket-sinks-binding" {
for_each = local.sink_bindings["logging"]
project = split("/", each.value.destination)[1]
role = "roles/logging.bucketWriter"
member = google_logging_folder_sink.sink[each.key].writer_identity
# TODO(jccb): use a condition to limit writer-identity only to this
# bucket
}
resource "google_logging_folder_exclusion" "logging-exclusion" {
for_each = coalesce(var.logging_exclusions, {})
name = each.key
folder = local.folder.name
description = "${each.key} (Terraform-managed)"
filter = each.value
}
resource "google_essential_contacts_contact" "contact" {
provider = google-beta
for_each = var.contacts

View File

@@ -0,0 +1,90 @@
/**
* Copyright 2022 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.
*/
# tfdoc:file:description Folder-level organization policies.
resource "google_folder_organization_policy" "boolean" {
for_each = var.policy_boolean
folder = local.folder.name
constraint = each.key
dynamic "boolean_policy" {
for_each = each.value == null ? [] : [each.value]
iterator = policy
content {
enforced = policy.value
}
}
dynamic "restore_policy" {
for_each = each.value == null ? [""] : []
content {
default = true
}
}
}
resource "google_folder_organization_policy" "list" {
for_each = var.policy_list
folder = local.folder.name
constraint = each.key
dynamic "list_policy" {
for_each = each.value.status == null ? [] : [each.value]
iterator = policy
content {
inherit_from_parent = policy.value.inherit_from_parent
suggested_value = policy.value.suggested_value
dynamic "allow" {
for_each = policy.value.status ? [""] : []
content {
values = (
try(length(policy.value.values) > 0, false)
? policy.value.values
: null
)
all = (
try(length(policy.value.values) > 0, false)
? null
: true
)
}
}
dynamic "deny" {
for_each = policy.value.status ? [] : [""]
content {
values = (
try(length(policy.value.values) > 0, false)
? policy.value.values
: null
)
all = (
try(length(policy.value.values) > 0, false)
? null
: true
)
}
}
}
}
dynamic "restore_policy" {
for_each = each.value.status == null ? [true] : []
content {
default = true
}
}
}

View File

@@ -18,6 +18,7 @@ variable "contacts" {
description = "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"
type = map(list(string))
default = {}
nullable = false
}
variable "firewall_policies" {
@@ -33,13 +34,15 @@ variable "firewall_policies" {
target_resources = list(string)
target_service_accounts = list(string)
})))
default = {}
default = {}
nullable = false
}
variable "firewall_policy_association" {
description = "The hierarchical firewall policy to associate to this folder. Must be either a key in the `firewall_policies` map or the id of a policy defined somewhere else."
type = map(string)
default = {}
nullable = false
}
variable "firewall_policy_factory" {
@@ -62,12 +65,14 @@ variable "group_iam" {
description = "Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable."
type = map(list(string))
default = {}
nullable = false
}
variable "iam" {
description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(list(string))
default = {}
nullable = false
}
variable "id" {
@@ -80,6 +85,7 @@ variable "logging_exclusions" {
description = "Logging exclusions for this folder in the form {NAME -> FILTER}."
type = map(string)
default = {}
nullable = false
}
variable "logging_sinks" {
@@ -92,7 +98,15 @@ variable "logging_sinks" {
# TODO exclusions also support description and disabled
exclusions = map(string)
}))
default = {}
validation {
condition = alltrue([
for k, v in(var.logging_sinks == null ? {} : var.logging_sinks) :
contains(["bigquery", "logging", "pubsub", "storage"], v.type)
])
error_message = "Type must be one of 'bigquery', 'logging', 'pubsub', 'storage'."
}
default = {}
nullable = false
}
variable "name" {
@@ -115,6 +129,7 @@ variable "policy_boolean" {
description = "Map of boolean org policies and enforcement value, set value to null for policy restore."
type = map(bool)
default = {}
nullable = false
}
variable "policy_list" {
@@ -125,5 +140,6 @@ variable "policy_list" {
status = bool
values = list(string)
}))
default = {}
default = {}
nullable = false
}

View File

@@ -13,7 +13,7 @@
# limitations under the License.
terraform {
required_version = ">= 1.0.0"
required_version = ">= 1.1.0"
required_providers {
google = {
source = "hashicorp/google"

View File

@@ -243,11 +243,11 @@ module "org" {
| name | description | resources |
|---|---|---|
| [firewall-policy.tf](./firewall-policy.tf) | Hierarchical firewall policies. | <code>google_compute_firewall_policy</code> · <code>google_compute_firewall_policy_association</code> · <code>google_compute_firewall_policy_rule</code> |
| [firewall_policies.tf](./firewall_policies.tf) | Hierarchical firewall policies. | <code>google_compute_firewall_policy</code> · <code>google_compute_firewall_policy_association</code> · <code>google_compute_firewall_policy_rule</code> |
| [iam.tf](./iam.tf) | IAM bindings, roles and audit logging resources. | <code>google_organization_iam_audit_config</code> · <code>google_organization_iam_binding</code> · <code>google_organization_iam_custom_role</code> · <code>google_organization_iam_member</code> · <code>google_organization_iam_policy</code> |
| [logging.tf](./logging.tf) | Log sinks and supporting resources. | <code>google_bigquery_dataset_iam_member</code> · <code>google_logging_organization_exclusion</code> · <code>google_logging_organization_sink</code> · <code>google_project_iam_member</code> · <code>google_pubsub_topic_iam_member</code> · <code>google_storage_bucket_iam_member</code> |
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_essential_contacts_contact</code> |
| [org-policy.tf](./org-policy.tf) | Organization policies. | <code>google_organization_policy</code> |
| [organization_policies.tf](./organization_policies.tf) | Organization-level organization policies. | <code>google_organization_policy</code> |
| [outputs.tf](./outputs.tf) | Module outputs. | |
| [variables.tf](./variables.tf) | Module variables. | |
| [versions.tf](./versions.tf) | Version pins. | |

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
# tfdoc:file:description Organization policies.
# tfdoc:file:description Organization-level organization policies.
resource "google_organization_policy" "boolean" {
for_each = var.policy_boolean

View File

@@ -108,7 +108,7 @@ module "project-host" {
parent = "folders/1234567890"
logging_sinks = {
warnings = {
type = "gcs"
type = "storage"
destination = module.gcs.name
filter = "severity=WARNING"
iam = false
@@ -184,49 +184,49 @@ module "project" {
| [iam.tf](./iam.tf) | Generic and OSLogin-specific IAM bindings and roles. | <code>google_project_iam_binding</code> · <code>google_project_iam_custom_role</code> · <code>google_project_iam_member</code> |
| [logging.tf](./logging.tf) | Log sinks and supporting resources. | <code>google_bigquery_dataset_iam_member</code> · <code>google_logging_project_exclusion</code> · <code>google_logging_project_sink</code> · <code>google_project_iam_member</code> · <code>google_pubsub_topic_iam_member</code> · <code>google_storage_bucket_iam_member</code> |
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_compute_project_metadata_item</code> · <code>google_essential_contacts_contact</code> · <code>google_monitoring_monitored_project</code> · <code>google_project</code> · <code>google_project_service</code> · <code>google_resource_manager_lien</code> |
| [organization-policies.tf](./organization-policies.tf) | Project-level organization policies. | <code>google_project_organization_policy</code> |
| [organization_policies.tf](./organization_policies.tf) | Project-level organization policies. | <code>google_project_organization_policy</code> |
| [outputs.tf](./outputs.tf) | Module outputs. | |
| [service_accounts.tf](./service_accounts.tf) | Service identities and supporting resources. | <code>google_kms_crypto_key_iam_member</code> · <code>google_project_service_identity</code> |
| [shared_vpc.tf](./shared_vpc.tf) | Shared VPC project-level configuration. | <code>google_compute_shared_vpc_host_project</code> · <code>google_compute_shared_vpc_service_project</code> |
| [variables.tf](./variables.tf) | Module variables. | |
| [versions.tf](./versions.tf) | Version pins. | |
| [vpc-sc.tf](./vpc-sc.tf) | VPC-SC project-level perimeter configuration. | <code>google_access_context_manager_service_perimeter_resource</code> |
| [vpc_sc.tf](./vpc_sc.tf) | VPC-SC project-level perimeter configuration. | <code>google_access_context_manager_service_perimeter_resource</code> |
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [name](variables.tf#L109) | Project name and id suffix. | <code>string</code> | ✓ | |
| [name](variables.tf#L125) | Project name and id suffix. | <code>string</code> | ✓ | |
| [auto_create_network](variables.tf#L17) | Whether to create the default network for the project | <code>bool</code> | | <code>false</code> |
| [billing_account](variables.tf#L23) | Billing account id. | <code>string</code> | | <code>null</code> |
| [contacts](variables.tf#L29) | 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&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [custom_roles](variables.tf#L35) | Map of role name => list of permissions to create in this project. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [descriptive_name](variables.tf#L41) | Name of the project name. Used for project name instead of `name` variable | <code>string</code> | | <code>null</code> |
| [group_iam](variables.tf#L47) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L53) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive](variables.tf#L59) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive_members](variables.tf#L65) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L71) | Resource labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [lien_reason](variables.tf#L77) | If non-empty, creates a project lien with this description. | <code>string</code> | | <code>&#34;&#34;</code> |
| [logging_exclusions](variables.tf#L83) | Logging exclusions for this project in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L89) | Logging sinks to create for this project. | <code title="map&#40;object&#40;&#123;&#10; destination &#61; string&#10; type &#61; string&#10; filter &#61; string&#10; iam &#61; bool&#10; unique_writer &#61; bool&#10; exclusions &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [metric_scopes](variables.tf#L103) | List of projects that will act as metric scopes for this project. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [oslogin](variables.tf#L114) | Enable OS Login. | <code>bool</code> | | <code>false</code> |
| [oslogin_admins](variables.tf#L120) | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [oslogin_users](variables.tf#L126) | List of IAM-style identities that will be granted roles necessary for OS Login users. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [parent](variables.tf#L132) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> |
| [policy_boolean](variables.tf#L142) | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code>map&#40;bool&#41;</code> | | <code>&#123;&#125;</code> |
| [policy_list](variables.tf#L148) | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; bool&#10; suggested_value &#61; string&#10; status &#61; bool&#10; values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [prefix](variables.tf#L159) | Prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
| [project_create](variables.tf#L165) | Create project. When set to false, uses a data source to reference existing project. | <code>bool</code> | | <code>true</code> |
| [service_config](variables.tf#L171) | Configure service API activation. | <code title="object&#40;&#123;&#10; disable_on_destroy &#61; bool&#10; disable_dependent_services &#61; bool&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; disable_on_destroy &#61; true&#10; disable_dependent_services &#61; true&#10;&#125;">&#123;&#8230;&#125;</code> |
| [service_encryption_key_ids](variables.tf#L183) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeter_bridges](variables.tf#L190) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [service_perimeter_standard](variables.tf#L197) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | <code>string</code> | | <code>null</code> |
| [services](variables.tf#L203) | Service APIs to enable. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [shared_vpc_host_config](variables.tf#L209) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object&#40;&#123;&#10; enabled &#61; bool&#10; service_projects &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; enabled &#61; false&#10; service_projects &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [shared_vpc_service_config](variables.tf#L221) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object&#40;&#123;&#10; attach &#61; bool&#10; host_project &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; attach &#61; false&#10; host_project &#61; &#34;&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [skip_delete](variables.tf#L233) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
| [custom_roles](variables.tf#L36) | Map of role name => list of permissions to create in this project. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [descriptive_name](variables.tf#L43) | Name of the project name. Used for project name instead of `name` variable | <code>string</code> | | <code>null</code> |
| [group_iam](variables.tf#L49) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L56) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive](variables.tf#L63) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_additive_members](variables.tf#L70) | IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L76) | Resource labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [lien_reason](variables.tf#L83) | If non-empty, creates a project lien with this description. | <code>string</code> | | <code>&#34;&#34;</code> |
| [logging_exclusions](variables.tf#L89) | Logging exclusions for this project in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L96) | Logging sinks to create for this project. | <code title="map&#40;object&#40;&#123;&#10; destination &#61; string&#10; type &#61; string&#10; filter &#61; string&#10; iam &#61; bool&#10; unique_writer &#61; bool&#10; exclusions &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [metric_scopes](variables.tf#L118) | List of projects that will act as metric scopes for this project. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [oslogin](variables.tf#L130) | Enable OS Login. | <code>bool</code> | | <code>false</code> |
| [oslogin_admins](variables.tf#L136) | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [oslogin_users](variables.tf#L144) | List of IAM-style identities that will be granted roles necessary for OS Login users. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [parent](variables.tf#L151) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> |
| [policy_boolean](variables.tf#L161) | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code>map&#40;bool&#41;</code> | | <code>&#123;&#125;</code> |
| [policy_list](variables.tf#L168) | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; bool&#10; suggested_value &#61; string&#10; status &#61; bool&#10; values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [prefix](variables.tf#L180) | Prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
| [project_create](variables.tf#L186) | Create project. When set to false, uses a data source to reference existing project. | <code>bool</code> | | <code>true</code> |
| [service_config](variables.tf#L192) | Configure service API activation. | <code title="object&#40;&#123;&#10; disable_on_destroy &#61; bool&#10; disable_dependent_services &#61; bool&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; disable_on_destroy &#61; true&#10; disable_dependent_services &#61; true&#10;&#125;">&#123;&#8230;&#125;</code> |
| [service_encryption_key_ids](variables.tf#L204) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeter_bridges](variables.tf#L211) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [service_perimeter_standard](variables.tf#L218) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | <code>string</code> | | <code>null</code> |
| [services](variables.tf#L224) | Service APIs to enable. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [shared_vpc_host_config](variables.tf#L230) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object&#40;&#123;&#10; enabled &#61; bool&#10; service_projects &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; enabled &#61; false&#10; service_projects &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [shared_vpc_service_config](variables.tf#L243) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object&#40;&#123;&#10; attach &#61; bool&#10; host_project &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; attach &#61; false&#10; host_project &#61; &#34;&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [skip_delete](variables.tf#L256) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
## Outputs

View File

@@ -17,28 +17,21 @@
# tfdoc:file:description Log sinks and supporting resources.
locals {
logging_sinks = coalesce(var.logging_sinks, {})
sink_bindings = {
for type in ["gcs", "bigquery", "pubsub", "logging"] :
for type in ["bigquery", "pubsub", "logging", "storage"] :
type => {
for name, sink in local.logging_sinks :
for name, sink in var.logging_sinks :
name => sink if sink.iam && sink.type == type
}
}
sink_type_destination = {
gcs = "storage.googleapis.com"
bigquery = "bigquery.googleapis.com"
pubsub = "pubsub.googleapis.com"
logging = "logging.googleapis.com"
}
}
resource "google_logging_project_sink" "sink" {
for_each = local.logging_sinks
for_each = var.logging_sinks
name = each.key
#description = "${each.key} (Terraform-managed)"
project = local.project.project_id
destination = "${local.sink_type_destination[each.value.type]}/${each.value.destination}"
destination = "${each.value.type}.googleapis.com/${each.value.destination}"
filter = each.value.filter
unique_writer_identity = each.value.unique_writer
@@ -58,7 +51,7 @@ resource "google_logging_project_sink" "sink" {
}
resource "google_storage_bucket_iam_member" "gcs-sinks-binding" {
for_each = local.sink_bindings["gcs"]
for_each = local.sink_bindings["storage"]
bucket = each.value.destination
role = "roles/storage.objectCreator"
member = google_logging_project_sink.sink[each.key].writer_identity
@@ -90,7 +83,7 @@ resource "google_project_iam_member" "bucket-sinks-binding" {
}
resource "google_logging_project_exclusion" "logging-exclusion" {
for_each = coalesce(var.logging_exclusions, {})
for_each = var.logging_exclusions
name = each.key
project = local.project.project_id
description = "${each.key} (Terraform-managed)"

View File

@@ -89,7 +89,7 @@ resource "google_essential_contacts_contact" "contact" {
resource "google_monitoring_monitored_project" "primary" {
provider = google-beta
for_each = toset(coalesce(var.metric_scopes, []))
for_each = toset(var.metric_scopes)
metrics_scope = each.value
name = local.project.project_id
}

View File

@@ -30,12 +30,14 @@ variable "contacts" {
description = "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"
type = map(list(string))
default = {}
nullable = false
}
variable "custom_roles" {
description = "Map of role name => list of permissions to create in this project."
type = map(list(string))
default = {}
nullable = false
}
variable "descriptive_name" {
@@ -48,18 +50,21 @@ variable "group_iam" {
description = "Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable."
type = map(list(string))
default = {}
nullable = false
}
variable "iam" {
description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(list(string))
default = {}
nullable = false
}
variable "iam_additive" {
description = "IAM additive bindings in {ROLE => [MEMBERS]} format."
type = map(list(string))
default = {}
nullable = false
}
variable "iam_additive_members" {
@@ -72,6 +77,7 @@ variable "labels" {
description = "Resource labels."
type = map(string)
default = {}
nullable = false
}
variable "lien_reason" {
@@ -84,6 +90,7 @@ variable "logging_exclusions" {
description = "Logging exclusions for this project in the form {NAME -> FILTER}."
type = map(string)
default = {}
nullable = false
}
variable "logging_sinks" {
@@ -97,13 +104,22 @@ variable "logging_sinks" {
# TODO exclusions also support description and disabled
exclusions = map(string)
}))
default = {}
validation {
condition = alltrue([
for k, v in(var.logging_sinks == null ? {} : var.logging_sinks) :
contains(["bigquery", "logging", "pubsub", "storage"], v.type)
])
error_message = "Type must be one of 'bigquery', 'logging', 'pubsub', 'storage'."
}
default = {}
nullable = false
}
variable "metric_scopes" {
description = "List of projects that will act as metric scopes for this project."
type = list(string)
default = null
default = []
nullable = false
}
variable "name" {
@@ -121,12 +137,15 @@ variable "oslogin_admins" {
description = "List of IAM-style identities that will be granted roles necessary for OS Login administrators."
type = list(string)
default = []
nullable = false
}
variable "oslogin_users" {
description = "List of IAM-style identities that will be granted roles necessary for OS Login users."
type = list(string)
default = []
nullable = false
}
variable "parent" {
@@ -143,6 +162,7 @@ variable "policy_boolean" {
description = "Map of boolean org policies and enforcement value, set value to null for policy restore."
type = map(bool)
default = {}
nullable = false
}
variable "policy_list" {
@@ -153,7 +173,8 @@ variable "policy_list" {
status = bool
values = list(string)
}))
default = {}
default = {}
nullable = false
}
variable "prefix" {
@@ -216,6 +237,7 @@ variable "shared_vpc_host_config" {
enabled = false
service_projects = []
}
nullable = false
}
variable "shared_vpc_service_config" {
@@ -228,6 +250,7 @@ variable "shared_vpc_service_config" {
attach = false
host_project = ""
}
nullable = false
}
variable "skip_delete" {

View File

@@ -13,7 +13,7 @@
# limitations under the License.
terraform {
required_version = ">= 1.0.0"
required_version = ">= 1.1.0"
required_providers {
google = {
source = "hashicorp/google"

View File

@@ -18,7 +18,7 @@ def test_sinks(plan_runner):
"Test folder-level sinks."
logging_sinks = """ {
warning = {
type = "gcs"
type = "storage"
destination = "mybucket"
filter = "severity=WARNING"
iam = true

View File

@@ -19,7 +19,7 @@ def test_sinks(plan_runner):
"Test folder-level sinks."
logging_sinks = """ {
warning = {
type = "gcs"
type = "storage"
destination = "mybucket"
filter = "severity=WARNING"
iam = true