Merge pull request #153 from terraform-google-modules/terraform-0.13

Add support for Terraform 0.13 features
This commit is contained in:
Julio Castillo 2020-11-06 11:57:19 +01:00 committed by GitHub
commit 94e0268e3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
176 changed files with 1953 additions and 1691 deletions

View File

@ -40,6 +40,8 @@ steps:
entrypoint: pytest entrypoint: pytest
args: args:
- -vv - -vv
- tests/cloud_operations
- tests/data_solutions
- tests/foundations - tests/foundations
- tests/networking - tests/networking
env: env:

View File

@ -33,7 +33,7 @@ The current list of modules supports most of the core foundational and networkin
Currently available modules: Currently available modules:
- **foundational** - [folders](./modules/folders), [log sinks](./modules/logging-sinks), [organization](./modules/organization), [project](./modules/project), [service accounts](./modules/iam-service-accounts) - **foundational** - [folder](./modules/folder), [log sinks](./modules/logging-sinks), [organization](./modules/organization), [project](./modules/project), [service accounts](./modules/iam-service-account)
- **networking** - [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), [VPN static](./modules/net-vpn-static), [VPN dynamic](./modules/net-vpn-dynamic), [VPN HA](./modules/net-vpn-ha), [NAT](./modules/net-cloudnat), [address reservation](./modules/net-address), [DNS](./modules/dns), [L4 ILB](./modules/net-ilb), [Service Directory](./modules/service-directory), [Cloud Endpoints](./modules/cloudenpoints) - **networking** - [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), [VPN static](./modules/net-vpn-static), [VPN dynamic](./modules/net-vpn-dynamic), [VPN HA](./modules/net-vpn-ha), [NAT](./modules/net-cloudnat), [address reservation](./modules/net-address), [DNS](./modules/dns), [L4 ILB](./modules/net-ilb), [Service Directory](./modules/service-directory), [Cloud Endpoints](./modules/cloudenpoints)
- **compute** - [VM/VM group](./modules/compute-vm), [MIG](./modules/compute-mig), [GKE cluster](./modules/gke-cluster), [GKE nodepool](./modules/gke-nodepool), [COS container](./modules/cos-container) (coredns, mysql, onprem, squid) - **compute** - [VM/VM group](./modules/compute-vm), [MIG](./modules/compute-mig), [GKE cluster](./modules/gke-cluster), [GKE nodepool](./modules/gke-nodepool), [COS container](./modules/cos-container) (coredns, mysql, onprem, squid)
- **data** - [GCS](./modules/gcs), [BigQuery dataset](./modules/bigquery-dataset), [Pub/Sub](./modules/pubsub), [Datafusion](./modules/datafusion), [Bigtable instance](./modules/bigtable-instance) - **data** - [GCS](./modules/gcs), [BigQuery dataset](./modules/bigquery-dataset), [Pub/Sub](./modules/pubsub), [Datafusion](./modules/datafusion), [Bigtable instance](./modules/bigtable-instance)

View File

