Extend FAST to support different principal types (#2064)
* add doc draft * typos * typo * typo * typos * rewording * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Update 0-domainless-iam.md * move iam variables to a separate file * move billing-account module to iam_principals * move data-catalog-policy-tag module to iam_principals * move dataplex-datascan module to iam_principals * move dataproc module to iam_principals * move folder module to iam_principals * copyright * move organization module to iam_principals * move project module to iam_principals * move source-repository module to iam_principals * update blueprints for iam_principals interface * FAST bootstrap * module READMEs fixes * FAST bootstrap * FAST networking stages * FAST security stage * FAST gke stage * FAST multitenant bootstrap stage * FAST multitenant resman stage * tfdoc * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Update 0-domainless-iam.md * fix module test * Update 0-domainless-iam.md * Update 0-domainless-iam.md * Rename iam_principals to iam_by_principals * Update IAM template to include iam_by_principals * Update Resman README * Fix ADR link format --------- Co-authored-by: Julio Castillo <jccb@google.com>
This commit is contained in:
committed by
GitHub
parent
3397d4cd52
commit
71a64487d5
@@ -1,12 +1,13 @@
|
||||
# Refactor IAM interface
|
||||
|
||||
**authors:** [Ludo](https://github.com/ludoo), [Julio](https://github.com/juliocc)
|
||||
**last modified:** August 17, 2023
|
||||
**last modified:** February 12, 2024
|
||||
|
||||
## Status
|
||||
|
||||
Implemented in [#1595](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1595).
|
||||
Authoritative bindings type changed as per [#1622](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/issues/1622).
|
||||
- Implemented in [#1595](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1595).
|
||||
- Authoritative bindings type changed as per [#1622](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/issues/1622).
|
||||
- Extended by [#2064](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/issues/2064).
|
||||
|
||||
## Context
|
||||
|
||||
@@ -113,10 +114,30 @@ variable "iam_bindings_additive" {
|
||||
|
||||
The **proposal** is to remove the IAM policy variable and resources, as its coverage is very uneven and we never used it in practice. This will also simplify data access log management, which is currently split between its own variable/resource and the IAM policy ones.
|
||||
|
||||
### IAM by Principals
|
||||
> [!NOTE]
|
||||
> This section was added on 2024-02-12
|
||||
|
||||
[#2064](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/issues/2064). replaced `group_iam` with `iam_by_principals`. The structure of `iam_by_principals` is similar to the original `group_iam` with the difference that now the user has to specify the principal type with the correct prefix. The new variable format is shown below
|
||||
|
||||
```hcl
|
||||
variable "iam_by_principals" {
|
||||
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
See #2064 and [this ADR](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/blob/ludo/iam-changes/fast/docs/0-domainless-iam.md) for more details.
|
||||
|
||||
|
||||
## Decision
|
||||
|
||||
The proposal above summarizes the state of discussions between the authors, and implementation will be tested.
|
||||
|
||||
|
||||
## Consequences
|
||||
|
||||
### FAST
|
||||
@@ -180,13 +201,36 @@ variable "iam_bindings_additive" {
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam_by_principals" {
|
||||
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
```
|
||||
|
||||
```terraform
|
||||
# iam.tf
|
||||
|
||||
locals {
|
||||
_iam_principal_roles = distinct(flatten(values(var.iam_by_principals)))
|
||||
_iam_principals = {
|
||||
for r in local._iam_principal_roles : r => [
|
||||
for k, v in var.iam_by_principals :
|
||||
k if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local._iam_principals[role], [])
|
||||
)
|
||||
}
|
||||
}
|
||||
resource "google_RESOURCE_TYPE_iam_binding" "authoritative" {
|
||||
for_each = var.iam
|
||||
for_each = local.iam
|
||||
role = each.key
|
||||
members = each.value
|
||||
// add extra attributes (e.g. resource id)
|
||||
|
||||
@@ -38,9 +38,6 @@ Billing account IAM bindings implement [the same interface](../__docs/20230816-i
|
||||
module "billing-account" {
|
||||
source = "./fabric/modules/billing-account"
|
||||
id = "012345-ABCDEF-012345"
|
||||
group_iam = {
|
||||
"billing-admins@example.org" = ["roles/billing.admin"]
|
||||
}
|
||||
iam = {
|
||||
"roles/billing.admin" = [
|
||||
"serviceAccount:foo@myprj.iam.gserviceaccount.com"
|
||||
@@ -66,6 +63,9 @@ module "billing-account" {
|
||||
role = "roles/billing.user"
|
||||
}
|
||||
}
|
||||
iam_by_principals = {
|
||||
"group:billing-admins@example.org" = ["roles/billing.admin"]
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=3 inventory=iam.yaml
|
||||
```
|
||||
@@ -260,16 +260,16 @@ update_rules:
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [id](variables.tf#L175) | Billing account id. | <code>string</code> | ✓ | |
|
||||
| [id](variables.tf#L131) | Billing account id. | <code>string</code> | ✓ | |
|
||||
| [budget_notification_channels](variables.tf#L17) | Notification channels used by budget alerts. | <code title="map(object({ project_id = string type = string description = optional(string) display_name = optional(string) enabled = optional(bool, true) force_delete = optional(bool) labels = optional(map(string)) sensitive_labels = optional(list(object({ auth_token = optional(string) password = optional(string) service_key = optional(string) }))) user_labels = optional(map(string)) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [budgets](variables.tf#L47) | Billing budgets. Notification channels are either keys in corresponding variable, or external ids. | <code title="map(object({ amount = object({ currency_code = optional(string) nanos = optional(number) units = optional(number) use_last_period = optional(bool) }) display_name = optional(string) filter = optional(object({ credit_types_treatment = optional(object({ exclude_all = optional(bool) include_specified = optional(list(string)) })) label = optional(object({ key = string value = string })) period = optional(object({ calendar = optional(string) custom = optional(object({ start_date = object({ day = number month = number year = number }) end_date = optional(object({ day = number month = number year = number })) })) })) projects = optional(list(string)) resource_ancestors = optional(list(string)) services = optional(list(string)) subaccounts = optional(list(string)) })) threshold_rules = optional(list(object({ percent = number forecasted_spend = optional(bool) })), []) update_rules = optional(map(object({ disable_default_iam_recipients = optional(bool) monitoring_notification_channels = optional(list(string)) pubsub_topic = optional(string) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [factory_config](variables.tf#L121) | Path to folder containing budget alerts data files. | <code title="object({ budgets_data_path = optional(string, "data/billing-budgets") })">object({…})</code> | | <code>{}</code> |
|
||||
| [group_iam](variables.tf#L131) | 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(list(string))</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L138) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables.tf#L145) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [iam_bindings_additive](variables.tf#L160) | 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> |
|
||||
| [logging_sinks](variables.tf#L180) | Logging sinks to create for the organization. | <code title="map(object({ destination = string type = string bq_partitioned_table = optional(bool, false) description = optional(string) disabled = optional(bool, false) exclusions = optional(map(object({ filter = string description = optional(string) disabled = optional(bool) })), {}) filter = optional(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [projects](variables.tf#L213) | Projects associated with this billing account. | <code>list(string)</code> | | <code>[]</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> |
|
||||
| [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L136) | Logging sinks to create for the organization. | <code title="map(object({ destination = string type = string bq_partitioned_table = optional(bool, false) description = optional(string) disabled = optional(bool, false) exclusions = optional(map(object({ filter = string description = optional(string) disabled = optional(bool) })), {}) filter = optional(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [projects](variables.tf#L169) | Projects associated with this billing account. | <code>list(string)</code> | | <code>[]</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,17 +17,18 @@
|
||||
# tfdoc:file:description IAM bindings.
|
||||
|
||||
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_principal_roles = distinct(flatten(values(var.iam_by_principals)))
|
||||
_iam_principals = {
|
||||
for r in local._iam_principal_roles : r => [
|
||||
for k, v in var.iam_by_principals :
|
||||
k if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
|
||||
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local._group_iam[role], [])
|
||||
try(local._iam_principals[role], [])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
59
modules/billing-account/variables-iam.tf
Normal file
59
modules/billing-account/variables-iam.tf
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_by_principals" {
|
||||
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
@@ -128,50 +128,6 @@ variable "factory_config" {
|
||||
default = {}
|
||||
}
|
||||
|
||||
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_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "id" {
|
||||
description = "Billing account id."
|
||||
type = string
|
||||
|
||||
@@ -18,11 +18,11 @@ Note: Data Catalog is still in beta, hence this module currently uses the beta p
|
||||
|
||||
IAM is managed via several variables that implement different features and levels of control:
|
||||
|
||||
- `iam` and `group_iam` configure authoritative bindings that manage individual roles exclusively, and are internally merged
|
||||
- `iam` and `iam_by_principals` configure authoritative bindings that manage individual roles exclusively, and are internally merged
|
||||
- `iam_bindings` configure authoritative bindings with optional support for conditions, and are not internally merged with the previous two variables
|
||||
- `iam_bindings_additive` configure additive bindings via individual role/member pairs with optional support conditions
|
||||
|
||||
The authoritative and additive approaches can be used together, provided different roles are managed by each. Some care must also be taken with the `groups_iam` variable to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph.
|
||||
The authoritative and additive approaches can be used together, provided different roles are managed by each. Some care must also be taken with the `iam_by_principals` variable to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph.
|
||||
|
||||
Refer to the [project module](../project/README.md#iam) for examples of the IAM interface.
|
||||
|
||||
@@ -79,17 +79,17 @@ module "cmn-dc" {
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [name](variables.tf#L77) | Name of this taxonomy. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L92) | GCP project id. | <code>string</code> | ✓ | |
|
||||
| [name](variables.tf#L35) | Name of this taxonomy. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L50) | GCP project id. | <code>string</code> | ✓ | |
|
||||
| [activated_policy_types](variables.tf#L17) | A list of policy types that are activated for this taxonomy. | <code>list(string)</code> | | <code>["FINE_GRAINED_ACCESS_CONTROL"]</code> |
|
||||
| [description](variables.tf#L23) | Description of this taxonomy. | <code>string</code> | | <code>"Taxonomy - Terraform managed"</code> |
|
||||
| [group_iam](variables.tf#L29) | 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(list(string))</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L35) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables.tf#L41) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [iam_bindings_additive](variables.tf#L56) | 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> |
|
||||
| [location](variables.tf#L71) | Data Catalog Taxonomy location. | <code>string</code> | | <code>"eu"</code> |
|
||||
| [prefix](variables.tf#L82) | Optional prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
|
||||
| [tags](variables.tf#L97) | List of Data Catalog Policy tags to be created with optional IAM binging configuration in {tag => {ROLE => [MEMBERS]}} format. | <code title="map(object({ description = optional(string) iam = optional(map(list(string)), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [iam](variables-iam.tf#L23) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables-iam.tf#L29) | 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#L44) | 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> |
|
||||
| [iam_by_principals](variables-iam.tf#L17) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [location](variables.tf#L29) | Data Catalog Taxonomy location. | <code>string</code> | | <code>"eu"</code> |
|
||||
| [prefix](variables.tf#L40) | Optional prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
|
||||
| [tags](variables.tf#L55) | List of Data Catalog Policy tags to be created with optional IAM binging configuration in {tag => {ROLE => [MEMBERS]}} format. | <code title="map(object({ description = optional(string) iam = optional(map(list(string)), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
||||
@@ -17,17 +17,18 @@
|
||||
# tfdoc:file:description Data Catalog Taxonomy IAM definition.
|
||||
|
||||
locals {
|
||||
_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_principal_roles = distinct(flatten(values(var.iam_by_principals)))
|
||||
_iam_principals = {
|
||||
for r in local._iam_principal_roles : r => [
|
||||
for k, v in var.iam_by_principals :
|
||||
k if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
_group_iam_roles = distinct(flatten(values(var.group_iam)))
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
|
||||
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local._group_iam[role], [])
|
||||
try(local._iam_principals[role], [])
|
||||
)
|
||||
}
|
||||
tags_iam = flatten([
|
||||
|
||||
57
modules/data-catalog-policy-tag/variables-iam.tf
Normal file
57
modules/data-catalog-policy-tag/variables-iam.tf
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "iam_by_principals" {
|
||||
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
@@ -26,48 +26,6 @@ variable "description" {
|
||||
default = "Taxonomy - Terraform managed"
|
||||
}
|
||||
|
||||
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 = {}
|
||||
}
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "location" {
|
||||
description = "Data Catalog Taxonomy location."
|
||||
type = string
|
||||
|
||||
@@ -382,11 +382,11 @@ module "dataplex-datascan" {
|
||||
|
||||
IAM is managed via several variables that implement different features and levels of control:
|
||||
|
||||
- `iam` and `group_iam` configure authoritative bindings that manage individual roles exclusively, and are internally merged
|
||||
- `iam` and `iam_by_principals` configure authoritative bindings that manage individual roles exclusively, and are internally merged
|
||||
- `iam_bindings` configure authoritative bindings with optional support for conditions, and are not internally merged with the previous two variables
|
||||
- `iam_bindings_additive` configure additive bindings via individual role/member pairs with optional support conditions
|
||||
|
||||
The authoritative and additive approaches can be used together, provided different roles are managed by each. Some care must also be taken with the `groups_iam` variable to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph.
|
||||
The authoritative and additive approaches can be used together, provided different roles are managed by each. Some care must also be taken with the `iam_by_principals` variable to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph.
|
||||
|
||||
An example is provided below for using some of these variables. Refer to the [project module](../project/README.md#iam) for complete examples of the IAM interface.
|
||||
|
||||
@@ -409,8 +409,8 @@ module "dataplex-datascan" {
|
||||
"user:admin-user@example.com"
|
||||
]
|
||||
}
|
||||
group_iam = {
|
||||
"user-group@example.com" = [
|
||||
iam_by_principals = {
|
||||
"group:user-group@example.com" = [
|
||||
"roles/dataplex.dataScanViewer"
|
||||
]
|
||||
}
|
||||
@@ -431,21 +431,21 @@ module "dataplex-datascan" {
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [data](variables.tf#L17) | The data source for DataScan. The source can be either a Dataplex `entity` or a BigQuery `resource`. | <code title="object({ entity = optional(string) resource = optional(string) })">object({…})</code> | ✓ | |
|
||||
| [name](variables.tf#L162) | Name of Dataplex Scan. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L173) | The ID of the project where the Dataplex DataScan will be created. | <code>string</code> | ✓ | |
|
||||
| [region](variables.tf#L178) | Region for the Dataplex DataScan. | <code>string</code> | ✓ | |
|
||||
| [name](variables.tf#L118) | Name of Dataplex Scan. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L129) | The ID of the project where the Dataplex DataScan will be created. | <code>string</code> | ✓ | |
|
||||
| [region](variables.tf#L134) | Region for the Dataplex DataScan. | <code>string</code> | ✓ | |
|
||||
| [data_profile_spec](variables.tf#L29) | DataProfileScan related setting. Variable descriptions are provided in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataProfileSpec. | <code title="object({ sampling_percent = optional(number) row_filter = optional(string) })">object({…})</code> | | <code>null</code> |
|
||||
| [data_quality_spec](variables.tf#L38) | DataQualityScan related setting. Variable descriptions are provided in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataQualitySpec. | <code title="object({ sampling_percent = optional(number) row_filter = optional(string) post_scan_actions = optional(object({ bigquery_export = optional(object({ results_table = optional(string) })) })) rules = list(object({ column = optional(string) ignore_null = optional(bool, null) dimension = string threshold = optional(number) non_null_expectation = optional(object({})) range_expectation = optional(object({ min_value = optional(number) max_value = optional(number) strict_min_enabled = optional(bool) strict_max_enabled = optional(bool) })) regex_expectation = optional(object({ regex = string })) set_expectation = optional(object({ values = list(string) })) uniqueness_expectation = optional(object({})) statistic_range_expectation = optional(object({ statistic = string min_value = optional(number) max_value = optional(number) strict_min_enabled = optional(bool) strict_max_enabled = optional(bool) })) row_condition_expectation = optional(object({ sql_expression = string })) table_condition_expectation = optional(object({ sql_expression = string })) })) })">object({…})</code> | | <code>null</code> |
|
||||
| [data_quality_spec_file](variables.tf#L85) | Path to a YAML file containing DataQualityScan related setting. Input content can use either camelCase or snake_case. Variables description are provided in https://cloud.google.com/dataplex/docs/reference/rest/v1/DataQualitySpec. | <code title="object({ path = string })">object({…})</code> | | <code>null</code> |
|
||||
| [description](variables.tf#L93) | Custom description for DataScan. | <code>string</code> | | <code>null</code> |
|
||||
| [execution_schedule](variables.tf#L99) | Schedule DataScan to run periodically based on a cron schedule expression. If not specified, the DataScan is created with `on_demand` schedule, which means it will not run until the user calls `dataScans.run` API. | <code>string</code> | | <code>null</code> |
|
||||
| [group_iam](variables.tf#L105) | 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(list(string))</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L112) | Dataplex DataScan IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables.tf#L119) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [iam_bindings_additive](variables.tf#L134) | 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> |
|
||||
| [incremental_field](variables.tf#L149) | The unnested field (of type Date or Timestamp) that contains values which monotonically increase over time. If not specified, a data scan will run for all data in the table. | <code>string</code> | | <code>null</code> |
|
||||
| [labels](variables.tf#L155) | Resource labels. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [prefix](variables.tf#L167) | Optional prefix used to generate Dataplex DataScan ID. | <code>string</code> | | <code>null</code> |
|
||||
| [iam](variables-iam.tf#L24) | Dataplex DataScan IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables-iam.tf#L31) | 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#L46) | 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> |
|
||||
| [iam_by_principals](variables-iam.tf#L17) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [incremental_field](variables.tf#L105) | The unnested field (of type Date or Timestamp) that contains values which monotonically increase over time. If not specified, a data scan will run for all data in the table. | <code>string</code> | | <code>null</code> |
|
||||
| [labels](variables.tf#L111) | Resource labels. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [prefix](variables.tf#L123) | Optional prefix used to generate Dataplex DataScan ID. | <code>string</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2023 Google LLC
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,17 +15,18 @@
|
||||
*/
|
||||
|
||||
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_principal_roles = distinct(flatten(values(var.iam_by_principals)))
|
||||
_iam_principals = {
|
||||
for r in local._iam_principal_roles : r => [
|
||||
for k, v in var.iam_by_principals :
|
||||
k if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
|
||||
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local._group_iam[role], [])
|
||||
try(local._iam_principals[role], [])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
60
modules/dataplex-datascan/variables-iam.tf
Normal file
60
modules/dataplex-datascan/variables-iam.tf
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "iam_by_principals" {
|
||||
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam" {
|
||||
description = "Dataplex DataScan IAM bindings in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
@@ -102,50 +102,6 @@ variable "execution_schedule" {
|
||||
default = null
|
||||
}
|
||||
|
||||
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 = "Dataplex DataScan IAM bindings in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "incremental_field" {
|
||||
description = "The unnested field (of type Date or Timestamp) that contains values which monotonically increase over time. If not specified, a data scan will run for all data in the table."
|
||||
type = string
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,20 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# # tfdoc:file:description Generic IAM bindings and roles.
|
||||
# tfdoc:file:description IAM bindings.
|
||||
|
||||
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_principal_roles = distinct(flatten(values(var.iam_by_principals)))
|
||||
_iam_principals = {
|
||||
for r in local._iam_principal_roles : r => [
|
||||
for k, v in var.iam_by_principals :
|
||||
k if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
|
||||
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local._group_iam[role], [])
|
||||
try(local._iam_principals[role], [])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
59
modules/dataproc/variables-iam.tf
Normal file
59
modules/dataproc/variables-iam.tf
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "iam_by_principals" {
|
||||
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally 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_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
@@ -182,50 +182,6 @@ variable "dataproc_config" {
|
||||
default = {}
|
||||
}
|
||||
|
||||
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_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "The resource labels for instance to use to annotate any related underlying resources, such as Compute Engine VMs."
|
||||
type = map(string)
|
||||
|
||||
@@ -23,8 +23,8 @@ module "folder" {
|
||||
source = "./fabric/modules/folder"
|
||||
parent = var.folder_id
|
||||
name = "Folder name"
|
||||
group_iam = {
|
||||
"${var.group_email}" = [
|
||||
iam_by_principals = {
|
||||
"group:${var.group_email}" = [
|
||||
"roles/owner",
|
||||
"roles/resourcemanager.folderAdmin",
|
||||
"roles/resourcemanager.projectCreator"
|
||||
@@ -47,11 +47,11 @@ module "folder" {
|
||||
|
||||
IAM is managed via several variables that implement different features and levels of control:
|
||||
|
||||
- `iam` and `group_iam` configure authoritative bindings that manage individual roles exclusively, and are internally merged
|
||||
- `iam` and `iam_by_principals` configure authoritative bindings that manage individual roles exclusively, and are internally merged
|
||||
- `iam_bindings` configure authoritative bindings with optional support for conditions, and are not internally merged with the previous two variables
|
||||
- `iam_bindings_additive` configure additive bindings via individual role/member pairs with optional support conditions
|
||||
|
||||
The authoritative and additive approaches can be used together, provided different roles are managed by each. Some care must also be taken with the `groups_iam` variable to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph.
|
||||
The authoritative and additive approaches can be used together, provided different roles are managed by each. Some care must also be taken with the `iam_by_principals` variable to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph.
|
||||
|
||||
Refer to the [project module](../project/README.md#iam) for examples of the IAM interface.
|
||||
|
||||
@@ -334,6 +334,7 @@ module "folder" {
|
||||
| [organization-policies.tf](./organization-policies.tf) | Folder-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> |
|
||||
| [variables-iam.tf](./variables-iam.tf) | None | |
|
||||
| [variables.tf](./variables.tf) | Module variables. | |
|
||||
| [versions.tf](./versions.tf) | Version pins. | |
|
||||
|
||||
@@ -345,18 +346,18 @@ module "folder" {
|
||||
| [factories_config](variables.tf#L24) | Paths to data files and folders that enable factory functionality. | <code title="object({ org_policies = optional(string) })">object({…})</code> | | <code>{}</code> |
|
||||
| [firewall_policy](variables.tf#L33) | Hierarchical firewall policy to associate to this folder. | <code title="object({ name = string policy = string })">object({…})</code> | | <code>null</code> |
|
||||
| [folder_create](variables.tf#L42) | Create folder. When set to false, uses id to reference an existing folder. | <code>bool</code> | | <code>true</code> |
|
||||
| [group_iam](variables.tf#L48) | 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(list(string))</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L55) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables.tf#L62) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [iam_bindings_additive](variables.tf#L77) | 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> |
|
||||
| [id](variables.tf#L92) | Folder ID in case you use folder_create=false. | <code>string</code> | | <code>null</code> |
|
||||
| [logging_data_access](variables.tf#L98) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map(map(list(string)))</code> | | <code>{}</code> |
|
||||
| [logging_exclusions](variables.tf#L113) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L120) | Logging sinks to create for the folder. | <code title="map(object({ bq_partitioned_table = optional(bool, false) description = optional(string) destination = string disabled = optional(bool, false) exclusions = optional(map(string), {}) filter = string iam = optional(bool, true) include_children = optional(bool, true) type = string }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [name](variables.tf#L151) | Folder name. | <code>string</code> | | <code>null</code> |
|
||||
| [org_policies](variables.tf#L157) | Organization policies applied to this folder 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) }), {}) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [parent](variables.tf#L184) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> |
|
||||
| [tag_bindings](variables.tf#L194) | Tag bindings for this folder, in key => tag value id format. | <code>map(string)</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> |
|
||||
| [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [id](variables.tf#L48) | Folder ID in case you use folder_create=false. | <code>string</code> | | <code>null</code> |
|
||||
| [logging_data_access](variables.tf#L54) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map(map(list(string)))</code> | | <code>{}</code> |
|
||||
| [logging_exclusions](variables.tf#L69) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L76) | Logging sinks to create for the folder. | <code title="map(object({ bq_partitioned_table = optional(bool, false) description = optional(string) destination = string disabled = optional(bool, false) exclusions = optional(map(string), {}) filter = string iam = optional(bool, true) include_children = optional(bool, true) type = string }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [name](variables.tf#L107) | Folder name. | <code>string</code> | | <code>null</code> |
|
||||
| [org_policies](variables.tf#L113) | Organization policies applied to this folder 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) }), {}) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [parent](variables.tf#L140) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> |
|
||||
| [tag_bindings](variables.tf#L150) | Tag bindings for this folder, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,17 +17,18 @@
|
||||
# tfdoc:file:description IAM bindings.
|
||||
|
||||
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_principal_roles = distinct(flatten(values(var.iam_by_principals)))
|
||||
_iam_principals = {
|
||||
for r in local._iam_principal_roles : r => [
|
||||
for k, v in var.iam_by_principals :
|
||||
k if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
|
||||
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local._group_iam[role], [])
|
||||
try(local._iam_principals[role], [])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
59
modules/folder/variables-iam.tf
Normal file
59
modules/folder/variables-iam.tf
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_by_principals" {
|
||||
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
@@ -45,50 +45,6 @@ variable "folder_create" {
|
||||
default = true
|
||||
}
|
||||
|
||||
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_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "id" {
|
||||
description = "Folder ID in case you use folder_create=false."
|
||||
type = string
|
||||
|
||||
@@ -36,8 +36,8 @@ To manage organization policies, the `orgpolicy.googleapis.com` service should b
|
||||
module "org" {
|
||||
source = "./fabric/modules/organization"
|
||||
organization_id = var.organization_id
|
||||
group_iam = {
|
||||
(var.group_email) = ["roles/owner"]
|
||||
iam_by_principals = {
|
||||
"group:${var.group_email}" = ["roles/owner"]
|
||||
}
|
||||
iam = {
|
||||
"roles/resourcemanager.projectCreator" = ["group:${var.group_email}"]
|
||||
@@ -121,11 +121,11 @@ module "org" {
|
||||
|
||||
IAM is managed via several variables that implement different features and levels of control:
|
||||
|
||||
- `iam` and `group_iam` configure authoritative bindings that manage individual roles exclusively, and are internally merged
|
||||
- `iam` and `iam_by_principals` configure authoritative bindings that manage individual roles exclusively, and are internally merged
|
||||
- `iam_bindings` configure authoritative bindings with optional support for conditions, and are not internally merged with the previous two variables
|
||||
- `iam_bindings_additive` configure additive bindings via individual role/member pairs with optional support conditions
|
||||
|
||||
The authoritative and additive approaches can be used together, provided different roles are managed by each. Some care must also be taken with the `groups_iam` variable to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph.
|
||||
The authoritative and additive approaches can be used together, provided different roles are managed by each. Some care must also be taken with the `iam_by_principals` variable to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph.
|
||||
|
||||
Refer to the [project module](../project/README.md#iam) for examples of the IAM interface.
|
||||
|
||||
@@ -473,13 +473,14 @@ module "org" {
|
||||
|
||||
| name | description | resources |
|
||||
|---|---|---|
|
||||
| [iam.tf](./iam.tf) | IAM bindings, roles and audit logging resources. | <code>google_organization_iam_binding</code> · <code>google_organization_iam_custom_role</code> · <code>google_organization_iam_member</code> |
|
||||
| [iam.tf](./iam.tf) | IAM bindings. | <code>google_organization_iam_binding</code> · <code>google_organization_iam_custom_role</code> · <code>google_organization_iam_member</code> |
|
||||
| [logging.tf](./logging.tf) | Log sinks and data access logs. | <code>google_bigquery_dataset_iam_member</code> · <code>google_logging_organization_exclusion</code> · <code>google_logging_organization_sink</code> · <code>google_organization_iam_audit_config</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_firewall_policy_association</code> · <code>google_essential_contacts_contact</code> |
|
||||
| [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_value</code> · <code>google_tags_tag_value_iam_binding</code> |
|
||||
| [variables-iam.tf](./variables-iam.tf) | None | |
|
||||
| [variables-tags.tf](./variables-tags.tf) | None | |
|
||||
| [variables.tf](./variables.tf) | Module variables. | |
|
||||
| [versions.tf](./versions.tf) | Version pins. | |
|
||||
@@ -488,21 +489,21 @@ module "org" {
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [organization_id](variables.tf#L189) | Organization id in organizations/nnnnnn format. | <code>string</code> | ✓ | |
|
||||
| [organization_id](variables.tf#L145) | 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) })">object({…})</code> | | <code>{}</code> |
|
||||
| [firewall_policy](variables.tf#L42) | Hierarchical firewall policies to associate to the organization. | <code title="object({ name = string policy = string })">object({…})</code> | | <code>null</code> |
|
||||
| [group_iam](variables.tf#L51) | 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(list(string))</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L58) | IAM bindings, in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables.tf#L65) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [iam_bindings_additive](variables.tf#L80) | 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> |
|
||||
| [logging_data_access](variables.tf#L95) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map(map(list(string)))</code> | | <code>{}</code> |
|
||||
| [logging_exclusions](variables.tf#L110) | Logging exclusions for this organization in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L117) | 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 = string iam = optional(bool, true) include_children = optional(bool, true) type = string }))">map(object({…}))</code> | | <code>{}</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> |
|
||||
| [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [logging_data_access](variables.tf#L51) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map(map(list(string)))</code> | | <code>{}</code> |
|
||||
| [logging_exclusions](variables.tf#L66) | Logging exclusions for this organization in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L73) | 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 = string iam = optional(bool, true) include_children = optional(bool, true) 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)), {}) 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)), {}) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policies](variables.tf#L148) | 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) }), {}) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policy_custom_constraints](variables.tf#L175) | 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#L104) | 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) }), {}) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policy_custom_constraints](variables.tf#L131) | 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#L45) | Tag bindings for this organization, in key => tag value id format. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [tags](variables-tags.tf#L52) | 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)), {}) id = optional(string) values = optional(map(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) id = optional(string) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description IAM bindings, roles and audit logging resources.
|
||||
# tfdoc:file:description IAM bindings.
|
||||
|
||||
locals {
|
||||
_custom_roles = {
|
||||
@@ -23,10 +23,11 @@ locals {
|
||||
file("${var.factories_config.custom_roles}/${f}")
|
||||
)
|
||||
}
|
||||
_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_principal_roles = distinct(flatten(values(var.iam_by_principals)))
|
||||
_iam_principals = {
|
||||
for r in local._iam_principal_roles : r => [
|
||||
for k, v in var.iam_by_principals :
|
||||
k if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
custom_roles = merge(
|
||||
@@ -44,10 +45,10 @@ locals {
|
||||
}
|
||||
)
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
|
||||
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local._group_iam[role], [])
|
||||
try(local._iam_principals[role], [])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
59
modules/organization/variables-iam.tf
Normal file
59
modules/organization/variables-iam.tf
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings, in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_by_principals" {
|
||||
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
@@ -48,50 +48,6 @@ variable "firewall_policy" {
|
||||
default = null
|
||||
}
|
||||
|
||||
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_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "logging_data_access" {
|
||||
description = "Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services."
|
||||
type = map(map(list(string)))
|
||||
|
||||
@@ -48,13 +48,13 @@ module "project" {
|
||||
|
||||
IAM is managed via several variables that implement different features and levels of control:
|
||||
|
||||
- `iam` and `group_iam` configure authoritative bindings that manage individual roles exclusively, and are internally merged
|
||||
- `iam` and `iam_by_principals` configure authoritative bindings that manage individual roles exclusively, and are internally merged
|
||||
- `iam_bindings` configure authoritative bindings with optional support for conditions, and are not internally merged with the previous two variables
|
||||
- `iam_bindings_additive` configure additive bindings via individual role/member pairs with optional support conditions
|
||||
|
||||
The authoritative and additive approaches can be used together, provided different roles are managed by each. Some care must also be taken with the `groups_iam` variable to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph.
|
||||
The authoritative and additive approaches can be used together, provided different roles are managed by each. Some care must also be taken with the `iam_by_principals` variable to ensure that variable keys are static values, so that Terraform is able to compute the dependency graph.
|
||||
|
||||
Be mindful about service identity roles when using authoritative IAM, as you might inadvertently remove a role from a [service identity](https://cloud.google.com/iam/docs/service-account-types#google-managed) or default service account. For example, using `roles/editor` with `iam` or `group_iam` will remove the default permissions for the Cloud Services identity. A simple workaround for these scenarios is described below.
|
||||
Be mindful about service identity roles when using authoritative IAM, as you might inadvertently remove a role from a [service identity](https://cloud.google.com/iam/docs/service-account-types#google-managed) or default service account. For example, using `roles/editor` with `iam` or `iam_principals` will remove the default permissions for the Cloud Services identity. A simple workaround for these scenarios is described below.
|
||||
|
||||
### Authoritative IAM
|
||||
|
||||
@@ -84,7 +84,7 @@ module "project" {
|
||||
# tftest modules=1 resources=4 inventory=iam-authoritative.yaml
|
||||
```
|
||||
|
||||
The `group_iam` variable uses group email addresses as keys and is a convenient way to assign roles to humans following Google's best practices. The end result is readable code that also serves as documentation.
|
||||
The `iam_by_principals` variable uses [principals](https://cloud.google.com/iam/docs/principal-identifiers) as keys and is a convenient way to assign roles to humans following Google's best practices. The end result is readable code that also serves as documentation.
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
@@ -93,8 +93,8 @@ module "project" {
|
||||
name = "project"
|
||||
parent = var.folder_id
|
||||
prefix = var.prefix
|
||||
group_iam = {
|
||||
(var.group_email) = [
|
||||
iam_by_principals = {
|
||||
"group:${var.group_email}" = [
|
||||
"roles/cloudasset.owner",
|
||||
"roles/cloudsupport.techSupportEditor",
|
||||
"roles/iam.securityReviewer",
|
||||
@@ -721,7 +721,6 @@ module "project" {
|
||||
# tftest modules=1 resources=8
|
||||
```
|
||||
|
||||
|
||||
## Outputs
|
||||
|
||||
Most of this module's outputs depend on its resources, to allow Terraform to compute all dependencies required for the project to be correctly configured. This allows you to reference outputs like `project_id` in other modules or resources without having to worry about setting `depends_on` blocks manually.
|
||||
@@ -768,8 +767,8 @@ module "project" {
|
||||
prefix = var.prefix
|
||||
project_create = false
|
||||
|
||||
group_iam = {
|
||||
(var.group_email) = [
|
||||
iam_by_principals = {
|
||||
"group:${var.group_email}" = [
|
||||
"roles/cloudasset.owner",
|
||||
"roles/cloudsupport.techSupportEditor",
|
||||
"roles/iam.securityReviewer",
|
||||
@@ -964,7 +963,7 @@ module "bucket" {
|
||||
|
||||
| name | description | resources |
|
||||
|---|---|---|
|
||||
| [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> |
|
||||
| [iam.tf](./iam.tf) | IAM bindings. | <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_audit_config</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_org_policy_policy</code> |
|
||||
@@ -972,6 +971,7 @@ module "bucket" {
|
||||
| [service-accounts.tf](./service-accounts.tf) | Service identities and supporting resources. | <code>google_kms_crypto_key_iam_member</code> · <code>google_project_default_service_accounts</code> · <code>google_project_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> · <code>google_compute_subnetwork_iam_member</code> · <code>google_project_iam_member</code> |
|
||||
| [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_value</code> · <code>google_tags_tag_value_iam_binding</code> |
|
||||
| [variables-iam.tf](./variables-iam.tf) | None | |
|
||||
| [variables-tags.tf](./variables-tags.tf) | None | |
|
||||
| [variables.tf](./variables.tf) | Module variables. | |
|
||||
| [versions.tf](./versions.tf) | Version pins. | |
|
||||
@@ -981,7 +981,7 @@ module "bucket" {
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [name](variables.tf#L196) | Project name and id suffix. | <code>string</code> | ✓ | |
|
||||
| [name](variables.tf#L152) | 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> |
|
||||
| [compute_metadata](variables.tf#L29) | Optional compute metadata key/values. Only usable if compute API has been enabled. | <code>map(string)</code> | | <code>{}</code> |
|
||||
@@ -990,29 +990,29 @@ module "bucket" {
|
||||
| [default_service_account](variables.tf#L50) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | <code>string</code> | | <code>"keep"</code> |
|
||||
| [descriptive_name](variables.tf#L63) | Name of the project name. Used for project name instead of `name` variable. | <code>string</code> | | <code>null</code> |
|
||||
| [factories_config](variables.tf#L69) | Paths to data files and folders that enable factory functionality. | <code title="object({ custom_roles = optional(string) org_policies = optional(string) })">object({…})</code> | | <code>{}</code> |
|
||||
| [group_iam](variables.tf#L79) | 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(list(string))</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L86) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables.tf#L93) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [iam_bindings_additive](variables.tf#L108) | 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> |
|
||||
| [labels](variables.tf#L123) | Resource labels. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [lien_reason](variables.tf#L130) | If non-empty, creates a project lien with this description. | <code>string</code> | | <code>null</code> |
|
||||
| [logging_data_access](variables.tf#L136) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map(map(list(string)))</code> | | <code>{}</code> |
|
||||
| [logging_exclusions](variables.tf#L151) | Logging exclusions for this project in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L158) | Logging sinks to create for this project. | <code title="map(object({ bq_partitioned_table = optional(bool, false) description = optional(string) destination = string disabled = optional(bool, false) exclusions = optional(map(string), {}) filter = string iam = optional(bool, true) type = string unique_writer = optional(bool, true) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [metric_scopes](variables.tf#L189) | List of projects that will act as metric scopes for this project. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [iam](variables-iam.tf#L17) | Authoritative 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> |
|
||||
| [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [labels](variables.tf#L79) | Resource labels. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [lien_reason](variables.tf#L86) | If non-empty, creates a project lien with this description. | <code>string</code> | | <code>null</code> |
|
||||
| [logging_data_access](variables.tf#L92) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map(map(list(string)))</code> | | <code>{}</code> |
|
||||
| [logging_exclusions](variables.tf#L107) | Logging exclusions for this project in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L114) | Logging sinks to create for this project. | <code title="map(object({ bq_partitioned_table = optional(bool, false) description = optional(string) destination = string disabled = optional(bool, false) exclusions = optional(map(string), {}) filter = string iam = optional(bool, true) type = string unique_writer = optional(bool, true) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [metric_scopes](variables.tf#L145) | List of projects that will act as metric scopes for this project. | <code>list(string)</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 project module.") iam = optional(map(list(string)), {}) id = optional(string) network = string # project_id/vpc_name values = optional(map(object({ description = optional(string, "Managed by the Terraform project module.") iam = optional(map(list(string)), {}) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [org_policies](variables.tf#L201) | Organization policies applied to this project 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) }), {}) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [parent](variables.tf#L228) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> |
|
||||
| [prefix](variables.tf#L238) | Optional prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
|
||||
| [project_create](variables.tf#L248) | Create project. When set to false, uses a data source to reference existing project. | <code>bool</code> | | <code>true</code> |
|
||||
| [service_config](variables.tf#L254) | Configure service API activation. | <code title="object({ disable_on_destroy = bool disable_dependent_services = bool })">object({…})</code> | | <code title="{ disable_on_destroy = false disable_dependent_services = false }">{…}</code> |
|
||||
| [service_encryption_key_ids](variables.tf#L266) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [service_perimeter_bridges](variables.tf#L273) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list(string)</code> | | <code>null</code> |
|
||||
| [service_perimeter_standard](variables.tf#L280) | 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#L286) | Service APIs to enable. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [shared_vpc_host_config](variables.tf#L292) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object({ enabled = bool service_projects = optional(list(string), []) })">object({…})</code> | | <code>null</code> |
|
||||
| [shared_vpc_service_config](variables.tf#L301) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object({ host_project = string network_users = optional(list(string), []) service_identity_iam = optional(map(list(string)), {}) service_identity_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) })">object({…})</code> | | <code title="{ host_project = null }">{…}</code> |
|
||||
| [skip_delete](variables.tf#L329) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
|
||||
| [org_policies](variables.tf#L157) | Organization policies applied to this project 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) }), {}) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [parent](variables.tf#L184) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> |
|
||||
| [prefix](variables.tf#L194) | Optional prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
|
||||
| [project_create](variables.tf#L204) | Create project. When set to false, uses a data source to reference existing project. | <code>bool</code> | | <code>true</code> |
|
||||
| [service_config](variables.tf#L210) | Configure service API activation. | <code title="object({ disable_on_destroy = bool disable_dependent_services = bool })">object({…})</code> | | <code title="{ disable_on_destroy = false disable_dependent_services = false }">{…}</code> |
|
||||
| [service_encryption_key_ids](variables.tf#L222) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [service_perimeter_bridges](variables.tf#L229) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list(string)</code> | | <code>null</code> |
|
||||
| [service_perimeter_standard](variables.tf#L236) | 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#L242) | Service APIs to enable. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [shared_vpc_host_config](variables.tf#L248) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object({ enabled = bool service_projects = optional(list(string), []) })">object({…})</code> | | <code>null</code> |
|
||||
| [shared_vpc_service_config](variables.tf#L257) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object({ host_project = string network_users = optional(list(string), []) service_identity_iam = optional(map(list(string)), {}) service_identity_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) })">object({…})</code> | | <code title="{ host_project = null }">{…}</code> |
|
||||
| [skip_delete](variables.tf#L285) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
|
||||
| [tag_bindings](variables-tags.tf#L45) | Tag bindings for this project, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [tags](variables-tags.tf#L51) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | <code title="map(object({ description = optional(string, "Managed by the Terraform project module.") iam = optional(map(list(string)), {}) id = optional(string) values = optional(map(object({ description = optional(string, "Managed by the Terraform project module.") iam = optional(map(list(string)), {}) id = optional(string) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Generic and OSLogin-specific IAM bindings and roles.
|
||||
# tfdoc:file:description IAM bindings.
|
||||
|
||||
# IAM notes:
|
||||
# - external users need to have accepted the invitation email to join
|
||||
@@ -26,10 +26,11 @@ locals {
|
||||
file("${var.factories_config.custom_roles}/${f}")
|
||||
)
|
||||
}
|
||||
_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_principal_roles = distinct(flatten(values(var.iam_by_principals)))
|
||||
_iam_principals = {
|
||||
for r in local._iam_principal_roles : r => [
|
||||
for k, v in var.iam_by_principals :
|
||||
k if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
custom_roles = merge(
|
||||
@@ -47,10 +48,10 @@ locals {
|
||||
}
|
||||
)
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
|
||||
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local._group_iam[role], [])
|
||||
try(local._iam_principals[role], [])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
59
modules/project/variables-iam.tf
Normal file
59
modules/project/variables-iam.tf
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "iam" {
|
||||
description = "Authoritative IAM bindings in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_by_principals" {
|
||||
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
@@ -76,50 +76,6 @@ variable "factories_config" {
|
||||
default = {}
|
||||
}
|
||||
|
||||
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 = "Authoritative IAM bindings in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "Resource labels."
|
||||
type = map(string)
|
||||
|
||||
@@ -65,9 +65,10 @@ module "repo" {
|
||||
|
||||
| name | description | resources |
|
||||
|---|---|---|
|
||||
| [iam.tf](./iam.tf) | IAM resources. | <code>google_sourcerepo_repository_iam_binding</code> · <code>google_sourcerepo_repository_iam_member</code> |
|
||||
| [iam.tf](./iam.tf) | IAM bindings. | <code>google_sourcerepo_repository_iam_binding</code> · <code>google_sourcerepo_repository_iam_member</code> |
|
||||
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_cloudbuild_trigger</code> · <code>google_sourcerepo_repository</code> |
|
||||
| [outputs.tf](./outputs.tf) | Module outputs. | |
|
||||
| [variables-iam.tf](./variables-iam.tf) | None | |
|
||||
| [variables.tf](./variables.tf) | Module variables. | |
|
||||
| [versions.tf](./versions.tf) | Version pins. | |
|
||||
|
||||
@@ -75,13 +76,13 @@ module "repo" {
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [name](variables.tf#L61) | Repository name. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L66) | Project used for resources. | <code>string</code> | ✓ | |
|
||||
| [group_iam](variables.tf#L17) | 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(list(string))</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L24) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_bindings](variables.tf#L31) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [iam_bindings_additive](variables.tf#L46) | 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> |
|
||||
| [triggers](variables.tf#L71) | Cloud Build triggers. | <code title="map(object({ filename = string included_files = list(string) service_account = string substitutions = map(string) template = object({ branch_name = string project_id = string tag_name = string }) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [name](variables.tf#L17) | Repository name. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L22) | Project used for resources. | <code>string</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> |
|
||||
| [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [triggers](variables.tf#L27) | Cloud Build triggers. | <code title="map(object({ filename = string included_files = list(string) service_account = string substitutions = map(string) template = object({ branch_name = string project_id = string tag_name = string }) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,20 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description IAM resources.
|
||||
# tfdoc:file:description IAM bindings.
|
||||
|
||||
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_principal_roles = distinct(flatten(values(var.iam_by_principals)))
|
||||
_iam_principals = {
|
||||
for r in local._iam_principal_roles : r => [
|
||||
for k, v in var.iam_by_principals :
|
||||
k if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
|
||||
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local._group_iam[role], [])
|
||||
try(local._iam_principals[role], [])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
59
modules/source-repository/variables-iam.tf
Normal file
59
modules/source-repository/variables-iam.tf
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_by_principals" {
|
||||
description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
@@ -14,50 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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_bindings" {
|
||||
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
|
||||
type = map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "name" {
|
||||
description = "Repository name."
|
||||
type = string
|
||||
|
||||
Reference in New Issue
Block a user