@ -41,8 +41,7 @@ module "project" {
"compute.zoneOperations.list" "compute.zoneOperations.list"
] ]
} }
iam_roles = [local.role_id] iam = {
iam_members = {
(local.role_id) = [module.service-account.iam_email] (local.role_id) = [module.service-account.iam_email]
} }
} }
@ -64,10 +63,7 @@ module "pubsub" {
project_id = module.project.project_id project_id = module.project.project_id
name = var.name name = var.name
subscriptions = { "${var.name}-default" = null } subscriptions = { "${var.name}-default" = null }
iam_roles = [ iam = {
"roles/pubsub.publisher"
]
iam_members = {
"roles/pubsub.publisher" = [ "roles/pubsub.publisher" = [
"serviceAccount:${module.project.service_accounts.robots.cloudasset}" "serviceAccount:${module.project.service_accounts.robots.cloudasset}"
] ]
@ -75,9 +71,9 @@ module "pubsub" {
} }
module "service-account" { module "service-account" {
source = "../../modules/iam-service-accounts" source = "../../modules/iam-service-account"
project_id = module.project.project_id project_id = module.project.project_id
names = ["${var.name}-cf"] name = "${var.name}-cf"
# iam_project_roles = { (module.project.project_id) = [local.role_id] } # iam_project_roles = { (module.project.project_id) = [local.role_id] }
} }

View File

@ -77,26 +77,22 @@ module "service-directory" {
project_id = module.project.project_id project_id = module.project.project_id
location = var.region location = var.region
name = var.name name = var.name
iam_members = { iam = {
"roles/servicedirectory.editor" = [ "roles/servicedirectory.editor" = [
module.vm-ns-editor.service_account_iam_email module.vm-ns-editor.service_account_iam_email
] ]
} }
iam_roles = ["roles/servicedirectory.editor"]
services = { services = {
app1 = { endpoints = ["vm1", "vm2"], metadata = null } app1 = { endpoints = ["vm1", "vm2"], metadata = null }
app2 = { endpoints = ["vm1", "vm2"], metadata = null } app2 = { endpoints = ["vm1", "vm2"], metadata = null }
} }
service_iam_members = { service_iam = {
app1 = { app1 = {
"roles/servicedirectory.editor" = [ "roles/servicedirectory.editor" = [
module.vm-svc-editor.service_account_iam_email module.vm-svc-editor.service_account_iam_email
] ]
} }
} }
service_iam_roles = {
app1 = ["roles/servicedirectory.editor"]
}
endpoint_config = { endpoint_config = {
"app1/vm1" = { address = "127.0.0.2", port = 80, metadata = {} } "app1/vm1" = { address = "127.0.0.2", port = 80, metadata = {} }
"app1/vm2" = { address = "127.0.0.3", port = 80, metadata = {} } "app1/vm2" = { address = "127.0.0.3", port = 80, metadata = {} }

View File

@ -34,10 +34,7 @@ module "project" {
disable_on_destroy = false, disable_on_destroy = false,
disable_dependent_services = false disable_dependent_services = false
} }
iam_roles = [ iam = {
"roles/monitoring.metricWriter",
]
iam_members = {
"roles/monitoring.metricWriter" = [module.cf.service_account_iam_email] "roles/monitoring.metricWriter" = [module.cf.service_account_iam_email]
} }
} }

View File

@ -1,4 +1,4 @@
# Copyright 2019 Google LLC # Copyright 2020 Google LLC
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.

View File

@ -17,6 +17,7 @@
############################################################################### ###############################################################################
# Projects # # Projects #
############################################################################### ###############################################################################
module "project" { module "project" {
source = "../../modules/project" source = "../../modules/project"
name = var.project_id name = var.project_id
@ -35,9 +36,9 @@ module "project" {
} }
module "service-account" { module "service-account" {
source = "../../modules/iam-service-accounts" source = "../../modules/iam-service-account"
project_id = module.project.project_id project_id = module.project.project_id
names = ["${var.name}-cf"] name = "${var.name}-cf"
iam_project_roles = { iam_project_roles = {
(var.project_id) = ["roles/cloudasset.viewer"] (var.project_id) = ["roles/cloudasset.viewer"]
} }
@ -46,6 +47,7 @@ module "service-account" {
############################################################################### ###############################################################################
# Pub/Sub # # Pub/Sub #
############################################################################### ###############################################################################
module "pubsub" { module "pubsub" {
source = "../../modules/pubsub" source = "../../modules/pubsub"
project_id = module.project.project_id project_id = module.project.project_id
@ -60,6 +62,7 @@ module "pubsub" {
############################################################################### ###############################################################################
# Cloud Function # # Cloud Function #
############################################################################### ###############################################################################
module "cf" { module "cf" {
source = "../../modules/cloud-function" source = "../../modules/cloud-function"
project_id = module.project.project_id project_id = module.project.project_id
@ -88,6 +91,7 @@ resource "random_pet" "random" {
############################################################################### ###############################################################################
# Cloud Scheduler # # Cloud Scheduler #
############################################################################### ###############################################################################
resource "google_app_engine_application" "app" { resource "google_app_engine_application" "app" {
project = module.project.project_id project = module.project.project_id
location_id = var.location location_id = var.location
@ -116,6 +120,7 @@ resource "google_cloud_scheduler_job" "job" {
############################################################################### ###############################################################################
# Bigquery # # Bigquery #
############################################################################### ###############################################################################
module "bq" { module "bq" {
source = "../../modules/bigquery-dataset" source = "../../modules/bigquery-dataset"
project_id = module.project.project_id project_id = module.project.project_id

View File

@ -1,17 +0,0 @@
# Copyright 2020 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
#
# https://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.
terraform {
required_version = ">= 0.12.6"
}

View File

@ -79,10 +79,7 @@ module "kms" {
location = var.location location = var.location
} }
keys = { key-gce = null, key-gcs = null } keys = { key-gce = null, key-gcs = null }
key_iam_roles = { key_iam = {
key-gce = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"]
}
key_iam_members = {
key-gce = { key-gce = {
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [
"serviceAccount:${module.project-service.service_accounts.robots.compute}", "serviceAccount:${module.project-service.service_accounts.robots.compute}",
@ -148,8 +145,6 @@ module "kms-gcs" {
source = "../../modules/gcs" source = "../../modules/gcs"
project_id = module.project-service.project_id project_id = module.project-service.project_id
prefix = "my-bucket-001" prefix = "my-bucket-001"
names = ["kms-gcs"] name = "kms-gcs"
encryption_keys = { encryption_key = module.kms.keys.key-gcs.self_link
kms-gcs = module.kms.keys.key-gce.self_link,
}
} }

View File

@ -13,19 +13,13 @@
# limitations under the License. # limitations under the License.
output "bucket" { output "bucket" {
description = "GCS Bucket Cloud KMS crypto keys." description = "GCS Bucket URL."
value = { value = module.kms-gcs.url
for bucket in module.kms-gcs.buckets :
bucket.name => bucket.url
}
} }
output "bucket_keys" { output "bucket_keys" {
description = "GCS Bucket Cloud KMS crypto keys." description = "GCS Bucket Cloud KMS crypto keys."
value = { value = module.kms-gcs.bucket.encryption
for bucket in module.kms-gcs.buckets :
bucket.name => bucket.encryption
}
} }
output "projects" { output "projects" {

View File

@ -57,9 +57,9 @@ module "project-kms" {
############################################################################### ###############################################################################
module "service-account-bq" { module "service-account-bq" {
source = "../../modules/iam-service-accounts" source = "../../modules/iam-service-account"
project_id = module.project-service.project_id project_id = module.project-service.project_id
names = ["bq-test"] name = "bq-test"
iam_project_roles = { iam_project_roles = {
(var.project_service_name) = [ (var.project_service_name) = [
"roles/logging.logWriter", "roles/logging.logWriter",
@ -70,9 +70,9 @@ module "service-account-bq" {
} }
module "service-account-gce" { module "service-account-gce" {
source = "../../modules/iam-service-accounts" source = "../../modules/iam-service-account"
project_id = module.project-service.project_id project_id = module.project-service.project_id
names = ["gce-test"] name = "gce-test"
iam_project_roles = { iam_project_roles = {
(var.project_service_name) = [ (var.project_service_name) = [
"roles/logging.logWriter", "roles/logging.logWriter",
@ -86,9 +86,9 @@ module "service-account-gce" {
} }
module "service-account-df" { module "service-account-df" {
source = "../../modules/iam-service-accounts" source = "../../modules/iam-service-account"
project_id = module.project-service.project_id project_id = module.project-service.project_id
names = ["df-test"] name = "df-test"
iam_project_roles = { iam_project_roles = {
(var.project_service_name) = [ (var.project_service_name) = [
"roles/dataflow.worker", "roles/dataflow.worker",
@ -120,12 +120,7 @@ module "kms" {
location = var.location location = var.location
} }
keys = { key-gce = null, key-gcs = null, key-bq = null } keys = { key-gce = null, key-gcs = null, key-bq = null }
key_iam_roles = { key_iam = {
key-gce = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"]
key-gcs = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"]
key-bq = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"]
}
key_iam_members = {
key-gce = { key-gce = {
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [
"serviceAccount:${module.project-service.service_accounts.robots.compute}", "serviceAccount:${module.project-service.service_accounts.robots.compute}",
@ -155,10 +150,7 @@ module "kms-regional" {
location = var.region location = var.region
} }
keys = { key-df = null } keys = { key-df = null }
key_iam_roles = { key_iam = {
key-df = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"]
}
key_iam_members = {
key-df = { key-df = {
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [
"serviceAccount:${module.project-service.service_accounts.robots.dataflow}", "serviceAccount:${module.project-service.service_accounts.robots.dataflow}",
@ -255,37 +247,32 @@ module "vm_example" {
module "kms-gcs" { module "kms-gcs" {
source = "../../modules/gcs" source = "../../modules/gcs"
project_id = module.project-service.project_id for_each = {
prefix = module.project-service.project_id
names = ["data", "df-tmplocation"]
iam_roles = {
data = ["roles/storage.admin", "roles/storage.objectViewer"],
df-tmplocation = ["roles/storage.admin"]
}
iam_members = {
data = { data = {
members = {
"roles/storage.admin" = [ "roles/storage.admin" = [
"serviceAccount:${module.service-account-gce.email}", "serviceAccount:${module.service-account-gce.email}",
], ],
"roles/storage.viewer" = [ "roles/storage.objectViewer" = [
"serviceAccount:${module.service-account-df.email}", "serviceAccount:${module.service-account-df.email}",
], ]
}, }
}
df-tmplocation = { df-tmplocation = {
members = {
"roles/storage.admin" = [ "roles/storage.admin" = [
"serviceAccount:${module.service-account-gce.email}", "serviceAccount:${module.service-account-gce.email}",
"serviceAccount:${module.service-account-df.email}", "serviceAccount:${module.service-account-df.email}",
] ]
} }
} }
encryption_keys = {
data = module.kms.keys.key-gcs.self_link,
df-tmplocation = module.kms.keys.key-gcs.self_link,
}
force_destroy = {
data = true,
df-tmplocation = true,
} }
project_id = module.project-service.project_id
prefix = module.project-service.project_id
name = each.key
iam = each.value.members
encryption_key = module.kms.keys.key-gcs.self_link
force_destroy = true
} }
############################################################################### ###############################################################################
@ -297,10 +284,11 @@ module "bigquery-dataset" {
project_id = module.project-service.project_id project_id = module.project-service.project_id
id = "bq_dataset" id = "bq_dataset"
access_roles = { access_roles = {
reader-group = { role = "READER", type = "domain" } reader-group = { role = "READER", type = "service_account" }
owner = { role = "OWNER", type = "user_by_email" } owner = { role = "OWNER", type = "user_by_email" }
} }
access_identities = { access_identities = {
reader-group = module.service-account-bq.email
owner = module.service-account-bq.email owner = module.service-account-bq.email
} }
encryption_key = module.kms.keys.key-bq.self_link encryption_key = module.kms.keys.key-bq.self_link

View File

@ -20,7 +20,7 @@ output "bq_tables" {
output "buckets" { output "buckets" {
description = "GCS Bucket Cloud KMS crypto keys." description = "GCS Bucket Cloud KMS crypto keys."
value = { value = {
for bucket in module.kms-gcs.buckets : for name, bucket in module.kms-gcs :
bucket.name => bucket.url bucket.name => bucket.url
} }
} }

View File

@ -21,9 +21,9 @@
# Shared folder # Shared folder
module "shared-folder" { module "shared-folder" {
source = "../../modules/folders" source = "../../modules/folder"
parent = var.root_node parent = var.root_node
names = ["shared"] name = "shared"
} }
# Terraform project # Terraform project
@ -34,7 +34,7 @@ module "tf-project" {
parent = module.shared-folder.id parent = module.shared-folder.id
prefix = var.prefix prefix = var.prefix
billing_account = var.billing_account_id billing_account = var.billing_account_id
iam_additive_bindings = { iam_additive = {
for name in var.iam_terraform_owners : (name) => ["roles/owner"] for name in var.iam_terraform_owners : (name) => ["roles/owner"]
} }
services = var.project_services services = var.project_services
@ -45,7 +45,7 @@ module "tf-project" {
module "tf-gcs-bootstrap" { module "tf-gcs-bootstrap" {
source = "../../modules/gcs" source = "../../modules/gcs"
project_id = module.tf-project.project_id project_id = module.tf-project.project_id
names = ["tf-bootstrap"] name = "tf-bootstrap"
prefix = "${var.prefix}-tf" prefix = "${var.prefix}-tf"
location = var.gcs_defaults.location location = var.gcs_defaults.location
} }
@ -96,14 +96,10 @@ module "audit-project" {
parent = var.root_node parent = var.root_node
prefix = var.prefix prefix = var.prefix
billing_account = var.billing_account_id billing_account = var.billing_account_id
iam_members = { iam = {
"roles/bigquery.dataEditor" = [module.audit-log-sinks.writer_identities[0]] "roles/bigquery.dataEditor" = [module.audit-log-sinks.writer_identities[0]]
"roles/viewer" = var.iam_audit_viewers "roles/viewer" = var.iam_audit_viewers
} }
iam_roles = [
"roles/bigquery.dataEditor",
"roles/viewer"
]
services = concat(var.project_services, [ services = concat(var.project_services, [
"bigquery.googleapis.com", "bigquery.googleapis.com",
]) ])
@ -147,7 +143,7 @@ module "shared-project" {
parent = module.shared-folder.id parent = module.shared-folder.id
prefix = var.prefix prefix = var.prefix
billing_account = var.billing_account_id billing_account = var.billing_account_id
iam_additive_bindings = { iam_additive = {
for name in var.iam_shared_owners : (name) => ["roles/owner"] for name in var.iam_shared_owners : (name) => ["roles/owner"]
} }
services = var.project_services services = var.project_services

View File

@ -33,7 +33,7 @@ If no shared services are needed, the shared service project module can of cours
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---: |:---:|:---:| |---|---|:---: |:---:|:---:|
| billing_account_id | Billing account id used as to create projects. | <code title="">string</code> | ✓ | | | billing_account_id | Billing account id used as to create projects. | <code title="">string</code> | ✓ | |
| environments | Environment short names. | <code title="list&#40;string&#41;">list(string)</code> | ✓ | | | environments | Environment short names. | <code title="set&#40;string&#41;">set(string)</code> | ✓ | |
| organization_id | Organization id in organizations/nnnnnnnn format. | <code title="">string</code> | ✓ | | | organization_id | Organization id in organizations/nnnnnnnn format. | <code title="">string</code> | ✓ | |
| prefix | Prefix used for resources that need unique names. | <code title="">string</code> | ✓ | | | prefix | Prefix used for resources that need unique names. | <code title="">string</code> | ✓ | |
| root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | <code title="">string</code> | ✓ | | | root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | <code title="">string</code> | ✓ | |

View File

@ -24,7 +24,7 @@ module "tf-project" {
parent = var.root_node parent = var.root_node
prefix = var.prefix prefix = var.prefix
billing_account = var.billing_account_id billing_account = var.billing_account_id
iam_additive_bindings = { iam_additive = {
for name in var.iam_terraform_owners : (name) => ["roles/owner"] for name in var.iam_terraform_owners : (name) => ["roles/owner"]
} }
services = var.project_services services = var.project_services
@ -33,9 +33,10 @@ module "tf-project" {
# per-environment service accounts # per-environment service accounts
module "tf-service-accounts" { module "tf-service-accounts" {
source = "../../modules/iam-service-accounts" source = "../../modules/iam-service-account"
for_each = var.environments
project_id = module.tf-project.project_id project_id = module.tf-project.project_id
names = var.environments name = each.value
prefix = var.prefix prefix = var.prefix
iam_billing_roles = { iam_billing_roles = {
(var.billing_account_id) = ( (var.billing_account_id) = (
@ -49,7 +50,7 @@ module "tf-service-accounts" {
var.iam_xpn_config.grant ? local.sa_xpn_org_roles : [] var.iam_xpn_config.grant ? local.sa_xpn_org_roles : []
) )
} }
generate_keys = var.service_account_keys generate_key = var.service_account_keys
} }
# bootstrap Terraform state GCS bucket # bootstrap Terraform state GCS bucket
@ -57,7 +58,7 @@ module "tf-service-accounts" {
module "tf-gcs-bootstrap" { module "tf-gcs-bootstrap" {
source = "../../modules/gcs" source = "../../modules/gcs"
project_id = module.tf-project.project_id project_id = module.tf-project.project_id
names = ["tf-bootstrap"] name = "tf-bootstrap"
prefix = "${var.prefix}-tf" prefix = "${var.prefix}-tf"
location = var.gcs_location location = var.gcs_location
} }
@ -66,17 +67,13 @@ module "tf-gcs-bootstrap" {
module "tf-gcs-environments" { module "tf-gcs-environments" {
source = "../../modules/gcs" source = "../../modules/gcs"
for_each = var.environments
project_id = module.tf-project.project_id project_id = module.tf-project.project_id
names = var.environments name = each.value
prefix = "${var.prefix}-tf" prefix = "${var.prefix}-tf"
location = var.gcs_location location = var.gcs_location
iam_roles = { iam = {
for name in var.environments : (name) => ["roles/storage.objectAdmin"] "roles/storage.objectAdmin" = [module.tf-service-accounts[each.value].iam_email]
}
iam_members = {
for name in var.environments : (name) => {
"roles/storage.objectAdmin" = [module.tf-service-accounts.iam_emails[name]]
}
} }
} }
@ -85,17 +82,13 @@ module "tf-gcs-environments" {
############################################################################### ###############################################################################
module "environment-folders" { module "environment-folders" {
source = "../../modules/folders" source = "../../modules/folder"
for_each = var.environments
parent = var.root_node parent = var.root_node
names = var.environments name = each.value
iam_roles = { iam = {
for name in var.environments : (name) => local.folder_roles
}
iam_members = {
for name in var.environments : (name) => {
for role in local.folder_roles : for role in local.folder_roles :
(role) => [module.tf-service-accounts.iam_emails[name]] (role) => [module.tf-service-accounts[each.value].iam_email]
}
} }
} }
@ -111,14 +104,10 @@ module "audit-project" {
parent = var.root_node parent = var.root_node
prefix = var.prefix prefix = var.prefix
billing_account = var.billing_account_id billing_account = var.billing_account_id
iam_members = { iam = {
"roles/bigquery.dataEditor" = [module.audit-log-sinks.writer_identities[0]] "roles/bigquery.dataEditor" = [module.audit-log-sinks.writer_identities[0]]
"roles/viewer" = var.iam_audit_viewers "roles/viewer" = var.iam_audit_viewers
} }
iam_roles = [
"roles/bigquery.dataEditor",
"roles/viewer"
]
services = concat(var.project_services, [ services = concat(var.project_services, [
"bigquery.googleapis.com", "bigquery.googleapis.com",
]) ])
@ -163,7 +152,7 @@ module "sharedsvc-project" {
parent = var.root_node parent = var.root_node
prefix = var.prefix prefix = var.prefix
billing_account = var.billing_account_id billing_account = var.billing_account_id
iam_additive_bindings = { iam_additive = {
for name in var.iam_shared_owners : (name) => ["roles/owner"] for name in var.iam_shared_owners : (name) => ["roles/owner"]
} }
services = var.project_services services = var.project_services

View File

@ -24,23 +24,23 @@ output "bootstrap_tf_gcs_bucket" {
output "environment_folders" { output "environment_folders" {
description = "Top-level environment folders." description = "Top-level environment folders."
value = module.environment-folders.ids value = { for folder in module.environment-folders : folder.name => folder.id }
} }
output "environment_tf_gcs_buckets" { output "environment_tf_gcs_buckets" {
description = "GCS buckets used for each environment Terraform state." description = "GCS buckets used for each environment Terraform state."
value = module.tf-gcs-environments.names value = { for env, bucket in module.tf-gcs-environments : env => bucket.name }
} }
output "environment_service_account_keys" { output "environment_service_account_keys" {
description = "Service account keys used to run each environment Terraform modules." description = "Service account keys used to run each environment Terraform modules."
sensitive = true sensitive = true
value = module.tf-service-accounts.keys value = { for env, sa in module.tf-service-accounts : env => sa.key }
} }
output "environment_service_accounts" { output "environment_service_accounts" {
description = "Service accounts used to run each environment Terraform modules." description = "Service accounts used to run each environment Terraform modules."
value = module.tf-service-accounts.emails value = { for env, sa in module.tf-service-accounts : env => sa.email }
} }
output "audit_logs_bq_dataset" { output "audit_logs_bq_dataset" {

View File

@ -29,7 +29,7 @@ variable "billing_account_id" {
variable "environments" { variable "environments" {
description = "Environment short names." description = "Environment short names."
type = list(string) type = set(string)
} }
variable "gcs_location" { variable "gcs_location" {

View File

@ -10,11 +10,11 @@ Specific modules also offer support for non-authoritative bindings (e.g. `google
## Foundational modules ## Foundational modules
- [folders](./folders) - [folder](./folder)
- [log sinks](./logging-sinks) - [log sinks](./logging-sinks)
- [organization](./organization) - [organization](./organization)
- [project](./project) - [project](./project)
- [service accounts](./iam-service-accounts) - [service account](./iam-service-account)
## Networking modules ## Networking modules

View File

@ -13,8 +13,7 @@ module "docker_artifact_registry" {
location = "europe-west1" location = "europe-west1"
format = "DOCKER" format = "DOCKER"
id = "myregistry" id = "myregistry"
iam_roles = ["roles/artifactregistry.admin"] iam = {
iam_members = {
"roles/artifactregistry.admin" = ["group:cicd@example.com"] "roles/artifactregistry.admin" = ["group:cicd@example.com"]
} }
} }
@ -29,8 +28,7 @@ module "docker_artifact_registry" {
| project_id | Registry project id. | <code title="">string</code> | ✓ | | | project_id | Registry project id. | <code title="">string</code> | ✓ | |
| *description* | An optional description for the repository | <code title="">string</code> | | <code title="">Terraform-managed registry</code> | | *description* | An optional description for the repository | <code title="">string</code> | | <code title="">Terraform-managed registry</code> |
| *format* | Repository format. One of DOCKER or UNSPECIFIED | <code title="">string</code> | | <code title="">DOCKER</code> | | *format* | Repository format. One of DOCKER or UNSPECIFIED | <code title="">string</code> | | <code title="">DOCKER</code> |
| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | List of roles used to set authoritative bindings. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *labels* | Labels to be attached to the registry. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> | | *labels* | Labels to be attached to the registry. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> |
| *location* | Registry location. Use `gcloud beta artifacts locations list' to get valid values | <code title="">string</code> | | <code title=""></code> | | *location* | Registry location. Use `gcloud beta artifacts locations list' to get valid values | <code title="">string</code> | | <code title=""></code> |

View File

@ -26,10 +26,10 @@ resource "google_artifact_registry_repository" "registry" {
resource "google_artifact_registry_repository_iam_binding" "bindings" { resource "google_artifact_registry_repository_iam_binding" "bindings" {
provider = google-beta provider = google-beta
for_each = toset(var.iam_roles) for_each = var.iam
project = var.project_id project = var.project_id
location = google_artifact_registry_repository.registry.location location = google_artifact_registry_repository.registry.location
repository = google_artifact_registry_repository.registry.name repository = google_artifact_registry_repository.registry.name
role = each.value role = each.key
members = lookup(var.iam_members, each.value, []) members = each.value
} }

View File

@ -14,18 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
variable "iam_members" { variable "iam" {
description = "Map of member lists used to set authoritative bindings, keyed by role." description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_roles" {
description = "List of roles used to set authoritative bindings."
type = list(string)
default = []
}
variable "location" { variable "location" {
description = "Registry location. Use `gcloud beta artifacts locations list' to get valid values" description = "Registry location. Use `gcloud beta artifacts locations list' to get valid values"
type = string type = string

View File

@ -13,23 +13,21 @@ This module allows managing a single BigTable instance, including access configu
```hcl ```hcl
module "big-table-instance" { module "bigtable-instance" {
source = "./modules/bigtable-instance" source = "./modules/bigtable-instance"
project_id = "my-project" project_id = "my-project"
name = "instance" name = "instance"
cluster_id = "instance" cluster_id = "instance"
instance_type = "PRODUCTION" zone = "europe-west1-b"
tables = { tables = {
test1 = { table_options = null }, test1 = null,
test2 = { table_options = { test2 = {
split_keys = ["a", "b", "c"] split_keys = ["a", "b", "c"]
column_family = null column_family = null
} }
} }
} iam = {
iam_roles = ["viewer"] "roles/bigtable.user" = ["user:viewer@testdomain.com"]
iam_members = {
viewer = ["user:viewer@testdomain.com"]
} }
} }
``` ```
@ -45,13 +43,12 @@ module "big-table-instance" {
| *cluster_id* | The ID of the Cloud Bigtable cluster. | <code title="">string</code> | | <code title="">europe-west1</code> | | *cluster_id* | The ID of the Cloud Bigtable cluster. | <code title="">string</code> | | <code title="">europe-west1</code> |
| *deletion_protection* | Whether or not to allow Terraform to destroy the instance. Unless this field is set to false in Terraform state, a terraform destroy or terraform apply that would delete the instance will fail. | <code title=""></code> | | <code title="">true</code> | | *deletion_protection* | Whether or not to allow Terraform to destroy the instance. Unless this field is set to false in Terraform state, a terraform destroy or terraform apply that would delete the instance will fail. | <code title=""></code> | | <code title="">true</code> |
| *display_name* | The human-readable display name of the Bigtable instance. | <code title=""></code> | | <code title="">null</code> | | *display_name* | The human-readable display name of the Bigtable instance. | <code title=""></code> | | <code title="">null</code> |
| *iam_members* | Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the instance are preserved. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> | | *instance_type* | (deprecated) The instance type to create. One of 'DEVELOPMENT' or 'PRODUCTION'. | <code title="">string</code> | | <code title="">null</code> |
| *instance_type* | None | <code title="">string</code> | | <code title="">DEVELOPMENT</code> |
| *num_nodes* | The number of nodes in your Cloud Bigtable cluster. | <code title="">number</code> | | <code title="">1</code> | | *num_nodes* | The number of nodes in your Cloud Bigtable cluster. | <code title="">number</code> | | <code title="">1</code> |
| *storage_type* | The storage type to use. | <code title="">string</code> | | <code title="">SSD</code> | | *storage_type* | The storage type to use. | <code title="">string</code> | | <code title="">SSD</code> |
| *table_options_defaults* | Default option of tables created in the BigTable instance. | <code title="object&#40;&#123;&#10;split_keys &#61; list&#40;string&#41;&#10;column_family &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;split_keys &#61; &#91;&#93;&#10;column_family &#61; null&#10;&#125;">...</code> | | *table_options_defaults* | Default option of tables created in the BigTable instance. | <code title="object&#40;&#123;&#10;split_keys &#61; list&#40;string&#41;&#10;column_family &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;split_keys &#61; &#91;&#93;&#10;column_family &#61; null&#10;&#125;">...</code> |
| *tables* | Tables to be created in the BigTable instance. | <code title="map&#40;object&#40;&#123;&#10;table_options &#61; object&#40;&#123;&#10;split_keys &#61; list&#40;string&#41;&#10;column_family &#61; string&#10;&#125;&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *tables* | Tables to be created in the BigTable instance, options can be null. | <code title="map&#40;object&#40;&#123;&#10;split_keys &#61; list&#40;string&#41;&#10;column_family &#61; string&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
## Outputs ## Outputs

View File

@ -16,11 +16,7 @@
locals { locals {
tables = { tables = {
for k, v in var.tables : k => v.table_options != null ? v.table_options : var.table_options_defaults for k, v in var.tables : k => v != null ? v : var.table_options_defaults
}
iam_roles_bindings = {
for k in var.iam_roles : k => lookup(var.iam_members, k, [])
} }
} }
@ -39,11 +35,10 @@ resource "google_bigtable_instance" "default" {
} }
resource "google_bigtable_instance_iam_binding" "default" { resource "google_bigtable_instance_iam_binding" "default" {
for_each = local.iam_roles_bindings for_each = var.iam
project = var.project_id project = var.project_id
instance = google_bigtable_instance.default.name instance = google_bigtable_instance.default.name
role = "roles/bigtable.${each.key}" role = each.key
members = each.value members = each.value
} }

View File

@ -18,8 +18,8 @@ output "id" {
description = "An identifier for the resource with format projects/{{project}}/instances/{{name}}." description = "An identifier for the resource with format projects/{{project}}/instances/{{name}}."
value = google_bigtable_instance.default.id value = google_bigtable_instance.default.id
depends_on = [ depends_on = [
google_bigtable_instance_iam_binding, google_bigtable_instance_iam_binding.default,
google_bigtable_table google_bigtable_table.default
] ]
} }
@ -27,8 +27,8 @@ output "instance" {
description = "BigTable intance." description = "BigTable intance."
value = google_bigtable_instance.default value = google_bigtable_instance.default
depends_on = [ depends_on = [
google_bigtable_instance_iam_binding, google_bigtable_instance_iam_binding.default,
google_bigtable_table google_bigtable_table.default
] ]
} }

View File

@ -14,18 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
variable "iam_roles" {
description = "Authoritative for a given role. Updates the IAM policy to grant a role to a list of members."
type = list(string)
default = []
}
variable "iam_members" {
description = "Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the instance are preserved."
type = map(list(string))
default = {}
}
variable "cluster_id" { variable "cluster_id" {
description = "The ID of the Cloud Bigtable cluster." description = "The ID of the Cloud Bigtable cluster."
type = string type = string
@ -42,10 +30,16 @@ variable "display_name" {
default = null default = null
} }
variable "iam" {
description = "IAM bindings for topic in {ROLE => [MEMBERS]} format."
type = map(list(string))
default = {}
}
variable "instance_type" { variable "instance_type" {
description = "The instance type to create. One of \"DEVELOPMENT\" or \"PRODUCTION\". Defaults to \"DEVELOPMENT\"" description = "(deprecated) The instance type to create. One of 'DEVELOPMENT' or 'PRODUCTION'."
type = string type = string
default = "DEVELOPMENT" default = null
} }
variable "name" { variable "name" {
@ -71,12 +65,10 @@ variable "storage_type" {
} }
variable "tables" { variable "tables" {
description = "Tables to be created in the BigTable instance." description = "Tables to be created in the BigTable instance, options can be null."
type = map(object({ type = map(object({
table_options = object({
split_keys = list(string) split_keys = list(string)
column_family = string column_family = string
})
})) }))
default = {} default = {}
} }

View File

@ -63,8 +63,7 @@ module "cf-http" {
source_dir = "my-cf-source-folder" source_dir = "my-cf-source-folder"
output_path = "bundle.zip" output_path = "bundle.zip"
} }
iam_roles = ["roles/cloudfunctions.invoker"] iam = {
iam_members = {
"roles/cloudfunctions.invoker" = ["allUsers"] "roles/cloudfunctions.invoker" = ["allUsers"]
} }
} }
@ -137,8 +136,7 @@ module "cf-http" {
| *bucket_config* | Enable and configure auto-created bucket. Set fields to null to use defaults. | <code title="object&#40;&#123;&#10;location &#61; string&#10;lifecycle_delete_age &#61; number&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> | | *bucket_config* | Enable and configure auto-created bucket. Set fields to null to use defaults. | <code title="object&#40;&#123;&#10;location &#61; string&#10;lifecycle_delete_age &#61; number&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> |
| *environment_variables* | Cloud function environment variables. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> | | *environment_variables* | Cloud function environment variables. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> |
| *function_config* | Cloud function configuration. | <code title="object&#40;&#123;&#10;entry_point &#61; string&#10;ingress_settings &#61; string&#10;instances &#61; number&#10;memory &#61; number&#10;runtime &#61; string&#10;timeout &#61; number&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;entry_point &#61; &#34;main&#34;&#10;ingress_settings &#61; null&#10;instances &#61; 1&#10;memory &#61; 256&#10;runtime &#61; &#34;python37&#34;&#10;timeout &#61; 180&#10;&#125;">...</code> | | *function_config* | Cloud function configuration. | <code title="object&#40;&#123;&#10;entry_point &#61; string&#10;ingress_settings &#61; string&#10;instances &#61; number&#10;memory &#61; number&#10;runtime &#61; string&#10;timeout &#61; number&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;entry_point &#61; &#34;main&#34;&#10;ingress_settings &#61; null&#10;instances &#61; 1&#10;memory &#61; 256&#10;runtime &#61; &#34;python37&#34;&#10;timeout &#61; 180&#10;&#125;">...</code> |
| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | List of roles used to set authoritative bindings. Ignored for template use. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *ingress_settings* | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL and ALLOW_INTERNAL_ONLY. | <code title="">string</code> | | <code title="">null</code> | | *ingress_settings* | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL and ALLOW_INTERNAL_ONLY. | <code title="">string</code> | | <code title="">null</code> |
| *labels* | Resource labels | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> | | *labels* | Resource labels | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> |
| *prefix* | Optional prefix used for resource names. | <code title="">string</code> | | <code title="">null</code> | | *prefix* | Optional prefix used for resource names. | <code title="">string</code> | | <code title="">null</code> |

View File

@ -95,12 +95,12 @@ resource "google_cloudfunctions_function" "function" {
} }
resource "google_cloudfunctions_function_iam_binding" "default" { resource "google_cloudfunctions_function_iam_binding" "default" {
for_each = toset(var.iam_roles) for_each = var.iam
project = var.project_id project = var.project_id
region = var.region region = var.region
cloud_function = google_cloudfunctions_function.function.name cloud_function = google_cloudfunctions_function.function.name
role = each.value role = each.key
members = try(var.iam_members[each.value], {}) members = each.value
} }
resource "google_storage_bucket" "bucket" { resource "google_storage_bucket" "bucket" {

View File

@ -42,18 +42,6 @@ variable "environment_variables" {
default = {} default = {}
} }
variable "iam_members" {
description = "Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use."
type = map(list(string))
default = {}
}
variable "iam_roles" {
description = "List of roles used to set authoritative bindings. Ignored for template use."
type = list(string)
default = []
}
variable "function_config" { variable "function_config" {
description = "Cloud function configuration." description = "Cloud function configuration."
type = object({ type = object({
@ -74,6 +62,12 @@ variable "function_config" {
} }
} }
variable "iam" {
description = "IAM bindings for topic in {ROLE => [MEMBERS]} format."
type = map(list(string))
default = {}
}
variable "ingress_settings" { variable "ingress_settings" {
description = "Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL and ALLOW_INTERNAL_ONLY." description = "Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL and ALLOW_INTERNAL_ONLY."
type = string type = string

View File

@ -184,8 +184,7 @@ module "instance-group" {
| *encryption* | Encryption options. Only one of kms_key_self_link and disk_encryption_key_raw may be set. If needed, you can specify to encrypt or not the boot disk. | <code title="object&#40;&#123;&#10;encrypt_boot &#61; bool&#10;disk_encryption_key_raw &#61; string&#10;kms_key_self_link &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> | | *encryption* | Encryption options. Only one of kms_key_self_link and disk_encryption_key_raw may be set. If needed, you can specify to encrypt or not the boot disk. | <code title="object&#40;&#123;&#10;encrypt_boot &#61; bool&#10;disk_encryption_key_raw &#61; string&#10;kms_key_self_link &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> |
| *group* | Define this variable to create an instance group for instances. Disabled for template use. | <code title="object&#40;&#123;&#10;named_ports &#61; map&#40;number&#41;&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> | | *group* | Define this variable to create an instance group for instances. Disabled for template use. | <code title="object&#40;&#123;&#10;named_ports &#61; map&#40;number&#41;&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> |
| *hostname* | Instance FQDN name. | <code title="">string</code> | | <code title="">null</code> | | *hostname* | Instance FQDN name. | <code title="">string</code> | | <code title="">null</code> |
| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | List of roles used to set authoritative bindings. Ignored for template use. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *instance_count* | Number of instances to create (only for non-template usage). | <code title="">number</code> | | <code title="">1</code> | | *instance_count* | Number of instances to create (only for non-template usage). | <code title="">number</code> | | <code title="">1</code> |
| *instance_type* | Instance type. | <code title="">string</code> | | <code title="">f1-micro</code> | | *instance_type* | Instance type. | <code title="">string</code> | | <code title="">f1-micro</code> |
| *labels* | Instance labels. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> | | *labels* | Instance labels. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> |

View File

@ -25,9 +25,9 @@ locals {
for pair in setproduct(keys(local.names), keys(local.attached_disks)) : for pair in setproduct(keys(local.names), keys(local.attached_disks)) :
"${pair[0]}-${pair[1]}" => { disk_name = pair[1], name = pair[0] } "${pair[0]}-${pair[1]}" => { disk_name = pair[1], name = pair[0] }
} }
iam_roles = var.use_instance_template ? {} : { iam_members = var.use_instance_template ? {} : {
for pair in setproduct(var.iam_roles, keys(local.names)) : for pair in setproduct(keys(var.iam), keys(local.names)) :
"${pair.0}/${pair.1}" => { role = pair.0, name = pair.1 } "${pair.0}/${pair.1}" => { role = pair.0, name = pair.1, members = var.iam[pair.0] }
} }
names = ( names = (
var.use_instance_template ? { (var.name) = 0 } : { var.use_instance_template ? { (var.name) = 0 } : {
@ -196,12 +196,12 @@ resource "google_compute_instance" "default" {
} }
resource "google_compute_instance_iam_binding" "default" { resource "google_compute_instance_iam_binding" "default" {
for_each = local.iam_roles for_each = local.iam_members
project = var.project_id project = var.project_id
zone = local.zones[each.value.name] zone = local.zones[each.value.name]
instance_name = each.value.name instance_name = each.value.name
role = each.value.role role = each.value.role
members = lookup(var.iam_members, each.value.role, []) members = each.value.members
depends_on = [google_compute_instance.default] depends_on = [google_compute_instance.default]
} }

View File

@ -90,18 +90,12 @@ variable "hostname" {
default = null default = null
} }
variable "iam_members" { variable "iam" {
description = "Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use." description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_roles" {
description = "List of roles used to set authoritative bindings. Ignored for template use."
type = list(string)
default = []
}
variable "instance_count" { variable "instance_count" {
description = "Number of instances to create (only for non-template usage)." description = "Number of instances to create (only for non-template usage)."
type = number type = number

View File

@ -9,8 +9,7 @@ module "container_registry" {
source = "../../modules/container-registry" source = "../../modules/container-registry"
project_id = "myproject" project_id = "myproject"
location = "EU" location = "EU"
iam_roles = ["roles/storage.admin"] iam = {
iam_members = {
"roles/storage.admin" = ["group:cicd@example.com"] "roles/storage.admin" = ["group:cicd@example.com"]
} }
} }
@ -22,8 +21,7 @@ module "container_registry" {
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---: |:---:|:---:| |---|---|:---: |:---:|:---:|
| project_id | Registry project id. | <code title="">string</code> | ✓ | | | project_id | Registry project id. | <code title="">string</code> | ✓ | |
| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">null</code> | | *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | List of roles used to set authoritative bindings. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">null</code> |
| *location* | Registry location. Can be US, EU, ASIA or empty | <code title="">string</code> | | <code title=""></code> | | *location* | Registry location. Can be US, EU, ASIA or empty | <code title="">string</code> | | <code title=""></code> |
## Outputs ## Outputs

View File

@ -20,8 +20,8 @@ resource "google_container_registry" "registry" {
} }
resource "google_storage_bucket_iam_binding" "bindings" { resource "google_storage_bucket_iam_binding" "bindings" {
for_each = toset(var.iam_roles) for_each = var.iam
bucket = google_container_registry.registry.id bucket = google_container_registry.registry.id
role = each.value role = each.key
members = lookup(var.iam_members, each.value, []) members = each.value
} }

View File

@ -14,16 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
variable "iam_members" { variable "iam" {
description = "Map of member lists used to set authoritative bindings, keyed by role." description = "IAM bindings for topic in {ROLE => [MEMBERS]} format."
type = map(list(string)) type = map(list(string))
default = null default = {}
}
variable "iam_roles" {
description = "List of roles used to set authoritative bindings."
type = list(string)
default = null
} }
variable "location" { variable "location" {

View File

@ -37,7 +37,7 @@ module "private-dns" {
| *peer_network* | Peering network self link, only valid for 'peering' zone types. | <code title="">string</code> | | <code title="">null</code> | | *peer_network* | Peering network self link, only valid for 'peering' zone types. | <code title="">string</code> | | <code title="">null</code> |
| *recordsets* | List of DNS record objects to manage. | <code title="list&#40;object&#40;&#123;&#10;name &#61; string&#10;type &#61; string&#10;ttl &#61; number&#10;records &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">list(object({...}))</code> | | <code title="">[]</code> | | *recordsets* | List of DNS record objects to manage. | <code title="list&#40;object&#40;&#123;&#10;name &#61; string&#10;type &#61; string&#10;ttl &#61; number&#10;records &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">list(object({...}))</code> | | <code title="">[]</code> |
| *service_directory_namespace* | Service directory namespace id (URL), only valid for 'service-directory' zone types. | <code title="">string</code> | | <code title="">null</code> | | *service_directory_namespace* | Service directory namespace id (URL), only valid for 'service-directory' zone types. | <code title="">string</code> | | <code title="">null</code> |
| *type* | Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'. | <code title="">string</code> | | <code title="">private</code> | | *type* | Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'. | <code title="">string</code> | | <code title="private&#10;validation &#123;&#10;condition &#61; contains&#40;&#91;&#34;public&#34;, &#34;private&#34;, &#34;forwarding&#34;, &#34;peering&#34;, &#34;service-directory&#34;&#93;, var.type&#41;&#10;error_message &#61; &#34;Zone must be one of &#39;public&#39;, &#39;private&#39;, &#39;forwarding&#39;, &#39;peering&#39;, &#39;service-directory&#39;.&#34;&#10;&#125;">...</code> |
| *zone_create* | Create zone. When set to false, uses a data source to reference existing zone. | <code title="">bool</code> | | <code title="">true</code> | | *zone_create* | Create zone. When set to false, uses a data source to reference existing zone. | <code title="">bool</code> | | <code title="">true</code> |
## Outputs ## Outputs

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,7 +39,7 @@ resource "google_dns_managed_zone" "non-public" {
project = var.project_id project = var.project_id
name = var.name name = var.name
dns_name = var.domain dns_name = var.domain
description = "Terraform-managed zone." description = var.description
visibility = "private" visibility = "private"
dynamic forwarding_config { dynamic forwarding_config {

View File

@ -97,6 +97,10 @@ variable "type" {
description = "Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'." description = "Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'."
type = string type = string
default = "private" default = "private"
validation {
condition = contains(["public", "private", "forwarding", "peering", "service-directory"], var.type)
error_message = "Zone must be one of 'public', 'private', 'forwarding', 'peering', 'service-directory'."
}
} }
variable "zone_create" { variable "zone_create" {
@ -106,3 +110,4 @@ variable "zone_create" {
} }

View File

@ -15,7 +15,7 @@
*/ */
terraform { terraform {
required_version = ">= 0.12.20" required_version = ">= 0.13.0"
required_providers { required_providers {
google = "~> 3.10" google = "~> 3.10"
google-beta = "~> 3.20" google-beta = "~> 3.20"

View File

@ -12,10 +12,8 @@ module "endpoint" {
project_id = "my-project" project_id = "my-project"
service_name = "YOUR-API.endpoints.YOUR-PROJECT-ID.cloud.goog" service_name = "YOUR-API.endpoints.YOUR-PROJECT-ID.cloud.goog"
openapi_config = { "yaml_path" = "openapi.yaml" } openapi_config = { "yaml_path" = "openapi.yaml" }
grpc_config = null iam = {
iam_roles = ["servicemanagement.serviceController"] "servicemanagement.serviceController" = ["serviceAccount:123456890-compute@developer.gserviceaccount.com"]
iam_members = {
"servicemanagement.serviceController" = ["serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com"]
} }
} }
``` ```
@ -27,11 +25,10 @@ module "endpoint" {
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---: |:---:|:---:| |---|---|:---: |:---:|:---:|
| grpc_config | The configuration for a gRPC enpoint. Either this or openapi_config must be specified. | <code title="object&#40;&#123;&#10;yaml_path &#61; string&#10;protoc_output_path &#61; string&#10;&#125;&#41;">object({...})</code> | ✓ | |
| openapi_config | The configuration for an OpenAPI endopoint. Either this or grpc_config must be specified. | <code title="object&#40;&#123;&#10;yaml_path &#61; string&#10;&#125;&#41;">object({...})</code> | ✓ | | | openapi_config | The configuration for an OpenAPI endopoint. Either this or grpc_config must be specified. | <code title="object&#40;&#123;&#10;yaml_path &#61; string&#10;&#125;&#41;">object({...})</code> | ✓ | |
| service_name | The name of the service. Usually of the form '$apiname.endpoints.$projectid.cloud.goog'. | <code title="">string</code> | ✓ | | | service_name | The name of the service. Usually of the form '$apiname.endpoints.$projectid.cloud.goog'. | <code title="">string</code> | ✓ | |
| *iam_members* | Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the instance are preserved. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *grpc_config* | The configuration for a gRPC enpoint. Either this or openapi_config must be specified. | <code title="object&#40;&#123;&#10;yaml_path &#61; string&#10;protoc_output_path &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> |
| *iam_roles* | Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> | | *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *project_id* | The project ID that the service belongs to. | <code title="">string</code> | | <code title="">null</code> | | *project_id* | The project ID that the service belongs to. | <code title="">string</code> | | <code title="">null</code> |
## Outputs ## Outputs

View File

@ -14,12 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
locals {
iam_roles_bindings = {
for k in var.iam_roles : k => lookup(var.iam_members, k, [])
}
}
resource "google_endpoints_service" "default" { resource "google_endpoints_service" "default" {
project = var.project_id project = var.project_id
service_name = var.service_name service_name = var.service_name
@ -29,8 +23,8 @@ resource "google_endpoints_service" "default" {
} }
resource "google_endpoints_service_iam_binding" "default" { resource "google_endpoints_service_iam_binding" "default" {
for_each = local.iam_roles_bindings for_each = var.iam
service_name = google_endpoints_service.default.service_name service_name = google_endpoints_service.default.service_name
role = "roles/${each.key}" role = each.key
members = each.value members = each.value
} }

View File

@ -20,16 +20,11 @@ variable "grpc_config" {
yaml_path = string yaml_path = string
protoc_output_path = string protoc_output_path = string
}) })
default = null
} }
variable "iam_roles" { variable "iam" {
description = "Authoritative for a given role. Updates the IAM policy to grant a role to a list of members." description = "IAM bindings for topic in {ROLE => [MEMBERS]} format."
type = list(string)
default = []
}
variable "iam_members" {
description = "Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the instance are preserved."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }

View File

@ -1,6 +1,6 @@
# Google Cloud Folder Module # Google Cloud Folder Module
This module allow creation and management of sets of folders sharing a common parent, and their individual IAM bindings. It also allows setting a common set of organization policies on all folders. This module allows the creation and management of folders together with their individual IAM bindings and organization policies.
## Examples ## Examples
@ -8,27 +8,22 @@ This module allow creation and management of sets of folders sharing a common pa
```hcl ```hcl
module "folder" { module "folder" {
source = "./modules/folders" source = "./modules/folder"
parent = "organizations/1234567890" parent = "organizations/1234567890"
names = ["Folder one", "Folder two"] name = "Folder name"
iam_members = { iam = {
"Folder one" = {
"roles/owner" = ["group:users@example.com"] "roles/owner" = ["group:users@example.com"]
} }
} }
iam_roles = {
"Folder one" = ["roles/owner"]
}
}
``` ```
### Organization policies ### Organization policies
```hcl ```hcl
module "folder" { module "folder" {
source = "./modules/folders" source = "./modules/folder"
parent = "organizations/1234567890" parent = "organizations/1234567890"
names = ["Folder one", "Folder two"] name = "Folder name"
policy_boolean = { policy_boolean = {
"constraints/compute.disableGuestAttributesAccess" = true "constraints/compute.disableGuestAttributesAccess" = true
"constraints/compute.skipDefaultNetworkCreation" = true "constraints/compute.skipDefaultNetworkCreation" = true
@ -49,10 +44,9 @@ module "folder" {
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---: |:---:|:---:| |---|---|:---: |:---:|:---:|
| parent | Parent in folders/folder_id or organizations/org_id format. | <code title="">string</code> | ✓ | | | name | Folder name. | <code title="">string</code> | ✓ | |
| *iam_members* | List of IAM members keyed by folder name and role. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">null</code> | | parent | Parent in folders/folder_id or organizations/org_id format. | <code title="string&#10;validation &#123;&#10;condition &#61; can&#40;regex&#40;&#34;&#40;organizations&#124;folders&#41;&#47;&#91;0-9&#93;&#43;&#34;, var.parent&#41;&#41;&#10;error_message &#61; &#34;Parent must be of the form folders&#47;folder_id or organizations&#47;organization_id.&#34;&#10;&#125;">string</code> | ✓ | |
| *iam_roles* | List of IAM roles keyed by folder name. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">null</code> | | *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | <code title="map&#40;set&#40;string&#41;&#41;">map(set(string))</code> | | <code title="">{}</code> |
| *names* | Folder names. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> | | *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> |
| *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10;inherit_from_parent &#61; bool&#10;suggested_value &#61; string&#10;status &#61; bool&#10;values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10;inherit_from_parent &#61; bool&#10;suggested_value &#61; string&#10;status &#61; bool&#10;values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
@ -60,12 +54,7 @@ module "folder" {
| name | description | sensitive | | name | description | sensitive |
|---|---|:---:| |---|---|:---:|
| folder | Folder resource (for single use). | | | folder | Folder resource. | |
| folders | Folder resources. | | | id | Folder id. | |
| id | Folder id (for single use). | | | name | Folder name. | |
| ids | Folder ids. | |
| ids_list | List of folder ids. | |
| name | Folder name (for single use). | |
| names | Folder names. | |
| names_list | List of folder names. | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,63 +14,26 @@
* limitations under the License. * limitations under the License.
*/ */
locals {
folders = (
local.has_folders
? [for name in var.names : google_folder.folders[name]]
: []
)
# needed when destroying
has_folders = length(google_folder.folders) > 0
iam_pairs = var.iam_roles == null ? [] : flatten([
for name, roles in var.iam_roles :
[for role in roles : { name = name, role = role }]
])
iam_keypairs = {
for pair in local.iam_pairs :
"${pair.name}-${pair.role}" => pair
}
iam_members = var.iam_members == null ? {} : var.iam_members
policy_boolean_pairs = {
for pair in setproduct(var.names, keys(var.policy_boolean)) :
"${pair.0}-${pair.1}" => {
folder = pair.0,
policy = pair.1,
policy_data = var.policy_boolean[pair.1]
}
}
policy_list_pairs = {
for pair in setproduct(var.names, keys(var.policy_list)) :
"${pair.0}-${pair.1}" => {
folder = pair.0,
policy = pair.1,
policy_data = var.policy_list[pair.1]
}
}
}
resource "google_folder" "folders" { resource "google_folder" "folder" {
for_each = toset(var.names) display_name = var.name
display_name = each.value
parent = var.parent parent = var.parent
} }
resource "google_folder_iam_binding" "authoritative" { resource "google_folder_iam_binding" "authoritative" {
for_each = local.iam_keypairs for_each = var.iam
folder = google_folder.folders[each.value.name].name folder = google_folder.folder.name
role = each.value.role role = each.key
members = lookup( members = each.value
lookup(local.iam_members, each.value.name, {}), each.value.role, []
)
} }
resource "google_folder_organization_policy" "boolean" { resource "google_folder_organization_policy" "boolean" {
for_each = local.policy_boolean_pairs for_each = var.policy_boolean
folder = google_folder.folders[each.value.folder].id folder = google_folder.folder.name
constraint = each.value.policy constraint = each.key
dynamic boolean_policy { dynamic boolean_policy {
for_each = each.value.policy_data == null ? [] : [each.value.policy_data] for_each = each.value == null ? [] : [each.value]
iterator = policy iterator = policy
content { content {
enforced = policy.value enforced = policy.value
@ -78,7 +41,7 @@ resource "google_folder_organization_policy" "boolean" {
} }
dynamic restore_policy { dynamic restore_policy {
for_each = each.value.policy_data == null ? [""] : [] for_each = each.value == null ? [""] : []
content { content {
default = true default = true
} }
@ -86,12 +49,12 @@ resource "google_folder_organization_policy" "boolean" {
} }
resource "google_folder_organization_policy" "list" { resource "google_folder_organization_policy" "list" {
for_each = local.policy_list_pairs for_each = var.policy_list
folder = google_folder.folders[each.value.folder].id folder = google_folder.folder.name
constraint = each.value.policy constraint = each.key
dynamic list_policy { dynamic list_policy {
for_each = each.value.policy_data.status == null ? [] : [each.value.policy_data] for_each = each.value.status == null ? [] : [each.value]
iterator = policy iterator = policy
content { content {
inherit_from_parent = policy.value.inherit_from_parent inherit_from_parent = policy.value.inherit_from_parent
@ -130,7 +93,7 @@ resource "google_folder_organization_policy" "list" {
} }
dynamic restore_policy { dynamic restore_policy {
for_each = each.value.policy_data.status == null ? [true] : [] for_each = each.value.status == null ? [true] : []
content { content {
default = true default = true
} }

35
modules/folder/outputs.tf Normal file
View File

@ -0,0 +1,35 @@
/**
* Copyright 2020 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.
*/
output "folder" {
description = "Folder resource."
value = google_folder.folder
}
output "id" {
description = "Folder id."
value = google_folder.folder.name
depends_on = [
google_folder_iam_binding.authoritative,
google_folder_organization_policy.boolean,
google_folder_organization_policy.list
]
}
output "name" {
description = "Folder name."
value = google_folder.folder.display_name
}

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,27 +14,24 @@
* limitations under the License. * limitations under the License.
*/ */
variable "iam_members" { variable "iam" {
description = "List of IAM members keyed by folder name and role." description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(map(list(string))) type = map(set(string))
default = null default = {}
} }
variable "iam_roles" { variable "name" {
description = "List of IAM roles keyed by folder name." description = "Folder name."
type = map(list(string)) type = string
default = null
}
variable "names" {
description = "Folder names."
type = list(string)
default = []
} }
variable "parent" { variable "parent" {
description = "Parent in folders/folder_id or organizations/org_id format." description = "Parent in folders/folder_id or organizations/org_id format."
type = string type = string
validation {
condition = can(regex("(organizations|folders)/[0-9]+", var.parent))
error_message = "Parent must be of the form folders/folder_id or organizations/organization_id."
}
} }
variable "policy_boolean" { variable "policy_boolean" {

View File

@ -15,5 +15,5 @@
*/ */
terraform { terraform {
required_version = ">= 0.12.6" required_version = ">= 0.13.0"
} }

View File

@ -36,10 +36,9 @@ module "folders-unit" {
| short_name | Short name used as GCS bucket and service account prefixes, do not use capital letters or spaces. | <code title="">string</code> | ✓ | | | short_name | Short name used as GCS bucket and service account prefixes, do not use capital letters or spaces. | <code title="">string</code> | ✓ | |
| *environments* | Unit environments short names. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="&#123;&#10;non-prod &#61; &#34;Non production&#34;&#10;prod &#61; &#34;Production&#34;&#10;&#125;">...</code> | | *environments* | Unit environments short names. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="&#123;&#10;non-prod &#61; &#34;Non production&#34;&#10;prod &#61; &#34;Production&#34;&#10;&#125;">...</code> |
| *gcs_defaults* | Defaults use for the state GCS buckets. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="&#123;&#10;location &#61; &#34;EU&#34;&#10;storage_class &#61; &#34;MULTI_REGIONAL&#34;&#10;&#125;">...</code> | | *gcs_defaults* | Defaults use for the state GCS buckets. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="&#123;&#10;location &#61; &#34;EU&#34;&#10;storage_class &#61; &#34;MULTI_REGIONAL&#34;&#10;&#125;">...</code> |
| *iam* | IAM bindings for the top-level folder in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_billing_config* | Grant billing user role to service accounts, defaults to granting on the billing account. | <code title="object&#40;&#123;&#10;grant &#61; bool&#10;target_org &#61; bool&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;grant &#61; true&#10;target_org &#61; false&#10;&#125;">...</code> | | *iam_billing_config* | Grant billing user role to service accounts, defaults to granting on the billing account. | <code title="object&#40;&#123;&#10;grant &#61; bool&#10;target_org &#61; bool&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;grant &#61; true&#10;target_org &#61; false&#10;&#125;">...</code> |
| *iam_enviroment_roles* | IAM roles granted to the environment service account on the environment sub-folder. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="&#91;&#10;&#34;roles&#47;compute.networkAdmin&#34;,&#10;&#34;roles&#47;owner&#34;,&#10;&#34;roles&#47;resourcemanager.folderAdmin&#34;,&#10;&#34;roles&#47;resourcemanager.projectCreator&#34;,&#10;&#93;">...</code> | | *iam_enviroment_roles* | IAM roles granted to the environment service account on the environment sub-folder. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="&#91;&#10;&#34;roles&#47;compute.networkAdmin&#34;,&#10;&#34;roles&#47;owner&#34;,&#10;&#34;roles&#47;resourcemanager.folderAdmin&#34;,&#10;&#34;roles&#47;resourcemanager.projectCreator&#34;,&#10;&#93;">...</code> |
| *iam_members* | IAM members for roles applied on the unit folder. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">null</code> |
| *iam_roles* | IAM roles applied on the unit folder. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">null</code> |
| *iam_xpn_config* | Grant Shared VPC creation roles to service accounts, defaults to granting at folder level. | <code title="object&#40;&#123;&#10;grant &#61; bool&#10;target_org &#61; bool&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;grant &#61; true&#10;target_org &#61; false&#10;&#125;">...</code> | | *iam_xpn_config* | Grant Shared VPC creation roles to service accounts, defaults to granting at folder level. | <code title="object&#40;&#123;&#10;grant &#61; bool&#10;target_org &#61; bool&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;grant &#61; true&#10;target_org &#61; false&#10;&#125;">...</code> |
| *prefix* | Optional prefix used for GCS bucket names to ensure uniqueness. | <code title="">string</code> | | <code title="">null</code> | | *prefix* | Optional prefix used for GCS bucket names to ensure uniqueness. | <code title="">string</code> | | <code title="">null</code> |
| *service_account_keys* | Generate and store service account keys in the state file. | <code title="">bool</code> | | <code title="">false</code> | | *service_account_keys* | Generate and store service account keys in the state file. | <code title="">bool</code> | | <code title="">false</code> |

View File

@ -16,12 +16,7 @@
locals { locals {
folder_roles = concat(var.iam_enviroment_roles, local.sa_xpn_folder_roles) folder_roles = concat(var.iam_enviroment_roles, local.sa_xpn_folder_roles)
iam_members = var.iam_members == null ? {} : var.iam_members iam = var.iam == null ? {} : var.iam
iam_roles = var.iam_roles == null ? [] : var.iam_roles
unit_iam_bindings = {
for role in local.iam_roles :
role => lookup(local.iam_members, role, [])
}
folder_iam_service_account_bindings = { folder_iam_service_account_bindings = {
for pair in setproduct(keys(var.environments), local.folder_roles) : for pair in setproduct(keys(var.environments), local.folder_roles) :
"${pair.0}-${pair.1}" => { environment = pair.0, role = pair.1 } "${pair.0}-${pair.1}" => { environment = pair.0, role = pair.1 }

View File

@ -34,7 +34,7 @@ resource "google_folder" "environment" {
} }
resource "google_folder_iam_binding" "unit" { resource "google_folder_iam_binding" "unit" {
for_each = local.unit_iam_bindings for_each = var.iam
folder = google_folder.unit.name folder = google_folder.unit.name
role = each.key role = each.key
members = each.value members = each.value

View File

@ -42,6 +42,12 @@ variable "gcs_defaults" {
} }
} }
variable "iam" {
description = "IAM bindings for the top-level folder in {ROLE => [MEMBERS]} format."
type = map(list(string))
default = {}
}
variable "iam_billing_config" { variable "iam_billing_config" {
description = "Grant billing user role to service accounts, defaults to granting on the billing account." description = "Grant billing user role to service accounts, defaults to granting on the billing account."
type = object({ type = object({
@ -65,18 +71,6 @@ variable "iam_enviroment_roles" {
] ]
} }
variable "iam_members" {
description = "IAM members for roles applied on the unit folder."
type = map(list(string))
default = null
}
variable "iam_roles" {
description = "IAM roles applied on the unit folder."
type = list(string)
default = null
}
variable "iam_xpn_config" { variable "iam_xpn_config" {
description = "Grant Shared VPC creation roles to service accounts, defaults to granting at folder level." description = "Grant Shared VPC creation roles to service accounts, defaults to granting at folder level."
type = object({ type = object({

View File

@ -1,78 +0,0 @@
/**
* Copyright 2018 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.
*/
output "folder" {
description = "Folder resource (for single use)."
value = local.has_folders ? local.folders[0] : null
}
output "id" {
description = "Folder id (for single use)."
value = local.has_folders ? local.folders[0].name : null
depends_on = [
google_folder_iam_binding.authoritative,
google_folder_organization_policy.boolean,
google_folder_organization_policy.list
]
}
output "name" {
description = "Folder name (for single use)."
value = local.has_folders ? local.folders[0].display_name : null
}
output "folders" {
description = "Folder resources."
value = local.folders
}
output "ids" {
description = "Folder ids."
value = (
local.has_folders
? zipmap(var.names, [for f in local.folders : f.name])
: {}
)
depends_on = [
google_folder_iam_binding.authoritative,
google_folder_organization_policy.boolean,
google_folder_organization_policy.list
]
}
output "names" {
description = "Folder names."
value = (
local.has_folders
? zipmap(var.names, [for f in local.folders : f.display_name])
: {}
)
}
output "ids_list" {
description = "List of folder ids."
value = [for f in local.folders : f.name]
depends_on = [
google_folder_iam_binding.authoritative,
google_folder_organization_policy.boolean,
google_folder_organization_policy.list
]
}
output "names_list" {
description = "List of folder names."
value = [for f in local.folders : f.display_name]
}

View File

@ -7,78 +7,52 @@
## Example ## Example
```hcl ```hcl
module "buckets" { module "bucket" {
source = "./modules/gcs" source = "./modules/gcs"
project_id = "myproject" project_id = "myproject"
prefix = "test" prefix = "test"
names = ["bucket-one", "bucket-two"] name = "my-bucket"
bucket_policy_only = { iam = {
bucket-one = false
}
iam_members = {
bucket-two = {
"roles/storage.admin" = ["group:storage@example.com"] "roles/storage.admin" = ["group:storage@example.com"]
} }
} }
iam_roles = {
bucket-two = ["roles/storage.admin"]
}
}
``` ```
### Example with Cloud KMS ### Example with Cloud KMS
```hcl ```hcl
module "buckets" { module "bucket" {
source = "./modules/gcs" source = "./modules/gcs"
project_id = "myproject" project_id = "myproject"
prefix = "test" prefix = "test"
names = ["bucket-one", "bucket-two"] name = "my-bucket"
bucket_policy_only = { iam = {
bucket-one = false
}
iam_members = {
bucket-two = {
"roles/storage.admin" = ["group:storage@example.com"] "roles/storage.admin" = ["group:storage@example.com"]
} }
} encryption_keys = local.kms_key.self_link
iam_roles = {
bucket-two = ["roles/storage.admin"]
}
encryption_keys = {
bucket-two = local.kms_key.self_link,
}
} }
``` ```
### Example with retention policy ### Example with retention policy
```hcl ```hcl
module "buckets" { module "bucket" {
source = "./modules/gcs" source = "./modules/gcs"
project_id = "myproject" project_id = "myproject"
prefix = "test" prefix = "test"
names = ["bucket-one", "bucket-two"] name = "my-bucket"
bucket_policy_only = { iam = {
bucket-one = false
}
iam_members = {
bucket-two = {
"roles/storage.admin" = ["group:storage@example.com"] "roles/storage.admin" = ["group:storage@example.com"]
} }
}
iam_roles = {
bucket-two = ["roles/storage.admin"]
}
retention_policies = { retention_policies = {
bucket-one = { retention_period = 100 , is_locked = true} retention_period = 100
bucket-two = { retention_period = 900 , is_locked = false} is_locked = true
} }
logging_config = { logging_config = {
bucket-one = { log_bucket = bucket_name_for_logging , log_object_prefix = null} log_bucket = bucket_name_for_logging
bucket-two = { log_bucket = bucket_name_for_logging , log_object_prefix = "logs_for_bucket_two"} log_object_prefix = null
} }
} }
``` ```
@ -88,31 +62,25 @@ module "buckets" {
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---: |:---:|:---:| |---|---|:---: |:---:|:---:|
| names | Bucket name suffixes. | <code title="list&#40;string&#41;">list(string)</code> | ✓ | | | name | Bucket name suffix. | <code title="">string</code> | ✓ | |
| project_id | Bucket project id. | <code title="">string</code> | ✓ | | | project_id | Bucket project id. | <code title="">string</code> | ✓ | |
| *uniform_bucket_level_access* | Optional map to enable object ACLs keyed by name, defaults to true. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> | | *encryption_key* | KMS key that will be used for encryption. | <code title="">string</code> | | <code title="">null</code> |
| *encryption_keys* | Per-bucket KMS keys that will be used for encryption. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> | | *force_destroy* | Optional map to set force destroy keyed by name, defaults to false. | <code title="">bool</code> | | <code title="">false</code> |
| *force_destroy* | Optional map to set force destroy keyed by name, defaults to false. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> | | *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_members* | IAM members keyed by bucket name and role. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> |
| *iam_roles* | IAM roles keyed by bucket name. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *labels* | Labels to be attached to all buckets. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> | | *labels* | Labels to be attached to all buckets. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> |
| *location* | Bucket location. | <code title="">string</code> | | <code title="">EU</code> | | *location* | Bucket location. | <code title="">string</code> | | <code title="">EU</code> |
| *logging_config* | Per-bucket logging. | <code title="map&#40;object&#40;&#123;&#10;log_bucket &#61; string&#10;log_object_prefix &#61; string&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *logging_config* | Bucket logging configuration. | <code title="object&#40;&#123;&#10;log_bucket &#61; string&#10;log_object_prefix &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> |
| *prefix* | Prefix used to generate the bucket name. | <code title="">string</code> | | <code title="">null</code> | | *prefix* | Prefix used to generate the bucket name. | <code title="">string</code> | | <code title="">null</code> |
| *retention_policies* | Per-bucket retention policy. | <code title="map&#40;object&#40;&#123;&#10;retention_period &#61; number&#10;is_locked &#61; bool&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *retention_policy* | Bucket retention policy. | <code title="object&#40;&#123;&#10;retention_period &#61; number&#10;is_locked &#61; bool&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> |
| *storage_class* | Bucket storage class. | <code title="">string</code> | | <code title="">MULTI_REGIONAL</code> | | *storage_class* | Bucket storage class. | <code title="">string</code> | | <code title="MULTI_REGIONAL&#10;validation &#123;&#10;condition &#61; contains&#40;&#91;&#34;STANDARD&#34;, &#34;MULTI_REGIONAL&#34;, &#34;REGIONAL&#34;, &#34;NEARLINE&#34;, &#34;COLDLINE&#34;, &#34;ARCHIVE&#34;&#93;, var.storage_class&#41;&#10;error_message &#61; &#34;Storage class must be one of STANDARD, MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, ARCHIVE.&#34;&#10;&#125;">...</code> |
| *versioning* | Optional map to set versioning keyed by name, defaults to false. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> | | *uniform_bucket_level_access* | Allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API). | <code title="">bool</code> | | <code title="">true</code> |
| *versioning* | Enable versioning, defaults to false. | <code title="">bool</code> | | <code title="">false</code> |
## Outputs ## Outputs
| name | description | sensitive | | name | description | sensitive |
|---|---|:---:| |---|---|:---:|
| bucket | Bucket resource (for single use). | | | bucket | Bucket resource. | |
| buckets | Bucket resources. | | | name | Bucket name. | |
| name | Bucket name (for single use). | | | url | Bucket URL. | |
| names | Bucket names. | |
| names_list | List of bucket names. | |
| url | Bucket URL (for single use). | |
| urls | Bucket URLs. | |
| urls_list | List of bucket URLs. | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -15,85 +15,57 @@
*/ */
locals { locals {
buckets = (
local.has_buckets
? [for name in var.names : google_storage_bucket.buckets[name]]
: []
)
# needed when destroying
has_buckets = length(google_storage_bucket.buckets) > 0
iam_pairs = var.iam_roles == null ? [] : flatten([
for name, roles in var.iam_roles :
[for role in roles : { name = name, role = role }]
])
iam_keypairs = {
for pair in local.iam_pairs :
"${pair.name}-${pair.role}" => pair
}
iam_members = var.iam_members == null ? {} : var.iam_members
prefix = ( prefix = (
var.prefix == null || var.prefix == "" # keep "" for backward compatibility var.prefix == null || var.prefix == "" # keep "" for backward compatibility
? "" ? ""
: join("-", [var.prefix, lower(var.location), ""]) : join("-", [var.prefix, lower(var.location), ""])
) )
kms_keys = {
for name in var.names : name => lookup(var.encryption_keys, name, null)
}
retention_policy = {
for name in var.names : name => lookup(var.retention_policies, name, null)
}
logging_config = {
for name in var.names : name => lookup(var.logging_config, name, null)
}
} }
resource "google_storage_bucket" "buckets" { resource "google_storage_bucket" "bucket" {
for_each = toset(var.names) name = "${local.prefix}${lower(var.name)}"
name = "${local.prefix}${lower(each.key)}"
project = var.project_id project = var.project_id
location = var.location location = var.location
storage_class = var.storage_class storage_class = var.storage_class
force_destroy = lookup(var.force_destroy, each.key, false) force_destroy = var.force_destroy
uniform_bucket_level_access = lookup(var.uniform_bucket_level_access, each.key, true) uniform_bucket_level_access = var.uniform_bucket_level_access
versioning { versioning {
enabled = lookup(var.versioning, each.key, false) enabled = var.versioning
} }
labels = merge(var.labels, { labels = merge(var.labels, {
location = lower(var.location) location = lower(var.location)
name = lower(each.key) name = lower(var.name)
storage_class = lower(var.storage_class) storage_class = lower(var.storage_class)
}) })
dynamic encryption { dynamic encryption {
for_each = local.kms_keys[each.key] == null ? [] : [""] for_each = var.encryption_key == null ? [] : [""]
content { content {
default_kms_key_name = local.kms_keys[each.key] default_kms_key_name = var.encryption_key
} }
} }
dynamic retention_policy { dynamic retention_policy {
for_each = local.retention_policy[each.key] == null ? [] : [""] for_each = var.retention_policy == null ? [] : [""]
content { content {
retention_period = local.retention_policy[each.key]["retention_period"] retention_period = var.retention_policy.retention_period
is_locked = local.retention_policy[each.key]["is_locked"] is_locked = var.retention_policy.is_locked
} }
} }
dynamic logging { dynamic logging {
for_each = local.logging_config[each.key] == null ? [] : [""] for_each = var.logging_config == null ? [] : [""]
content { content {
log_bucket = local.logging_config[each.key]["log_bucket"] log_bucket = var.logging_config.log_bucket
log_object_prefix = local.logging_config[each.key]["log_object_prefix"] log_object_prefix = var.logging_config.log_object_prefix
} }
} }
} }
resource "google_storage_bucket_iam_binding" "bindings" { resource "google_storage_bucket_iam_binding" "bindings" {
for_each = local.iam_keypairs for_each = var.iam
bucket = google_storage_bucket.buckets[each.value.name].name bucket = google_storage_bucket.bucket.name
role = each.value.role role = each.key
members = lookup( members = each.value
lookup(local.iam_members, each.value.name, {}), each.value.role, []
)
} }

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,49 +15,16 @@
*/ */
output "bucket" { output "bucket" {
description = "Bucket resource (for single use)." description = "Bucket resource."
value = local.has_buckets ? local.buckets[0] : null value = google_storage_bucket.bucket
} }
output "name" { output "name" {
description = "Bucket name (for single use)." description = "Bucket name."
value = local.has_buckets ? local.buckets[0].name : null value = google_storage_bucket.bucket.name
} }
output "url" { output "url" {
description = "Bucket URL (for single use)." description = "Bucket URL."
value = local.has_buckets ? local.buckets[0].url : null value = google_storage_bucket.bucket.url
}
output "buckets" {
description = "Bucket resources."
value = local.buckets
}
output "names" {
description = "Bucket names."
value = (
local.has_buckets
? zipmap(var.names, [for b in local.buckets : lookup(b, "name", null)])
: {}
)
}
output "urls" {
description = "Bucket URLs."
value = (
local.has_buckets
? zipmap(var.names, [for b in local.buckets : b.url])
: {}
)
}
output "names_list" {
description = "List of bucket names."
value = [for b in local.buckets : b.name]
}
output "urls_list" {
description = "List of bucket URLs."
value = [for b in local.buckets : b.name]
} }

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,33 +15,27 @@
*/ */
variable "uniform_bucket_level_access" { variable "uniform_bucket_level_access" {
description = "Optional map to allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API)." description = "Allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API)."
type = map(bool) type = bool
default = {} default = true
} }
variable "force_destroy" { variable "force_destroy" {
description = "Optional map to set force destroy keyed by name, defaults to false." description = "Optional map to set force destroy keyed by name, defaults to false."
type = map(bool) type = bool
default = {} default = false
} }
variable "iam_members" { variable "iam" {
description = "IAM members keyed by bucket name and role." description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(map(list(string)))
default = {}
}
variable "iam_roles" {
description = "IAM roles keyed by bucket name."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "encryption_keys" { variable "encryption_key" {
description = "Per-bucket KMS keys that will be used for encryption." description = "KMS key that will be used for encryption."
type = map(string) type = string
default = {} default = null
} }
variable "labels" { variable "labels" {
@ -57,17 +51,17 @@ variable "location" {
} }
variable "logging_config" { variable "logging_config" {
description = "Per-bucket logging." description = "Bucket logging configuration."
type = map(object({ type = object({
log_bucket = string log_bucket = string
log_object_prefix = string log_object_prefix = string
})) })
default = {} default = null
} }
variable "names" { variable "name" {
description = "Bucket name suffixes." description = "Bucket name suffix."
type = list(string) type = string
} }
variable "prefix" { variable "prefix" {
@ -81,23 +75,27 @@ variable "project_id" {
type = string type = string
} }
variable "retention_policies" { variable "retention_policy" {
description = "Per-bucket retention policy." description = "Bucket retention policy."
type = map(object({ type = object({
retention_period = number retention_period = number
is_locked = bool is_locked = bool
})) })
default = {} default = null
} }
variable "storage_class" { variable "storage_class" {
description = "Bucket storage class." description = "Bucket storage class."
type = string type = string
default = "MULTI_REGIONAL" default = "MULTI_REGIONAL"
validation {
condition = contains(["STANDARD", "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "ARCHIVE"], var.storage_class)
error_message = "Storage class must be one of STANDARD, MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, ARCHIVE."
}
} }
variable "versioning" { variable "versioning" {
description = "Optional map to set versioning keyed by name, defaults to false." description = "Enable versioning, defaults to false."
type = map(bool) type = bool
default = {} default = false
} }

View File

@ -15,5 +15,5 @@
*/ */
terraform { terraform {
required_version = ">= 0.12.6" required_version = ">= 0.13.0"
} }

View File

@ -0,0 +1,52 @@
# Google Service Account Module
This module allows simplified creation and management of one a service account and its IAM bindings. A key can optionally be generated and will be stored in Terraform state. To use it create a sensitive output in your root modules referencing the `key` output, then extract the private key from the JSON formatted outputs.
## Example
```hcl
module "myproject-default-service-accounts" {
source = "./modules/iam-service-account"
project_id = "myproject"
name = "vm-default"
generate_key = true
# authoritative roles granted *on* the service accounts to other identities
iam = {
"roles/iam.serviceAccountUser" = ["user:foo@example.com"]
}
# non-authoritative roles granted *to* the service accounts on other resources
iam_project_roles = {
"myproject" = [
"roles/logging.logWriter",
"roles/monitoring.metricWriter",
]
}
}
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---: |:---:|:---:|
| name | Name of the service account to create. | <code title="">string</code> | ✓ | |
| project_id | Project id where service account will be created. | <code title="">string</code> | ✓ | |
| *display_name* | Display name of the service account to create. | <code title="">string</code> | | <code title="">Terraform-managed.</code> |
| *generate_key* | Generate a key for service account. | <code title="">bool</code> | | <code title="">false</code> |
| *iam* | IAM bindings on the service account in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_billing_roles* | Project roles granted to the service account, by billing account id. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_folder_roles* | Project roles granted to the service account, by folder id. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_organization_roles* | Project roles granted to the service account, by organization id. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_project_roles* | Project roles granted to the service account, by project id. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_storage_roles* | Storage roles granted to the service account, by bucket name. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *prefix* | Prefix applied to service account names. | <code title="">string</code> | | <code title="">null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| email | Service account email. | |
| iam_email | IAM-format service account email. | |
| key | Service account key. | ✓ |
| service_account | Service account resource. | |
<!-- END TFDOC -->

View File

@ -0,0 +1,124 @@
/**
* Copyright 2020 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.
*/
locals {
iam_billing_pairs = flatten([
for entity, roles in var.iam_billing_roles : [
for role in roles : [
{ entity = entity, role = role }
]
]
])
iam_folder_pairs = flatten([
for entity, roles in var.iam_folder_roles : [
for role in roles : [
{ entity = entity, role = role }
]
]
])
iam_organization_pairs = flatten([
for entity, roles in var.iam_organization_roles : [
for role in roles : [
{ entity = entity, role = role }
]
]
])
iam_project_pairs = flatten([
for entity, roles in var.iam_project_roles : [
for role in roles : [
{ entity = entity, role = role }
]
]
])
iam_storage_pairs = flatten([
for entity, roles in var.iam_storage_roles : [
for role in roles : [
{ entity = entity, role = role }
]
]
])
key = var.generate_key ? google_service_account_key.key["1"] : {}
prefix = var.prefix != null ? "${var.prefix}-" : ""
resource_iam_email = "serviceAccount:${google_service_account.service_account.email}"
}
resource "google_service_account" "service_account" {
project = var.project_id
account_id = "${local.prefix}${var.name}"
display_name = var.display_name
}
resource "google_service_account_key" "key" {
for_each = var.generate_key ? { 1 = 1 } : {}
service_account_id = google_service_account.service_account.email
}
resource "google_service_account_iam_binding" "roles" {
for_each = var.iam
service_account_id = google_service_account.service_account.name
role = each.key
members = each.value
}
resource "google_billing_account_iam_member" "billing-roles" {
for_each = {
for pair in local.iam_billing_pairs :
"${pair.entity}-${pair.role}" => pair
}
billing_account_id = each.value.entity
role = each.value.role
member = local.resource_iam_email
}
resource "google_folder_iam_member" "folder-roles" {
for_each = {
for pair in local.iam_folder_pairs :
"${pair.entity}-${pair.role}" => pair
}
folder = each.value.entity
role = each.value.role
member = local.resource_iam_email
}
resource "google_organization_iam_member" "organization-roles" {
for_each = {
for pair in local.iam_organization_pairs :
"${pair.entity}-${pair.role}" => pair
}
org_id = each.value.entity
role = each.value.role
member = local.resource_iam_email
}
resource "google_project_iam_member" "project-roles" {
for_each = {
for pair in local.iam_project_pairs :
"${pair.entity}-${pair.role}" => pair
}
project = each.value.entity
role = each.value.role
member = local.resource_iam_email
}
resource "google_storage_bucket_iam_member" "bucket-roles" {
for_each = {
for pair in local.iam_storage_pairs :
"${pair.entity}-${pair.role}" => pair
}
bucket = each.value.entity
role = each.value.role
member = local.resource_iam_email
}

View File

@ -0,0 +1,36 @@
/**
* Copyright 2020 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.
*/
output "service_account" {
description = "Service account resource."
value = google_service_account.service_account
}
output "email" {
description = "Service account email."
value = google_service_account.service_account.email
}
output "iam_email" {
description = "IAM-format service account email."
value = local.resource_iam_email
}
output "key" {
description = "Service account key."
sensitive = true
value = local.key
}

View File

@ -14,58 +14,57 @@
* limitations under the License. * limitations under the License.
*/ */
variable "generate_keys" { variable "generate_key" {
description = "Generate keys for service accounts." description = "Generate a key for service account."
type = bool type = bool
default = false default = false
} }
variable "iam_members" { variable "iam" {
description = "Map of member lists which are granted authoritative roles on the service accounts, keyed by role." description = "IAM bindings on the service account in {ROLE => [MEMBERS]} format."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_roles" {
description = "List of authoritative roles granted on the service accounts."
type = list(string)
default = []
}
variable "iam_billing_roles" { variable "iam_billing_roles" {
description = "Project roles granted to all service accounts, by billing account id." description = "Project roles granted to the service account, by billing account id."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_folder_roles" { variable "iam_folder_roles" {
description = "Project roles granted to all service accounts, by folder id." description = "Project roles granted to the service account, by folder id."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_organization_roles" { variable "iam_organization_roles" {
description = "Project roles granted to all service accounts, by organization id." description = "Project roles granted to the service account, by organization id."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_project_roles" { variable "iam_project_roles" {
description = "Project roles granted to all service accounts, by project id." description = "Project roles granted to the service account, by project id."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_storage_roles" { variable "iam_storage_roles" {
description = "Storage roles granted to all service accounts, by bucket name." description = "Storage roles granted to the service account, by bucket name."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "names" { variable "name" {
description = "Names of the service accounts to create." description = "Name of the service account to create."
type = list(string) type = string
default = [] }
variable "display_name" {
description = "Display name of the service account to create."
type = string
default = "Terraform-managed."
} }
variable "prefix" { variable "prefix" {

View File

@ -1,59 +0,0 @@
# Google Service Accounts Module
This module allows simplified creation and management of one or more service accounts and their IAM bindings. Keys can optionally be generated and will be stored in Terraform state. To use them create a sensitive output in your root modules referencing the `keys` or `key` outputs, then extract the private key from the JSON formatted outputs.
## Example
```hcl
module "myproject-default-service-accounts" {
source = "./modules/iam-service-accounts"
project_id = "myproject"
names = ["vm-default", "gke-node-default"]
generate_keys = true
# authoritative roles granted *on* the service accounts to other identities
iam_roles = ["roles/iam.serviceAccountUser"]
iam_members = {
"roles/iam.serviceAccountUser" = ["user:foo@example.com"]
}
# non-authoritative roles granted *to* the service accounts on other resources
iam_project_roles = {
"myproject" = [
"roles/logging.logWriter",
"roles/monitoring.metricWriter",
]
}
}
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---: |:---:|:---:|
| project_id | Project id where service account will be created. | <code title="">string</code> | ✓ | |
| *generate_keys* | Generate keys for service accounts. | <code title="">bool</code> | | <code title="">false</code> |
| *iam_billing_roles* | Project roles granted to all service accounts, by billing account id. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_folder_roles* | Project roles granted to all service accounts, by folder id. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_members* | Map of member lists which are granted authoritative roles on the service accounts, keyed by role. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_organization_roles* | Project roles granted to all service accounts, by organization id. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_project_roles* | Project roles granted to all service accounts, by project id. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | List of authoritative roles granted on the service accounts. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *iam_storage_roles* | Storage roles granted to all service accounts, by bucket name. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *names* | Names of the service accounts to create. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *prefix* | Prefix applied to service account names. | <code title="">string</code> | | <code title="">null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| email | Service account email (for single use). | |
| emails | Service account emails. | |
| emails_list | Service account emails. | |
| iam_email | IAM-format service account email (for single use). | |
| iam_emails | IAM-format service account emails. | |
| iam_emails_list | IAM-format service account emails. | |
| key | Service account key (for single use). | |
| keys | Map of service account keys. | ✓ |
| service_account | Service account resource (for single use). | |
| service_accounts | Service account resources. | |
<!-- END TFDOC -->

View File

@ -1,136 +0,0 @@
/**
* Copyright 2020 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.
*/
locals {
iam_pairs = {
for pair in setproduct(var.names, var.iam_roles) :
"${pair.0}-${pair.1}" => { name = pair.0, role = pair.1 }
}
iam_billing_pairs = flatten([
for entity, roles in var.iam_billing_roles : [
for role in roles : [
for name in var.names : { entity = entity, role = role, name = name }
]
]
])
iam_folder_pairs = flatten([
for entity, roles in var.iam_folder_roles : [
for role in roles : [
for name in var.names : { entity = entity, role = role, name = name }
]
]
])
iam_organization_pairs = flatten([
for entity, roles in var.iam_organization_roles : [
for role in roles : [
for name in var.names : { entity = entity, role = role, name = name }
]
]
])
iam_project_pairs = flatten([
for entity, roles in var.iam_project_roles : [
for role in roles : [
for name in var.names : { entity = entity, role = role, name = name }
]
]
])
iam_storage_pairs = flatten([
for entity, roles in var.iam_storage_roles : [
for role in roles : [
for name in var.names : { entity = entity, role = role, name = name }
]
]
])
keys = var.generate_keys ? google_service_account_key.keys : {}
prefix = var.prefix != null ? "${var.prefix}-" : ""
resource = try(google_service_account.service_accounts[var.names[0]], null)
resource_iam_emails = {
for name, resource in google_service_account.service_accounts :
name => "serviceAccount:${resource.email}"
}
}
resource "google_service_account" "service_accounts" {
for_each = toset(var.names)
project = var.project_id
account_id = "${local.prefix}${lower(each.value)}"
display_name = "Terraform-managed."
}
resource "google_service_account_key" "keys" {
for_each = var.generate_keys ? toset(var.names) : toset([])
service_account_id = google_service_account.service_accounts[each.value].email
}
resource "google_service_account_iam_binding" "sa-roles" {
for_each = local.iam_pairs
service_account_id = google_service_account.service_accounts[each.value.name].name
role = each.value.role
members = lookup(var.iam_members, each.value.role, [])
}
resource "google_billing_account_iam_member" "roles" {
for_each = {
for pair in local.iam_billing_pairs :
"${pair.name}-${pair.entity}-${pair.role}" => pair
}
billing_account_id = each.value.entity
role = each.value.role
member = local.resource_iam_emails[each.value.name]
}
resource "google_folder_iam_member" "roles" {
for_each = {
for pair in local.iam_folder_pairs :
"${pair.name}-${pair.entity}-${pair.role}" => pair
}
folder = each.value.entity
role = each.value.role
member = local.resource_iam_emails[each.value.name]
}
resource "google_organization_iam_member" "roles" {
for_each = {
for pair in local.iam_organization_pairs :
"${pair.name}-${pair.entity}-${pair.role}" => pair
}
org_id = each.value.entity
role = each.value.role
member = local.resource_iam_emails[each.value.name]
}
resource "google_project_iam_member" "project-roles" {
for_each = {
for pair in local.iam_project_pairs :
"${pair.name}-${pair.entity}-${pair.role}" => pair
}
project = each.value.entity
role = each.value.role
member = local.resource_iam_emails[each.value.name]
}
resource "google_storage_bucket_iam_member" "bucket-roles" {
for_each = {
for pair in local.iam_storage_pairs :
"${pair.name}-${pair.entity}-${pair.role}" => pair
}
bucket = each.value.entity
role = each.value.role
member = local.resource_iam_emails[each.value.name]
}
# TODO(ludoo): link from README
# ref: https://cloud.google.com/vpc/docs/shared-vpc

View File

@ -1,75 +0,0 @@
/**
* Copyright 2020 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.
*/
output "service_account" {
description = "Service account resource (for single use)."
value = local.resource
}
output "service_accounts" {
description = "Service account resources."
value = google_service_account.service_accounts
}
output "email" {
description = "Service account email (for single use)."
value = try(local.resource.email, null)
}
output "iam_email" {
description = "IAM-format service account email (for single use)."
value = try("serviceAccount:${local.resource.email}", null)
}
output "key" {
description = "Service account key (for single use)."
value = try(local.keys[var.names[0]], null)
}
output "emails" {
description = "Service account emails."
value = {
for name, resource in google_service_account.service_accounts :
name => resource.email
}
}
output "iam_emails" {
description = "IAM-format service account emails."
value = local.resource_iam_emails
}
output "emails_list" {
description = "Service account emails."
value = [
for name, resource in google_service_account.service_accounts :
resource.email
]
}
output "iam_emails_list" {
description = "IAM-format service account emails."
value = [
for name, resource in google_service_account.service_accounts :
"serviceAccount:${resource.email}"
]
}
output "keys" {
description = "Map of service account keys."
sensitive = true
value = local.keys
}

View File

@ -16,8 +16,7 @@ In this module **no lifecycle blocks are set on resources to prevent destroy**,
module "kms" { module "kms" {
source = "../modules/kms" source = "../modules/kms"
project_id = "my-project" project_id = "my-project"
iam_roles = ["roles/owner"] iam = {
iam_members = {
"roles/owner" = ["user:user1@example.com"] "roles/owner" = ["user:user1@example.com"]
} }
keyring = { location = "europe-west1", name = "test" } keyring = { location = "europe-west1", name = "test" }
@ -32,10 +31,7 @@ module "kms" {
module "kms" { module "kms" {
source = "../modules/kms" source = "../modules/kms"
project_id = "my-project" project_id = "my-project"
key_iam_roles = { key_iam = {
key-a = ["roles/owner"]
}
key_iam_members = {
key-a = { key-a = {
"roles/owner" = ["user:user1@example.com"] "roles/owner" = ["user:user1@example.com"]
} }
@ -76,10 +72,8 @@ module "kms" {
|---|---|:---: |:---:|:---:| |---|---|:---: |:---:|:---:|
| keyring | Keyring attributes. | <code title="object&#40;&#123;&#10;location &#61; string&#10;name &#61; string&#10;&#125;&#41;">object({...})</code> | ✓ | | | keyring | Keyring attributes. | <code title="object&#40;&#123;&#10;location &#61; string&#10;name &#61; string&#10;&#125;&#41;">object({...})</code> | ✓ | |
| project_id | Project id where the keyring will be created. | <code title="">string</code> | ✓ | | | project_id | Project id where the keyring will be created. | <code title="">string</code> | ✓ | |
| *iam_members* | Keyring IAM members. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam* | Keyring IAM bindings for topic in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | Keyring IAM roles. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> | | *key_iam* | Key IAM bindings for topic in {KEY => {ROLE => [MEMBERS]}} format. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> |
| *key_iam_members* | IAM members keyed by key name and role. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> |
| *key_iam_roles* | IAM roles keyed by key name. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *key_purpose* | Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | <code title="map&#40;object&#40;&#123;&#10;purpose &#61; string&#10;version_template &#61; object&#40;&#123;&#10;algorithm &#61; string&#10;protection_level &#61; string&#10;&#125;&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *key_purpose* | Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | <code title="map&#40;object&#40;&#123;&#10;purpose &#61; string&#10;version_template &#61; object&#40;&#123;&#10;algorithm &#61; string&#10;protection_level &#61; string&#10;&#125;&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
| *key_purpose_defaults* | Defaults used for key purpose when not defined at the key level. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | <code title="object&#40;&#123;&#10;purpose &#61; string&#10;version_template &#61; object&#40;&#123;&#10;algorithm &#61; string&#10;protection_level &#61; string&#10;&#125;&#41;&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;purpose &#61; null&#10;version_template &#61; null&#10;&#125;">...</code> | | *key_purpose_defaults* | Defaults used for key purpose when not defined at the key level. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | <code title="object&#40;&#123;&#10;purpose &#61; string&#10;version_template &#61; object&#40;&#123;&#10;algorithm &#61; string&#10;protection_level &#61; string&#10;&#125;&#41;&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;purpose &#61; null&#10;version_template &#61; null&#10;&#125;">...</code> |
| *keyring_create* | Set to false to manage keys and IAM bindings in an existing keyring. | <code title="">bool</code> | | <code title="">true</code> | | *keyring_create* | Set to false to manage keys and IAM bindings in an existing keyring. | <code title="">bool</code> | | <code title="">true</code> |

View File

@ -15,14 +15,15 @@
*/ */
locals { locals {
key_iam_pairs = flatten([ key_iam_members = flatten([
for name, roles in var.key_iam_roles : for key, roles in var.key_iam : [
[for role in roles : { name = name, role = role }] for role, members in roles : {
]) key = key
key_iam_keypairs = { role = role
for pair in local.key_iam_pairs : members = members
"${pair.name}-${pair.role}" => pair
} }
]
])
key_purpose = { key_purpose = {
for key, attrs in var.keys : key => try( for key, attrs in var.keys : key => try(
var.key_purpose[key], var.key_purpose_defaults var.key_purpose[key], var.key_purpose_defaults
@ -47,16 +48,13 @@ resource "google_kms_key_ring" "default" {
project = var.project_id project = var.project_id
name = var.keyring.name name = var.keyring.name
location = var.keyring.location location = var.keyring.location
# lifecycle {
# prevent_destroy = true
# }
} }
resource "google_kms_key_ring_iam_binding" "default" { resource "google_kms_key_ring_iam_binding" "default" {
for_each = toset(var.iam_roles) for_each = var.iam
key_ring_id = local.keyring.self_link key_ring_id = local.keyring.self_link
role = each.value role = each.key
members = lookup(var.iam_members, each.value, []) members = each.value
} }
resource "google_kms_crypto_key" "default" { resource "google_kms_crypto_key" "default" {
@ -73,16 +71,14 @@ resource "google_kms_crypto_key" "default" {
protection_level = local.key_purpose[each.key].version_template.protection_level protection_level = local.key_purpose[each.key].version_template.protection_level
} }
} }
# lifecycle {
# prevent_destroy = true
# }
} }
resource "google_kms_crypto_key_iam_binding" "default" { resource "google_kms_crypto_key_iam_binding" "default" {
for_each = local.key_iam_keypairs for_each = {
role = each.value.role for binding in local.key_iam_members :
crypto_key_id = google_kms_crypto_key.default[each.value.name].self_link "${binding.key}.${binding.role}" => binding
members = lookup( }
lookup(var.key_iam_members, each.value.name, {}), each.value.role, [] role = each.value.role
) crypto_key_id = google_kms_crypto_key.default[each.value.key].self_link
members = each.value.members
} }

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,30 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
variable "iam_members" { variable "iam" {
description = "Keyring IAM members." description = "Keyring IAM bindings for topic in {ROLE => [MEMBERS]} format."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_roles" { variable "key_iam" {
description = "Keyring IAM roles." description = "Key IAM bindings for topic in {KEY => {ROLE => [MEMBERS]}} format."
type = list(string)
default = []
}
variable "key_iam_members" {
description = "IAM members keyed by key name and role."
type = map(map(list(string))) type = map(map(list(string)))
default = {} default = {}
} }
variable "key_iam_roles" {
description = "IAM roles keyed by key name."
type = map(list(string))
default = {}
}
variable "key_purpose" { variable "key_purpose" {
description = "Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required." description = "Per-key purpose, if not set defaults will be used. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required."
type = map(object({ type = map(object({

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -21,7 +21,7 @@ module "peering" {
} }
``` ```
If you need to create more than one peering for the same VPC Network `(A -> B, A -> C)` you have to use output from the first module as a dependency for the second one to keep order of peering creation (It is not currently possible to create more than one peering connection for a VPC Network at the same time). If you need to create more than one peering for the same VPC Network `(A -> B, A -> C)` you use a `depends_on` for second one to keep order of peering creation (It is not currently possible to create more than one peering connection for a VPC Network at the same time).
```hcl ```hcl
module "peering-a-b" { module "peering-a-b" {
@ -39,7 +39,7 @@ module "peering-a-c" {
local_network = "<A NETWORK SELF LINK>" local_network = "<A NETWORK SELF LINK>"
peer_network = "<C NETWORK SELF LINK>" peer_network = "<C NETWORK SELF LINK>"
module_depends_on = [module.peering-a-b.complete] depends_on = [ module.peering-a-b ]
} }
``` ```
@ -52,7 +52,6 @@ module "peering-a-c" {
| peer_network | Resource link of the peer network. | <code title="">string</code> | ✓ | | | peer_network | Resource link of the peer network. | <code title="">string</code> | ✓ | |
| *export_local_custom_routes* | Export custom routes to peer network from local network. | <code title="">bool</code> | | <code title="">false</code> | | *export_local_custom_routes* | Export custom routes to peer network from local network. | <code title="">bool</code> | | <code title="">false</code> |
| *export_peer_custom_routes* | Export custom routes to local network from peer network. | <code title="">bool</code> | | <code title="">false</code> | | *export_peer_custom_routes* | Export custom routes to local network from peer network. | <code title="">bool</code> | | <code title="">false</code> |
| *module_depends_on* | List of modules or resources this module depends on. | <code title="">list</code> | | <code title="">[]</code> |
| *peer_create_peering* | Create the peering on the remote side. If false, only the peering from this network to the remote network is created. | <code title="">bool</code> | | <code title="">true</code> | | *peer_create_peering* | Create the peering on the remote side. If false, only the peering from this network to the remote network is created. | <code title="">bool</code> | | <code title="">true</code> |
| *prefix* | Name prefix for the network peerings. | <code title="">string</code> | | <code title="">network-peering</code> | | *prefix* | Name prefix for the network peerings. | <code title="">string</code> | | <code title="">network-peering</code> |
@ -60,7 +59,6 @@ module "peering-a-c" {
| name | description | sensitive | | name | description | sensitive |
|---|---|:---:| |---|---|:---:|
| complete | Output to be used as a module dependency. | |
| local_network_peering | Network peering resource. | | | local_network_peering | Network peering resource. | |
| peer_network_peering | Peer network peering resource. | | | peer_network_peering | Peer network peering resource. | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -25,8 +25,6 @@ resource "google_compute_network_peering" "local_network_peering" {
peer_network = var.peer_network peer_network = var.peer_network
export_custom_routes = var.export_local_custom_routes export_custom_routes = var.export_local_custom_routes
import_custom_routes = var.export_peer_custom_routes import_custom_routes = var.export_peer_custom_routes
depends_on = [null_resource.module_depends_on]
} }
resource "google_compute_network_peering" "peer_network_peering" { resource "google_compute_network_peering" "peer_network_peering" {
@ -37,15 +35,5 @@ resource "google_compute_network_peering" "peer_network_peering" {
export_custom_routes = var.export_peer_custom_routes export_custom_routes = var.export_peer_custom_routes
import_custom_routes = var.export_local_custom_routes import_custom_routes = var.export_local_custom_routes
depends_on = [null_resource.module_depends_on, google_compute_network_peering.local_network_peering] depends_on = [google_compute_network_peering.local_network_peering]
}
resource "null_resource" "module_depends_on" {
triggers = {
value = length(var.module_depends_on)
}
}
resource "null_resource" "complete" {
depends_on = [google_compute_network_peering.local_network_peering, google_compute_network_peering.peer_network_peering]
} }

View File

@ -23,8 +23,3 @@ output "peer_network_peering" {
description = "Peer network peering resource." description = "Peer network peering resource."
value = google_compute_network_peering.peer_network_peering value = google_compute_network_peering.peer_network_peering
} }
output "complete" {
description = "Output to be used as a module dependency."
value = null_resource.complete.id
}

View File

@ -42,12 +42,6 @@ variable "export_local_custom_routes" {
default = false default = false
} }
variable "module_depends_on" {
description = "List of modules or resources this module depends on."
type = list
default = []
}
variable "peer_create_peering" { variable "peer_create_peering" {
description = "Create the peering on the remote side. If false, only the peering from this network to the remote network is created." description = "Create the peering on the remote side. If false, only the peering from this network to the remote network is created."
type = bool type = bool

View File

@ -86,13 +86,7 @@ module "vpc-host" {
local.service_project_1.project_id, local.service_project_1.project_id,
local.service_project_2.project_id local.service_project_2.project_id
] ]
iam_roles = { iam = {
"europe-west1/subnet-1" = [
"roles/compute.networkUser",
"roles/compute.securityAdmin"
]
}
iam_members = {
"europe-west1/subnet-1" = { "europe-west1/subnet-1" = {
"roles/compute.networkUser" = [ "roles/compute.networkUser" = [
local.service_project_1.cloudsvc_sa, local.service_project_1.cloudsvc_sa,
@ -116,14 +110,13 @@ module "vpc-host" {
| *auto_create_subnetworks* | Set to true to create an auto mode subnet, defaults to custom mode. | <code title="">bool</code> | | <code title="">false</code> | | *auto_create_subnetworks* | Set to true to create an auto mode subnet, defaults to custom mode. | <code title="">bool</code> | | <code title="">false</code> |
| *delete_default_routes_on_create* | Set to true to delete the default routes at creation time. | <code title="">bool</code> | | <code title="">false</code> | | *delete_default_routes_on_create* | Set to true to delete the default routes at creation time. | <code title="">bool</code> | | <code title="">false</code> |
| *description* | An optional description of this resource (triggers recreation on change). | <code title="">string</code> | | <code title="">Terraform-managed.</code> | | *description* | An optional description of this resource (triggers recreation on change). | <code title="">string</code> | | <code title="">Terraform-managed.</code> |
| *iam_members* | List of IAM members keyed by subnet 'region/name' and role. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> | | *iam* | Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> |
| *iam_roles* | List of IAM roles keyed by subnet 'region/name'. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *log_config_defaults* | Default configuration for flow logs when enabled. | <code title="object&#40;&#123;&#10;aggregation_interval &#61; string&#10;flow_sampling &#61; number&#10;metadata &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;aggregation_interval &#61; &#34;INTERVAL_5_SEC&#34;&#10;flow_sampling &#61; 0.5&#10;metadata &#61; &#34;INCLUDE_ALL_METADATA&#34;&#10;&#125;">...</code> | | *log_config_defaults* | Default configuration for flow logs when enabled. | <code title="object&#40;&#123;&#10;aggregation_interval &#61; string&#10;flow_sampling &#61; number&#10;metadata &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;aggregation_interval &#61; &#34;INTERVAL_5_SEC&#34;&#10;flow_sampling &#61; 0.5&#10;metadata &#61; &#34;INCLUDE_ALL_METADATA&#34;&#10;&#125;">...</code> |
| *log_configs* | Map keyed by subnet 'region/name' of optional configurations for flow logs when enabled. | <code title="map&#40;map&#40;string&#41;&#41;">map(map(string))</code> | | <code title="">{}</code> | | *log_configs* | Map keyed by subnet 'region/name' of optional configurations for flow logs when enabled. | <code title="map&#40;map&#40;string&#41;&#41;">map(map(string))</code> | | <code title="">{}</code> |
| *peering_config* | VPC peering configuration. | <code title="object&#40;&#123;&#10;peer_vpc_self_link &#61; string&#10;export_routes &#61; bool&#10;import_routes &#61; bool&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> | | *peering_config* | VPC peering configuration. | <code title="object&#40;&#123;&#10;peer_vpc_self_link &#61; string&#10;export_routes &#61; bool&#10;import_routes &#61; bool&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> |
| *peering_create_remote_end* | Skip creation of peering on the remote end when using peering_config | <code title="">bool</code> | | <code title="">true</code> | | *peering_create_remote_end* | Skip creation of peering on the remote end when using peering_config | <code title="">bool</code> | | <code title="">true</code> |
| *routes* | Network routes, keyed by name. | <code title="map&#40;object&#40;&#123;&#10;dest_range &#61; string&#10;priority &#61; number&#10;tags &#61; list&#40;string&#41;&#10;next_hop_type &#61; string &#35; gateway, instance, ip, vpn_tunnel, ilb&#10;next_hop &#61; string&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *routes* | Network routes, keyed by name. | <code title="map&#40;object&#40;&#123;&#10;dest_range &#61; string&#10;priority &#61; number&#10;tags &#61; list&#40;string&#41;&#10;next_hop_type &#61; string &#35; gateway, instance, ip, vpn_tunnel, ilb&#10;next_hop &#61; string&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
| *routing_mode* | The network routing mode (default 'GLOBAL') | <code title="">string</code> | | <code title="">GLOBAL</code> | | *routing_mode* | The network routing mode (default 'GLOBAL') | <code title="">string</code> | | <code title="GLOBAL&#10;validation &#123;&#10;condition &#61; var.routing_mode &#61;&#61; &#34;GLOBAL&#34; &#124;&#124; var.routing_mode &#61;&#61; &#34;REGIONAL&#34;&#10;error_message &#61; &#34;Routing type must be GLOBAL or REGIONAL.&#34;&#10;&#125;">...</code> |
| *shared_vpc_host* | Enable shared VPC for this project. | <code title="">bool</code> | | <code title="">false</code> | | *shared_vpc_host* | Enable shared VPC for this project. | <code title="">bool</code> | | <code title="">false</code> |
| *shared_vpc_service_projects* | Shared VPC service projects to register with this host | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> | | *shared_vpc_service_projects* | Shared VPC service projects to register with this host | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *subnet_descriptions* | Optional map of subnet descriptions, keyed by subnet 'region/name'. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> | | *subnet_descriptions* | Optional map of subnet descriptions, keyed by subnet 'region/name'. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> |

View File

@ -15,15 +15,17 @@
*/ */
locals { locals {
iam_members = var.iam_members == null ? {} : var.iam_members iam_members = var.iam == null ? {} : var.iam
iam_pairs = var.iam_roles == null ? [] : flatten([ subnet_iam_members = flatten([
for subnet, roles in var.iam_roles : for subnet, roles in local.iam_members : [
[for role in roles : { subnet = subnet, role = role }] for role, members in roles : {
]) subnet = subnet
iam_keypairs = { role = role
for pair in local.iam_pairs : members = members
"${pair.subnet}-${pair.role}" => pair
} }
]
])
log_configs = var.log_configs == null ? {} : var.log_configs log_configs = var.log_configs == null ? {} : var.log_configs
peer_network = ( peer_network = (
var.peering_config == null var.peering_config == null
@ -152,14 +154,15 @@ resource "google_compute_subnetwork" "subnetwork" {
} }
resource "google_compute_subnetwork_iam_binding" "binding" { resource "google_compute_subnetwork_iam_binding" "binding" {
for_each = local.iam_keypairs for_each = {
for binding in local.subnet_iam_members :
"${binding.subnet}.${binding.role}" => binding
}
project = var.project_id project = var.project_id
subnetwork = google_compute_subnetwork.subnetwork[each.value.subnet].name subnetwork = google_compute_subnetwork.subnetwork[each.value.subnet].name
region = google_compute_subnetwork.subnetwork[each.value.subnet].region region = google_compute_subnetwork.subnetwork[each.value.subnet].region
role = each.value.role role = each.value.role
members = lookup( members = each.value.members
lookup(local.iam_members, each.value.subnet, {}), each.value.role, []
)
} }
resource "google_compute_route" "gateway" { resource "google_compute_route" "gateway" {

View File

@ -32,14 +32,8 @@ variable "description" {
default = "Terraform-managed." default = "Terraform-managed."
} }
variable "iam_roles" { variable "iam" {
description = "List of IAM roles keyed by subnet 'region/name'." description = "Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format."
type = map(list(string))
default = {}
}
variable "iam_members" {
description = "List of IAM members keyed by subnet 'region/name' and role."
type = map(map(list(string))) type = map(map(list(string)))
default = {} default = {}
} }
@ -106,6 +100,11 @@ variable "routing_mode" {
description = "The network routing mode (default 'GLOBAL')" description = "The network routing mode (default 'GLOBAL')"
type = string type = string
default = "GLOBAL" default = "GLOBAL"
validation {
condition = var.routing_mode == "GLOBAL" || var.routing_mode == "REGIONAL"
error_message = "Routing type must be GLOBAL or REGIONAL."
}
} }
variable "shared_vpc_host" { variable "shared_vpc_host" {

View File

@ -15,5 +15,5 @@
*/ */
terraform { terraform {
required_version = ">= 0.12.6" required_version = ">= 0.13.0"
} }

View File

@ -13,8 +13,7 @@ This module allows managing several organization properties:
module "org" { module "org" {
source = "./modules/organization" source = "./modules/organization"
org_id = 1234567890 org_id = 1234567890
iam_roles = ["roles/projectCreator"] iam = { "roles/projectCreator" = ["group:cloud-admins@example.org"] }
iam_members = { "roles/projectCreator" = ["group:cloud-admins@example.org"] }
policy_boolean = { policy_boolean = {
"constraints/compute.disableGuestAttributesAccess" = true "constraints/compute.disableGuestAttributesAccess" = true
"constraints/compute.skipDefaultNetworkCreation" = true "constraints/compute.skipDefaultNetworkCreation" = true
@ -37,10 +36,9 @@ module "org" {
|---|---|:---: |:---:|:---:| |---|---|:---: |:---:|:---:|
| org_id | Organization id in nnnnnn format. | <code title="">number</code> | ✓ | | | org_id | Organization id in nnnnnn format. | <code title="">number</code> | ✓ | |
| *custom_roles* | Map of role name => list of permissions to create in this project. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *custom_roles* | Map of role name => list of permissions to create in this project. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_additive_bindings* | Map of roles lists used to set non authoritative bindings, keyed by members. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam* | IAM bindings, in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_additive* | Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_audit_config* | Service audit logging configuration. Service as key, map of log permission (eg DATA_READ) and excluded members as value for each service. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> | | *iam_audit_config* | Service audit logging configuration. Service as key, map of log permission (eg DATA_READ) and excluded members as value for each service. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> |
| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | List of roles used to set authoritative bindings. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> | | *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> |
| *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10;inherit_from_parent &#61; bool&#10;suggested_value &#61; string&#10;status &#61; bool&#10;values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10;inherit_from_parent &#61; bool&#10;suggested_value &#61; string&#10;status &#61; bool&#10;values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |

View File

@ -16,7 +16,7 @@
locals { locals {
iam_additive_pairs = flatten([ iam_additive_pairs = flatten([
for member, roles in var.iam_additive_bindings : [ for member, roles in var.iam_additive : [
for role in roles : for role in roles :
{ role = role, member = member } { role = role, member = member }
] ]
@ -37,14 +37,14 @@ resource "google_organization_iam_custom_role" "roles" {
} }
resource "google_organization_iam_binding" "authoritative" { resource "google_organization_iam_binding" "authoritative" {
for_each = toset(var.iam_roles) for_each = var.iam
org_id = var.org_id org_id = var.org_id
role = each.value role = each.key
members = lookup(var.iam_members, each.value, []) members = each.value
} }
resource "google_organization_iam_member" "additive" { resource "google_organization_iam_member" "additive" {
for_each = length(var.iam_additive_bindings) > 0 ? local.iam_additive : {} for_each = length(var.iam_additive) > 0 ? local.iam_additive : {}
org_id = var.org_id org_id = var.org_id
role = each.value.role role = each.value.role
member = each.value.member member = each.value.member

View File

@ -20,20 +20,14 @@ variable "custom_roles" {
default = {} default = {}
} }
variable "iam_members" { variable "iam" {
description = "Map of member lists used to set authoritative bindings, keyed by role." description = "IAM bindings, in {ROLE => [MEMBERS]} format."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_roles" { variable "iam_additive" {
description = "List of roles used to set authoritative bindings." description = "Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format."
type = list(string)
default = []
}
variable "iam_additive_bindings" {
description = "Map of roles lists used to set non authoritative bindings, keyed by members."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }

View File

@ -15,8 +15,7 @@ module "project" {
"container.googleapis.com", "container.googleapis.com",
"stackdriver.googleapis.com" "stackdriver.googleapis.com"
] ]
iam_roles = ["roles/container.hostServiceAgentUser"] iam = {
iam_members = {
"roles/container.hostServiceAgentUser" = [ "roles/container.hostServiceAgentUser" = [
"serviceAccount:${var.gke_service_account}" "serviceAccount:${var.gke_service_account}"
] ]
@ -32,7 +31,7 @@ module "project" {
name = "project-example" name = "project-example"
project_create = false project_create = false
iam_additive_bindings = { iam_additive = {
"group:usergroup_watermlon_experimentation@lemonadeinc.io" = [ "group:usergroup_watermlon_experimentation@lemonadeinc.io" = [
"roles/viewer", "roles/viewer",
"roles/storage.objectAdmin" "roles/storage.objectAdmin"
@ -88,15 +87,14 @@ module "project" {
| *auto_create_network* | Whether to create the default network for the project | <code title="">bool</code> | | <code title="">false</code> | | *auto_create_network* | Whether to create the default network for the project | <code title="">bool</code> | | <code title="">false</code> |
| *billing_account* | Billing account id. | <code title="">string</code> | | <code title="">null</code> | | *billing_account* | Billing account id. | <code title="">string</code> | | <code title="">null</code> |
| *custom_roles* | Map of role name => list of permissions to create in this project. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *custom_roles* | Map of role name => list of permissions to create in this project. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_additive_bindings* | Map of roles lists used to set non authoritative bindings, keyed by members | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | <code title="map&#40;set&#40;string&#41;&#41;">map(set(string))</code> | | <code title="">{}</code> |
| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam_additive* | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | List of roles used to set authoritative bindings. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *labels* | Resource labels. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> | | *labels* | Resource labels. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> |
| *lien_reason* | If non-empty, creates a project lien with this description. | <code title="">string</code> | | <code title=""></code> | | *lien_reason* | If non-empty, creates a project lien with this description. | <code title="">string</code> | | <code title=""></code> |
| *oslogin* | Enable OS Login. | <code title="">bool</code> | | <code title="">false</code> | | *oslogin* | Enable OS Login. | <code title="">bool</code> | | <code title="">false</code> |
| *oslogin_admins* | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> | | *oslogin_admins* | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *oslogin_users* | List of IAM-style identities that will be granted roles necessary for OS Login users. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> | | *oslogin_users* | List of IAM-style identities that will be granted roles necessary for OS Login users. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *parent* | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code title="">string</code> | | <code title="">null</code> | | *parent* | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code title="">string</code> | | <code title="null&#10;validation &#123;&#10;condition &#61; var.parent &#61;&#61; null &#124;&#124; can&#40;regex&#40;&#34;&#40;organizations&#124;folders&#41;&#47;&#91;0-9&#93;&#43;&#34;, var.parent&#41;&#41;&#10;error_message &#61; &#34;Parent must be of the form folders&#47;folder_id or organizations&#47;organization_id.&#34;&#10;&#125;">...</code> |
| *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> | | *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> |
| *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10;inherit_from_parent &#61; bool&#10;suggested_value &#61; string&#10;status &#61; bool&#10;values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10;inherit_from_parent &#61; bool&#10;suggested_value &#61; string&#10;status &#61; bool&#10;values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
| *prefix* | Prefix used to generate project id and name. | <code title="">string</code> | | <code title="">null</code> | | *prefix* | Prefix used to generate project id and name. | <code title="">string</code> | | <code title="">null</code> |

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
locals { locals {
iam_additive_pairs = flatten([ iam_additive_pairs = flatten([
for member, roles in var.iam_additive_bindings : [ for member, roles in var.iam_additive : [
for role in roles : for role in roles :
{ role = role, member = member } { role = role, member = member }
] ]
@ -91,10 +91,10 @@ resource "google_project_service" "project_services" {
# - additive (non-authoritative) roles might fail due to dynamic values # - additive (non-authoritative) roles might fail due to dynamic values
resource "google_project_iam_binding" "authoritative" { resource "google_project_iam_binding" "authoritative" {
for_each = toset(var.iam_roles) for_each = var.iam
project = local.project.project_id project = local.project.project_id
role = each.value role = each.key
members = lookup(var.iam_members, each.value, []) members = each.value
depends_on = [ depends_on = [
google_project_service.project_services, google_project_service.project_services,
google_project_iam_custom_role.roles google_project_iam_custom_role.roles
@ -102,7 +102,7 @@ resource "google_project_iam_binding" "authoritative" {
} }
resource "google_project_iam_member" "additive" { resource "google_project_iam_member" "additive" {
for_each = length(var.iam_additive_bindings) > 0 ? local.iam_additive : {} for_each = length(var.iam_additive) > 0 ? local.iam_additive : {}
project = local.project.project_id project = local.project.project_id
role = each.value.role role = each.value.role
member = each.value.member member = each.value.member

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -32,21 +32,14 @@ variable "custom_roles" {
default = {} default = {}
} }
variable "iam_members" { variable "iam" {
description = "Map of member lists used to set authoritative bindings, keyed by role." description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(list(string)) type = map(set(string))
default = {} default = {}
} }
variable "iam_roles" { variable "iam_additive" {
description = "List of roles used to set authoritative bindings." description = "IAM additive bindings in {ROLE => [MEMBERS]} format."
type = list(string)
default = []
}
variable "iam_additive_bindings" {
description = "Map of roles lists used to set non authoritative bindings, keyed by members"
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
@ -90,6 +83,10 @@ variable "parent" {
description = "Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format." description = "Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format."
type = string type = string
default = null default = null
validation {
condition = var.parent == null || can(regex("(organizations|folders)/[0-9]+", var.parent))
error_message = "Parent must be of the form folders/folder_id or organizations/organization_id."
}
} }
variable "policy_boolean" { variable "policy_boolean" {

View File

@ -15,5 +15,5 @@
*/ */
terraform { terraform {
required_version = ">= 0.12.6" required_version = ">= 0.13.0"
} }

View File

@ -12,11 +12,7 @@ module "pubsub" {
source = "./modules/pubsub" source = "./modules/pubsub"
project_id = "my-project" project_id = "my-project"
name = "my-topic" name = "my-topic"
iam_roles = [ iam = {
"roles/pubsub.viewer",
"roles/pubsub.subscriber"
]
iam_members = {
"roles/pubsub.viewer" = ["group:foo@example.com"] "roles/pubsub.viewer" = ["group:foo@example.com"]
"roles/pubsub.subscriber" = ["user:user1@example.com"] "roles/pubsub.subscriber" = ["user:user1@example.com"]
} }
@ -30,7 +26,7 @@ Subscriptions are defined with the `subscriptions` variable, allowing optional c
```hcl ```hcl
module "pubsub" { module "pubsub" {
source = "./modules/pubsub" source = "./modules/pubsub"
project_id = "my-project project_id = "my-project"
name = "my-topic" name = "my-topic"
subscriptions = { subscriptions = {
test-pull = null test-pull = null
@ -54,7 +50,7 @@ Push subscriptions need extra configuration in the `push_configs` variable.
```hcl ```hcl
module "pubsub" { module "pubsub" {
source = "./modules/pubsub" source = "./modules/pubsub"
project_id = "my-project project_id = "my-project"
name = "my-topic" name = "my-topic"
subscriptions = { subscriptions = {
test-push = null test-push = null
@ -74,16 +70,13 @@ module "pubsub" {
```hcl ```hcl
module "pubsub" { module "pubsub" {
source = "./modules/pubsub" source = "./modules/pubsub"
project_id = "my-project project_id = "my-project"
name = "my-topic" name = "my-topic"
subscriptions = { subscriptions = {
test-1 = null test-1 = null
test-1 = null test-1 = null
} }
subscription_iam_roles = { subscription_iam = {
test-1 = ["roles/pubsub.subscriber"]
}
subscription_iam_members = {
test-1 = { test-1 = {
"roles/pubsub.subscriber" = ["user:user1@ludomagno.net"] "roles/pubsub.subscriber" = ["user:user1@ludomagno.net"]
} }
@ -100,14 +93,12 @@ module "pubsub" {
| project_id | Project used for resources. | <code title="">string</code> | ✓ | | | project_id | Project used for resources. | <code title="">string</code> | ✓ | |
| *dead_letter_configs* | Per-subscription dead letter policy configuration. | <code title="map&#40;object&#40;&#123;&#10;topic &#61; string&#10;max_delivery_attemps &#61; number&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *dead_letter_configs* | Per-subscription dead letter policy configuration. | <code title="map&#40;object&#40;&#123;&#10;topic &#61; string&#10;max_delivery_attemps &#61; number&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
| *defaults* | Subscription defaults for options. | <code title="object&#40;&#123;&#10;ack_deadline_seconds &#61; number&#10;message_retention_duration &#61; number&#10;retain_acked_messages &#61; bool&#10;expiration_policy_ttl &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;ack_deadline_seconds &#61; null&#10;message_retention_duration &#61; null&#10;retain_acked_messages &#61; null&#10;expiration_policy_ttl &#61; null&#10;&#125;">...</code> | | *defaults* | Subscription defaults for options. | <code title="object&#40;&#123;&#10;ack_deadline_seconds &#61; number&#10;message_retention_duration &#61; number&#10;retain_acked_messages &#61; bool&#10;expiration_policy_ttl &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;ack_deadline_seconds &#61; null&#10;message_retention_duration &#61; null&#10;retain_acked_messages &#61; null&#10;expiration_policy_ttl &#61; null&#10;&#125;">...</code> |
| *iam_members* | IAM members for each topic role. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | IAM roles for topic. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *kms_key* | KMS customer managed encryption key. | <code title="">string</code> | | <code title="">null</code> | | *kms_key* | KMS customer managed encryption key. | <code title="">string</code> | | <code title="">null</code> |
| *labels* | Labels. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> | | *labels* | Labels. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> |
| *push_configs* | Push subscription configurations. | <code title="map&#40;object&#40;&#123;&#10;attributes &#61; map&#40;string&#41;&#10;endpoint &#61; string&#10;oidc_token &#61; object&#40;&#123;&#10;audience &#61; string&#10;service_account_email &#61; string&#10;&#125;&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *push_configs* | Push subscription configurations. | <code title="map&#40;object&#40;&#123;&#10;attributes &#61; map&#40;string&#41;&#10;endpoint &#61; string&#10;oidc_token &#61; object&#40;&#123;&#10;audience &#61; string&#10;service_account_email &#61; string&#10;&#125;&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
| *regions* | List of regions used to set persistence policy. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> | | *regions* | List of regions used to set persistence policy. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *subscription_iam_members* | IAM members for each subscription and role. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> | | *subscription_iam* | IAM bindings for subscriptions in {SUBSCRIPTION => {ROLE => [MEMBERS]}} format. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> |
| *subscription_iam_roles* | IAM roles for each subscription. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *subscriptions* | Topic subscriptions. Also define push configs for push subscriptions. If options is set to null subscription defaults will be used. Labels default to topic labels if set to null. | <code title="map&#40;object&#40;&#123;&#10;labels &#61; map&#40;string&#41;&#10;options &#61; object&#40;&#123;&#10;ack_deadline_seconds &#61; number&#10;message_retention_duration &#61; number&#10;retain_acked_messages &#61; bool&#10;expiration_policy_ttl &#61; string&#10;&#125;&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *subscriptions* | Topic subscriptions. Also define push configs for push subscriptions. If options is set to null subscription defaults will be used. Labels default to topic labels if set to null. | <code title="map&#40;object&#40;&#123;&#10;labels &#61; map&#40;string&#41;&#10;options &#61; object&#40;&#123;&#10;ack_deadline_seconds &#61; number&#10;message_retention_duration &#61; number&#10;retain_acked_messages &#61; bool&#10;expiration_policy_ttl &#61; string&#10;&#125;&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
## Outputs ## Outputs

View File

@ -15,17 +15,15 @@
*/ */
locals { locals {
iam_pairs = var.subscription_iam_roles == null ? [] : flatten([ sub_iam_members = flatten([
for name, roles in var.subscription_iam_roles : for sub, roles in var.subscription_iam : [
[for role in roles : { name = name, role = role }] for role, members in roles : {
]) sub = sub
iam_keypairs = { role = role
for pair in local.iam_pairs : members = members
"${pair.name}-${pair.role}" => pair
} }
iam_members = ( ]
var.subscription_iam_members == null ? {} : var.subscription_iam_members ])
)
oidc_config = { oidc_config = {
for k, v in var.push_configs : k => v.oidc_token for k, v in var.push_configs : k => v.oidc_token
} }
@ -52,11 +50,11 @@ resource "google_pubsub_topic" "default" {
} }
resource "google_pubsub_topic_iam_binding" "default" { resource "google_pubsub_topic_iam_binding" "default" {
for_each = toset(var.iam_roles) for_each = var.iam
project = var.project_id project = var.project_id
topic = google_pubsub_topic.default.name topic = google_pubsub_topic.default.name
role = each.value role = each.key
members = lookup(var.iam_members, each.value, []) members = each.value
} }
resource "google_pubsub_subscription" "default" { resource "google_pubsub_subscription" "default" {
@ -103,11 +101,12 @@ resource "google_pubsub_subscription" "default" {
} }
resource "google_pubsub_subscription_iam_binding" "default" { resource "google_pubsub_subscription_iam_binding" "default" {
for_each = local.iam_keypairs for_each = {
project = var.project_id for binding in local.sub_iam_members :
subscription = google_pubsub_subscription.default[each.value.name].name "${binding.sub}.${binding.role}" => binding
role = each.value.role }
members = lookup( project = var.project_id
lookup(local.iam_members, each.value.name, {}), each.value.role, [] subscription = google_pubsub_subscription.default[each.value.sub].name
) role = each.value.role
members = each.value.members
} }

View File

@ -39,18 +39,12 @@ variable "defaults" {
} }
} }
variable "iam_members" { variable "iam" {
description = "IAM members for each topic role." description = "IAM bindings for topic in {ROLE => [MEMBERS]} format."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_roles" {
description = "IAM roles for topic."
type = list(string)
default = []
}
variable "kms_key" { variable "kms_key" {
description = "KMS customer managed encryption key." description = "KMS customer managed encryption key."
type = string type = string
@ -107,14 +101,8 @@ variable "subscriptions" {
default = {} default = {}
} }
variable "subscription_iam_members" { variable "subscription_iam" {
description = "IAM members for each subscription and role." description = "IAM bindings for subscriptions in {SUBSCRIPTION => {ROLE => [MEMBERS]}} format."
type = map(map(list(string))) type = map(map(list(string)))
default = {} default = {}
} }
variable "subscription_iam_roles" {
description = "IAM roles for each subscription."
type = map(list(string))
default = {}
}

View File

@ -25,7 +25,7 @@ module "secret-manager" {
### Secret IAM bindings ### Secret IAM bindings
IAM bindings can be set per secret in the same way as for most other modules supporting IAM, via `iam_roles` and `iam_members` variables. IAM bindings can be set per secret in the same way as for most other modules supporting IAM, using the `iam` variable.
```hcl ```hcl
module "secret-manager" { module "secret-manager" {
@ -35,11 +35,7 @@ module "secret-manager" {
test-auto = null test-auto = null
test-manual = ["europe-west1", "europe-west4"] test-manual = ["europe-west1", "europe-west4"]
} }
iam_roles = { iam = {
test-auto = ["roles/secretmanager.secretAccessor"]
test-manual = ["roles/secretmanager.secretAccessor"]
}
iam_members = {
test-auto = { test-auto = {
"roles/secretmanager.secretAccessor" = ["group:auto-readers@example.com"] "roles/secretmanager.secretAccessor" = ["group:auto-readers@example.com"]
} }
@ -80,8 +76,7 @@ module "secret-manager" {
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---: |:---:|:---:| |---|---|:---: |:---:|:---:|
| project_id | Project id where the keyring will be created. | <code title="">string</code> | ✓ | | | project_id | Project id where the keyring will be created. | <code title="">string</code> | ✓ | |
| *iam_members* | IAM members keyed by secret name and role. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> | | *iam* | IAM bindings in {SECRET => {ROLE => [MEMBERS]}} format. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> |
| *iam_roles* | IAM roles keyed by secret name. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *labels* | Optional labels for each secret. | <code title="map&#40;map&#40;string&#41;&#41;">map(map(string))</code> | | <code title="">{}</code> | | *labels* | Optional labels for each secret. | <code title="map&#40;map&#40;string&#41;&#41;">map(map(string))</code> | | <code title="">{}</code> |
| *secrets* | Map of secrets to manage and their locations. If locations is null, automatic management will be set. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *secrets* | Map of secrets to manage and their locations. If locations is null, automatic management will be set. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *versions* | Optional versions to manage for each secret. Version names are only used internally to track individual versions. | <code title="map&#40;map&#40;object&#40;&#123;&#10;enabled &#61; bool&#10;data &#61; string&#10;&#125;&#41;&#41;&#41;">map(map(object({...})))</code> | | <code title="">{}</code> | | *versions* | Optional versions to manage for each secret. Version names are only used internally to track individual versions. | <code title="map&#40;map&#40;object&#40;&#123;&#10;enabled &#61; bool&#10;data &#61; string&#10;&#125;&#41;&#41;&#41;">map(map(object({...})))</code> | | <code title="">{}</code> |

View File

@ -16,13 +16,15 @@
locals { locals {
# distinct is needed to make the expanding function argument work # distinct is needed to make the expanding function argument work
iam_pairs = flatten([ iam = flatten([
for name, roles in var.iam_roles : for secret, roles in var.iam : [
[for role in roles : { name = name, role = role }] for role, members in roles : {
]) secret = secret
iam_keypairs = { role = role
for pair in local.iam_pairs : "${pair.name}-${pair.role}" => pair members = members
} }
]
])
version_pairs = flatten([ version_pairs = flatten([
for secret, versions in var.versions : [ for secret, versions in var.versions : [
for name, attrs in versions : merge(attrs, { name = name, secret = secret }) for name, attrs in versions : merge(attrs, { name = name, secret = secret })
@ -74,10 +76,10 @@ resource "google_secret_manager_secret_version" "default" {
resource "google_secret_manager_secret_iam_binding" "default" { resource "google_secret_manager_secret_iam_binding" "default" {
provider = google-beta provider = google-beta
for_each = local.iam_keypairs for_each = {
role = each.value.role for binding in local.iam : "${binding.secret}.${binding.role}" => binding
secret_id = google_secret_manager_secret.default[each.value.name].id }
members = lookup( role = each.value.role
lookup(var.iam_members, each.value.name, {}), each.value.role, [] secret_id = google_secret_manager_secret.default[each.value.secret].id
) members = each.value.members
} }

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2018 Google LLC * Copyright 2020 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,18 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
variable "iam_members" { variable "iam" {
description = "IAM members keyed by secret name and role." description = "IAM bindings in {SECRET => {ROLE => [MEMBERS]}} format."
type = map(map(list(string))) type = map(map(list(string)))
default = {} default = {}
} }
variable "iam_roles" {
description = "IAM roles keyed by secret name."
type = map(list(string))
default = {}
}
variable "labels" { variable "labels" {
description = "Optional labels for each secret." description = "Optional labels for each secret."
type = map(map(string)) type = map(map(string))

View File

@ -15,14 +15,11 @@ module "service-directory" {
project_id = "my-project" project_id = "my-project"
location = "europe-west1" location = "europe-west1"
name = "sd-1" name = "sd-1"
iam_members = { iam = {
"roles/servicedirectory.editor" = [ "roles/servicedirectory.editor" = [
"serviceAccount:namespace-editor@example.com" "serviceAccount:namespace-editor@example.com"
] ]
} }
iam_roles = [
"roles/servicedirectory.editor"
]
} }
``` ```
@ -40,16 +37,13 @@ module "service-directory" {
metadata = null metadata = null
} }
} }
service_iam_members = { service_iam = {
one = { one = {
"roles/servicedirectory.editor" = [ "roles/servicedirectory.editor" = [
"serviceAccount:service-editor.example.com" "serviceAccount:service-editor.example.com"
] ]
} }
} }
service_iam_roles = {
one = ["roles/servicedirectory.editor"]
}
endpoint_config = { endpoint_config = {
"one/first" = { address = "127.0.0.1", port = 80, metadata = {} } "one/first" = { address = "127.0.0.1", port = 80, metadata = {} }
"one/second" = { address = "127.0.0.2", port = 80, metadata = {} } "one/second" = { address = "127.0.0.2", port = 80, metadata = {} }
@ -67,14 +61,11 @@ module "service-directory" {
project_id = "my-project" project_id = "my-project"
location = "europe-west1" location = "europe-west1"
name = "apps" name = "apps"
iam_members = { iam = {
"roles/servicedirectory.editor" = [ "roles/servicedirectory.editor" = [
"serviceAccount:namespace-editor@example.com" "serviceAccount:namespace-editor@example.com"
] ]
} }
iam_roles = [
"roles/servicedirectory.editor"
]
services = { services = {
app1 = { endpoints = ["one"], metadata = null } app1 = { endpoints = ["one"], metadata = null }
} }
@ -104,11 +95,9 @@ module "dns-sd" {
| name | Namespace name. | <code title="">string</code> | ✓ | | | name | Namespace name. | <code title="">string</code> | ✓ | |
| project_id | Project used for resources. | <code title="">string</code> | ✓ | | | project_id | Project used for resources. | <code title="">string</code> | ✓ | |
| *endpoint_config* | Map of endpoint attributes, keys are in service/endpoint format. | <code title="map&#40;object&#40;&#123;&#10;address &#61; string&#10;port &#61; number&#10;metadata &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *endpoint_config* | Map of endpoint attributes, keys are in service/endpoint format. | <code title="map&#40;object&#40;&#123;&#10;address &#61; string&#10;port &#61; number&#10;metadata &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
| *iam_members* | IAM members for each namespace role. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam* | IAM bindings for namespace, in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | IAM roles for the namespace. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *labels* | Labels. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> | | *labels* | Labels. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> |
| *service_iam_members* | IAM members for each service and role. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> | | *service_iam* | IAM bindings for services, in {SERVICE => {ROLE => [MEMBERS]}} format. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> |
| *service_iam_roles* | IAM roles for each service. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *services* | Service configuration, using service names as keys. | <code title="map&#40;object&#40;&#123;&#10;endpoints &#61; list&#40;string&#41;&#10;metadata &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> | | *services* | Service configuration, using service names as keys. | <code title="map&#40;object&#40;&#123;&#10;endpoints &#61; list&#40;string&#41;&#10;metadata &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
## Outputs ## Outputs

View File

@ -23,17 +23,14 @@ locals {
endpoints = { endpoints = {
for ep in local.endpoint_list : "${ep.service}/${ep.endpoint}" => ep for ep in local.endpoint_list : "${ep.service}/${ep.endpoint}" => ep
} }
iam_pairs = var.service_iam_roles == null ? [] : flatten([ iam_pairs = var.service_iam == null ? [] : flatten([
for name, roles in var.service_iam_roles : for name, bindings in var.service_iam :
[for role in roles : { name = name, role = role }] [for role in keys(bindings) : { name = name, role = role }]
]) ])
iam_keypairs = { iam_keypairs = {
for pair in local.iam_pairs : for pair in local.iam_pairs :
"${pair.name}-${pair.role}" => pair "${pair.name}-${pair.role}" => pair
} }
iam_members = (
var.service_iam_members == null ? {} : var.service_iam_members
)
} }
resource "google_service_directory_namespace" "default" { resource "google_service_directory_namespace" "default" {
@ -46,10 +43,10 @@ resource "google_service_directory_namespace" "default" {
resource "google_service_directory_namespace_iam_binding" "default" { resource "google_service_directory_namespace_iam_binding" "default" {
provider = google-beta provider = google-beta
for_each = toset(var.iam_roles) for_each = var.iam
name = google_service_directory_namespace.default.name name = google_service_directory_namespace.default.name
role = each.value role = each.key
members = lookup(var.iam_members, each.value, []) members = each.value
} }
resource "google_service_directory_service" "default" { resource "google_service_directory_service" "default" {
@ -66,7 +63,7 @@ resource "google_service_directory_service_iam_binding" "default" {
name = google_service_directory_service.default[each.value.name].name name = google_service_directory_service.default[each.value.name].name
role = each.value.role role = each.value.role
members = lookup( members = lookup(
lookup(local.iam_members, each.value.name, {}), each.value.role, [] lookup(var.service_iam, each.value.name, {}), each.value.role, []
) )
} }

View File

@ -25,18 +25,12 @@ variable "endpoint_config" {
default = {} default = {}
} }
variable "iam_members" { variable "iam" {
description = "IAM members for each namespace role." description = "IAM bindings for namespace, in {ROLE => [MEMBERS]} format."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_roles" {
description = "IAM roles for the namespace."
type = list(string)
default = []
}
variable "labels" { variable "labels" {
description = "Labels." description = "Labels."
type = map(string) type = map(string)
@ -58,18 +52,12 @@ variable "project_id" {
type = string type = string
} }
variable "service_iam_members" { variable "service_iam" {
description = "IAM members for each service and role." description = "IAM bindings for services, in {SERVICE => {ROLE => [MEMBERS]}} format."
type = map(map(list(string))) type = map(map(list(string)))
default = {} default = {}
} }
variable "service_iam_roles" {
description = "IAM roles for each service."
type = map(list(string))
default = {}
}
variable "services" { variable "services" {
description = "Service configuration, using service names as keys." description = "Service configuration, using service names as keys."
type = map(object({ type = map(object({

View File

@ -12,8 +12,7 @@ module "repo" {
source e = "./modules/source-repository" source e = "./modules/source-repository"
project_id = "my-project" project_id = "my-project"
name = "my-repo" name = "my-repo"
iam_roles = ["roles/source.reader"] iam = {
iam_members = {
"roles/source.reader" = ["user:foo@example.com"] "roles/source.reader" = ["user:foo@example.com"]
} }
} }
@ -24,10 +23,9 @@ module "repo" {
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---: |:---:|:---:| |---|---|:---: |:---:|:---:|
| name | Repository topic name. | <code title="">string</code> | ✓ | | | name | Repository name. | <code title="">string</code> | ✓ | |
| project_id | Project used for resources. | <code title="">string</code> | ✓ | | | project_id | Project used for resources. | <code title="">string</code> | ✓ | |
| *iam_members* | IAM members for each topic role. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> | | *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | IAM roles for topic. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
## Outputs ## Outputs

View File

@ -20,11 +20,11 @@ resource "google_sourcerepo_repository" "default" {
} }
resource "google_sourcerepo_repository_iam_binding" "default" { resource "google_sourcerepo_repository_iam_binding" "default" {
for_each = toset(var.iam_roles) for_each = var.iam
project = var.project_id project = var.project_id
repository = google_sourcerepo_repository.default.name repository = google_sourcerepo_repository.default.name
role = each.value role = each.key
members = lookup(var.iam_members, each.value, []) members = each.value
depends_on = [ depends_on = [
google_sourcerepo_repository.default google_sourcerepo_repository.default

View File

@ -19,19 +19,13 @@ variable "project_id" {
type = string type = string
} }
variable "iam_members" { variable "iam" {
description = "IAM members for each topic role." description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(list(string)) type = map(list(string))
default = {} default = {}
} }
variable "iam_roles" {
description = "IAM roles for topic."
type = list(string)
default = []
}
variable "name" { variable "name" {
description = "Repository topic name." description = "Repository name."
type = string type = string
} }

View File

@ -136,7 +136,7 @@ module "hub-to-spoke-2-peering" {
peer_network = module.vpc-spoke-2.self_link peer_network = module.vpc-spoke-2.self_link
export_local_custom_routes = true export_local_custom_routes = true
export_peer_custom_routes = false export_peer_custom_routes = false
module_depends_on = [module.hub-to-spoke-1-peering.complete] depends_on = [module.hub-to-spoke-1-peering]
} }
################################################################################ ################################################################################
@ -180,9 +180,9 @@ module "vm-spoke-2" {
} }
module "service-account-gce" { module "service-account-gce" {
source = "../../modules/iam-service-accounts" source = "../../modules/iam-service-account"
project_id = var.project_id project_id = var.project_id
names = ["gce-test"] name = "gce-test"
iam_project_roles = { iam_project_roles = {
(var.project_id) = [ (var.project_id) = [
"roles/container.developer", "roles/container.developer",
@ -232,9 +232,9 @@ module "cluster-1-nodepool-1" {
# project level, with no risk of conflicts with pre-existing roles # project level, with no risk of conflicts with pre-existing roles
module "service-account-gke-node" { module "service-account-gke-node" {
source = "../../modules/iam-service-accounts" source = "../../modules/iam-service-account"
project_id = var.project_id project_id = var.project_id
names = ["gke-node"] name = "gke-node"
iam_project_roles = { iam_project_roles = {
(var.project_id) = [ (var.project_id) = [
"roles/logging.logWriter", "roles/monitoring.metricWriter", "roles/logging.logWriter", "roles/monitoring.metricWriter",

View File

@ -37,9 +37,9 @@ module "project" {
} }
module "service-accounts" { module "service-accounts" {
source = "../../modules/iam-service-accounts" source = "../../modules/iam-service-account"
project_id = module.project.project_id project_id = module.project.project_id
names = ["${local.prefix}gce-vm"] name = "${local.prefix}gce-vm"
iam_project_roles = { iam_project_roles = {
(var.project_id) = [ (var.project_id) = [
"roles/logging.logWriter", "roles/logging.logWriter",

Some files were not shown because too many files have changed in this diff Show More