From 2e7876b4c764936a012c527949dd0345b7fa9785 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Tue, 20 Oct 2020 15:41:03 +0200 Subject: [PATCH 01/53] Update folders module to Terraform 0.13 With this commit the folders module (now called simply 'folder') only creates a single google_folder resource. Support for creating multiple folders is no longer needed since Terraform 0.13 added for_each support to modules. --- foundations/environments/main.tf | 17 ++-- foundations/environments/outputs.tf | 2 +- foundations/environments/variables.tf | 2 +- modules/{folders => folder}/README.md | 37 ++++----- modules/{folders => folder}/main.tf | 71 ++++------------- modules/folder/outputs.tf | 35 +++++++++ modules/{folders => folder}/variables.tf | 21 ++--- modules/{folders => folder}/versions.tf | 2 +- modules/folders/outputs.tf | 78 ------------------- tests/foundations/environments/test_plan.py | 27 +++---- tests/modules/folders/fixture/main.tf | 4 +- tests/modules/folders/fixture/variables.tf | 8 +- tests/modules/folders/test_plan.py | 53 +++++++++---- .../modules/folders/test_plan_org_policies.py | 47 +++++------ 14 files changed, 163 insertions(+), 241 deletions(-) rename modules/{folders => folder}/README.md (53%) rename modules/{folders => folder}/main.tf (51%) create mode 100644 modules/folder/outputs.tf rename modules/{folders => folder}/variables.tf (74%) rename modules/{folders => folder}/versions.tf (94%) delete mode 100644 modules/folders/outputs.tf diff --git a/foundations/environments/main.tf b/foundations/environments/main.tf index 701bca93..34ad8d08 100644 --- a/foundations/environments/main.tf +++ b/foundations/environments/main.tf @@ -85,17 +85,14 @@ module "tf-gcs-environments" { ############################################################################### module "environment-folders" { - source = "../../modules/folders" - parent = var.root_node - names = var.environments - iam_roles = { - for name in var.environments : (name) => local.folder_roles - } + source = "../../modules/folder" + for_each = var.environments + parent = var.root_node + name = each.value + iam_roles = local.folder_roles iam_members = { - for name in var.environments : (name) => { - for role in local.folder_roles : - (role) => [module.tf-service-accounts.iam_emails[name]] - } + for role in local.folder_roles : + (role) => [module.tf-service-accounts.iam_emails[each.value]] } } diff --git a/foundations/environments/outputs.tf b/foundations/environments/outputs.tf index 495de1f7..fee9506f 100644 --- a/foundations/environments/outputs.tf +++ b/foundations/environments/outputs.tf @@ -24,7 +24,7 @@ output "bootstrap_tf_gcs_bucket" { output "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" { diff --git a/foundations/environments/variables.tf b/foundations/environments/variables.tf index 436d8557..7fa2e500 100644 --- a/foundations/environments/variables.tf +++ b/foundations/environments/variables.tf @@ -29,7 +29,7 @@ variable "billing_account_id" { variable "environments" { description = "Environment short names." - type = list(string) + type = set(string) } variable "gcs_location" { diff --git a/modules/folders/README.md b/modules/folder/README.md similarity index 53% rename from modules/folders/README.md rename to modules/folder/README.md index 7efb4b68..c9d84cc0 100644 --- a/modules/folders/README.md +++ b/modules/folder/README.md @@ -1,6 +1,6 @@ # 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 @@ -8,17 +8,13 @@ This module allow creation and management of sets of folders sharing a common pa ```hcl module "folder" { - source = "./modules/folders" + source = "./modules/folder" parent = "organizations/1234567890" - names = ["Folder one", "Folder two"] + name = "Folder name" iam_members = { - "Folder one" = { - "roles/owner" = ["group:users@example.com"] - } - } - iam_roles = { - "Folder one" = ["roles/owner"] + "roles/owner" = ["group:users@example.com"] } + iam_roles = ["roles/owner"] } ``` @@ -26,9 +22,9 @@ module "folder" { ```hcl module "folder" { - source = "./modules/folders" + source = "./modules/folder" parent = "organizations/1234567890" - names = ["Folder one", "Folder two"] + name = "Folder name" policy_boolean = { "constraints/compute.disableGuestAttributesAccess" = true "constraints/compute.skipDefaultNetworkCreation" = true @@ -49,10 +45,10 @@ module "folder" { | name | description | type | required | default | |---|---|:---: |:---:|:---:| -| parent | Parent in folders/folder_id or organizations/org_id format. | string | ✓ | | -| *iam_members* | List of IAM members keyed by folder name and role. | map(map(list(string))) | | null | -| *iam_roles* | List of IAM roles keyed by folder name. | map(list(string)) | | null | -| *names* | Folder names. | list(string) | | [] | +| name | Folder name. | string | ✓ | | +| parent | Parent in folders/folder_id or organizations/org_id format. | string | ✓ | | +| *iam_members* | List of IAM members keyed by role. | map(set(string)) | | null | +| *iam_roles* | List of IAM roles. | set(string) | | null | | *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | | *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. | map(object({...})) | | {} | @@ -60,12 +56,7 @@ module "folder" { | name | description | sensitive | |---|---|:---:| -| folder | Folder resource (for single use). | | -| folders | Folder resources. | | -| id | Folder id (for single use). | | -| ids | Folder ids. | | -| ids_list | List of folder ids. | | -| name | Folder name (for single use). | | -| names | Folder names. | | -| names_list | List of folder names. | | +| folder | Folder resource. | | +| id | Folder id. | | +| name | Folder name. | | diff --git a/modules/folders/main.tf b/modules/folder/main.tf similarity index 51% rename from modules/folders/main.tf rename to modules/folder/main.tf index e144726b..ce183910 100644 --- a/modules/folders/main.tf +++ b/modules/folder/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. @@ -14,63 +14,26 @@ * 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" { - for_each = toset(var.names) - display_name = each.value +resource "google_folder" "folder" { + display_name = var.name parent = var.parent } resource "google_folder_iam_binding" "authoritative" { - for_each = local.iam_keypairs - folder = google_folder.folders[each.value.name].name - role = each.value.role - members = lookup( - lookup(local.iam_members, each.value.name, {}), each.value.role, [] - ) + for_each = var.iam_roles + folder = google_folder.folder.name + role = each.key + members = lookup(var.iam_members, each.key, []) } resource "google_folder_organization_policy" "boolean" { - for_each = local.policy_boolean_pairs - folder = google_folder.folders[each.value.folder].id - constraint = each.value.policy + for_each = var.policy_boolean + folder = google_folder.folder.name + constraint = each.key dynamic boolean_policy { - for_each = each.value.policy_data == null ? [] : [each.value.policy_data] + for_each = each.value == null ? [] : [each.value] iterator = policy content { enforced = policy.value @@ -78,7 +41,7 @@ resource "google_folder_organization_policy" "boolean" { } dynamic restore_policy { - for_each = each.value.policy_data == null ? [""] : [] + for_each = each.value == null ? [""] : [] content { default = true } @@ -86,12 +49,12 @@ resource "google_folder_organization_policy" "boolean" { } resource "google_folder_organization_policy" "list" { - for_each = local.policy_list_pairs - folder = google_folder.folders[each.value.folder].id - constraint = each.value.policy + for_each = var.policy_list + folder = google_folder.folder.name + constraint = each.key 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 content { inherit_from_parent = policy.value.inherit_from_parent @@ -130,7 +93,7 @@ resource "google_folder_organization_policy" "list" { } dynamic restore_policy { - for_each = each.value.policy_data.status == null ? [true] : [] + for_each = each.value.status == null ? [true] : [] content { default = true } diff --git a/modules/folder/outputs.tf b/modules/folder/outputs.tf new file mode 100644 index 00000000..4be12eb1 --- /dev/null +++ b/modules/folder/outputs.tf @@ -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 +} diff --git a/modules/folders/variables.tf b/modules/folder/variables.tf similarity index 74% rename from modules/folders/variables.tf rename to modules/folder/variables.tf index 3dbf502b..47c2843b 100644 --- a/modules/folders/variables.tf +++ b/modules/folder/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. @@ -15,26 +15,29 @@ */ variable "iam_members" { - description = "List of IAM members keyed by folder name and role." - type = map(map(list(string))) + description = "List of IAM members keyed by role." + type = map(set(string)) default = null } variable "iam_roles" { - description = "List of IAM roles keyed by folder name." - type = map(list(string)) + description = "List of IAM roles." + type = set(string) default = null } -variable "names" { - description = "Folder names." - type = list(string) - default = [] +variable "name" { + description = "Folder name." + type = string } variable "parent" { description = "Parent in folders/folder_id or organizations/org_id format." 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" { diff --git a/modules/folders/versions.tf b/modules/folder/versions.tf similarity index 94% rename from modules/folders/versions.tf rename to modules/folder/versions.tf index bc4c2a9d..2a088552 100644 --- a/modules/folders/versions.tf +++ b/modules/folder/versions.tf @@ -15,5 +15,5 @@ */ terraform { - required_version = ">= 0.12.6" + required_version = ">= 0.13.0" } diff --git a/modules/folders/outputs.tf b/modules/folders/outputs.tf deleted file mode 100644 index cf7b54ca..00000000 --- a/modules/folders/outputs.tf +++ /dev/null @@ -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] -} diff --git a/tests/foundations/environments/test_plan.py b/tests/foundations/environments/test_plan.py index 96fd124e..5bdc18b1 100644 --- a/tests/foundations/environments/test_plan.py +++ b/tests/foundations/environments/test_plan.py @@ -23,16 +23,16 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') def test_folder_roles(plan_runner): "Test folder roles." _, modules = plan_runner(FIXTURES_DIR, is_module=False) - resources = modules['module.test.module.environment-folders'] - folders = [r for r in resources if r['type'] == 'google_folder'] - assert len(folders) == 2 - assert set(r['values']['display_name'] - for r in folders) == set(['prod', 'test']) - bindings = [r['index'].split('-') - for r in resources if r['type'] == 'google_folder_iam_binding'] - assert len(bindings) == 10 - assert set(b[0] for b in bindings) == set(['prod', 'test']) - assert len(set(b[1] for b in bindings)) == 5 + for env in ["test", "prod"]: + resources = modules[f'module.test.module.environment-folders["{env}"]'] + folders = [r for r in resources if r['type'] == 'google_folder'] + assert len(folders) == 1 + folder = folders[0] + assert folder['values']['display_name'] == env + + bindings = [r['index'] + for r in resources if r['type'] == 'google_folder_iam_binding'] + assert len(bindings) == 5 def test_org_roles(plan_runner): @@ -42,12 +42,13 @@ def test_org_roles(plan_runner): 'iam_xpn_config': '{grant = true, target_org = true}' } _, modules = plan_runner(FIXTURES_DIR, is_module=False, **vars) - resources = modules['module.test.module.environment-folders'] - folder_bindings = [r['index'].split('-') + resources = (modules['module.test.module.environment-folders["test"]'] + + modules['module.test.module.environment-folders["prod"]']) + folder_bindings = [r['index'] for r in resources if r['type'] == 'google_folder_iam_binding'] assert len(folder_bindings) == 8 resources = modules['module.test.module.tf-service-accounts'] org_bindings = [r['index'].split('-') for r in resources if r['type'] == 'google_organization_iam_member'] assert len(org_bindings) == 4 - assert set(b[0] for b in org_bindings) == set(['prod', 'test']) + assert {b[0] for b in org_bindings} == {'prod', 'test'} diff --git a/tests/modules/folders/fixture/main.tf b/tests/modules/folders/fixture/main.tf index 71d13cd2..5eef7c12 100644 --- a/tests/modules/folders/fixture/main.tf +++ b/tests/modules/folders/fixture/main.tf @@ -15,9 +15,9 @@ */ module "test" { - source = "../../../../modules/folders" + source = "../../../../modules/folder" parent = "organizations/12345678" - names = ["folder-a", "folder-b"] + name = "folder-a" iam_members = var.iam_members iam_roles = var.iam_roles policy_boolean = var.policy_boolean diff --git a/tests/modules/folders/fixture/variables.tf b/tests/modules/folders/fixture/variables.tf index d8a71531..9b91f610 100644 --- a/tests/modules/folders/fixture/variables.tf +++ b/tests/modules/folders/fixture/variables.tf @@ -15,13 +15,13 @@ */ variable "iam_members" { - type = map(map(list(string))) - default = null + type = map(list(string)) + default = {} } variable "iam_roles" { - type = map(list(string)) - default = null + type = list(string) + default = [] } variable "policy_boolean" { diff --git a/tests/modules/folders/test_plan.py b/tests/modules/folders/test_plan.py index fcb8fa64..d8d100de 100644 --- a/tests/modules/folders/test_plan.py +++ b/tests/modules/folders/test_plan.py @@ -23,27 +23,48 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') def test_folder(plan_runner): "Test folder resources." _, resources = plan_runner(FIXTURES_DIR) - assert len(resources) == 2 - assert set(r['type'] for r in resources) == set(['google_folder']) - assert set(r['values']['display_name'] for r in resources) == set([ - 'folder-a', 'folder-b' - ]) - assert set(r['values']['parent'] for r in resources) == set([ - 'organizations/12345678' - ]) + assert len(resources) == 1 + resource = resources[0] + assert resource['type'] == 'google_folder' + assert resource['values']['display_name'] == 'folder-a' + assert resource['values']['parent'] == 'organizations/12345678' def test_iam_roles_only(plan_runner): "Test folder resources with only iam roles passed." - _, resources = plan_runner( - FIXTURES_DIR, iam_roles='{folder-a = [ "roles/owner"]}') - assert len(resources) == 3 + _, resources = plan_runner(FIXTURES_DIR, + iam_roles='["roles/owner"]') + assert len(resources) == 2 def test_iam(plan_runner): "Test folder resources with iam roles and members." - iam_roles = '{folder-a = ["roles/owner"], folder-b = ["roles/viewer"]}' - iam_members = '{folder-a = { "roles/owner" = ["user:a@b.com"] }}' - _, resources = plan_runner( - FIXTURES_DIR, iam_roles=iam_roles, iam_members=iam_members) - assert len(resources) == 4 + iam_roles = '["roles/owner"]' + iam_members = '{"roles/owner" = ["user:a@b.com"] }' + _, resources = plan_runner(FIXTURES_DIR, + iam_roles=iam_roles, + iam_members=iam_members) + assert len(resources) == 2 + +def test_iam_multiple_members(plan_runner): + "Test folder resources with multiple iam members." + iam_roles = '["roles/owner"]' + iam_members = '{"roles/owner" = ["user:a@b.com", "user:c@d.com"] }' + _, resources = plan_runner(FIXTURES_DIR, + iam_roles=iam_roles, + iam_members=iam_members) + assert len(resources) == 2 + +def test_iam_multiple_roles(plan_runner): + "Test folder resources with multiple iam roles." + iam_roles = '["roles/owner", "roles/viewer"]' + iam_members = ( + '{ ' + '"roles/owner" = ["user:a@b.com"], ' + '"roles/viewer" = ["user:c@d.com"] ' + '} ' + ) + _, resources = plan_runner(FIXTURES_DIR, + iam_roles=iam_roles, + iam_members=iam_members) + assert len(resources) == 3 diff --git a/tests/modules/folders/test_plan_org_policies.py b/tests/modules/folders/test_plan_org_policies.py index dde2b30c..09d83cf5 100644 --- a/tests/modules/folders/test_plan_org_policies.py +++ b/tests/modules/folders/test_plan_org_policies.py @@ -24,16 +24,14 @@ def test_policy_boolean(plan_runner): "Test boolean folder policy." policy_boolean = '{policy-a = true, policy-b = false, policy-c = null}' _, resources = plan_runner(FIXTURES_DIR, policy_boolean=policy_boolean) - assert len(resources) == 8 + + assert len(resources) == 4 resources = [r for r in resources if r['type'] == 'google_folder_organization_policy'] assert sorted([r['index'] for r in resources]) == [ - 'folder-a-policy-a', - 'folder-a-policy-b', - 'folder-a-policy-c', - 'folder-b-policy-a', - 'folder-b-policy-b', - 'folder-b-policy-c' + 'policy-a', + 'policy-b', + 'policy-c', ] policy_values = [] for resource in resources: @@ -42,12 +40,9 @@ def test_policy_boolean(plan_runner): if value: policy_values.append((resource['index'], policy,) + value[0].popitem()) assert sorted(policy_values) == [ - ('folder-a-policy-a', 'boolean_policy', 'enforced', True), - ('folder-a-policy-b', 'boolean_policy', 'enforced', False), - ('folder-a-policy-c', 'restore_policy', 'default', True), - ('folder-b-policy-a', 'boolean_policy', 'enforced', True), - ('folder-b-policy-b', 'boolean_policy', 'enforced', False), - ('folder-b-policy-c', 'restore_policy', 'default', True) + ('policy-a', 'boolean_policy', 'enforced', True), + ('policy-b', 'boolean_policy', 'enforced', False), + ('policy-c', 'restore_policy', 'default', True), ] @@ -61,26 +56,20 @@ def test_policy_list(plan_runner): '}' ) _, resources = plan_runner(FIXTURES_DIR, policy_list=policy_list) - assert len(resources) == 8 + assert len(resources) == 4 resources = [r for r in resources if r['type'] == 'google_folder_organization_policy'] assert sorted([r['index'] for r in resources]) == [ - 'folder-a-policy-a', - 'folder-a-policy-b', - 'folder-a-policy-c', - 'folder-b-policy-a', - 'folder-b-policy-b', - 'folder-b-policy-c' + 'policy-a', + 'policy-b', + 'policy-c', ] values = [r['values'] for r in resources] assert [r['constraint'] for r in values] == [ - 'policy-a', 'policy-b', 'policy-c', 'policy-a', 'policy-b', 'policy-c' + 'policy-a', 'policy-b', 'policy-c' ] - for i in (0, 3): - assert values[i]['list_policy'][0]['allow'] == [ - {'all': True, 'values': None}] - for i in (1, 4): - assert values[i]['list_policy'][0]['deny'] == [ - {'all': False, 'values': ["bar"]}] - for i in (2, 5): - assert values[i]['restore_policy'] == [{'default': True}] + assert values[0]['list_policy'][0]['allow'] == [ + {'all': True, 'values': None}] + assert values[1]['list_policy'][0]['deny'] == [ + {'all': False, 'values': ["bar"]}] + assert values[2]['restore_policy'] == [{'default': True}] From 13ed799a8b8353ae64a675ab1c5079b649f1c742 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Tue, 20 Oct 2020 22:36:03 +0200 Subject: [PATCH 02/53] Update service account module to Terraform 0.13 --- modules/iam-service-account/README.md | 54 +++++++ modules/iam-service-account/main.tf | 125 ++++++++++++++++ modules/iam-service-account/outputs.tf | 36 +++++ .../variables.tf | 45 +++--- .../versions.tf | 0 modules/iam-service-accounts/README.md | 59 -------- modules/iam-service-accounts/main.tf | 136 ------------------ modules/iam-service-accounts/outputs.tf | 75 ---------- 8 files changed, 240 insertions(+), 290 deletions(-) create mode 100644 modules/iam-service-account/README.md create mode 100644 modules/iam-service-account/main.tf create mode 100644 modules/iam-service-account/outputs.tf rename modules/{iam-service-accounts => iam-service-account}/variables.tf (52%) rename modules/{iam-service-accounts => iam-service-account}/versions.tf (100%) delete mode 100644 modules/iam-service-accounts/README.md delete mode 100644 modules/iam-service-accounts/main.tf delete mode 100644 modules/iam-service-accounts/outputs.tf diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md new file mode 100644 index 00000000..f55c0b51 --- /dev/null +++ b/modules/iam-service-account/README.md @@ -0,0 +1,54 @@ +# 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-accounts" + project_id = "myproject" + name = "vm-default" + generate_key = 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", + ] + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| name | Name of the service account to create. | string | ✓ | | +| project_id | Project id where service account will be created. | string | ✓ | | +| *display_name* | Display name of the service account to create. | string | | Terraform-managed. | +| *generate_key* | Generate a key for service account. | bool | | false | +| *iam_billing_roles* | Project roles granted to the service account, by billing account id. | map(set(string)) | | {} | +| *iam_folder_roles* | Project roles granted to the service account, by folder id. | map(set(string)) | | {} | +| *iam_members* | Map of members which are granted authoritative roles on the service account, keyed by role. | map(set(string)) | | {} | +| *iam_organization_roles* | Project roles granted to the service account, by organization id. | map(set(string)) | | {} | +| *iam_project_roles* | Project roles granted to the service account, by project id. | map(set(string)) | | {} | +| *iam_roles* | Authoritative roles granted on the service account. | set(string) | | [] | +| *iam_storage_roles* | Storage roles granted to the service account, by bucket name. | map(set(string)) | | {} | +| *prefix* | Prefix applied to service account names. | string | | null | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| email | Service account email. | | +| iam_email | IAM-format service account email. | | +| key | Service account key. | ✓ | +| service_account | Service account resource. | | + diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf new file mode 100644 index 00000000..b749e172 --- /dev/null +++ b/modules/iam-service-account/main.tf @@ -0,0 +1,125 @@ +/** + * 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_roles + #for_each = toset(keys(var.iam_members)) + service_account_id = google_service_account.service_account.name + role = each.key + members = lookup(var.iam_members, each.key, []) +} + +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 +} diff --git a/modules/iam-service-account/outputs.tf b/modules/iam-service-account/outputs.tf new file mode 100644 index 00000000..86de600b --- /dev/null +++ b/modules/iam-service-account/outputs.tf @@ -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 +} diff --git a/modules/iam-service-accounts/variables.tf b/modules/iam-service-account/variables.tf similarity index 52% rename from modules/iam-service-accounts/variables.tf rename to modules/iam-service-account/variables.tf index 1ffedff8..6d29cb80 100644 --- a/modules/iam-service-accounts/variables.tf +++ b/modules/iam-service-account/variables.tf @@ -14,58 +14,63 @@ * limitations under the License. */ -variable "generate_keys" { - description = "Generate keys for service accounts." +variable "generate_key" { + description = "Generate a key for service account." type = bool default = false } variable "iam_members" { - description = "Map of member lists which are granted authoritative roles on the service accounts, keyed by role." - type = map(list(string)) + description = "Map of members which are granted authoritative roles on the service account, keyed by role." + type = map(set(string)) default = {} } variable "iam_roles" { - description = "List of authoritative roles granted on the service accounts." - type = list(string) + description = "Authoritative roles granted on the service account." + type = set(string) default = [] } variable "iam_billing_roles" { - description = "Project roles granted to all service accounts, by billing account id." - type = map(list(string)) + description = "Project roles granted to the service account, by billing account id." + type = map(set(string)) default = {} } variable "iam_folder_roles" { - description = "Project roles granted to all service accounts, by folder id." - type = map(list(string)) + description = "Project roles granted to the service account, by folder id." + type = map(set(string)) default = {} } variable "iam_organization_roles" { - description = "Project roles granted to all service accounts, by organization id." - type = map(list(string)) + description = "Project roles granted to the service account, by organization id." + type = map(set(string)) default = {} } variable "iam_project_roles" { - description = "Project roles granted to all service accounts, by project id." - type = map(list(string)) + description = "Project roles granted to the service account, by project id." + type = map(set(string)) default = {} } variable "iam_storage_roles" { - description = "Storage roles granted to all service accounts, by bucket name." - type = map(list(string)) + description = "Storage roles granted to the service account, by bucket name." + type = map(set(string)) default = {} } -variable "names" { - description = "Names of the service accounts to create." - type = list(string) - default = [] +variable "name" { + description = "Name of the service account to create." + type = string +} + +variable "display_name" { + description = "Display name of the service account to create." + type = string + default = "Terraform-managed." } variable "prefix" { diff --git a/modules/iam-service-accounts/versions.tf b/modules/iam-service-account/versions.tf similarity index 100% rename from modules/iam-service-accounts/versions.tf rename to modules/iam-service-account/versions.tf diff --git a/modules/iam-service-accounts/README.md b/modules/iam-service-accounts/README.md deleted file mode 100644 index d348db72..00000000 --- a/modules/iam-service-accounts/README.md +++ /dev/null @@ -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", - ] - } -} -``` - - -## Variables - -| name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| project_id | Project id where service account will be created. | string | ✓ | | -| *generate_keys* | Generate keys for service accounts. | bool | | false | -| *iam_billing_roles* | Project roles granted to all service accounts, by billing account id. | map(list(string)) | | {} | -| *iam_folder_roles* | Project roles granted to all service accounts, by folder id. | map(list(string)) | | {} | -| *iam_members* | Map of member lists which are granted authoritative roles on the service accounts, keyed by role. | map(list(string)) | | {} | -| *iam_organization_roles* | Project roles granted to all service accounts, by organization id. | map(list(string)) | | {} | -| *iam_project_roles* | Project roles granted to all service accounts, by project id. | map(list(string)) | | {} | -| *iam_roles* | List of authoritative roles granted on the service accounts. | list(string) | | [] | -| *iam_storage_roles* | Storage roles granted to all service accounts, by bucket name. | map(list(string)) | | {} | -| *names* | Names of the service accounts to create. | list(string) | | [] | -| *prefix* | Prefix applied to service account names. | string | | null | - -## 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. | | - diff --git a/modules/iam-service-accounts/main.tf b/modules/iam-service-accounts/main.tf deleted file mode 100644 index 67ff8095..00000000 --- a/modules/iam-service-accounts/main.tf +++ /dev/null @@ -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 diff --git a/modules/iam-service-accounts/outputs.tf b/modules/iam-service-accounts/outputs.tf deleted file mode 100644 index 3673a71f..00000000 --- a/modules/iam-service-accounts/outputs.tf +++ /dev/null @@ -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 -} From d6242957028e76cf1d07652edc7645532a9d4337 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Tue, 20 Oct 2020 23:25:50 +0200 Subject: [PATCH 03/53] Fix service account tests --- .../asset-inventory-feed-remediation/main.tf | 4 +- .../main.tf | 4 +- .../gcs-to-bq-with-dataflow/main.tf | 14 ++--- foundations/environments/main.tf | 11 ++-- foundations/environments/outputs.tf | 4 +- networking/hub-and-spoke-peering/main.tf | 8 +-- networking/ilb-next-hop/main.tf | 4 +- networking/ilb-next-hop/vms.tf | 4 +- networking/onprem-google-access-dns/main.tf | 8 +-- networking/shared-vpc-gke/main.tf | 4 +- tests/foundations/environments/test_plan.py | 26 +++++---- .../__init__.py | 0 .../fixture/main.tf | 6 +- .../fixture/variables.tf | 2 +- .../modules/iam_service_account/test_plan.py | 56 +++++++++++++++++++ .../modules/iam_service_accounts/test_plan.py | 51 ----------------- 16 files changed, 108 insertions(+), 98 deletions(-) rename tests/modules/{iam_service_accounts => iam_service_account}/__init__.py (100%) rename tests/modules/{iam_service_accounts => iam_service_account}/fixture/main.tf (90%) rename tests/modules/{iam_service_accounts => iam_service_account}/fixture/variables.tf (97%) create mode 100644 tests/modules/iam_service_account/test_plan.py delete mode 100644 tests/modules/iam_service_accounts/test_plan.py diff --git a/cloud-operations/asset-inventory-feed-remediation/main.tf b/cloud-operations/asset-inventory-feed-remediation/main.tf index 0d6b29ef..da256576 100644 --- a/cloud-operations/asset-inventory-feed-remediation/main.tf +++ b/cloud-operations/asset-inventory-feed-remediation/main.tf @@ -75,9 +75,9 @@ module "pubsub" { } module "service-account" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = module.project.project_id - names = ["${var.name}-cf"] + name = "${var.name}-cf" # iam_project_roles = { (module.project.project_id) = [local.role_id] } } diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index f1f07aea..479afe71 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -35,9 +35,9 @@ module "project" { } module "service-account" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = module.project.project_id - names = ["${var.name}-cf"] + name = "${var.name}-cf" iam_project_roles = { (var.project_id) = ["roles/cloudasset.viewer"] } diff --git a/data-solutions/gcs-to-bq-with-dataflow/main.tf b/data-solutions/gcs-to-bq-with-dataflow/main.tf index 39a9ffd3..57ec8e2a 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/main.tf +++ b/data-solutions/gcs-to-bq-with-dataflow/main.tf @@ -57,9 +57,9 @@ module "project-kms" { ############################################################################### module "service-account-bq" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = module.project-service.project_id - names = ["bq-test"] + name = "bq-test" iam_project_roles = { (var.project_service_name) = [ "roles/logging.logWriter", @@ -70,9 +70,9 @@ module "service-account-bq" { } module "service-account-gce" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = module.project-service.project_id - names = ["gce-test"] + name = "gce-test" iam_project_roles = { (var.project_service_name) = [ "roles/logging.logWriter", @@ -86,9 +86,9 @@ module "service-account-gce" { } module "service-account-df" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = module.project-service.project_id - names = ["df-test"] + name = "df-test" iam_project_roles = { (var.project_service_name) = [ "roles/dataflow.worker", @@ -301,7 +301,7 @@ module "bigquery-dataset" { owner = { role = "OWNER", type = "user_by_email" } } access_identities = { - owner = module.service-account-bq.email + owner = module.service-account-bq.email } encryption_key = module.kms.keys.key-bq.self_link tables = { diff --git a/foundations/environments/main.tf b/foundations/environments/main.tf index 34ad8d08..3f839a3c 100644 --- a/foundations/environments/main.tf +++ b/foundations/environments/main.tf @@ -33,9 +33,10 @@ module "tf-project" { # per-environment service accounts module "tf-service-accounts" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = module.tf-project.project_id - names = var.environments + for_each = var.environments + name = each.value prefix = var.prefix iam_billing_roles = { (var.billing_account_id) = ( @@ -49,7 +50,7 @@ module "tf-service-accounts" { 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 @@ -75,7 +76,7 @@ module "tf-gcs-environments" { } iam_members = { for name in var.environments : (name) => { - "roles/storage.objectAdmin" = [module.tf-service-accounts.iam_emails[name]] + "roles/storage.objectAdmin" = [module.tf-service-accounts[name].iam_email] } } } @@ -92,7 +93,7 @@ module "environment-folders" { iam_roles = local.folder_roles iam_members = { for role in local.folder_roles : - (role) => [module.tf-service-accounts.iam_emails[each.value]] + (role) => [module.tf-service-accounts[each.value].iam_email] } } diff --git a/foundations/environments/outputs.tf b/foundations/environments/outputs.tf index fee9506f..c5315d50 100644 --- a/foundations/environments/outputs.tf +++ b/foundations/environments/outputs.tf @@ -35,12 +35,12 @@ output "environment_tf_gcs_buckets" { output "environment_service_account_keys" { description = "Service account keys used to run each environment Terraform modules." sensitive = true - value = module.tf-service-accounts.keys + value = { for env, sa in module.tf-service-accounts : env => sa.key } } output "environment_service_accounts" { 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" { diff --git a/networking/hub-and-spoke-peering/main.tf b/networking/hub-and-spoke-peering/main.tf index f77d49f6..6b39929a 100644 --- a/networking/hub-and-spoke-peering/main.tf +++ b/networking/hub-and-spoke-peering/main.tf @@ -180,9 +180,9 @@ module "vm-spoke-2" { } module "service-account-gce" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = var.project_id - names = ["gce-test"] + name = "gce-test" iam_project_roles = { (var.project_id) = [ "roles/container.developer", @@ -232,9 +232,9 @@ module "cluster-1-nodepool-1" { # project level, with no risk of conflicts with pre-existing roles module "service-account-gke-node" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = var.project_id - names = ["gke-node"] + name = "gke-node" iam_project_roles = { (var.project_id) = [ "roles/logging.logWriter", "roles/monitoring.metricWriter", diff --git a/networking/ilb-next-hop/main.tf b/networking/ilb-next-hop/main.tf index b5ca75d2..a630ec97 100644 --- a/networking/ilb-next-hop/main.tf +++ b/networking/ilb-next-hop/main.tf @@ -37,9 +37,9 @@ module "project" { } module "service-accounts" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = module.project.project_id - names = ["${local.prefix}gce-vm"] + name = "${local.prefix}gce-vm" iam_project_roles = { (var.project_id) = [ "roles/logging.logWriter", diff --git a/networking/ilb-next-hop/vms.tf b/networking/ilb-next-hop/vms.tf index d3fbc0f8..c80226aa 100644 --- a/networking/ilb-next-hop/vms.tf +++ b/networking/ilb-next-hop/vms.tf @@ -42,7 +42,7 @@ module "vm-left" { startup-script = local.vm_startup_script } service_account = try( - module.service-accounts.emails["${local.prefix}gce-vm"], null + module.service-accounts.email, null ) service_account_scopes = ["https://www.googleapis.com/auth/cloud-platform"] instance_count = 2 @@ -68,7 +68,7 @@ module "vm-right" { startup-script = local.vm_startup_script } service_account = try( - module.service-accounts.emails["${local.prefix}gce-vm"], null + module.service-accounts.email, null ) service_account_scopes = ["https://www.googleapis.com/auth/cloud-platform"] instance_count = 2 diff --git a/networking/onprem-google-access-dns/main.tf b/networking/onprem-google-access-dns/main.tf index 2d803673..2c24d72e 100644 --- a/networking/onprem-google-access-dns/main.tf +++ b/networking/onprem-google-access-dns/main.tf @@ -170,9 +170,9 @@ resource "google_dns_policy" "inbound" { ################################################################################ module "service-account-gce" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = var.project_id - names = ["gce-test"] + name = "gce-test" iam_project_roles = { (var.project_id) = [ "roles/logging.logWriter", @@ -222,9 +222,9 @@ module "config-onprem" { } module "service-account-onprem" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = var.project_id - names = ["gce-onprem"] + name = "gce-onprem" iam_project_roles = { (var.project_id) = [ "roles/compute.viewer", diff --git a/networking/shared-vpc-gke/main.tf b/networking/shared-vpc-gke/main.tf index 2e0296e2..ce46aba7 100644 --- a/networking/shared-vpc-gke/main.tf +++ b/networking/shared-vpc-gke/main.tf @@ -240,7 +240,7 @@ module "cluster-1-nodepool-1" { # project level, with no risk of conflicts with pre-existing roles module "service-account-gke-node" { - source = "../../modules/iam-service-accounts" + source = "../../modules/iam-service-account" project_id = module.project-svc-gke.project_id - names = ["gke-node"] + name = "gke-node" } diff --git a/tests/foundations/environments/test_plan.py b/tests/foundations/environments/test_plan.py index 5bdc18b1..a2c7c6a3 100644 --- a/tests/foundations/environments/test_plan.py +++ b/tests/foundations/environments/test_plan.py @@ -23,7 +23,7 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') def test_folder_roles(plan_runner): "Test folder roles." _, modules = plan_runner(FIXTURES_DIR, is_module=False) - for env in ["test", "prod"]: + for env in ['test', 'prod']: resources = modules[f'module.test.module.environment-folders["{env}"]'] folders = [r for r in resources if r['type'] == 'google_folder'] assert len(folders) == 1 @@ -42,13 +42,17 @@ def test_org_roles(plan_runner): 'iam_xpn_config': '{grant = true, target_org = true}' } _, modules = plan_runner(FIXTURES_DIR, is_module=False, **vars) - resources = (modules['module.test.module.environment-folders["test"]'] + - modules['module.test.module.environment-folders["prod"]']) - folder_bindings = [r['index'] - for r in resources if r['type'] == 'google_folder_iam_binding'] - assert len(folder_bindings) == 8 - resources = modules['module.test.module.tf-service-accounts'] - org_bindings = [r['index'].split('-') - for r in resources if r['type'] == 'google_organization_iam_member'] - assert len(org_bindings) == 4 - assert {b[0] for b in org_bindings} == {'prod', 'test'} + for env in ['test', 'prod']: + resources = modules[f'module.test.module.environment-folders["{env}"]'] + folder_bindings = [r['index'] + for r in resources if r['type'] == 'google_folder_iam_binding'] + assert len(folder_bindings) == 4 + + resources = modules[f'module.test.module.tf-service-accounts["{env}"]'] + org_bindings = [r for r in resources + if r['type'] == 'google_organization_iam_member'] + assert len(org_bindings) == 2 + assert {b['values']['role'] for b in org_bindings} == { + 'roles/resourcemanager.organizationViewer', + 'roles/compute.xpnAdmin' + } diff --git a/tests/modules/iam_service_accounts/__init__.py b/tests/modules/iam_service_account/__init__.py similarity index 100% rename from tests/modules/iam_service_accounts/__init__.py rename to tests/modules/iam_service_account/__init__.py diff --git a/tests/modules/iam_service_accounts/fixture/main.tf b/tests/modules/iam_service_account/fixture/main.tf similarity index 90% rename from tests/modules/iam_service_accounts/fixture/main.tf rename to tests/modules/iam_service_account/fixture/main.tf index 69188086..5f70b175 100644 --- a/tests/modules/iam_service_accounts/fixture/main.tf +++ b/tests/modules/iam_service_account/fixture/main.tf @@ -15,11 +15,11 @@ */ module "test" { - source = "../../../../modules/iam-service-accounts" + source = "../../../../modules/iam-service-account" project_id = var.project_id - names = ["sa-one", "sa-two", "sa-three"] + name = "sa-one" prefix = var.prefix - generate_keys = var.generate_keys + generate_key = var.generate_key iam_members = var.iam_members iam_roles = var.iam_roles iam_billing_roles = var.iam_billing_roles diff --git a/tests/modules/iam_service_accounts/fixture/variables.tf b/tests/modules/iam_service_account/fixture/variables.tf similarity index 97% rename from tests/modules/iam_service_accounts/fixture/variables.tf rename to tests/modules/iam_service_account/fixture/variables.tf index 0a784a94..0d5eba5b 100644 --- a/tests/modules/iam_service_accounts/fixture/variables.tf +++ b/tests/modules/iam_service_account/fixture/variables.tf @@ -14,7 +14,7 @@ * limitations under the License. */ -variable "generate_keys" { +variable "generate_key" { type = bool default = false } diff --git a/tests/modules/iam_service_account/test_plan.py b/tests/modules/iam_service_account/test_plan.py new file mode 100644 index 00000000..719bc574 --- /dev/null +++ b/tests/modules/iam_service_account/test_plan.py @@ -0,0 +1,56 @@ +# 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_resources(plan_runner): + "Test service account resource." + _, resources = plan_runner(FIXTURES_DIR) + assert len(resources) == 1 + resource = resources[0] + assert resource['type'] == 'google_service_account' + assert resource['values']['account_id'] == 'sa-one' + + _, resources = plan_runner(FIXTURES_DIR, prefix='foo') + assert len(resources) == 1 + resource = resources[0] + assert resource['values']['account_id'] == 'foo-sa-one' + + +def test_iam_roles(plan_runner): + "Test iam roles with one member." + variables = dict( + iam_roles='["roles/iam.serviceAccountUser"]', + iam_members=( + '{' + '"roles/iam.serviceAccountUser" = ["user:a@b.com"] ' + '}') + ) + _, resources = plan_runner(FIXTURES_DIR, **variables) + assert len(resources) == 2 + iam_resources = [r for r in resources + if r['type'] != 'google_service_account'] + assert len(iam_resources) == 1 + + iam_resource = iam_resources[0] + assert iam_resource['type'] == 'google_service_account_iam_binding' + assert iam_resource['index'] == 'roles/iam.serviceAccountUser' + assert iam_resource['values']['role'] == 'roles/iam.serviceAccountUser' + assert iam_resource['values']['members'] == ["user:a@b.com"] diff --git a/tests/modules/iam_service_accounts/test_plan.py b/tests/modules/iam_service_accounts/test_plan.py deleted file mode 100644 index dbcb2bef..00000000 --- a/tests/modules/iam_service_accounts/test_plan.py +++ /dev/null @@ -1,51 +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. - - -import os -import pytest - - -FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') - - -def test_resources(plan_runner): - "Test service account resource." - _, resources = plan_runner(FIXTURES_DIR) - assert len(resources) == 3 - assert set(r['type'] for r in resources) == set(['google_service_account']) - assert set(r['values']['account_id'] for r in resources) == set([ - 'sa-one', 'sa-two', 'sa-three' - ]) - _, resources = plan_runner(FIXTURES_DIR, prefix='foo') - assert set(r['values']['account_id'] for r in resources) == set([ - 'foo-sa-one', 'foo-sa-two', 'foo-sa-three' - ]) - - -def test_iam_roles(plan_runner): - "Test iam roles with no memmbers." - _, resources = plan_runner(FIXTURES_DIR, - iam_roles='["roles/iam.serviceAccountUser"]') - assert len(resources) == 6 - iam_resources = [r for r in resources if r['type'] - != 'google_service_account'] - assert len(iam_resources) == 3 - assert set(r['type'] for r in iam_resources) == set( - ['google_service_account_iam_binding']) - assert [r['index'] for r in iam_resources] == [ - 'sa-one-roles/iam.serviceAccountUser', - 'sa-three-roles/iam.serviceAccountUser', - 'sa-two-roles/iam.serviceAccountUser', - ] From 85fda5b1fe419f5fd2d58f840e95a7d304f10c44 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Wed, 21 Oct 2020 09:37:05 +0200 Subject: [PATCH 04/53] Remove iam_roles from service accounts, folder and organization modules --- foundations/environments/main.tf | 9 ++++----- modules/folder/README.md | 1 - modules/folder/main.tf | 2 +- modules/folder/variables.tf | 6 ------ modules/iam-service-account/README.md | 1 - modules/iam-service-account/main.tf | 3 +-- modules/iam-service-account/variables.tf | 6 ------ modules/organization/README.md | 2 -- modules/organization/main.tf | 2 +- modules/organization/variables.tf | 6 ------ tests/modules/folders/fixture/main.tf | 1 - tests/modules/folders/fixture/variables.tf | 5 ----- tests/modules/folders/test_plan.py | 13 ------------- .../modules/iam_service_account/fixture/main.tf | 1 - .../iam_service_account/fixture/variables.tf | 5 ----- tests/modules/iam_service_account/test_plan.py | 1 - tests/modules/organization/fixture/main.tf | 17 ++++++++--------- tests/modules/organization/fixture/variables.tf | 6 ------ 18 files changed, 15 insertions(+), 72 deletions(-) diff --git a/foundations/environments/main.tf b/foundations/environments/main.tf index 3f839a3c..065dd5ac 100644 --- a/foundations/environments/main.tf +++ b/foundations/environments/main.tf @@ -86,11 +86,10 @@ module "tf-gcs-environments" { ############################################################################### module "environment-folders" { - source = "../../modules/folder" - for_each = var.environments - parent = var.root_node - name = each.value - iam_roles = local.folder_roles + source = "../../modules/folder" + for_each = var.environments + parent = var.root_node + name = each.value iam_members = { for role in local.folder_roles : (role) => [module.tf-service-accounts[each.value].iam_email] diff --git a/modules/folder/README.md b/modules/folder/README.md index c9d84cc0..f4a164cf 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -48,7 +48,6 @@ module "folder" { | name | Folder name. | string | ✓ | | | parent | Parent in folders/folder_id or organizations/org_id format. | string | ✓ | | | *iam_members* | List of IAM members keyed by role. | map(set(string)) | | null | -| *iam_roles* | List of IAM roles. | set(string) | | null | | *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | | *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. | map(object({...})) | | {} | diff --git a/modules/folder/main.tf b/modules/folder/main.tf index ce183910..a0ac9b55 100644 --- a/modules/folder/main.tf +++ b/modules/folder/main.tf @@ -21,7 +21,7 @@ resource "google_folder" "folder" { } resource "google_folder_iam_binding" "authoritative" { - for_each = var.iam_roles + for_each = toset(keys(var.iam_members)) folder = google_folder.folder.name role = each.key members = lookup(var.iam_members, each.key, []) diff --git a/modules/folder/variables.tf b/modules/folder/variables.tf index 47c2843b..d298ec4e 100644 --- a/modules/folder/variables.tf +++ b/modules/folder/variables.tf @@ -20,12 +20,6 @@ variable "iam_members" { default = null } -variable "iam_roles" { - description = "List of IAM roles." - type = set(string) - default = null -} - variable "name" { description = "Folder name." type = string diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index f55c0b51..915b8b41 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -39,7 +39,6 @@ module "myproject-default-service-accounts" { | *iam_members* | Map of members which are granted authoritative roles on the service account, keyed by role. | map(set(string)) | | {} | | *iam_organization_roles* | Project roles granted to the service account, by organization id. | map(set(string)) | | {} | | *iam_project_roles* | Project roles granted to the service account, by project id. | map(set(string)) | | {} | -| *iam_roles* | Authoritative roles granted on the service account. | set(string) | | [] | | *iam_storage_roles* | Storage roles granted to the service account, by bucket name. | map(set(string)) | | {} | | *prefix* | Prefix applied to service account names. | string | | null | diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf index b749e172..e9f6ef6c 100644 --- a/modules/iam-service-account/main.tf +++ b/modules/iam-service-account/main.tf @@ -67,8 +67,7 @@ resource "google_service_account_key" "key" { } resource "google_service_account_iam_binding" "roles" { - for_each = var.iam_roles - #for_each = toset(keys(var.iam_members)) + for_each = toset(keys(var.iam_members)) service_account_id = google_service_account.service_account.name role = each.key members = lookup(var.iam_members, each.key, []) diff --git a/modules/iam-service-account/variables.tf b/modules/iam-service-account/variables.tf index 6d29cb80..524a820e 100644 --- a/modules/iam-service-account/variables.tf +++ b/modules/iam-service-account/variables.tf @@ -26,12 +26,6 @@ variable "iam_members" { default = {} } -variable "iam_roles" { - description = "Authoritative roles granted on the service account." - type = set(string) - default = [] -} - variable "iam_billing_roles" { description = "Project roles granted to the service account, by billing account id." type = map(set(string)) diff --git a/modules/organization/README.md b/modules/organization/README.md index c95bba8f..e0ea140f 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -13,7 +13,6 @@ This module allows managing several organization properties: module "org" { source = "./modules/organization" org_id = 1234567890 - iam_roles = ["roles/projectCreator"] iam_members = { "roles/projectCreator" = ["group:cloud-admins@example.org"] } policy_boolean = { "constraints/compute.disableGuestAttributesAccess" = true @@ -40,7 +39,6 @@ module "org" { | *iam_additive_bindings* | Map of roles lists used to set non authoritative bindings, keyed by members. | map(list(string)) | | {} | | *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. | map(map(list(string))) | | {} | | *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(list(string)) | | {} | -| *iam_roles* | List of roles used to set authoritative bindings. | list(string) | | [] | | *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | | *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. | map(object({...})) | | {} | diff --git a/modules/organization/main.tf b/modules/organization/main.tf index a96ff141..77af682f 100644 --- a/modules/organization/main.tf +++ b/modules/organization/main.tf @@ -37,7 +37,7 @@ resource "google_organization_iam_custom_role" "roles" { } resource "google_organization_iam_binding" "authoritative" { - for_each = toset(var.iam_roles) + for_each = toset(keys(var.iam_members)) org_id = var.org_id role = each.value members = lookup(var.iam_members, each.value, []) diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf index 240e920f..b2cf18a1 100644 --- a/modules/organization/variables.tf +++ b/modules/organization/variables.tf @@ -26,12 +26,6 @@ variable "iam_members" { default = {} } -variable "iam_roles" { - description = "List of roles used to set authoritative bindings." - 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)) diff --git a/tests/modules/folders/fixture/main.tf b/tests/modules/folders/fixture/main.tf index 5eef7c12..926675d5 100644 --- a/tests/modules/folders/fixture/main.tf +++ b/tests/modules/folders/fixture/main.tf @@ -19,7 +19,6 @@ module "test" { parent = "organizations/12345678" name = "folder-a" iam_members = var.iam_members - iam_roles = var.iam_roles policy_boolean = var.policy_boolean policy_list = var.policy_list } diff --git a/tests/modules/folders/fixture/variables.tf b/tests/modules/folders/fixture/variables.tf index 9b91f610..1267bdd7 100644 --- a/tests/modules/folders/fixture/variables.tf +++ b/tests/modules/folders/fixture/variables.tf @@ -19,11 +19,6 @@ variable "iam_members" { default = {} } -variable "iam_roles" { - type = list(string) - default = [] -} - variable "policy_boolean" { type = map(bool) default = {} diff --git a/tests/modules/folders/test_plan.py b/tests/modules/folders/test_plan.py index d8d100de..73896ea7 100644 --- a/tests/modules/folders/test_plan.py +++ b/tests/modules/folders/test_plan.py @@ -30,34 +30,22 @@ def test_folder(plan_runner): assert resource['values']['parent'] == 'organizations/12345678' -def test_iam_roles_only(plan_runner): - "Test folder resources with only iam roles passed." - _, resources = plan_runner(FIXTURES_DIR, - iam_roles='["roles/owner"]') - assert len(resources) == 2 - - def test_iam(plan_runner): "Test folder resources with iam roles and members." - iam_roles = '["roles/owner"]' iam_members = '{"roles/owner" = ["user:a@b.com"] }' _, resources = plan_runner(FIXTURES_DIR, - iam_roles=iam_roles, iam_members=iam_members) assert len(resources) == 2 def test_iam_multiple_members(plan_runner): "Test folder resources with multiple iam members." - iam_roles = '["roles/owner"]' iam_members = '{"roles/owner" = ["user:a@b.com", "user:c@d.com"] }' _, resources = plan_runner(FIXTURES_DIR, - iam_roles=iam_roles, iam_members=iam_members) assert len(resources) == 2 def test_iam_multiple_roles(plan_runner): "Test folder resources with multiple iam roles." - iam_roles = '["roles/owner", "roles/viewer"]' iam_members = ( '{ ' '"roles/owner" = ["user:a@b.com"], ' @@ -65,6 +53,5 @@ def test_iam_multiple_roles(plan_runner): '} ' ) _, resources = plan_runner(FIXTURES_DIR, - iam_roles=iam_roles, iam_members=iam_members) assert len(resources) == 3 diff --git a/tests/modules/iam_service_account/fixture/main.tf b/tests/modules/iam_service_account/fixture/main.tf index 5f70b175..be903b70 100644 --- a/tests/modules/iam_service_account/fixture/main.tf +++ b/tests/modules/iam_service_account/fixture/main.tf @@ -21,7 +21,6 @@ module "test" { prefix = var.prefix generate_key = var.generate_key iam_members = var.iam_members - iam_roles = var.iam_roles iam_billing_roles = var.iam_billing_roles iam_folder_roles = var.iam_folder_roles iam_organization_roles = var.iam_organization_roles diff --git a/tests/modules/iam_service_account/fixture/variables.tf b/tests/modules/iam_service_account/fixture/variables.tf index 0d5eba5b..1c2933b0 100644 --- a/tests/modules/iam_service_account/fixture/variables.tf +++ b/tests/modules/iam_service_account/fixture/variables.tf @@ -24,11 +24,6 @@ variable "iam_members" { default = {} } -variable "iam_roles" { - type = list(string) - default = [] -} - variable "iam_billing_roles" { type = map(list(string)) default = {} diff --git a/tests/modules/iam_service_account/test_plan.py b/tests/modules/iam_service_account/test_plan.py index 719bc574..a8a4d2cf 100644 --- a/tests/modules/iam_service_account/test_plan.py +++ b/tests/modules/iam_service_account/test_plan.py @@ -37,7 +37,6 @@ def test_resources(plan_runner): def test_iam_roles(plan_runner): "Test iam roles with one member." variables = dict( - iam_roles='["roles/iam.serviceAccountUser"]', iam_members=( '{' '"roles/iam.serviceAccountUser" = ["user:a@b.com"] ' diff --git a/tests/modules/organization/fixture/main.tf b/tests/modules/organization/fixture/main.tf index 63d1f466..718fe459 100644 --- a/tests/modules/organization/fixture/main.tf +++ b/tests/modules/organization/fixture/main.tf @@ -15,13 +15,12 @@ */ module "test" { - source = "../../../../modules/organization" - org_id = 1234567890 - custom_roles = var.custom_roles - iam_members = var.iam_members - iam_roles = var.iam_roles - iam_additive_bindings= var.iam_additive_bindings - iam_audit_config = var.iam_audit_config - policy_boolean = var.policy_boolean - policy_list = var.policy_list + source = "../../../../modules/organization" + org_id = 1234567890 + custom_roles = var.custom_roles + iam_members = var.iam_members + iam_additive_bindings = var.iam_additive_bindings + iam_audit_config = var.iam_audit_config + policy_boolean = var.policy_boolean + policy_list = var.policy_list } diff --git a/tests/modules/organization/fixture/variables.tf b/tests/modules/organization/fixture/variables.tf index 148a43b7..a6b2123b 100644 --- a/tests/modules/organization/fixture/variables.tf +++ b/tests/modules/organization/fixture/variables.tf @@ -24,17 +24,11 @@ variable "iam_members" { default = {} } -variable "iam_roles" { - type = list(string) - default = [] -} - variable "iam_additive_bindings" { type = map(list(string)) default = {} } - variable "iam_audit_config" { type = map(map(list(string))) default = {} From 61d402fece6b97a519555cbc5942309aa997bc90 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Thu, 29 Oct 2020 23:22:58 +0100 Subject: [PATCH 05/53] Update gcs module to terraform 0.13 --- .../cmek-via-centralized-kms/main.tf | 13 ++- .../cmek-via-centralized-kms/outputs.tf | 12 +-- .../gcs-to-bq-with-dataflow/main.tf | 50 +++++------ .../gcs-to-bq-with-dataflow/outputs.tf | 4 +- foundations/environments/main.tf | 12 +-- foundations/environments/outputs.tf | 2 +- modules/gcs/README.md | 82 ++++++------------ modules/gcs/main.tf | 70 +++++----------- modules/gcs/outputs.tf | 47 ++--------- modules/gcs/variables.tf | 58 ++++++------- .../cmek_via_centralized_kms/test_plan.py | 2 +- .../__init__.py | 0 .../fixture/main.tf | 0 .../fixture/variables.tf | 0 .../test_plan.py | 4 +- tests/modules/gcs/fixture/main.tf | 21 +++-- tests/modules/gcs/fixture/variables.tf | 37 ++++---- tests/modules/gcs/test_plan.py | 84 ++++++------------- 18 files changed, 174 insertions(+), 324 deletions(-) rename tests/data_solutions/{gc_to_bq_with_dataflow => gcs_to_bq_with_dataflow}/__init__.py (100%) rename tests/data_solutions/{gc_to_bq_with_dataflow => gcs_to_bq_with_dataflow}/fixture/main.tf (100%) rename tests/data_solutions/{gc_to_bq_with_dataflow => gcs_to_bq_with_dataflow}/fixture/variables.tf (100%) rename tests/data_solutions/{gc_to_bq_with_dataflow => gcs_to_bq_with_dataflow}/test_plan.py (93%) diff --git a/data-solutions/cmek-via-centralized-kms/main.tf b/data-solutions/cmek-via-centralized-kms/main.tf index 08b7b7de..66b7e6a3 100644 --- a/data-solutions/cmek-via-centralized-kms/main.tf +++ b/data-solutions/cmek-via-centralized-kms/main.tf @@ -81,6 +81,7 @@ module "kms" { keys = { key-gce = null, key-gcs = null } key_iam_roles = { key-gce = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"] + key-gcs = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"] } key_iam_members = { key-gce = { @@ -145,11 +146,9 @@ module "kms_vm_example" { ############################################################################### module "kms-gcs" { - source = "../../modules/gcs" - project_id = module.project-service.project_id - prefix = "my-bucket-001" - names = ["kms-gcs"] - encryption_keys = { - kms-gcs = module.kms.keys.key-gce.self_link, - } + source = "../../modules/gcs" + project_id = module.project-service.project_id + prefix = "my-bucket-001" + name = "kms-gcs" + encryption_key = module.kms.keys.key-gcs.self_link } diff --git a/data-solutions/cmek-via-centralized-kms/outputs.tf b/data-solutions/cmek-via-centralized-kms/outputs.tf index 99d26e15..efde89c2 100644 --- a/data-solutions/cmek-via-centralized-kms/outputs.tf +++ b/data-solutions/cmek-via-centralized-kms/outputs.tf @@ -13,19 +13,13 @@ # limitations under the License. output "bucket" { - description = "GCS Bucket Cloud KMS crypto keys." - value = { - for bucket in module.kms-gcs.buckets : - bucket.name => bucket.url - } + description = "GCS Bucket URL." + value = module.kms-gcs.url } output "bucket_keys" { description = "GCS Bucket Cloud KMS crypto keys." - value = { - for bucket in module.kms-gcs.buckets : - bucket.name => bucket.encryption - } + value = module.kms-gcs.bucket.encryption } output "projects" { diff --git a/data-solutions/gcs-to-bq-with-dataflow/main.tf b/data-solutions/gcs-to-bq-with-dataflow/main.tf index 57ec8e2a..61bc24cf 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/main.tf +++ b/data-solutions/gcs-to-bq-with-dataflow/main.tf @@ -257,35 +257,30 @@ module "kms-gcs" { source = "../../modules/gcs" project_id = module.project-service.project_id 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 = { + for_each = { data = { - "roles/storage.admin" = [ - "serviceAccount:${module.service-account-gce.email}", - ], - "roles/storage.viewer" = [ - "serviceAccount:${module.service-account-df.email}", - ], - }, + members = { + "roles/storage.admin" = [ + "serviceAccount:${module.service-account-gce.email}", + ], + "roles/storage.objectViewer" = [ + "serviceAccount:${module.service-account-df.email}", + ] + } + } df-tmplocation = { - "roles/storage.admin" = [ - "serviceAccount:${module.service-account-gce.email}", - "serviceAccount:${module.service-account-df.email}", - ] + members = { + "roles/storage.admin" = [ + "serviceAccount:${module.service-account-gce.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, - } + name = each.key + iam_members = each.value.members + encryption_key = module.kms.keys.key-gcs.self_link + force_destroy = true } ############################################################################### @@ -297,11 +292,12 @@ module "bigquery-dataset" { project_id = module.project-service.project_id id = "bq_dataset" access_roles = { - reader-group = { role = "READER", type = "domain" } + reader-group = { role = "READER", type = "service_account" } owner = { role = "OWNER", type = "user_by_email" } } access_identities = { - owner = module.service-account-bq.email + reader-group = module.service-account-bq.email + owner = module.service-account-bq.email } encryption_key = module.kms.keys.key-bq.self_link tables = { diff --git a/data-solutions/gcs-to-bq-with-dataflow/outputs.tf b/data-solutions/gcs-to-bq-with-dataflow/outputs.tf index 9dc4de31..af683896 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/outputs.tf +++ b/data-solutions/gcs-to-bq-with-dataflow/outputs.tf @@ -14,13 +14,13 @@ output "bq_tables" { description = "Bigquery Tables." - value = module.bigquery-dataset.table_ids + value = module.bigquery-dataset.table_ids } output "buckets" { description = "GCS Bucket Cloud KMS crypto keys." value = { - for bucket in module.kms-gcs.buckets : + for name, bucket in module.kms-gcs : bucket.name => bucket.url } } diff --git a/foundations/environments/main.tf b/foundations/environments/main.tf index 065dd5ac..25d62026 100644 --- a/foundations/environments/main.tf +++ b/foundations/environments/main.tf @@ -58,7 +58,7 @@ module "tf-service-accounts" { module "tf-gcs-bootstrap" { source = "../../modules/gcs" project_id = module.tf-project.project_id - names = ["tf-bootstrap"] + name = "tf-bootstrap" prefix = "${var.prefix}-tf" location = var.gcs_location } @@ -68,16 +68,12 @@ module "tf-gcs-bootstrap" { module "tf-gcs-environments" { source = "../../modules/gcs" project_id = module.tf-project.project_id - names = var.environments + for_each = var.environments + name = each.value prefix = "${var.prefix}-tf" location = var.gcs_location - iam_roles = { - for name in var.environments : (name) => ["roles/storage.objectAdmin"] - } iam_members = { - for name in var.environments : (name) => { - "roles/storage.objectAdmin" = [module.tf-service-accounts[name].iam_email] - } + "roles/storage.objectAdmin" = [module.tf-service-accounts[each.value].iam_email] } } diff --git a/foundations/environments/outputs.tf b/foundations/environments/outputs.tf index c5315d50..a550b6a6 100644 --- a/foundations/environments/outputs.tf +++ b/foundations/environments/outputs.tf @@ -29,7 +29,7 @@ output "environment_folders" { output "environment_tf_gcs_buckets" { 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" { diff --git a/modules/gcs/README.md b/modules/gcs/README.md index 672ec12e..4d2ae30d 100644 --- a/modules/gcs/README.md +++ b/modules/gcs/README.md @@ -7,21 +7,13 @@ ## Example ```hcl -module "buckets" { +module "bucket" { source = "./modules/gcs" project_id = "myproject" prefix = "test" - names = ["bucket-one", "bucket-two"] - bucket_policy_only = { - bucket-one = false - } + name = "my-bucket" iam_members = { - bucket-two = { - "roles/storage.admin" = ["group:storage@example.com"] - } - } - iam_roles = { - bucket-two = ["roles/storage.admin"] + "roles/storage.admin" = ["group:storage@example.com"] } } ``` @@ -29,56 +21,38 @@ module "buckets" { ### Example with Cloud KMS ```hcl -module "buckets" { +module "bucket" { source = "./modules/gcs" project_id = "myproject" prefix = "test" - names = ["bucket-one", "bucket-two"] - bucket_policy_only = { - bucket-one = false - } + name = "my-bucket" iam_members = { - bucket-two = { - "roles/storage.admin" = ["group:storage@example.com"] - } - } - iam_roles = { - bucket-two = ["roles/storage.admin"] - } - encryption_keys = { - bucket-two = local.kms_key.self_link, + "roles/storage.admin" = ["group:storage@example.com"] } + encryption_keys = local.kms_key.self_link } ``` ### Example with retention policy ```hcl -module "buckets" { +module "bucket" { source = "./modules/gcs" project_id = "myproject" prefix = "test" - names = ["bucket-one", "bucket-two"] - bucket_policy_only = { - bucket-one = false - } + name = "my-bucket" iam_members = { - bucket-two = { - "roles/storage.admin" = ["group:storage@example.com"] - } - } - iam_roles = { - bucket-two = ["roles/storage.admin"] + "roles/storage.admin" = ["group:storage@example.com"] } retention_policies = { - bucket-one = { retention_period = 100 , is_locked = true} - bucket-two = { retention_period = 900 , is_locked = false} + retention_period = 100 + is_locked = true } logging_config = { - bucket-one = { log_bucket = bucket_name_for_logging , log_object_prefix = null} - bucket-two = { log_bucket = bucket_name_for_logging , log_object_prefix = "logs_for_bucket_two"} + log_bucket = bucket_name_for_logging + log_object_prefix = null } } ``` @@ -88,31 +62,25 @@ module "buckets" { | name | description | type | required | default | |---|---|:---: |:---:|:---:| -| names | Bucket name suffixes. | list(string) | ✓ | | +| name | Bucket name suffix. | string | ✓ | | | project_id | Bucket project id. | string | ✓ | | -| *uniform_bucket_level_access* | Optional map to enable object ACLs keyed by name, defaults to true. | map(bool) | | {} | -| *encryption_keys* | Per-bucket KMS keys that will be used for encryption. | map(string) | | {} | -| *force_destroy* | Optional map to set force destroy keyed by name, defaults to false. | map(bool) | | {} | -| *iam_members* | IAM members keyed by bucket name and role. | map(map(list(string))) | | {} | -| *iam_roles* | IAM roles keyed by bucket name. | map(list(string)) | | {} | +| *encryption_key* | KMS key that will be used for encryption. | string | | null | +| *force_destroy* | Optional map to set force destroy keyed by name, defaults to false. | bool | | false | +| *iam_members* | IAM members keyed by bucket name and role. | map(set(string)) | | {} | | *labels* | Labels to be attached to all buckets. | map(string) | | {} | | *location* | Bucket location. | string | | EU | -| *logging_config* | Per-bucket logging. | map(object({...})) | | {} | +| *logging_config* | Bucket logging configuration. | object({...}) | | null | | *prefix* | Prefix used to generate the bucket name. | string | | null | -| *retention_policies* | Per-bucket retention policy. | map(object({...})) | | {} | +| *retention_policy* | Bucket retention policy. | object({...}) | | null | | *storage_class* | Bucket storage class. | string | | MULTI_REGIONAL | -| *versioning* | Optional map to set versioning keyed by name, defaults to false. | map(bool) | | {} | +| *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). | bool | | true | +| *versioning* | Enable versioning, defaults to false. | bool | | false | ## Outputs | name | description | sensitive | |---|---|:---:| -| bucket | Bucket resource (for single use). | | -| buckets | Bucket resources. | | -| name | Bucket name (for single use). | | -| names | Bucket names. | | -| names_list | List of bucket names. | | -| url | Bucket URL (for single use). | | -| urls | Bucket URLs. | | -| urls_list | List of bucket URLs. | | +| bucket | Bucket resource. | | +| name | Bucket name. | | +| url | Bucket URL. | | diff --git a/modules/gcs/main.tf b/modules/gcs/main.tf index cd2ca1f2..d9f866ae 100644 --- a/modules/gcs/main.tf +++ b/modules/gcs/main.tf @@ -15,85 +15,57 @@ */ 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 = ( var.prefix == null || var.prefix == "" # keep "" for backward compatibility ? "" : 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" { - for_each = toset(var.names) - name = "${local.prefix}${lower(each.key)}" - project = var.project_id - location = var.location - storage_class = var.storage_class - force_destroy = lookup(var.force_destroy, each.key, false) - uniform_bucket_level_access = lookup(var.uniform_bucket_level_access, each.key, true) +resource "google_storage_bucket" "bucket" { + name = "${local.prefix}${lower(var.name)}" + project = var.project_id + location = var.location + storage_class = var.storage_class + force_destroy = var.force_destroy + uniform_bucket_level_access = var.uniform_bucket_level_access versioning { - enabled = lookup(var.versioning, each.key, false) + enabled = var.versioning } labels = merge(var.labels, { location = lower(var.location) - name = lower(each.key) + name = lower(var.name) storage_class = lower(var.storage_class) }) dynamic encryption { - for_each = local.kms_keys[each.key] == null ? [] : [""] + for_each = var.encryption_key == null ? [] : [""] content { - default_kms_key_name = local.kms_keys[each.key] + default_kms_key_name = var.encryption_key } } dynamic retention_policy { - for_each = local.retention_policy[each.key] == null ? [] : [""] + for_each = var.retention_policy == null ? [] : [""] content { - retention_period = local.retention_policy[each.key]["retention_period"] - is_locked = local.retention_policy[each.key]["is_locked"] + retention_period = var.retention_policy.retention_period + is_locked = var.retention_policy.is_locked } } dynamic logging { - for_each = local.logging_config[each.key] == null ? [] : [""] + for_each = var.logging_config == null ? [] : [""] content { - log_bucket = local.logging_config[each.key]["log_bucket"] - log_object_prefix = local.logging_config[each.key]["log_object_prefix"] + log_bucket = var.logging_config.log_bucket + log_object_prefix = var.logging_config.log_object_prefix } } } resource "google_storage_bucket_iam_binding" "bindings" { - for_each = local.iam_keypairs - bucket = google_storage_bucket.buckets[each.value.name].name - role = each.value.role - members = lookup( - lookup(local.iam_members, each.value.name, {}), each.value.role, [] - ) + for_each = var.iam_members + bucket = google_storage_bucket.bucket.name + role = each.key + members = each.value } diff --git a/modules/gcs/outputs.tf b/modules/gcs/outputs.tf index 9a8f8dfa..8d434874 100644 --- a/modules/gcs/outputs.tf +++ b/modules/gcs/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. @@ -15,49 +15,16 @@ */ output "bucket" { - description = "Bucket resource (for single use)." - value = local.has_buckets ? local.buckets[0] : null + description = "Bucket resource." + value = google_storage_bucket.bucket } output "name" { - description = "Bucket name (for single use)." - value = local.has_buckets ? local.buckets[0].name : null + description = "Bucket name." + value = google_storage_bucket.bucket.name } output "url" { - description = "Bucket URL (for single use)." - value = local.has_buckets ? local.buckets[0].url : null -} - -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] + description = "Bucket URL." + value = google_storage_bucket.bucket.url } diff --git a/modules/gcs/variables.tf b/modules/gcs/variables.tf index 19ada263..3f48caa6 100644 --- a/modules/gcs/variables.tf +++ b/modules/gcs/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. @@ -15,33 +15,27 @@ */ 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)." - type = map(bool) - default = {} + 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 = bool + default = true } variable "force_destroy" { description = "Optional map to set force destroy keyed by name, defaults to false." - type = map(bool) - default = {} + type = bool + default = false } variable "iam_members" { description = "IAM members keyed by bucket name and role." - type = map(map(list(string))) + type = map(set(string)) default = {} } -variable "iam_roles" { - description = "IAM roles keyed by bucket name." - type = map(list(string)) - default = {} -} - -variable "encryption_keys" { - description = "Per-bucket KMS keys that will be used for encryption." - type = map(string) - default = {} +variable "encryption_key" { + description = "KMS key that will be used for encryption." + type = string + default = null } variable "labels" { @@ -57,17 +51,17 @@ variable "location" { } variable "logging_config" { - description = "Per-bucket logging." - type = map(object({ + description = "Bucket logging configuration." + type = object({ log_bucket = string log_object_prefix = string - })) - default = {} + }) + default = null } -variable "names" { - description = "Bucket name suffixes." - type = list(string) +variable "name" { + description = "Bucket name suffix." + type = string } variable "prefix" { @@ -81,13 +75,13 @@ variable "project_id" { type = string } -variable "retention_policies" { - description = "Per-bucket retention policy." - type = map(object({ +variable "retention_policy" { + description = "Bucket retention policy." + type = object({ retention_period = number is_locked = bool - })) - default = {} + }) + default = null } variable "storage_class" { @@ -97,7 +91,7 @@ variable "storage_class" { } variable "versioning" { - description = "Optional map to set versioning keyed by name, defaults to false." - type = map(bool) - default = {} + description = "Enable versioning, defaults to false." + type = bool + default = false } diff --git a/tests/data_solutions/cmek_via_centralized_kms/test_plan.py b/tests/data_solutions/cmek_via_centralized_kms/test_plan.py index 21514522..66b971ec 100644 --- a/tests/data_solutions/cmek_via_centralized_kms/test_plan.py +++ b/tests/data_solutions/cmek_via_centralized_kms/test_plan.py @@ -24,4 +24,4 @@ def test_resources(e2e_plan_runner): "Test that plan works and the numbers of resources is as expected." modules, resources = e2e_plan_runner(FIXTURES_DIR) assert len(modules) == 7 - assert len(resources) == 22 + assert len(resources) == 23 diff --git a/tests/data_solutions/gc_to_bq_with_dataflow/__init__.py b/tests/data_solutions/gcs_to_bq_with_dataflow/__init__.py similarity index 100% rename from tests/data_solutions/gc_to_bq_with_dataflow/__init__.py rename to tests/data_solutions/gcs_to_bq_with_dataflow/__init__.py diff --git a/tests/data_solutions/gc_to_bq_with_dataflow/fixture/main.tf b/tests/data_solutions/gcs_to_bq_with_dataflow/fixture/main.tf similarity index 100% rename from tests/data_solutions/gc_to_bq_with_dataflow/fixture/main.tf rename to tests/data_solutions/gcs_to_bq_with_dataflow/fixture/main.tf diff --git a/tests/data_solutions/gc_to_bq_with_dataflow/fixture/variables.tf b/tests/data_solutions/gcs_to_bq_with_dataflow/fixture/variables.tf similarity index 100% rename from tests/data_solutions/gc_to_bq_with_dataflow/fixture/variables.tf rename to tests/data_solutions/gcs_to_bq_with_dataflow/fixture/variables.tf diff --git a/tests/data_solutions/gc_to_bq_with_dataflow/test_plan.py b/tests/data_solutions/gcs_to_bq_with_dataflow/test_plan.py similarity index 93% rename from tests/data_solutions/gc_to_bq_with_dataflow/test_plan.py rename to tests/data_solutions/gcs_to_bq_with_dataflow/test_plan.py index 1828f7f4..03fc27da 100644 --- a/tests/data_solutions/gc_to_bq_with_dataflow/test_plan.py +++ b/tests/data_solutions/gcs_to_bq_with_dataflow/test_plan.py @@ -23,5 +23,5 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') def test_resources(e2e_plan_runner): "Test that plan works and the numbers of resources is as expected." modules, resources = e2e_plan_runner(FIXTURES_DIR) - assert len(modules) == 13 - assert len(resources) == 61 + assert len(modules) == 14 + assert len(resources) == 60 diff --git a/tests/modules/gcs/fixture/main.tf b/tests/modules/gcs/fixture/main.tf index 6275c616..a8b892b0 100644 --- a/tests/modules/gcs/fixture/main.tf +++ b/tests/modules/gcs/fixture/main.tf @@ -15,16 +15,15 @@ */ module "test" { - source = "../../../../modules/gcs" - project_id = "my-project" + source = "../../../../modules/gcs" + project_id = "my-project" uniform_bucket_level_access = var.uniform_bucket_level_access - force_destroy = var.force_destroy - iam_members = var.iam_members - iam_roles = var.iam_roles - labels = var.labels - logging_config = var.logging_config - names = ["bucket-a", "bucket-b"] - prefix = var.prefix - retention_policies = var.retention_policies - versioning = var.versioning + force_destroy = var.force_destroy + iam_members = var.iam_members + labels = var.labels + logging_config = var.logging_config + name = "bucket-a" + prefix = var.prefix + retention_policy = var.retention_policy + versioning = var.versioning } diff --git a/tests/modules/gcs/fixture/variables.tf b/tests/modules/gcs/fixture/variables.tf index f79485f8..dfc9a83a 100644 --- a/tests/modules/gcs/fixture/variables.tf +++ b/tests/modules/gcs/fixture/variables.tf @@ -15,23 +15,18 @@ */ variable "uniform_bucket_level_access" { - type = map(bool) - default = { bucket-a = false } + type = bool + default = false } variable "force_destroy" { - type = map(bool) - default = { bucket-a = true } + type = bool + default = true } variable "iam_members" { - type = map(map(list(string))) - default = null -} - -variable "iam_roles" { - type = map(list(string)) - default = null + type = map(set(string)) + default = {} } variable "labels" { @@ -40,12 +35,13 @@ variable "labels" { } variable "logging_config" { - type = map(object({ + type = object({ log_bucket = string log_object_prefix = string - })) + }) default = { - bucket-a = { log_bucket = "foo", log_object_prefix = null } + log_bucket = "foo" + log_object_prefix = null } } @@ -59,13 +55,14 @@ variable "project_id" { default = "my-project" } -variable "retention_policies" { - type = map(object({ +variable "retention_policy" { + type = object({ retention_period = number is_locked = bool - })) + }) default = { - bucket-b = { retention_period = 5, is_locked = false } + retention_period = 5 + is_locked = false } } @@ -75,6 +72,6 @@ variable "storage_class" { } variable "versioning" { - type = map(bool) - default = { bucket-a = true } + type = bool + default = true } diff --git a/tests/modules/gcs/test_plan.py b/tests/modules/gcs/test_plan.py index 051eb042..8516b544 100644 --- a/tests/modules/gcs/test_plan.py +++ b/tests/modules/gcs/test_plan.py @@ -19,75 +19,43 @@ import pytest FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') - def test_buckets(plan_runner): "Test bucket resources." _, resources = plan_runner(FIXTURES_DIR) - assert len(resources) == 2 - assert set(r['type'] for r in resources) == set(['google_storage_bucket']) - assert set(r['values']['name'] for r in resources) == set([ - 'bucket-a', 'bucket-b' - ]) - assert set(r['values']['project'] for r in resources) == set([ - 'my-project' - ]) - + assert len(resources) == 1 + r = resources[0] + assert r['type'] == 'google_storage_bucket' + assert r['values']['name'] == 'bucket-a' + assert r['values']['project'] == 'my-project' def test_prefix(plan_runner): "Test bucket name when prefix is set." _, resources = plan_runner(FIXTURES_DIR, prefix='foo') - assert set(r['values']['name'] for r in resources) == set([ - 'foo-eu-bucket-a', 'foo-eu-bucket-b' - ]) + assert resources[0]['values']['name'] == 'foo-eu-bucket-a' -def test_map_values(plan_runner): - "Test that map values set the correct attributes on buckets." - _, resources = plan_runner(FIXTURES_DIR) - bpo = dict((r['values']['name'], r['values']['uniform_bucket_level_access']) - for r in resources) - assert bpo == {'bucket-a': False, 'bucket-b': True} - force_destroy = dict((r['values']['name'], r['values']['force_destroy']) - for r in resources) - assert force_destroy == {'bucket-a': True, 'bucket-b': False} - versioning = dict((r['values']['name'], r['values']['versioning']) - for r in resources) - assert versioning == { - 'bucket-a': [{'enabled': True}], 'bucket-b': [{'enabled': False}] - } - logging_config = dict((r['values']['name'], r['values']['logging']) - for r in resources) - assert logging_config == { - 'bucket-a': [{'log_bucket': 'foo'}], - 'bucket-b': [] - } - retention_policies = dict((r['values']['name'], r['values']['retention_policy']) - for r in resources) - assert retention_policies == { - 'bucket-a': [], - 'bucket-b': [{'is_locked': False, 'retention_period': 5}] - } - for r in resources: - assert r['values']['labels'] == { - 'environment': 'test', 'location': 'eu', - 'storage_class': 'multi_regional', 'name': r['values']['name'] - } - - -def test_iam_roles_only(plan_runner): - "Test bucket resources with only iam roles passed." - _, resources = plan_runner( - FIXTURES_DIR, iam_roles='{bucket-a = [ "roles/storage.admin"]}') - assert len(resources) == 3 +def test_config_values(plan_runner): + "Test that variables set the correct attributes on buckets." + variables = dict( + uniform_bucket_level_access='true', + force_destroy='true', + versioning='true' + ) + _, resources = plan_runner(FIXTURES_DIR, **variables) + assert len(resources) == 1 + r = resources[0] + assert r['values']['uniform_bucket_level_access'] is True + assert r['values']['force_destroy'] is True + assert r['values']['versioning'] == [{'enabled': True}] + assert r['values']['logging'] == [{'log_bucket': 'foo'}] + assert r['values']['retention_policy'] == [ + {'is_locked': False, 'retention_period': 5} + ] def test_iam(plan_runner): "Test bucket resources with iam roles and members." - iam_roles = ( - '{bucket-a = ["roles/storage.admin"], ' - 'bucket-b = ["roles/storage.objectAdmin"]}' - ) - iam_members = '{folder-a = { "roles/storage.admin" = ["user:a@b.com"] }}' + iam_members = '{ "roles/storage.admin" = ["user:a@b.com"] }' _, resources = plan_runner( - FIXTURES_DIR, iam_roles=iam_roles, iam_members=iam_members) - assert len(resources) == 4 + FIXTURES_DIR, iam_members=iam_members) + assert len(resources) == 2 From 6e81125c6abca966b2af0ebe47383cc13a497768 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Thu, 29 Oct 2020 23:29:20 +0100 Subject: [PATCH 06/53] Remove useless calls to toset --- modules/folder/main.tf | 4 ++-- modules/iam-service-account/main.tf | 4 ++-- modules/organization/main.tf | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/folder/main.tf b/modules/folder/main.tf index a0ac9b55..6ac0940e 100644 --- a/modules/folder/main.tf +++ b/modules/folder/main.tf @@ -21,10 +21,10 @@ resource "google_folder" "folder" { } resource "google_folder_iam_binding" "authoritative" { - for_each = toset(keys(var.iam_members)) + for_each = var.iam_members folder = google_folder.folder.name role = each.key - members = lookup(var.iam_members, each.key, []) + members = each.value } resource "google_folder_organization_policy" "boolean" { diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf index e9f6ef6c..5fc7d266 100644 --- a/modules/iam-service-account/main.tf +++ b/modules/iam-service-account/main.tf @@ -67,10 +67,10 @@ resource "google_service_account_key" "key" { } resource "google_service_account_iam_binding" "roles" { - for_each = toset(keys(var.iam_members)) + for_each = var.iam_members service_account_id = google_service_account.service_account.name role = each.key - members = lookup(var.iam_members, each.key, []) + members = each.value } resource "google_billing_account_iam_member" "billing-roles" { diff --git a/modules/organization/main.tf b/modules/organization/main.tf index 77af682f..f82caeda 100644 --- a/modules/organization/main.tf +++ b/modules/organization/main.tf @@ -37,10 +37,10 @@ resource "google_organization_iam_custom_role" "roles" { } resource "google_organization_iam_binding" "authoritative" { - for_each = toset(keys(var.iam_members)) + for_each = var.iam_members org_id = var.org_id - role = each.value - members = lookup(var.iam_members, each.value, []) + role = each.key + members = each.value } resource "google_organization_iam_member" "additive" { From 78efb63b4a9500eab027931b01467749cca35c06 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Thu, 29 Oct 2020 23:49:33 +0100 Subject: [PATCH 07/53] Remove iam_roles from project module --- .../asset-inventory-feed-remediation/main.tf | 1 - cloud-operations/quota-monitoring/main.tf | 3 -- foundations/environments/main.tf | 4 --- modules/project/README.md | 3 +- modules/project/main.tf | 8 ++--- modules/project/outputs.tf | 2 +- modules/project/variables.tf | 9 +---- networking/shared-vpc-gke/main.tf | 14 -------- tests/modules/project/fixture/main.tf | 33 +++++++++---------- tests/modules/project/fixture/variables.tf | 5 --- 10 files changed, 23 insertions(+), 59 deletions(-) diff --git a/cloud-operations/asset-inventory-feed-remediation/main.tf b/cloud-operations/asset-inventory-feed-remediation/main.tf index da256576..1ac8d7aa 100644 --- a/cloud-operations/asset-inventory-feed-remediation/main.tf +++ b/cloud-operations/asset-inventory-feed-remediation/main.tf @@ -41,7 +41,6 @@ module "project" { "compute.zoneOperations.list" ] } - iam_roles = [local.role_id] iam_members = { (local.role_id) = [module.service-account.iam_email] } diff --git a/cloud-operations/quota-monitoring/main.tf b/cloud-operations/quota-monitoring/main.tf index 0727ae4b..cde62ea3 100644 --- a/cloud-operations/quota-monitoring/main.tf +++ b/cloud-operations/quota-monitoring/main.tf @@ -34,9 +34,6 @@ module "project" { disable_on_destroy = false, disable_dependent_services = false } - iam_roles = [ - "roles/monitoring.metricWriter", - ] iam_members = { "roles/monitoring.metricWriter" = [module.cf.service_account_iam_email] } diff --git a/foundations/environments/main.tf b/foundations/environments/main.tf index 25d62026..df4e0676 100644 --- a/foundations/environments/main.tf +++ b/foundations/environments/main.tf @@ -108,10 +108,6 @@ module "audit-project" { "roles/bigquery.dataEditor" = [module.audit-log-sinks.writer_identities[0]] "roles/viewer" = var.iam_audit_viewers } - iam_roles = [ - "roles/bigquery.dataEditor", - "roles/viewer" - ] services = concat(var.project_services, [ "bigquery.googleapis.com", ]) diff --git a/modules/project/README.md b/modules/project/README.md index e4611cec..8c4e917c 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -89,8 +89,7 @@ module "project" { | *billing_account* | Billing account id. | string | | null | | *custom_roles* | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | | *iam_additive_bindings* | Map of roles lists used to set non authoritative bindings, keyed by members | map(list(string)) | | {} | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(list(string)) | | {} | -| *iam_roles* | List of roles used to set authoritative bindings. | list(string) | | [] | +| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(set(string)) | | {} | | *labels* | Resource labels. | map(string) | | {} | | *lien_reason* | If non-empty, creates a project lien with this description. | string | | | | *oslogin* | Enable OS Login. | bool | | false | diff --git a/modules/project/main.tf b/modules/project/main.tf index e8acb861..1ef2be8e 100644 --- a/modules/project/main.tf +++ b/modules/project/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. @@ -91,10 +91,10 @@ resource "google_project_service" "project_services" { # - additive (non-authoritative) roles might fail due to dynamic values resource "google_project_iam_binding" "authoritative" { - for_each = toset(var.iam_roles) + for_each = var.iam_members project = local.project.project_id - role = each.value - members = lookup(var.iam_members, each.value, []) + role = each.key + members = each.value depends_on = [ google_project_service.project_services, google_project_iam_custom_role.roles diff --git a/modules/project/outputs.tf b/modules/project/outputs.tf index a64b7c56..f20c78b0 100644 --- a/modules/project/outputs.tf +++ b/modules/project/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. diff --git a/modules/project/variables.tf b/modules/project/variables.tf index c47ba07a..a0e965bf 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -34,17 +34,10 @@ variable "custom_roles" { variable "iam_members" { description = "Map of member lists used to set authoritative bindings, keyed by role." - type = map(list(string)) + type = map(set(string)) default = {} } -variable "iam_roles" { - description = "List of roles used to set authoritative bindings." - 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)) diff --git a/networking/shared-vpc-gke/main.tf b/networking/shared-vpc-gke/main.tf index ce46aba7..a31185ec 100644 --- a/networking/shared-vpc-gke/main.tf +++ b/networking/shared-vpc-gke/main.tf @@ -33,9 +33,6 @@ module "project-host" { module.project-svc-gke.project_id ] } - iam_roles = [ - "roles/container.hostServiceAgentUser", "roles/owner" - ] iam_members = { "roles/container.hostServiceAgentUser" = [ "serviceAccount:${module.project-svc-gke.service_accounts.robots.container-engine}" @@ -53,11 +50,6 @@ module "project-svc-gce" { services = var.project_services oslogin = true oslogin_admins = var.owners_gce - iam_roles = [ - "roles/logging.logWriter", - "roles/monitoring.metricWriter", - "roles/owner" - ] iam_members = { "roles/logging.logWriter" = [module.vm-bastion.service_account_iam_email], "roles/monitoring.metricWriter" = [module.vm-bastion.service_account_iam_email], @@ -75,12 +67,6 @@ module "project-svc-gke" { prefix = var.prefix name = "gke" services = var.project_services - iam_roles = [ - "roles/container.developer", - "roles/logging.logWriter", - "roles/monitoring.metricWriter", - "roles/owner", - ] iam_members = { "roles/container.developer" = [module.vm-bastion.service_account_iam_email], "roles/logging.logWriter" = [module.service-account-gke-node.iam_email], diff --git a/tests/modules/project/fixture/main.tf b/tests/modules/project/fixture/main.tf index 924b2648..8fce40f8 100644 --- a/tests/modules/project/fixture/main.tf +++ b/tests/modules/project/fixture/main.tf @@ -15,22 +15,21 @@ */ module "test" { - source = "../../../../modules/project" - name = "my-project" - billing_account = "12345-12345-12345" - auto_create_network = var.auto_create_network - custom_roles = var.custom_roles - iam_members = var.iam_members - iam_roles = var.iam_roles + source = "../../../../modules/project" + name = "my-project" + billing_account = "12345-12345-12345" + auto_create_network = var.auto_create_network + custom_roles = var.custom_roles + iam_members = var.iam_members iam_additive_bindings = var.iam_additive_bindings - labels = var.labels - lien_reason = var.lien_reason - oslogin = var.oslogin - oslogin_admins = var.oslogin_admins - oslogin_users = var.oslogin_users - parent = var.parent - policy_boolean = var.policy_boolean - policy_list = var.policy_list - prefix = var.prefix - services = var.services + labels = var.labels + lien_reason = var.lien_reason + oslogin = var.oslogin + oslogin_admins = var.oslogin_admins + oslogin_users = var.oslogin_users + parent = var.parent + policy_boolean = var.policy_boolean + policy_list = var.policy_list + prefix = var.prefix + services = var.services } diff --git a/tests/modules/project/fixture/variables.tf b/tests/modules/project/fixture/variables.tf index 3b36a5fd..83d06352 100644 --- a/tests/modules/project/fixture/variables.tf +++ b/tests/modules/project/fixture/variables.tf @@ -29,11 +29,6 @@ variable "iam_members" { default = {} } -variable "iam_roles" { - type = list(string) - default = [] -} - variable "iam_additive_bindings" { type = map(list(string)) default = {} From 1a7b9836eab907ad679304e561687c2d0215aa92 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Thu, 29 Oct 2020 23:54:40 +0100 Subject: [PATCH 08/53] Update folder and service account READMEs --- modules/folder/README.md | 1 - modules/iam-service-account/README.md | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/folder/README.md b/modules/folder/README.md index f4a164cf..8a98dc5e 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -14,7 +14,6 @@ module "folder" { iam_members = { "roles/owner" = ["group:users@example.com"] } - iam_roles = ["roles/owner"] } ``` diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index 915b8b41..43ef5bb4 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -6,12 +6,11 @@ This module allows simplified creation and management of one a service account a ```hcl module "myproject-default-service-accounts" { - source = "./modules/iam-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 = ["roles/iam.serviceAccountUser"] iam_members = { "roles/iam.serviceAccountUser" = ["user:foo@example.com"] } From b13a10efe54d0844eef558d9c094a925d16c766e Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 30 Oct 2020 00:12:15 +0100 Subject: [PATCH 09/53] Remove iam_roles from compute-vm module --- modules/compute-vm/README.md | 3 +-- modules/compute-vm/main.tf | 10 +++++----- modules/compute-vm/variables.tf | 8 +------- tests/modules/compute_vm/fixture/main.tf | 1 - tests/modules/compute_vm/fixture/variables.tf | 7 +------ tests/modules/compute_vm/test_plan.py | 3 +-- tests/modules/compute_vm/test_plan_zones.py | 3 +-- 7 files changed, 10 insertions(+), 25 deletions(-) diff --git a/modules/compute-vm/README.md b/modules/compute-vm/README.md index 45895f28..18aaf269 100644 --- a/modules/compute-vm/README.md +++ b/modules/compute-vm/README.md @@ -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. | object({...}) | | null | | *group* | Define this variable to create an instance group for instances. Disabled for template use. | object({...}) | | null | | *hostname* | Instance FQDN name. | string | | null | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use. | map(list(string)) | | {} | -| *iam_roles* | List of roles used to set authoritative bindings. Ignored for template use. | list(string) | | [] | +| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use. | map(set(string)) | | {} | | *instance_count* | Number of instances to create (only for non-template usage). | number | | 1 | | *instance_type* | Instance type. | string | | f1-micro | | *labels* | Instance labels. | map(string) | | {} | diff --git a/modules/compute-vm/main.tf b/modules/compute-vm/main.tf index 1a911022..45749790 100644 --- a/modules/compute-vm/main.tf +++ b/modules/compute-vm/main.tf @@ -25,9 +25,9 @@ locals { for pair in setproduct(keys(local.names), keys(local.attached_disks)) : "${pair[0]}-${pair[1]}" => { disk_name = pair[1], name = pair[0] } } - iam_roles = var.use_instance_template ? {} : { - for pair in setproduct(var.iam_roles, keys(local.names)) : - "${pair.0}/${pair.1}" => { role = pair.0, name = pair.1 } + iam_members = var.use_instance_template ? {} : { + for pair in setproduct(keys(var.iam_members), keys(local.names)) : + "${pair.0}/${pair.1}" => { role = pair.0, name = pair.1, members = var.iam_members[pair.0] } } names = ( var.use_instance_template ? { (var.name) = 0 } : { @@ -196,12 +196,12 @@ resource "google_compute_instance" "default" { } resource "google_compute_instance_iam_binding" "default" { - for_each = local.iam_roles + for_each = local.iam_members project = var.project_id zone = local.zones[each.value.name] instance_name = each.value.name role = each.value.role - members = lookup(var.iam_members, each.value.role, []) + members = each.value.members depends_on = [google_compute_instance.default] } diff --git a/modules/compute-vm/variables.tf b/modules/compute-vm/variables.tf index 02c13480..604a5766 100644 --- a/modules/compute-vm/variables.tf +++ b/modules/compute-vm/variables.tf @@ -92,16 +92,10 @@ variable "hostname" { variable "iam_members" { description = "Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use." - type = map(list(string)) + type = map(set(string)) default = {} } -variable "iam_roles" { - description = "List of roles used to set authoritative bindings. Ignored for template use." - type = list(string) - default = [] -} - variable "instance_count" { description = "Number of instances to create (only for non-template usage)." type = number diff --git a/tests/modules/compute_vm/fixture/main.tf b/tests/modules/compute_vm/fixture/main.tf index b80dcb23..fb3dd42c 100644 --- a/tests/modules/compute_vm/fixture/main.tf +++ b/tests/modules/compute_vm/fixture/main.tf @@ -25,7 +25,6 @@ module "test" { instance_count = var.instance_count use_instance_template = var.use_instance_template group = var.group - iam_roles = var.iam_roles iam_members = var.iam_members metadata = var.metadata metadata_list = var.metadata_list diff --git a/tests/modules/compute_vm/fixture/variables.tf b/tests/modules/compute_vm/fixture/variables.tf index 6258905b..bc61f3d6 100644 --- a/tests/modules/compute_vm/fixture/variables.tf +++ b/tests/modules/compute_vm/fixture/variables.tf @@ -20,15 +20,10 @@ variable "group" { } variable "iam_members" { - type = map(list(string)) + type = map(set(string)) default = {} } -variable "iam_roles" { - type = list(string) - default = [] -} - variable "instance_count" { type = number default = 1 diff --git a/tests/modules/compute_vm/test_plan.py b/tests/modules/compute_vm/test_plan.py index a37b55ac..5cf1458d 100644 --- a/tests/modules/compute_vm/test_plan.py +++ b/tests/modules/compute_vm/test_plan.py @@ -56,13 +56,12 @@ def test_group(plan_runner): def test_iam(plan_runner): - iam_roles = '["roles/compute.instanceAdmin", "roles/iam.serviceAccountUser"]' iam_members = ( '{"roles/compute.instanceAdmin" = ["user:a@a.com", "user:b@a.com"],' '"roles/iam.serviceAccountUser" = ["user:a@a.com"]}' ) _, resources = plan_runner( - FIXTURES_DIR, instance_count=2, iam_roles=iam_roles, iam_members=iam_members) + FIXTURES_DIR, instance_count=2, iam_members=iam_members) assert len(resources) == 6 assert set(r['type'] for r in resources) == set([ 'google_compute_instance', 'google_compute_instance_iam_binding']) diff --git a/tests/modules/compute_vm/test_plan_zones.py b/tests/modules/compute_vm/test_plan_zones.py index f4a00359..9aa1ae24 100644 --- a/tests/modules/compute_vm/test_plan_zones.py +++ b/tests/modules/compute_vm/test_plan_zones.py @@ -49,10 +49,9 @@ def test_group(plan_runner): def test_iam(plan_runner): - iam_roles = '["roles/a", "roles/b"]' iam_members = '{"roles/a" = ["user:a@a.com"], "roles/b" = ["user:a@a.com"]}' _, resources = plan_runner(FIXTURES_DIR, instance_count=3, - iam_roles=iam_roles, iam_members=iam_members, + iam_members=iam_members, zones='["a", "b"]') iam_bindings = dict( (r['index'], r['values']['zone']) for r in resources if r['type'] From fc5017cc2495f0003a4687a78704adc37c3ce438 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 30 Oct 2020 00:19:28 +0100 Subject: [PATCH 10/53] Remove dependency hooks to net-vpc-peerings. --- modules/net-vpc-peering/README.md | 8 +++----- modules/net-vpc-peering/main.tf | 14 +------------- modules/net-vpc-peering/outputs.tf | 5 ----- modules/net-vpc-peering/variables.tf | 6 ------ 4 files changed, 4 insertions(+), 29 deletions(-) diff --git a/modules/net-vpc-peering/README.md b/modules/net-vpc-peering/README.md index b83964f7..df9170a8 100644 --- a/modules/net-vpc-peering/README.md +++ b/modules/net-vpc-peering/README.md @@ -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 module "peering-a-b" { @@ -39,7 +39,7 @@ module "peering-a-c" { local_network = "" peer_network = "" - 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. | string | ✓ | | | *export_local_custom_routes* | Export custom routes to peer network from local network. | bool | | false | | *export_peer_custom_routes* | Export custom routes to local network from peer network. | bool | | false | -| *module_depends_on* | List of modules or resources this module depends on. | list | | [] | | *peer_create_peering* | Create the peering on the remote side. If false, only the peering from this network to the remote network is created. | bool | | true | | *prefix* | Name prefix for the network peerings. | string | | network-peering | @@ -60,7 +59,6 @@ module "peering-a-c" { | name | description | sensitive | |---|---|:---:| -| complete | Output to be used as a module dependency. | | | local_network_peering | Network peering resource. | | | peer_network_peering | Peer network peering resource. | | - \ No newline at end of file + diff --git a/modules/net-vpc-peering/main.tf b/modules/net-vpc-peering/main.tf index cd004422..0d00a9ef 100644 --- a/modules/net-vpc-peering/main.tf +++ b/modules/net-vpc-peering/main.tf @@ -25,8 +25,6 @@ resource "google_compute_network_peering" "local_network_peering" { peer_network = var.peer_network export_custom_routes = var.export_local_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" { @@ -37,15 +35,5 @@ resource "google_compute_network_peering" "peer_network_peering" { export_custom_routes = var.export_peer_custom_routes import_custom_routes = var.export_local_custom_routes - depends_on = [null_resource.module_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] + depends_on = [google_compute_network_peering.local_network_peering] } diff --git a/modules/net-vpc-peering/outputs.tf b/modules/net-vpc-peering/outputs.tf index 92bdc536..b2781115 100644 --- a/modules/net-vpc-peering/outputs.tf +++ b/modules/net-vpc-peering/outputs.tf @@ -23,8 +23,3 @@ output "peer_network_peering" { description = "Peer network peering resource." value = google_compute_network_peering.peer_network_peering } - -output "complete" { - description = "Output to be used as a module dependency." - value = null_resource.complete.id -} diff --git a/modules/net-vpc-peering/variables.tf b/modules/net-vpc-peering/variables.tf index edea0188..ca42c79c 100644 --- a/modules/net-vpc-peering/variables.tf +++ b/modules/net-vpc-peering/variables.tf @@ -42,12 +42,6 @@ variable "export_local_custom_routes" { default = false } -variable "module_depends_on" { - description = "List of modules or resources this module depends on." - type = list - default = [] -} - 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." type = bool From 53cb8359ee3a5cca170e2266479b4793b6eb3495 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 30 Oct 2020 17:58:45 +0100 Subject: [PATCH 11/53] Remove *_roles variables in kms module --- .../cmek-via-centralized-kms/main.tf | 4 -- .../gcs-to-bq-with-dataflow/main.tf | 8 ---- modules/kms/README.md | 10 +---- modules/kms/main.tf | 38 +++++++++---------- modules/kms/variables.tf | 16 +------- tests/modules/kms/fixture/main.tf | 2 - tests/modules/kms/fixture/variables.tf | 12 ------ 7 files changed, 21 insertions(+), 69 deletions(-) diff --git a/data-solutions/cmek-via-centralized-kms/main.tf b/data-solutions/cmek-via-centralized-kms/main.tf index 66b7e6a3..94832905 100644 --- a/data-solutions/cmek-via-centralized-kms/main.tf +++ b/data-solutions/cmek-via-centralized-kms/main.tf @@ -79,10 +79,6 @@ module "kms" { location = var.location } keys = { key-gce = null, key-gcs = null } - key_iam_roles = { - key-gce = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"] - key-gcs = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"] - } key_iam_members = { key-gce = { "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ diff --git a/data-solutions/gcs-to-bq-with-dataflow/main.tf b/data-solutions/gcs-to-bq-with-dataflow/main.tf index 61bc24cf..41eca04a 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/main.tf +++ b/data-solutions/gcs-to-bq-with-dataflow/main.tf @@ -120,11 +120,6 @@ module "kms" { location = var.location } keys = { key-gce = null, key-gcs = null, key-bq = null } - key_iam_roles = { - key-gce = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"] - key-gcs = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"] - key-bq = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"] - } key_iam_members = { key-gce = { "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ @@ -155,9 +150,6 @@ module "kms-regional" { location = var.region } keys = { key-df = null } - key_iam_roles = { - key-df = ["roles/cloudkms.cryptoKeyEncrypterDecrypter"] - } key_iam_members = { key-df = { "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ diff --git a/modules/kms/README.md b/modules/kms/README.md index 69ceb882..98c5743e 100644 --- a/modules/kms/README.md +++ b/modules/kms/README.md @@ -16,7 +16,6 @@ In this module **no lifecycle blocks are set on resources to prevent destroy**, module "kms" { source = "../modules/kms" project_id = "my-project" - iam_roles = ["roles/owner"] iam_members = { "roles/owner" = ["user:user1@example.com"] } @@ -32,9 +31,6 @@ module "kms" { module "kms" { source = "../modules/kms" project_id = "my-project" - key_iam_roles = { - key-a = ["roles/owner"] - } key_iam_members = { key-a = { "roles/owner" = ["user:user1@example.com"] @@ -76,10 +72,8 @@ module "kms" { |---|---|:---: |:---:|:---:| | keyring | Keyring attributes. | object({...}) | ✓ | | | project_id | Project id where the keyring will be created. | string | ✓ | | -| *iam_members* | Keyring IAM members. | map(list(string)) | | {} | -| *iam_roles* | Keyring IAM roles. | list(string) | | [] | -| *key_iam_members* | IAM members keyed by key name and role. | map(map(list(string))) | | {} | -| *key_iam_roles* | IAM roles keyed by key name. | map(list(string)) | | {} | +| *iam_members* | Keyring IAM members. | map(set(string)) | | {} | +| *key_iam_members* | IAM members keyed by key name and role. | map(map(set(string))) | | {} | | *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. | map(object({...})) | | {} | | *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. | object({...}) | | ... | | *keyring_create* | Set to false to manage keys and IAM bindings in an existing keyring. | bool | | true | diff --git a/modules/kms/main.tf b/modules/kms/main.tf index e69f3381..a6f4795f 100644 --- a/modules/kms/main.tf +++ b/modules/kms/main.tf @@ -15,14 +15,15 @@ */ locals { - key_iam_pairs = flatten([ - for name, roles in var.key_iam_roles : - [for role in roles : { name = name, role = role }] + key_iam_members = flatten([ + for key, roles in var.key_iam_members : [ + for role, members in roles : { + key = key + role = role + members = members + } + ] ]) - key_iam_keypairs = { - for pair in local.key_iam_pairs : - "${pair.name}-${pair.role}" => pair - } key_purpose = { for key, attrs in var.keys : key => try( var.key_purpose[key], var.key_purpose_defaults @@ -47,16 +48,13 @@ resource "google_kms_key_ring" "default" { project = var.project_id name = var.keyring.name location = var.keyring.location - # lifecycle { - # prevent_destroy = true - # } } resource "google_kms_key_ring_iam_binding" "default" { - for_each = toset(var.iam_roles) + for_each = var.iam_members key_ring_id = local.keyring.self_link - role = each.value - members = lookup(var.iam_members, each.value, []) + role = each.key + members = each.value } 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 } } - # lifecycle { - # prevent_destroy = true - # } } resource "google_kms_crypto_key_iam_binding" "default" { - for_each = local.key_iam_keypairs + for_each = { + for binding in local.key_iam_members : + "${binding.key}.${binding.role}" => binding + } role = each.value.role - crypto_key_id = google_kms_crypto_key.default[each.value.name].self_link - members = lookup( - lookup(var.key_iam_members, each.value.name, {}), each.value.role, [] - ) + crypto_key_id = google_kms_crypto_key.default[each.value.key].self_link + members = each.value.members } diff --git a/modules/kms/variables.tf b/modules/kms/variables.tf index 42ec689b..1f104cdf 100644 --- a/modules/kms/variables.tf +++ b/modules/kms/variables.tf @@ -16,25 +16,13 @@ variable "iam_members" { description = "Keyring IAM members." - type = map(list(string)) + type = map(set(string)) default = {} } -variable "iam_roles" { - description = "Keyring IAM roles." - type = list(string) - default = [] -} - variable "key_iam_members" { description = "IAM members keyed by key name and role." - type = map(map(list(string))) - default = {} -} - -variable "key_iam_roles" { - description = "IAM roles keyed by key name." - type = map(list(string)) + type = map(map(set(string))) default = {} } diff --git a/tests/modules/kms/fixture/main.tf b/tests/modules/kms/fixture/main.tf index f027f978..45fd119b 100644 --- a/tests/modules/kms/fixture/main.tf +++ b/tests/modules/kms/fixture/main.tf @@ -17,9 +17,7 @@ module "test" { source = "../../../../modules/kms" iam_members = var.iam_members - iam_roles = var.iam_roles key_iam_members = var.key_iam_members - key_iam_roles = var.key_iam_roles key_purpose = var.key_purpose key_purpose_defaults = var.key_purpose_defaults keyring = var.keyring diff --git a/tests/modules/kms/fixture/variables.tf b/tests/modules/kms/fixture/variables.tf index 10f3f318..04b77d84 100644 --- a/tests/modules/kms/fixture/variables.tf +++ b/tests/modules/kms/fixture/variables.tf @@ -21,11 +21,6 @@ variable "iam_members" { } } -variable "iam_roles" { - type = list(string) - default = ["roles/owner"] -} - variable "key_iam_members" { type = map(map(list(string))) default = { @@ -35,13 +30,6 @@ variable "key_iam_members" { } } -variable "key_iam_roles" { - type = map(list(string)) - default = { - key-a = ["roles/owner"] - } -} - variable "key_purpose" { type = map(object({ purpose = string From a27ec7bf61f40840b8ab54ae25e2e5c3bafb2938 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 30 Oct 2020 18:07:56 +0100 Subject: [PATCH 12/53] Fix peering test cases --- networking/hub-and-spoke-peering/main.tf | 2 +- tests/networking/hub_and_spoke_peering/test_plan.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/networking/hub-and-spoke-peering/main.tf b/networking/hub-and-spoke-peering/main.tf index 6b39929a..d5c09cc6 100644 --- a/networking/hub-and-spoke-peering/main.tf +++ b/networking/hub-and-spoke-peering/main.tf @@ -136,7 +136,7 @@ module "hub-to-spoke-2-peering" { peer_network = module.vpc-spoke-2.self_link export_local_custom_routes = true export_peer_custom_routes = false - module_depends_on = [module.hub-to-spoke-1-peering.complete] + depends_on = [module.hub-to-spoke-1-peering] } ################################################################################ diff --git a/tests/networking/hub_and_spoke_peering/test_plan.py b/tests/networking/hub_and_spoke_peering/test_plan.py index 34853ffa..fad58fb1 100644 --- a/tests/networking/hub_and_spoke_peering/test_plan.py +++ b/tests/networking/hub_and_spoke_peering/test_plan.py @@ -23,5 +23,7 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') def test_resources(e2e_plan_runner): "Test that plan works and the numbers of resources is as expected." modules, resources = e2e_plan_runner(FIXTURES_DIR) + import pprint + pprint.pprint(resources) assert len(modules) == 18 - assert len(resources) == 57 + assert len(resources) == 53 From 96dba2256e5c18e697c421c8a3c8afdba4d72efe Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 30 Oct 2020 18:23:46 +0100 Subject: [PATCH 13/53] Remove iam_roles from secret_manager --- modules/secret-manager/README.md | 9 ++------- modules/secret-manager/main.tf | 27 +++++++++++++++------------ modules/secret-manager/variables.tf | 8 +------- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/modules/secret-manager/README.md b/modules/secret-manager/README.md index b8224c53..601abc35 100644 --- a/modules/secret-manager/README.md +++ b/modules/secret-manager/README.md @@ -25,7 +25,7 @@ module "secret-manager" { ### 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_members` variable. ```hcl module "secret-manager" { @@ -35,10 +35,6 @@ module "secret-manager" { test-auto = null test-manual = ["europe-west1", "europe-west4"] } - iam_roles = { - test-auto = ["roles/secretmanager.secretAccessor"] - test-manual = ["roles/secretmanager.secretAccessor"] - } iam_members = { test-auto = { "roles/secretmanager.secretAccessor" = ["group:auto-readers@example.com"] @@ -80,8 +76,7 @@ module "secret-manager" { | name | description | type | required | default | |---|---|:---: |:---:|:---:| | project_id | Project id where the keyring will be created. | string | ✓ | | -| *iam_members* | IAM members keyed by secret name and role. | map(map(list(string))) | | {} | -| *iam_roles* | IAM roles keyed by secret name. | map(list(string)) | | {} | +| *iam_members* | IAM members keyed by secret name and role. | map(map(set(string))) | | {} | | *labels* | Optional labels for each secret. | map(map(string)) | | {} | | *secrets* | Map of secrets to manage and their locations. If locations is null, automatic management will be set. | map(list(string)) | | {} | | *versions* | Optional versions to manage for each secret. Version names are only used internally to track individual versions. | map(map(object({...}))) | | {} | diff --git a/modules/secret-manager/main.tf b/modules/secret-manager/main.tf index 57aaaf43..db52b12b 100644 --- a/modules/secret-manager/main.tf +++ b/modules/secret-manager/main.tf @@ -16,13 +16,15 @@ locals { # distinct is needed to make the expanding function argument work - iam_pairs = flatten([ - for name, roles in var.iam_roles : - [for role in roles : { name = name, role = role }] + iam_members = flatten([ + for secret, roles in var.iam_members : [ + for role, members in roles : { + secret = secret + role = role + members = members + } + ] ]) - iam_keypairs = { - for pair in local.iam_pairs : "${pair.name}-${pair.role}" => pair - } version_pairs = flatten([ for secret, versions in var.versions : [ for name, attrs in versions : merge(attrs, { name = name, secret = secret }) @@ -73,11 +75,12 @@ resource "google_secret_manager_secret_version" "default" { } resource "google_secret_manager_secret_iam_binding" "default" { - provider = google-beta - for_each = local.iam_keypairs + provider = google-beta + for_each = { + for binding in local.iam_members : + "${binding.secret}.${binding.role}" => binding + } role = each.value.role - secret_id = google_secret_manager_secret.default[each.value.name].id - members = lookup( - 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 } diff --git a/modules/secret-manager/variables.tf b/modules/secret-manager/variables.tf index b097018a..1517fea4 100644 --- a/modules/secret-manager/variables.tf +++ b/modules/secret-manager/variables.tf @@ -16,13 +16,7 @@ variable "iam_members" { description = "IAM members keyed by secret name and role." - type = map(map(list(string))) - default = {} -} - -variable "iam_roles" { - description = "IAM roles keyed by secret name." - type = map(list(string)) + type = map(map(set(string))) default = {} } From 82a1fe3c2089848260e3798d19387d09f313ad13 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 30 Oct 2020 18:55:54 +0100 Subject: [PATCH 14/53] Removing iam_roles from multiple modules This commits removes the iam_roles variables from the modules: - artifact-registry - bigtable-instance - cloud-function - container-registry - endopoints - pubsub - source-repository --- .../asset-inventory-feed-remediation/main.tf | 3 -- modules/artifact-registry/README.md | 4 +-- modules/artifact-registry/main.tf | 6 ++-- modules/artifact-registry/variables.tf | 8 +---- modules/bigtable-instance/README.md | 4 +-- modules/bigtable-instance/main.tf | 8 ++--- modules/bigtable-instance/variables.tf | 8 +---- modules/cloud-function/README.md | 4 +-- modules/cloud-function/main.tf | 6 ++-- modules/cloud-function/variables.tf | 8 +---- modules/container-registry/README.md | 4 +-- modules/container-registry/main.tf | 6 ++-- modules/container-registry/variables.tf | 8 +---- modules/endpoints/README.md | 4 +-- modules/endpoints/main.tf | 10 ++---- modules/endpoints/variables.tf | 11 ++---- modules/pubsub/README.md | 13 ++----- modules/pubsub/main.tf | 35 +++++++++---------- modules/pubsub/variables.tf | 16 ++------- modules/source-repository/README.md | 4 +-- modules/source-repository/main.tf | 6 ++-- modules/source-repository/variables.tf | 8 +---- 22 files changed, 51 insertions(+), 133 deletions(-) diff --git a/cloud-operations/asset-inventory-feed-remediation/main.tf b/cloud-operations/asset-inventory-feed-remediation/main.tf index 1ac8d7aa..07026681 100644 --- a/cloud-operations/asset-inventory-feed-remediation/main.tf +++ b/cloud-operations/asset-inventory-feed-remediation/main.tf @@ -63,9 +63,6 @@ module "pubsub" { project_id = module.project.project_id name = var.name subscriptions = { "${var.name}-default" = null } - iam_roles = [ - "roles/pubsub.publisher" - ] iam_members = { "roles/pubsub.publisher" = [ "serviceAccount:${module.project.service_accounts.robots.cloudasset}" diff --git a/modules/artifact-registry/README.md b/modules/artifact-registry/README.md index 480ea23a..bf0a82cc 100644 --- a/modules/artifact-registry/README.md +++ b/modules/artifact-registry/README.md @@ -13,7 +13,6 @@ module "docker_artifact_registry" { location = "europe-west1" format = "DOCKER" id = "myregistry" - iam_roles = ["roles/artifactregistry.admin"] iam_members = { "roles/artifactregistry.admin" = ["group:cicd@example.com"] } @@ -29,8 +28,7 @@ module "docker_artifact_registry" { | project_id | Registry project id. | string | ✓ | | | *description* | An optional description for the repository | string | | Terraform-managed registry | | *format* | Repository format. One of DOCKER or UNSPECIFIED | string | | DOCKER | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(list(string)) | | {} | -| *iam_roles* | List of roles used to set authoritative bindings. | list(string) | | [] | +| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(set(string)) | | {} | | *labels* | Labels to be attached to the registry. | map(string) | | {} | | *location* | Registry location. Use `gcloud beta artifacts locations list' to get valid values | string | | | diff --git a/modules/artifact-registry/main.tf b/modules/artifact-registry/main.tf index 81c366f4..7a7a07d2 100644 --- a/modules/artifact-registry/main.tf +++ b/modules/artifact-registry/main.tf @@ -26,10 +26,10 @@ resource "google_artifact_registry_repository" "registry" { resource "google_artifact_registry_repository_iam_binding" "bindings" { provider = google-beta - for_each = toset(var.iam_roles) + for_each = var.iam_members project = var.project_id location = google_artifact_registry_repository.registry.location repository = google_artifact_registry_repository.registry.name - role = each.value - members = lookup(var.iam_members, each.value, []) + role = each.key + members = each.value } diff --git a/modules/artifact-registry/variables.tf b/modules/artifact-registry/variables.tf index 7aa8fdca..0726053a 100644 --- a/modules/artifact-registry/variables.tf +++ b/modules/artifact-registry/variables.tf @@ -16,16 +16,10 @@ variable "iam_members" { description = "Map of member lists used to set authoritative bindings, keyed by role." - type = map(list(string)) + type = map(set(string)) default = {} } -variable "iam_roles" { - description = "List of roles used to set authoritative bindings." - type = list(string) - default = [] -} - variable "location" { description = "Registry location. Use `gcloud beta artifacts locations list' to get valid values" type = string diff --git a/modules/bigtable-instance/README.md b/modules/bigtable-instance/README.md index f63e1cf0..da332180 100644 --- a/modules/bigtable-instance/README.md +++ b/modules/bigtable-instance/README.md @@ -27,7 +27,6 @@ module "big-table-instance" { } } } - iam_roles = ["viewer"] iam_members = { viewer = ["user:viewer@testdomain.com"] } @@ -45,8 +44,7 @@ module "big-table-instance" { | *cluster_id* | The ID of the Cloud Bigtable cluster. | string | | europe-west1 | | *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. | | | true | | *display_name* | The human-readable display name of the Bigtable instance. | | | null | -| *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. | map(list(string)) | | {} | -| *iam_roles* | Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. | list(string) | | [] | +| *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. | map(set(string)) | | {} | | *instance_type* | None | string | | DEVELOPMENT | | *num_nodes* | The number of nodes in your Cloud Bigtable cluster. | number | | 1 | | *storage_type* | The storage type to use. | string | | SSD | diff --git a/modules/bigtable-instance/main.tf b/modules/bigtable-instance/main.tf index 0e7129ff..32c27bb5 100644 --- a/modules/bigtable-instance/main.tf +++ b/modules/bigtable-instance/main.tf @@ -18,10 +18,6 @@ locals { tables = { for k, v in var.tables : k => v.table_options != null ? v.table_options : var.table_options_defaults } - - iam_roles_bindings = { - for k in var.iam_roles : k => lookup(var.iam_members, k, []) - } } resource "google_bigtable_instance" "default" { @@ -39,11 +35,11 @@ resource "google_bigtable_instance" "default" { } resource "google_bigtable_instance_iam_binding" "default" { - for_each = local.iam_roles_bindings + for_each = var.iam_members project = var.project_id instance = google_bigtable_instance.default.name - role = "roles/bigtable.${each.key}" + role = each.key members = each.value } diff --git a/modules/bigtable-instance/variables.tf b/modules/bigtable-instance/variables.tf index 7dbbaa25..0e2db64f 100644 --- a/modules/bigtable-instance/variables.tf +++ b/modules/bigtable-instance/variables.tf @@ -14,15 +14,9 @@ * 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)) + type = map(set(string)) default = {} } diff --git a/modules/cloud-function/README.md b/modules/cloud-function/README.md index ca9848f4..71e135e8 100644 --- a/modules/cloud-function/README.md +++ b/modules/cloud-function/README.md @@ -63,7 +63,6 @@ module "cf-http" { source_dir = "my-cf-source-folder" output_path = "bundle.zip" } - iam_roles = ["roles/cloudfunctions.invoker"] iam_members = { "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. | object({...}) | | null | | *environment_variables* | Cloud function environment variables. | map(string) | | {} | | *function_config* | Cloud function configuration. | object({...}) | | ... | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use. | map(list(string)) | | {} | -| *iam_roles* | List of roles used to set authoritative bindings. Ignored for template use. | list(string) | | [] | +| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use. | map(set(string)) | | {} | | *ingress_settings* | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL and ALLOW_INTERNAL_ONLY. | string | | null | | *labels* | Resource labels | map(string) | | {} | | *prefix* | Optional prefix used for resource names. | string | | null | diff --git a/modules/cloud-function/main.tf b/modules/cloud-function/main.tf index 22424765..3b1818a1 100644 --- a/modules/cloud-function/main.tf +++ b/modules/cloud-function/main.tf @@ -95,12 +95,12 @@ resource "google_cloudfunctions_function" "function" { } resource "google_cloudfunctions_function_iam_binding" "default" { - for_each = toset(var.iam_roles) + for_each = var.iam_members project = var.project_id region = var.region cloud_function = google_cloudfunctions_function.function.name - role = each.value - members = try(var.iam_members[each.value], {}) + role = each.key + members = each.value } resource "google_storage_bucket" "bucket" { diff --git a/modules/cloud-function/variables.tf b/modules/cloud-function/variables.tf index eea9131f..427e3417 100644 --- a/modules/cloud-function/variables.tf +++ b/modules/cloud-function/variables.tf @@ -44,16 +44,10 @@ variable "environment_variables" { variable "iam_members" { description = "Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use." - type = map(list(string)) + type = map(set(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" { description = "Cloud function configuration." type = object({ diff --git a/modules/container-registry/README.md b/modules/container-registry/README.md index 167f79b3..ffafa387 100644 --- a/modules/container-registry/README.md +++ b/modules/container-registry/README.md @@ -9,7 +9,6 @@ module "container_registry" { source = "../../modules/container-registry" project_id = "myproject" location = "EU" - iam_roles = ["roles/storage.admin"] iam_members = { "roles/storage.admin" = ["group:cicd@example.com"] } @@ -22,8 +21,7 @@ module "container_registry" { | name | description | type | required | default | |---|---|:---: |:---:|:---:| | project_id | Registry project id. | string | ✓ | | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(list(string)) | | null | -| *iam_roles* | List of roles used to set authoritative bindings. | list(string) | | null | +| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(set(string)) | | null | | *location* | Registry location. Can be US, EU, ASIA or empty | string | | | ## Outputs diff --git a/modules/container-registry/main.tf b/modules/container-registry/main.tf index 073e2995..7a84a281 100644 --- a/modules/container-registry/main.tf +++ b/modules/container-registry/main.tf @@ -20,8 +20,8 @@ resource "google_container_registry" "registry" { } resource "google_storage_bucket_iam_binding" "bindings" { - for_each = toset(var.iam_roles) + for_each = var.iam_members bucket = google_container_registry.registry.id - role = each.value - members = lookup(var.iam_members, each.value, []) + role = each.key + members = each.value } diff --git a/modules/container-registry/variables.tf b/modules/container-registry/variables.tf index 15074aca..1e5ae3f6 100644 --- a/modules/container-registry/variables.tf +++ b/modules/container-registry/variables.tf @@ -16,13 +16,7 @@ variable "iam_members" { description = "Map of member lists used to set authoritative bindings, keyed by role." - type = map(list(string)) - default = null -} - -variable "iam_roles" { - description = "List of roles used to set authoritative bindings." - type = list(string) + type = map(set(string)) default = null } diff --git a/modules/endpoints/README.md b/modules/endpoints/README.md index f57952e4..adb98de5 100644 --- a/modules/endpoints/README.md +++ b/modules/endpoints/README.md @@ -13,7 +13,6 @@ module "endpoint" { service_name = "YOUR-API.endpoints.YOUR-PROJECT-ID.cloud.goog" openapi_config = { "yaml_path" = "openapi.yaml" } grpc_config = null - iam_roles = ["servicemanagement.serviceController"] iam_members = { "servicemanagement.serviceController" = ["serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com"] } @@ -30,8 +29,7 @@ module "endpoint" { | grpc_config | The configuration for a gRPC enpoint. Either this or openapi_config must be specified. | object({...}) | ✓ | | | openapi_config | The configuration for an OpenAPI endopoint. Either this or grpc_config must be specified. | object({...}) | ✓ | | | service_name | The name of the service. Usually of the form '$apiname.endpoints.$projectid.cloud.goog'. | string | ✓ | | -| *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. | map(list(string)) | | {} | -| *iam_roles* | Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. | list(string) | | [] | +| *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. | map(set(string)) | | {} | | *project_id* | The project ID that the service belongs to. | string | | null | ## Outputs diff --git a/modules/endpoints/main.tf b/modules/endpoints/main.tf index 1b9cedbe..872b7107 100644 --- a/modules/endpoints/main.tf +++ b/modules/endpoints/main.tf @@ -14,12 +14,6 @@ * 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" { project = var.project_id service_name = var.service_name @@ -29,8 +23,8 @@ resource "google_endpoints_service" "default" { } resource "google_endpoints_service_iam_binding" "default" { - for_each = local.iam_roles_bindings + for_each = var.iam_members service_name = google_endpoints_service.default.service_name - role = "roles/${each.key}" + role = each.key members = each.value } diff --git a/modules/endpoints/variables.tf b/modules/endpoints/variables.tf index 76fb8b8b..acf23401 100644 --- a/modules/endpoints/variables.tf +++ b/modules/endpoints/variables.tf @@ -16,27 +16,22 @@ variable "grpc_config" { description = "The configuration for a gRPC enpoint. Either this or openapi_config must be specified." - type = object({ + type = object({ yaml_path = string protoc_output_path = string }) } -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)) + type = map(set(string)) default = {} } variable "openapi_config" { description = "The configuration for an OpenAPI endopoint. Either this or grpc_config must be specified." - type = object({ + type = object({ yaml_path = string }) } diff --git a/modules/pubsub/README.md b/modules/pubsub/README.md index 4b672760..974fe42c 100644 --- a/modules/pubsub/README.md +++ b/modules/pubsub/README.md @@ -12,10 +12,6 @@ module "pubsub" { source = "./modules/pubsub" project_id = "my-project" name = "my-topic" - iam_roles = [ - "roles/pubsub.viewer", - "roles/pubsub.subscriber" - ] iam_members = { "roles/pubsub.viewer" = ["group:foo@example.com"] "roles/pubsub.subscriber" = ["user:user1@example.com"] @@ -80,9 +76,6 @@ module "pubsub" { test-1 = null test-1 = null } - subscription_iam_roles = { - test-1 = ["roles/pubsub.subscriber"] - } subscription_iam_members = { test-1 = { "roles/pubsub.subscriber" = ["user:user1@ludomagno.net"] @@ -100,14 +93,12 @@ module "pubsub" { | project_id | Project used for resources. | string | ✓ | | | *dead_letter_configs* | Per-subscription dead letter policy configuration. | map(object({...})) | | {} | | *defaults* | Subscription defaults for options. | object({...}) | | ... | -| *iam_members* | IAM members for each topic role. | map(list(string)) | | {} | -| *iam_roles* | IAM roles for topic. | list(string) | | [] | +| *iam_members* | IAM members for each topic role. | map(set(string)) | | {} | | *kms_key* | KMS customer managed encryption key. | string | | null | | *labels* | Labels. | map(string) | | {} | | *push_configs* | Push subscription configurations. | map(object({...})) | | {} | | *regions* | List of regions used to set persistence policy. | list(string) | | [] | -| *subscription_iam_members* | IAM members for each subscription and role. | map(map(list(string))) | | {} | -| *subscription_iam_roles* | IAM roles for each subscription. | map(list(string)) | | {} | +| *subscription_iam_members* | IAM members for each subscription and role. | map(map(set(string))) | | {} | | *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. | map(object({...})) | | {} | ## Outputs diff --git a/modules/pubsub/main.tf b/modules/pubsub/main.tf index 50876070..31caa258 100644 --- a/modules/pubsub/main.tf +++ b/modules/pubsub/main.tf @@ -15,17 +15,15 @@ */ locals { - iam_pairs = var.subscription_iam_roles == null ? [] : flatten([ - for name, roles in var.subscription_iam_roles : - [for role in roles : { name = name, role = role }] + sub_iam_members = flatten([ + for sub, roles in var.subscription_iam_members : [ + for role, members in roles : { + sub = sub + role = role + members = members + } + ] ]) - iam_keypairs = { - for pair in local.iam_pairs : - "${pair.name}-${pair.role}" => pair - } - iam_members = ( - var.subscription_iam_members == null ? {} : var.subscription_iam_members - ) oidc_config = { 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" { - for_each = toset(var.iam_roles) + for_each = var.iam_members project = var.project_id topic = google_pubsub_topic.default.name - role = each.value - members = lookup(var.iam_members, each.value, []) + role = each.key + members = each.value } resource "google_pubsub_subscription" "default" { @@ -103,11 +101,12 @@ resource "google_pubsub_subscription" "default" { } resource "google_pubsub_subscription_iam_binding" "default" { - for_each = local.iam_keypairs + for_each = { + for binding in local.sub_iam_members : + "${binding.sub}.${binding.role}" => binding + } project = var.project_id - subscription = google_pubsub_subscription.default[each.value.name].name + subscription = google_pubsub_subscription.default[each.value.sub].name role = each.value.role - members = lookup( - lookup(local.iam_members, each.value.name, {}), each.value.role, [] - ) + members = each.value.members } diff --git a/modules/pubsub/variables.tf b/modules/pubsub/variables.tf index d642c7a6..e6b15083 100644 --- a/modules/pubsub/variables.tf +++ b/modules/pubsub/variables.tf @@ -41,16 +41,10 @@ variable "defaults" { variable "iam_members" { description = "IAM members for each topic role." - type = map(list(string)) + type = map(set(string)) default = {} } -variable "iam_roles" { - description = "IAM roles for topic." - type = list(string) - default = [] -} - variable "kms_key" { description = "KMS customer managed encryption key." type = string @@ -109,12 +103,6 @@ variable "subscriptions" { variable "subscription_iam_members" { description = "IAM members for each subscription and role." - type = map(map(list(string))) - default = {} -} - -variable "subscription_iam_roles" { - description = "IAM roles for each subscription." - type = map(list(string)) + type = map(map(set(string))) default = {} } diff --git a/modules/source-repository/README.md b/modules/source-repository/README.md index c5a2eb85..9beae244 100644 --- a/modules/source-repository/README.md +++ b/modules/source-repository/README.md @@ -12,7 +12,6 @@ module "repo" { source e = "./modules/source-repository" project_id = "my-project" name = "my-repo" - iam_roles = ["roles/source.reader"] iam_members = { "roles/source.reader" = ["user:foo@example.com"] } @@ -26,8 +25,7 @@ module "repo" { |---|---|:---: |:---:|:---:| | name | Repository topic name. | string | ✓ | | | project_id | Project used for resources. | string | ✓ | | -| *iam_members* | IAM members for each topic role. | map(list(string)) | | {} | -| *iam_roles* | IAM roles for topic. | list(string) | | [] | +| *iam_members* | IAM members for each topic role. | map(set(string)) | | {} | ## Outputs diff --git a/modules/source-repository/main.tf b/modules/source-repository/main.tf index 810b4482..71432e16 100644 --- a/modules/source-repository/main.tf +++ b/modules/source-repository/main.tf @@ -20,11 +20,11 @@ resource "google_sourcerepo_repository" "default" { } resource "google_sourcerepo_repository_iam_binding" "default" { - for_each = toset(var.iam_roles) + for_each = var.iam_members project = var.project_id repository = google_sourcerepo_repository.default.name - role = each.value - members = lookup(var.iam_members, each.value, []) + role = each.key + members = each.value depends_on = [ google_sourcerepo_repository.default diff --git a/modules/source-repository/variables.tf b/modules/source-repository/variables.tf index 1725d4b4..a7254370 100644 --- a/modules/source-repository/variables.tf +++ b/modules/source-repository/variables.tf @@ -21,16 +21,10 @@ variable "project_id" { variable "iam_members" { description = "IAM members for each topic role." - type = map(list(string)) + type = map(set(string)) default = {} } -variable "iam_roles" { - description = "IAM roles for topic." - type = list(string) - default = [] -} - variable "name" { description = "Repository topic name." type = string From 405a3c23d5ff078675f44bef638976558801686e Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 30 Oct 2020 19:06:20 +0100 Subject: [PATCH 15/53] Update project README --- modules/project/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/project/README.md b/modules/project/README.md index 8c4e917c..72dd368b 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -15,7 +15,6 @@ module "project" { "container.googleapis.com", "stackdriver.googleapis.com" ] - iam_roles = ["roles/container.hostServiceAgentUser"] iam_members = { "roles/container.hostServiceAgentUser" = [ "serviceAccount:${var.gke_service_account}" From c75230adf3471cee19f811f66cb1fe6371fe18a4 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 30 Oct 2020 19:19:40 +0100 Subject: [PATCH 16/53] Remove iam_roles from net-vpc --- modules/net-vpc/README.md | 7 ------ modules/net-vpc/main.tf | 25 ++++++++++++---------- modules/net-vpc/variables.tf | 6 ------ networking/shared-vpc-gke/main.tf | 4 ---- tests/modules/net_vpc/fixture/main.tf | 1 - tests/modules/net_vpc/fixture/variables.tf | 7 +----- 6 files changed, 15 insertions(+), 35 deletions(-) diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md index 39406973..d541d176 100644 --- a/modules/net-vpc/README.md +++ b/modules/net-vpc/README.md @@ -86,12 +86,6 @@ module "vpc-host" { local.service_project_1.project_id, local.service_project_2.project_id ] - iam_roles = { - "europe-west1/subnet-1" = [ - "roles/compute.networkUser", - "roles/compute.securityAdmin" - ] - } iam_members = { "europe-west1/subnet-1" = { "roles/compute.networkUser" = [ @@ -117,7 +111,6 @@ module "vpc-host" { | *delete_default_routes_on_create* | Set to true to delete the default routes at creation time. | bool | | false | | *description* | An optional description of this resource (triggers recreation on change). | string | | Terraform-managed. | | *iam_members* | List of IAM members keyed by subnet 'region/name' and role. | map(map(list(string))) | | {} | -| *iam_roles* | List of IAM roles keyed by subnet 'region/name'. | map(list(string)) | | {} | | *log_config_defaults* | Default configuration for flow logs when enabled. | object({...}) | | ... | | *log_configs* | Map keyed by subnet 'region/name' of optional configurations for flow logs when enabled. | map(map(string)) | | {} | | *peering_config* | VPC peering configuration. | object({...}) | | null | diff --git a/modules/net-vpc/main.tf b/modules/net-vpc/main.tf index 9f22d375..bad4f870 100644 --- a/modules/net-vpc/main.tf +++ b/modules/net-vpc/main.tf @@ -16,14 +16,16 @@ locals { iam_members = var.iam_members == null ? {} : var.iam_members - iam_pairs = var.iam_roles == null ? [] : flatten([ - for subnet, roles in var.iam_roles : - [for role in roles : { subnet = subnet, role = role }] + subnet_iam_members = flatten([ + for subnet, roles in local.iam_members : [ + for role, members in roles : { + subnet = subnet + role = role + members = members + } + ] ]) - iam_keypairs = { - for pair in local.iam_pairs : - "${pair.subnet}-${pair.role}" => pair - } + log_configs = var.log_configs == null ? {} : var.log_configs peer_network = ( var.peering_config == null @@ -152,14 +154,15 @@ resource "google_compute_subnetwork" "subnetwork" { } 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 subnetwork = google_compute_subnetwork.subnetwork[each.value.subnet].name region = google_compute_subnetwork.subnetwork[each.value.subnet].region role = each.value.role - members = lookup( - lookup(local.iam_members, each.value.subnet, {}), each.value.role, [] - ) + members = each.value.members } resource "google_compute_route" "gateway" { diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf index 3a4d0c03..7aa0fd8b 100644 --- a/modules/net-vpc/variables.tf +++ b/modules/net-vpc/variables.tf @@ -32,12 +32,6 @@ variable "description" { default = "Terraform-managed." } -variable "iam_roles" { - description = "List of IAM roles keyed by subnet 'region/name'." - 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))) diff --git a/networking/shared-vpc-gke/main.tf b/networking/shared-vpc-gke/main.tf index ef6ee338..f3f73bb0 100644 --- a/networking/shared-vpc-gke/main.tf +++ b/networking/shared-vpc-gke/main.tf @@ -107,10 +107,6 @@ module "vpc-shared" { } } ] - iam_roles = { - "${var.region}/gke" = ["roles/compute.networkUser", "roles/compute.securityAdmin"] - "${var.region}/gce" = ["roles/compute.networkUser"] - } iam_members = { "${var.region}/gce" = { "roles/compute.networkUser" = concat(var.owners_gce, [ diff --git a/tests/modules/net_vpc/fixture/main.tf b/tests/modules/net_vpc/fixture/main.tf index a9d92d47..5ab2c4f8 100644 --- a/tests/modules/net_vpc/fixture/main.tf +++ b/tests/modules/net_vpc/fixture/main.tf @@ -19,7 +19,6 @@ module "test" { project_id = var.project_id name = var.name iam_members = var.iam_members - iam_roles = var.iam_roles log_configs = var.log_configs log_config_defaults = var.log_config_defaults peering_config = var.peering_config diff --git a/tests/modules/net_vpc/fixture/variables.tf b/tests/modules/net_vpc/fixture/variables.tf index 908548dd..7388ad66 100644 --- a/tests/modules/net_vpc/fixture/variables.tf +++ b/tests/modules/net_vpc/fixture/variables.tf @@ -29,13 +29,8 @@ variable "auto_create_subnetworks" { default = false } -variable "iam_roles" { - type = map(list(string)) - default = null -} - variable "iam_members" { - type = map(map(list(string))) + type = map(map(set(string))) default = null } From 352b7a064536e601cee2c144d7e0a8e65ef8425b Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Tue, 3 Nov 2020 07:59:52 +0100 Subject: [PATCH 17/53] Use description variable in dns module --- modules/dns/main.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dns/main.tf b/modules/dns/main.tf index d7e98b52..19c2d0d7 100644 --- a/modules/dns/main.tf +++ b/modules/dns/main.tf @@ -34,12 +34,12 @@ locals { } resource "google_dns_managed_zone" "non-public" { - count = (var.zone_create && var.type != "public" ) ? 1 : 0 + count = (var.zone_create && var.type != "public") ? 1 : 0 provider = google-beta project = var.project_id name = var.name dns_name = var.domain - description = "Terraform-managed zone." + description = var.description visibility = "private" dynamic forwarding_config { From 7bd31ccf52f16837db32fb7780c6fe3d8d4dbe65 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Tue, 3 Nov 2020 08:00:16 +0100 Subject: [PATCH 18/53] Added some basic variable validation --- modules/dns/main.tf | 14 +++++++------- modules/dns/variables.tf | 5 +++++ modules/gcs/variables.tf | 4 ++++ modules/net-vpc/variables.tf | 5 +++++ modules/project/variables.tf | 4 ++++ 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/modules/dns/main.tf b/modules/dns/main.tf index 19c2d0d7..7e6154a2 100644 --- a/modules/dns/main.tf +++ b/modules/dns/main.tf @@ -22,10 +22,10 @@ locals { zone = ( var.zone_create ? try( - google_dns_managed_zone.non-public.0, try( - google_dns_managed_zone.public.0, null - ) + google_dns_managed_zone.non-public.0, try( + google_dns_managed_zone.public.0, null ) + ) : try(data.google_dns_managed_zone.public.0, null) ) dns_keys = try( @@ -94,12 +94,12 @@ resource "google_dns_managed_zone" "non-public" { } data "google_dns_managed_zone" "public" { - count = var.zone_create ? 0 : 1 - name = var.name + count = var.zone_create ? 0 : 1 + name = var.name } resource "google_dns_managed_zone" "public" { - count = (var.zone_create && var.type == "public" ) ? 1 : 0 + count = (var.zone_create && var.type == "public") ? 1 : 0 project = var.project_id name = var.name dns_name = var.domain @@ -132,7 +132,7 @@ resource "google_dns_managed_zone" "public" { } data "google_dns_keys" "dns_keys" { - count = var.zone_create && ( var.dnssec_config == {} || var.type != "public" ) ? 0 : 1 + count = var.zone_create && (var.dnssec_config == {} || var.type != "public") ? 0 : 1 managed_zone = local.zone.id } diff --git a/modules/dns/variables.tf b/modules/dns/variables.tf index d6e267d9..be32349b 100644 --- a/modules/dns/variables.tf +++ b/modules/dns/variables.tf @@ -97,6 +97,10 @@ variable "type" { description = "Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'." type = string 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" { @@ -106,3 +110,4 @@ variable "zone_create" { } + diff --git a/modules/gcs/variables.tf b/modules/gcs/variables.tf index 3f48caa6..7c8d800f 100644 --- a/modules/gcs/variables.tf +++ b/modules/gcs/variables.tf @@ -88,6 +88,10 @@ variable "storage_class" { description = "Bucket storage class." type = string 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" { diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf index 7aa0fd8b..6c3ab855 100644 --- a/modules/net-vpc/variables.tf +++ b/modules/net-vpc/variables.tf @@ -100,6 +100,11 @@ variable "routing_mode" { description = "The network routing mode (default 'GLOBAL')" type = string 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" { diff --git a/modules/project/variables.tf b/modules/project/variables.tf index a0e965bf..8972166a 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -83,6 +83,10 @@ variable "parent" { description = "Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format." type = string default = null + 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" { From 8dc131550f2f0ceb4e75461f3dc6af26ada35607 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 12:06:01 +0100 Subject: [PATCH 19/53] fix variable validation in project module to allow for null values --- modules/project/variables.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 8972166a..75150cc1 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -84,7 +84,7 @@ variable "parent" { type = string default = null validation { - condition = can(regex("(organizations|folders)/[0-9]+", var.parent)) + 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." } } From c372980e1508396abf65ba6773c80c8311e93b12 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 14:08:33 +0100 Subject: [PATCH 20/53] update service directory module to 0.13 --- modules/service-directory/README.md | 21 ++----- modules/service-directory/main.tf | 17 +++--- modules/service-directory/variables.tf | 20 ++----- tests/modules/service_directory/__init__.py | 13 +++++ .../modules/service_directory/fixture/main.tf | 55 ++++++++++++++++++ .../service_directory/fixture/variables.tf | 20 +++++++ tests/modules/service_directory/test_plan.py | 57 +++++++++++++++++++ 7 files changed, 161 insertions(+), 42 deletions(-) create mode 100644 tests/modules/service_directory/__init__.py create mode 100644 tests/modules/service_directory/fixture/main.tf create mode 100644 tests/modules/service_directory/fixture/variables.tf create mode 100644 tests/modules/service_directory/test_plan.py diff --git a/modules/service-directory/README.md b/modules/service-directory/README.md index c57bd9f8..5bd32fd3 100644 --- a/modules/service-directory/README.md +++ b/modules/service-directory/README.md @@ -15,14 +15,11 @@ module "service-directory" { project_id = "my-project" location = "europe-west1" name = "sd-1" - iam_members = { + iam = { "roles/servicedirectory.editor" = [ "serviceAccount:namespace-editor@example.com" ] } - iam_roles = [ - "roles/servicedirectory.editor" - ] } ``` @@ -40,16 +37,13 @@ module "service-directory" { metadata = null } } - service_iam_members = { + service_iam = { one = { "roles/servicedirectory.editor" = [ "serviceAccount:service-editor.example.com" ] } } - service_iam_roles = { - one = ["roles/servicedirectory.editor"] - } endpoint_config = { "one/first" = { address = "127.0.0.1", port = 80, metadata = {} } "one/second" = { address = "127.0.0.2", port = 80, metadata = {} } @@ -67,14 +61,11 @@ module "service-directory" { project_id = "my-project" location = "europe-west1" name = "apps" - iam_members = { + iam = { "roles/servicedirectory.editor" = [ "serviceAccount:namespace-editor@example.com" ] } - iam_roles = [ - "roles/servicedirectory.editor" - ] services = { app1 = { endpoints = ["one"], metadata = null } } @@ -104,11 +95,9 @@ module "dns-sd" { | name | Namespace name. | string | ✓ | | | project_id | Project used for resources. | string | ✓ | | | *endpoint_config* | Map of endpoint attributes, keys are in service/endpoint format. | map(object({...})) | | {} | -| *iam_members* | IAM members for each namespace role. | map(list(string)) | | {} | -| *iam_roles* | IAM roles for the namespace. | list(string) | | [] | +| *iam* | IAM bindings for namespace, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *labels* | Labels. | map(string) | | {} | -| *service_iam_members* | IAM members for each service and role. | map(map(list(string))) | | {} | -| *service_iam_roles* | IAM roles for each service. | map(list(string)) | | {} | +| *service_iam* | IAM bindings for services, in {SERVICE => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | | *services* | Service configuration, using service names as keys. | map(object({...})) | | {} | ## Outputs diff --git a/modules/service-directory/main.tf b/modules/service-directory/main.tf index 8348de44..bc27f1d5 100644 --- a/modules/service-directory/main.tf +++ b/modules/service-directory/main.tf @@ -23,17 +23,14 @@ locals { endpoints = { for ep in local.endpoint_list : "${ep.service}/${ep.endpoint}" => ep } - iam_pairs = var.service_iam_roles == null ? [] : flatten([ - for name, roles in var.service_iam_roles : - [for role in roles : { name = name, role = role }] + iam_pairs = var.service_iam == null ? [] : flatten([ + for name, bindings in var.service_iam : + [for role in keys(bindings) : { name = name, role = role }] ]) iam_keypairs = { for pair in local.iam_pairs : "${pair.name}-${pair.role}" => pair } - iam_members = ( - var.service_iam_members == null ? {} : var.service_iam_members - ) } resource "google_service_directory_namespace" "default" { @@ -46,10 +43,10 @@ resource "google_service_directory_namespace" "default" { resource "google_service_directory_namespace_iam_binding" "default" { provider = google-beta - for_each = toset(var.iam_roles) + for_each = var.iam name = google_service_directory_namespace.default.name - role = each.value - members = lookup(var.iam_members, each.value, []) + role = each.key + members = each.value } 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 role = each.value.role members = lookup( - lookup(local.iam_members, each.value.name, {}), each.value.role, [] + lookup(var.service_iam, each.value.name, {}), each.value.role, [] ) } diff --git a/modules/service-directory/variables.tf b/modules/service-directory/variables.tf index 8b921d56..15563256 100644 --- a/modules/service-directory/variables.tf +++ b/modules/service-directory/variables.tf @@ -25,18 +25,12 @@ variable "endpoint_config" { default = {} } -variable "iam_members" { - description = "IAM members for each namespace role." +variable "iam" { + description = "IAM bindings for namespace, in {ROLE => [MEMBERS]} format." type = map(list(string)) default = {} } -variable "iam_roles" { - description = "IAM roles for the namespace." - type = list(string) - default = [] -} - variable "labels" { description = "Labels." type = map(string) @@ -58,18 +52,12 @@ variable "project_id" { type = string } -variable "service_iam_members" { - description = "IAM members for each service and role." +variable "service_iam" { + description = "IAM bindings for services, in {SERVICE => {ROLE => [MEMBERS]}} format." type = map(map(list(string))) default = {} } -variable "service_iam_roles" { - description = "IAM roles for each service." - type = map(list(string)) - default = {} -} - variable "services" { description = "Service configuration, using service names as keys." type = map(object({ diff --git a/tests/modules/service_directory/__init__.py b/tests/modules/service_directory/__init__.py new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/modules/service_directory/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/modules/service_directory/fixture/main.tf b/tests/modules/service_directory/fixture/main.tf new file mode 100644 index 00000000..91835372 --- /dev/null +++ b/tests/modules/service_directory/fixture/main.tf @@ -0,0 +1,55 @@ +/** + * 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. + */ + +module "test" { + source = "../../../../modules/service-directory" + project_id = "my-project" + location = "europe-west1" + name = "ns-test" + iam = { + "roles/servicedirectory.viewer" = [ + "serviceAccount:service-editor.example.com" + ] + } + services = { + srv-one = { + endpoints = ["alpha", "beta"] + metadata = null + } + srv-two = { + endpoints = ["alpha"] + metadata = null + } + } + service_iam = { + srv-one = { + "roles/servicedirectory.editor" = [ + "serviceAccount:service-editor.example.com" + ] + } + srv-two = { + "roles/servicedirectory.admin" = [ + "serviceAccount:service-editor.example.com" + ] + } + } + endpoint_config = { + "srv-one/alpha" = { address = "127.0.0.1", port = 80, metadata = {} } + "srv-one/beta" = { address = "127.0.0.2", port = 80, metadata = {} } + "srv-two/alpha" = { address = "127.0.0.3", port = 80, metadata = {} } + } + labels = var.labels +} diff --git a/tests/modules/service_directory/fixture/variables.tf b/tests/modules/service_directory/fixture/variables.tf new file mode 100644 index 00000000..4e0e0396 --- /dev/null +++ b/tests/modules/service_directory/fixture/variables.tf @@ -0,0 +1,20 @@ +/** + * 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. + */ + +variable "labels" { + type = map(string) + default = {} +} diff --git a/tests/modules/service_directory/test_plan.py b/tests/modules/service_directory/test_plan.py new file mode 100644 index 00000000..dd74e926 --- /dev/null +++ b/tests/modules/service_directory/test_plan.py @@ -0,0 +1,57 @@ +# 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +@pytest.fixture +def resources(plan_runner): + _, resources = plan_runner(FIXTURES_DIR) + return resources + + +def test_resource_count(resources): + "Test number of resources created." + assert len(resources) == 9 + + +def test_service_iam(resources): + "Test service IAM binding resources." + bindings = [r['values'] for r in resources if r['type'] + == 'google_service_directory_namespace_iam_binding'] + assert len(bindings) == 1 + assert bindings[0]['role'] == 'roles/servicedirectory.editor' + + +def test_services(resources): + "Test service resources." + services = [r['values'] for r in resources if r['type'] + == 'google_service_directory_service'] + assert len(services) == 2 + assert set(s['service_id'] for s in services) == set(['srv-one', 'srv-two']) + + +def test_service_iam(resources): + "Test service IAM binding resources." + bindings = [r['values'] for r in resources if r['type'] + == 'google_service_directory_service_iam_binding'] + assert len(bindings) == 2 + assert set(b['role'] for b in bindings) == set([ + 'roles/servicedirectory.admin', 'roles/servicedirectory.editor' + ]) From 627f86219fa912c2957f4fd330a3979697dbf5a2 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 14:19:16 +0100 Subject: [PATCH 21/53] rename iam variable in secret manager module, add tests --- modules/secret-manager/README.md | 4 +- modules/secret-manager/main.tf | 7 ++-- modules/secret-manager/variables.tf | 6 +-- tests/modules/secret_manager/__init__.py | 13 ++++++ tests/modules/secret_manager/fixture/main.tf | 42 +++++++++++++++++++ .../secret_manager/fixture/variables.tf | 20 +++++++++ tests/modules/secret_manager/test_plan.py | 41 ++++++++++++++++++ 7 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 tests/modules/secret_manager/__init__.py create mode 100644 tests/modules/secret_manager/fixture/main.tf create mode 100644 tests/modules/secret_manager/fixture/variables.tf create mode 100644 tests/modules/secret_manager/test_plan.py diff --git a/modules/secret-manager/README.md b/modules/secret-manager/README.md index 601abc35..b1617e62 100644 --- a/modules/secret-manager/README.md +++ b/modules/secret-manager/README.md @@ -35,7 +35,7 @@ module "secret-manager" { test-auto = null test-manual = ["europe-west1", "europe-west4"] } - iam_members = { + iam = { test-auto = { "roles/secretmanager.secretAccessor" = ["group:auto-readers@example.com"] } @@ -76,7 +76,7 @@ module "secret-manager" { | name | description | type | required | default | |---|---|:---: |:---:|:---:| | project_id | Project id where the keyring will be created. | string | ✓ | | -| *iam_members* | IAM members keyed by secret name and role. | map(map(set(string))) | | {} | +| *iam* | IAM bindings in {SECRET => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | | *labels* | Optional labels for each secret. | map(map(string)) | | {} | | *secrets* | Map of secrets to manage and their locations. If locations is null, automatic management will be set. | map(list(string)) | | {} | | *versions* | Optional versions to manage for each secret. Version names are only used internally to track individual versions. | map(map(object({...}))) | | {} | diff --git a/modules/secret-manager/main.tf b/modules/secret-manager/main.tf index db52b12b..74179780 100644 --- a/modules/secret-manager/main.tf +++ b/modules/secret-manager/main.tf @@ -16,8 +16,8 @@ locals { # distinct is needed to make the expanding function argument work - iam_members = flatten([ - for secret, roles in var.iam_members : [ + iam = flatten([ + for secret, roles in var.iam : [ for role, members in roles : { secret = secret role = role @@ -77,8 +77,7 @@ resource "google_secret_manager_secret_version" "default" { resource "google_secret_manager_secret_iam_binding" "default" { provider = google-beta for_each = { - for binding in local.iam_members : - "${binding.secret}.${binding.role}" => binding + for binding in local.iam : "${binding.secret}.${binding.role}" => binding } role = each.value.role secret_id = google_secret_manager_secret.default[each.value.secret].id diff --git a/modules/secret-manager/variables.tf b/modules/secret-manager/variables.tf index 1517fea4..0b8afef9 100644 --- a/modules/secret-manager/variables.tf +++ b/modules/secret-manager/variables.tf @@ -14,9 +14,9 @@ * limitations under the License. */ -variable "iam_members" { - description = "IAM members keyed by secret name and role." - type = map(map(set(string))) +variable "iam" { + description = "IAM bindings in {SECRET => {ROLE => [MEMBERS]}} format." + type = map(map(list(string))) default = {} } diff --git a/tests/modules/secret_manager/__init__.py b/tests/modules/secret_manager/__init__.py new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/modules/secret_manager/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/modules/secret_manager/fixture/main.tf b/tests/modules/secret_manager/fixture/main.tf new file mode 100644 index 00000000..42b43102 --- /dev/null +++ b/tests/modules/secret_manager/fixture/main.tf @@ -0,0 +1,42 @@ +/** + * 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. + */ + +module "test" { + source = "../../../../modules/secret-manager" + project_id = "my-project" + iam = { + secret-1 = { + "roles/secretmanager.secretAccessor" = [ + "serviceAccount:service-account.example.com" + ] + } + secret-2 = { + "roles/secretmanager.viewer" = [ + "serviceAccount:service-account.example.com" + ] + } + } + secrets = { + secret-1 = ["europe-west1"], + secret-2 = null + } + versions = { + secret-1 = { + foobar = { enabled = true, data = "foobar" } + } + } + labels = var.labels +} diff --git a/tests/modules/secret_manager/fixture/variables.tf b/tests/modules/secret_manager/fixture/variables.tf new file mode 100644 index 00000000..12460698 --- /dev/null +++ b/tests/modules/secret_manager/fixture/variables.tf @@ -0,0 +1,20 @@ +/** + * 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. + */ + +variable "labels" { + type = map(map(string)) + default = {} +} diff --git a/tests/modules/secret_manager/test_plan.py b/tests/modules/secret_manager/test_plan.py new file mode 100644 index 00000000..44f1f5db --- /dev/null +++ b/tests/modules/secret_manager/test_plan.py @@ -0,0 +1,41 @@ +# 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +@pytest.fixture +def resources(plan_runner): + _, resources = plan_runner(FIXTURES_DIR) + return resources + + +def test_resource_count(resources): + "Test number of resources created." + assert len(resources) == 5 + + +def test_secret_iam(resources): + "Test secret IAM binding resources." + bindings = [r['values'] for r in resources if r['type'] + == 'google_secret_manager_secret_iam_binding'] + assert len(bindings) == 2 + assert set(b['role'] for b in bindings) == set([ + 'roles/secretmanager.secretAccessor', 'roles/secretmanager.viewer' + ]) From 7c05b497144d89974d8e959706f2f648fc2e3af9 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 15:44:03 +0100 Subject: [PATCH 22/53] update iam variables for service directory in dns example --- cloud-operations/dns-fine-grained-iam/main.tf | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cloud-operations/dns-fine-grained-iam/main.tf b/cloud-operations/dns-fine-grained-iam/main.tf index 35e59dc0..19675295 100644 --- a/cloud-operations/dns-fine-grained-iam/main.tf +++ b/cloud-operations/dns-fine-grained-iam/main.tf @@ -77,26 +77,22 @@ module "service-directory" { project_id = module.project.project_id location = var.region name = var.name - iam_members = { + iam = { "roles/servicedirectory.editor" = [ module.vm-ns-editor.service_account_iam_email ] } - iam_roles = ["roles/servicedirectory.editor"] services = { app1 = { endpoints = ["vm1", "vm2"], metadata = null } app2 = { endpoints = ["vm1", "vm2"], metadata = null } } - service_iam_members = { + service_iam = { app1 = { "roles/servicedirectory.editor" = [ module.vm-svc-editor.service_account_iam_email ] } } - service_iam_roles = { - app1 = ["roles/servicedirectory.editor"] - } endpoint_config = { "app1/vm1" = { address = "127.0.0.2", port = 80, metadata = {} } "app1/vm2" = { address = "127.0.0.3", port = 80, metadata = {} } From d2c84de8ff2547ba7e629e3adebfc8f045b62c7d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 15:44:28 +0100 Subject: [PATCH 23/53] use new variable names in organization module --- modules/organization/README.md | 6 +++--- modules/organization/main.tf | 6 +++--- modules/organization/variables.tf | 8 ++++---- tests/modules/organization/fixture/main.tf | 16 ++++++++-------- tests/modules/organization/fixture/variables.tf | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/modules/organization/README.md b/modules/organization/README.md index e0ea140f..0a090b12 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -13,7 +13,7 @@ This module allows managing several organization properties: module "org" { source = "./modules/organization" org_id = 1234567890 - iam_members = { "roles/projectCreator" = ["group:cloud-admins@example.org"] } + iam = { "roles/projectCreator" = ["group:cloud-admins@example.org"] } policy_boolean = { "constraints/compute.disableGuestAttributesAccess" = true "constraints/compute.skipDefaultNetworkCreation" = true @@ -36,9 +36,9 @@ module "org" { |---|---|:---: |:---:|:---:| | org_id | Organization id in nnnnnn format. | number | ✓ | | | *custom_roles* | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | -| *iam_additive_bindings* | Map of roles lists used to set non authoritative bindings, keyed by members. | map(list(string)) | | {} | +| *iam* | IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| *iam_additive* | Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *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. | map(map(list(string))) | | {} | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(list(string)) | | {} | | *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | | *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. | map(object({...})) | | {} | diff --git a/modules/organization/main.tf b/modules/organization/main.tf index f82caeda..6cf41017 100644 --- a/modules/organization/main.tf +++ b/modules/organization/main.tf @@ -16,7 +16,7 @@ locals { iam_additive_pairs = flatten([ - for member, roles in var.iam_additive_bindings : [ + for member, roles in var.iam_additive : [ for role in roles : { role = role, member = member } ] @@ -37,14 +37,14 @@ resource "google_organization_iam_custom_role" "roles" { } resource "google_organization_iam_binding" "authoritative" { - for_each = var.iam_members + for_each = var.iam org_id = var.org_id role = each.key members = each.value } 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 role = each.value.role member = each.value.member diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf index b2cf18a1..293f0176 100644 --- a/modules/organization/variables.tf +++ b/modules/organization/variables.tf @@ -20,14 +20,14 @@ variable "custom_roles" { default = {} } -variable "iam_members" { - description = "Map of member lists used to set authoritative bindings, keyed by role." +variable "iam" { + description = "IAM bindings, in {ROLE => [MEMBERS]} format." type = map(list(string)) default = {} } -variable "iam_additive_bindings" { - description = "Map of roles lists used to set non authoritative bindings, keyed by members." +variable "iam_additive" { + description = "Non authoritative IAM bindings, in {ROLE => [MEMBERS]} format." type = map(list(string)) default = {} } diff --git a/tests/modules/organization/fixture/main.tf b/tests/modules/organization/fixture/main.tf index 718fe459..6c5d0bca 100644 --- a/tests/modules/organization/fixture/main.tf +++ b/tests/modules/organization/fixture/main.tf @@ -15,12 +15,12 @@ */ module "test" { - source = "../../../../modules/organization" - org_id = 1234567890 - custom_roles = var.custom_roles - iam_members = var.iam_members - iam_additive_bindings = var.iam_additive_bindings - iam_audit_config = var.iam_audit_config - policy_boolean = var.policy_boolean - policy_list = var.policy_list + source = "../../../../modules/organization" + org_id = 1234567890 + custom_roles = var.custom_roles + iam = var.iam + iam_additive = var.iam_additive + iam_audit_config = var.iam_audit_config + policy_boolean = var.policy_boolean + policy_list = var.policy_list } diff --git a/tests/modules/organization/fixture/variables.tf b/tests/modules/organization/fixture/variables.tf index a6b2123b..887c3345 100644 --- a/tests/modules/organization/fixture/variables.tf +++ b/tests/modules/organization/fixture/variables.tf @@ -19,12 +19,12 @@ variable "custom_roles" { default = {} } -variable "iam_members" { +variable "iam" { type = map(list(string)) default = {} } -variable "iam_additive_bindings" { +variable "iam_additive" { type = map(list(string)) default = {} } From d0ed3b7614b7560b3d6b89db8d3f36a1d74c8ab2 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 16:17:12 +0100 Subject: [PATCH 24/53] rename iam additive variable in project module --- modules/project/README.md | 4 ++-- modules/project/main.tf | 4 ++-- modules/project/variables.tf | 2 +- tests/modules/project/fixture/main.tf | 2 +- tests/modules/project/fixture/variables.tf | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/project/README.md b/modules/project/README.md index 72dd368b..fcf7e189 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -87,14 +87,14 @@ module "project" { | *auto_create_network* | Whether to create the default network for the project | bool | | false | | *billing_account* | Billing account id. | string | | null | | *custom_roles* | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | -| *iam_additive_bindings* | Map of roles lists used to set non authoritative bindings, keyed by members | map(list(string)) | | {} | +| *iam_additive* | Map of roles lists used to set non authoritative bindings, keyed by members | map(list(string)) | | {} | | *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(set(string)) | | {} | | *labels* | Resource labels. | map(string) | | {} | | *lien_reason* | If non-empty, creates a project lien with this description. | string | | | | *oslogin* | Enable OS Login. | bool | | false | | *oslogin_admins* | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | list(string) | | [] | | *oslogin_users* | List of IAM-style identities that will be granted roles necessary for OS Login users. | list(string) | | [] | -| *parent* | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +| *parent* | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | ... | | *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | | *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. | map(object({...})) | | {} | | *prefix* | Prefix used to generate project id and name. | string | | null | diff --git a/modules/project/main.tf b/modules/project/main.tf index 1ef2be8e..eb899d70 100644 --- a/modules/project/main.tf +++ b/modules/project/main.tf @@ -16,7 +16,7 @@ locals { iam_additive_pairs = flatten([ - for member, roles in var.iam_additive_bindings : [ + for member, roles in var.iam_additive : [ for role in roles : { role = role, member = member } ] @@ -102,7 +102,7 @@ resource "google_project_iam_binding" "authoritative" { } 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 role = each.value.role member = each.value.member diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 75150cc1..26f36385 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -38,7 +38,7 @@ variable "iam_members" { default = {} } -variable "iam_additive_bindings" { +variable "iam_additive" { description = "Map of roles lists used to set non authoritative bindings, keyed by members" type = map(list(string)) default = {} diff --git a/tests/modules/project/fixture/main.tf b/tests/modules/project/fixture/main.tf index 8fce40f8..ea5dff0c 100644 --- a/tests/modules/project/fixture/main.tf +++ b/tests/modules/project/fixture/main.tf @@ -21,7 +21,7 @@ module "test" { auto_create_network = var.auto_create_network custom_roles = var.custom_roles iam_members = var.iam_members - iam_additive_bindings = var.iam_additive_bindings + iam_additive = var.iam_additive labels = var.labels lien_reason = var.lien_reason oslogin = var.oslogin diff --git a/tests/modules/project/fixture/variables.tf b/tests/modules/project/fixture/variables.tf index 83d06352..e8c8df00 100644 --- a/tests/modules/project/fixture/variables.tf +++ b/tests/modules/project/fixture/variables.tf @@ -29,7 +29,7 @@ variable "iam_members" { default = {} } -variable "iam_additive_bindings" { +variable "iam_additive" { type = map(list(string)) default = {} } From d47478a466f8de28bdf2493d16044f8b13a7e770 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 16:22:00 +0100 Subject: [PATCH 25/53] rename iam members variable in project module --- modules/project/README.md | 4 ++-- modules/project/main.tf | 2 +- modules/project/variables.tf | 6 +++--- tests/modules/project/fixture/main.tf | 2 +- tests/modules/project/fixture/variables.tf | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/project/README.md b/modules/project/README.md index fcf7e189..61b62286 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -87,8 +87,8 @@ module "project" { | *auto_create_network* | Whether to create the default network for the project | bool | | false | | *billing_account* | Billing account id. | string | | null | | *custom_roles* | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | -| *iam_additive* | Map of roles lists used to set non authoritative bindings, keyed by members | map(list(string)) | | {} | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(set(string)) | | {} | +| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(set(string)) | | {} | +| *iam_additive* | IAM additive bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *labels* | Resource labels. | map(string) | | {} | | *lien_reason* | If non-empty, creates a project lien with this description. | string | | | | *oslogin* | Enable OS Login. | bool | | false | diff --git a/modules/project/main.tf b/modules/project/main.tf index eb899d70..f7cf5ddc 100644 --- a/modules/project/main.tf +++ b/modules/project/main.tf @@ -91,7 +91,7 @@ resource "google_project_service" "project_services" { # - additive (non-authoritative) roles might fail due to dynamic values resource "google_project_iam_binding" "authoritative" { - for_each = var.iam_members + for_each = var.iam project = local.project.project_id role = each.key members = each.value diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 26f36385..760a9183 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -32,14 +32,14 @@ variable "custom_roles" { default = {} } -variable "iam_members" { - description = "Map of member lists used to set authoritative bindings, keyed by role." +variable "iam" { + description = "IAM bindings in {ROLE => [MEMBERS]} format." type = map(set(string)) default = {} } variable "iam_additive" { - description = "Map of roles lists used to set non authoritative bindings, keyed by members" + description = "IAM additive bindings in {ROLE => [MEMBERS]} format." type = map(list(string)) default = {} } diff --git a/tests/modules/project/fixture/main.tf b/tests/modules/project/fixture/main.tf index ea5dff0c..e7a9fd0e 100644 --- a/tests/modules/project/fixture/main.tf +++ b/tests/modules/project/fixture/main.tf @@ -20,7 +20,7 @@ module "test" { billing_account = "12345-12345-12345" auto_create_network = var.auto_create_network custom_roles = var.custom_roles - iam_members = var.iam_members + iam = var.iam iam_additive = var.iam_additive labels = var.labels lien_reason = var.lien_reason diff --git a/tests/modules/project/fixture/variables.tf b/tests/modules/project/fixture/variables.tf index e8c8df00..1a60f856 100644 --- a/tests/modules/project/fixture/variables.tf +++ b/tests/modules/project/fixture/variables.tf @@ -24,7 +24,7 @@ variable "custom_roles" { default = {} } -variable "iam_members" { +variable "iam" { type = map(list(string)) default = {} } From 02d867ff3dbbb45dabeaaf4f23a5a6217dcf4be1 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 16:49:19 +0100 Subject: [PATCH 26/53] rename iam variables in folder module --- modules/folder/README.md | 4 ++-- modules/folder/main.tf | 2 +- modules/folder/variables.tf | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/folder/README.md b/modules/folder/README.md index 8a98dc5e..5b15204c 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -11,7 +11,7 @@ module "folder" { source = "./modules/folder" parent = "organizations/1234567890" name = "Folder name" - iam_members = { + iam = { "roles/owner" = ["group:users@example.com"] } } @@ -46,7 +46,7 @@ module "folder" { |---|---|:---: |:---:|:---:| | name | Folder name. | string | ✓ | | | parent | Parent in folders/folder_id or organizations/org_id format. | string | ✓ | | -| *iam_members* | List of IAM members keyed by role. | map(set(string)) | | null | +| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(set(string)) | | null | | *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | | *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. | map(object({...})) | | {} | diff --git a/modules/folder/main.tf b/modules/folder/main.tf index 6ac0940e..fb018b7a 100644 --- a/modules/folder/main.tf +++ b/modules/folder/main.tf @@ -21,7 +21,7 @@ resource "google_folder" "folder" { } resource "google_folder_iam_binding" "authoritative" { - for_each = var.iam_members + for_each = var.iam folder = google_folder.folder.name role = each.key members = each.value diff --git a/modules/folder/variables.tf b/modules/folder/variables.tf index d298ec4e..1231be0d 100644 --- a/modules/folder/variables.tf +++ b/modules/folder/variables.tf @@ -14,10 +14,10 @@ * limitations under the License. */ -variable "iam_members" { - description = "List of IAM members keyed by role." +variable "iam" { + description = "IAM bindings in {ROLE => [MEMBERS]} format." type = map(set(string)) - default = null + default = {} } variable "name" { From 0e1fb9bf9ec7653eeda8ae489a17115b3fd3af88 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 16:49:29 +0100 Subject: [PATCH 27/53] rename iam variables in folder unit module --- modules/folders-unit/locals.tf | 7 +------ modules/folders-unit/main.tf | 8 ++++---- modules/folders-unit/variables.tf | 18 ++++++------------ 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/modules/folders-unit/locals.tf b/modules/folders-unit/locals.tf index 1ad80700..96edad4a 100644 --- a/modules/folders-unit/locals.tf +++ b/modules/folders-unit/locals.tf @@ -16,12 +16,7 @@ locals { folder_roles = concat(var.iam_enviroment_roles, local.sa_xpn_folder_roles) - iam_members = var.iam_members == null ? {} : var.iam_members - iam_roles = var.iam_roles == null ? [] : var.iam_roles - unit_iam_bindings = { - for role in local.iam_roles : - role => lookup(local.iam_members, role, []) - } + iam = var.iam == null ? {} : var.iam folder_iam_service_account_bindings = { for pair in setproduct(keys(var.environments), local.folder_roles) : "${pair.0}-${pair.1}" => { environment = pair.0, role = pair.1 } diff --git a/modules/folders-unit/main.tf b/modules/folders-unit/main.tf index 3034d20f..25e03071 100644 --- a/modules/folders-unit/main.tf +++ b/modules/folders-unit/main.tf @@ -34,7 +34,7 @@ resource "google_folder" "environment" { } resource "google_folder_iam_binding" "unit" { - for_each = local.unit_iam_bindings + for_each = var.iam folder = google_folder.unit.name role = each.key members = each.value @@ -92,9 +92,9 @@ resource "google_storage_bucket" "tfstate" { var.prefix == null ? "" : "${var.prefix}-", "${var.short_name}-${each.key}-tf" ]) - location = var.gcs_defaults.location - storage_class = var.gcs_defaults.storage_class - force_destroy = false + location = var.gcs_defaults.location + storage_class = var.gcs_defaults.storage_class + force_destroy = false uniform_bucket_level_access = true versioning { enabled = true diff --git a/modules/folders-unit/variables.tf b/modules/folders-unit/variables.tf index f7a8df3f..d8782edc 100644 --- a/modules/folders-unit/variables.tf +++ b/modules/folders-unit/variables.tf @@ -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" { description = "Grant billing user role to service accounts, defaults to granting on the billing account." 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" { description = "Grant Shared VPC creation roles to service accounts, defaults to granting at folder level." type = object({ From 25e1b8ac46595716e234f881e62db1c16124ca1f Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 16:49:55 +0100 Subject: [PATCH 28/53] update examples and tests for project and folder modules variable changes --- .../asset-inventory-feed-remediation/main.tf | 2 +- cloud-operations/quota-monitoring/main.tf | 2 +- foundations/business-units/main.tf | 16 ++++----- foundations/environments/README.md | 2 +- foundations/environments/main.tf | 12 +++---- networking/shared-vpc-gke/main.tf | 6 ++-- tests/foundations/business_units/__init__.py | 13 +++++++ .../business_units/fixture/main.tf | 23 ++++++++++++ .../business_units/fixture/variables.tf | 35 +++++++++++++++++++ tests/foundations/business_units/test_plan.py | 27 ++++++++++++++ tests/modules/folders/fixture/main.tf | 2 +- tests/modules/folders/fixture/variables.tf | 2 +- tests/modules/folders/test_plan.py | 25 +++++++------ 13 files changed, 130 insertions(+), 37 deletions(-) create mode 100644 tests/foundations/business_units/__init__.py create mode 100644 tests/foundations/business_units/fixture/main.tf create mode 100644 tests/foundations/business_units/fixture/variables.tf create mode 100644 tests/foundations/business_units/test_plan.py diff --git a/cloud-operations/asset-inventory-feed-remediation/main.tf b/cloud-operations/asset-inventory-feed-remediation/main.tf index 07026681..74e2bc2b 100644 --- a/cloud-operations/asset-inventory-feed-remediation/main.tf +++ b/cloud-operations/asset-inventory-feed-remediation/main.tf @@ -41,7 +41,7 @@ module "project" { "compute.zoneOperations.list" ] } - iam_members = { + iam = { (local.role_id) = [module.service-account.iam_email] } } diff --git a/cloud-operations/quota-monitoring/main.tf b/cloud-operations/quota-monitoring/main.tf index cde62ea3..171f4805 100644 --- a/cloud-operations/quota-monitoring/main.tf +++ b/cloud-operations/quota-monitoring/main.tf @@ -34,7 +34,7 @@ module "project" { disable_on_destroy = false, disable_dependent_services = false } - iam_members = { + iam = { "roles/monitoring.metricWriter" = [module.cf.service_account_iam_email] } } diff --git a/foundations/business-units/main.tf b/foundations/business-units/main.tf index b30b5c46..39fecf6f 100644 --- a/foundations/business-units/main.tf +++ b/foundations/business-units/main.tf @@ -21,9 +21,9 @@ # Shared folder module "shared-folder" { - source = "../../modules/folders" + source = "../../modules/folder" parent = var.root_node - names = ["shared"] + name = "shared" } # Terraform project @@ -34,7 +34,7 @@ module "tf-project" { parent = module.shared-folder.id prefix = var.prefix billing_account = var.billing_account_id - iam_additive_bindings = { + iam_additive = { for name in var.iam_terraform_owners : (name) => ["roles/owner"] } services = var.project_services @@ -45,7 +45,7 @@ module "tf-project" { module "tf-gcs-bootstrap" { source = "../../modules/gcs" project_id = module.tf-project.project_id - names = ["tf-bootstrap"] + name = "tf-bootstrap" prefix = "${var.prefix}-tf" location = var.gcs_defaults.location } @@ -96,14 +96,10 @@ module "audit-project" { parent = var.root_node prefix = var.prefix billing_account = var.billing_account_id - iam_members = { + iam = { "roles/bigquery.dataEditor" = [module.audit-log-sinks.writer_identities[0]] "roles/viewer" = var.iam_audit_viewers } - iam_roles = [ - "roles/bigquery.dataEditor", - "roles/viewer" - ] services = concat(var.project_services, [ "bigquery.googleapis.com", ]) @@ -147,7 +143,7 @@ module "shared-project" { parent = module.shared-folder.id prefix = var.prefix billing_account = var.billing_account_id - iam_additive_bindings = { + iam_additive = { for name in var.iam_shared_owners : (name) => ["roles/owner"] } services = var.project_services diff --git a/foundations/environments/README.md b/foundations/environments/README.md index 338c7286..8993e599 100644 --- a/foundations/environments/README.md +++ b/foundations/environments/README.md @@ -33,7 +33,7 @@ If no shared services are needed, the shared service project module can of cours | name | description | type | required | default | |---|---|:---: |:---:|:---:| | billing_account_id | Billing account id used as to create projects. | string | ✓ | | -| environments | Environment short names. | list(string) | ✓ | | +| environments | Environment short names. | set(string) | ✓ | | | organization_id | Organization id in organizations/nnnnnnnn format. | string | ✓ | | | prefix | Prefix used for resources that need unique names. | string | ✓ | | | root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | diff --git a/foundations/environments/main.tf b/foundations/environments/main.tf index df4e0676..542a3a15 100644 --- a/foundations/environments/main.tf +++ b/foundations/environments/main.tf @@ -24,7 +24,7 @@ module "tf-project" { parent = var.root_node prefix = var.prefix billing_account = var.billing_account_id - iam_additive_bindings = { + iam_additive = { for name in var.iam_terraform_owners : (name) => ["roles/owner"] } services = var.project_services @@ -34,8 +34,8 @@ module "tf-project" { module "tf-service-accounts" { source = "../../modules/iam-service-account" - project_id = module.tf-project.project_id for_each = var.environments + project_id = module.tf-project.project_id name = each.value prefix = var.prefix iam_billing_roles = { @@ -67,8 +67,8 @@ module "tf-gcs-bootstrap" { module "tf-gcs-environments" { source = "../../modules/gcs" - project_id = module.tf-project.project_id for_each = var.environments + project_id = module.tf-project.project_id name = each.value prefix = "${var.prefix}-tf" location = var.gcs_location @@ -86,7 +86,7 @@ module "environment-folders" { for_each = var.environments parent = var.root_node name = each.value - iam_members = { + iam = { for role in local.folder_roles : (role) => [module.tf-service-accounts[each.value].iam_email] } @@ -104,7 +104,7 @@ module "audit-project" { parent = var.root_node prefix = var.prefix billing_account = var.billing_account_id - iam_members = { + iam = { "roles/bigquery.dataEditor" = [module.audit-log-sinks.writer_identities[0]] "roles/viewer" = var.iam_audit_viewers } @@ -152,7 +152,7 @@ module "sharedsvc-project" { parent = var.root_node prefix = var.prefix billing_account = var.billing_account_id - iam_additive_bindings = { + iam_additive = { for name in var.iam_shared_owners : (name) => ["roles/owner"] } services = var.project_services diff --git a/networking/shared-vpc-gke/main.tf b/networking/shared-vpc-gke/main.tf index f3f73bb0..05e34bc4 100644 --- a/networking/shared-vpc-gke/main.tf +++ b/networking/shared-vpc-gke/main.tf @@ -30,7 +30,7 @@ module "project-host" { enabled = true service_projects = [] # defined later } - iam_members = { + iam = { "roles/container.hostServiceAgentUser" = [ "serviceAccount:${module.project-svc-gke.service_accounts.robots.container-engine}" ] @@ -51,7 +51,7 @@ module "project-svc-gce" { attach = true host_project = module.project-host.project_id } - iam_members = { + iam = { "roles/logging.logWriter" = [module.vm-bastion.service_account_iam_email], "roles/monitoring.metricWriter" = [module.vm-bastion.service_account_iam_email], "roles/owner" = var.owners_gce, @@ -72,7 +72,7 @@ module "project-svc-gke" { attach = true host_project = module.project-host.project_id } - iam_members = { + iam = { "roles/container.developer" = [module.vm-bastion.service_account_iam_email], "roles/logging.logWriter" = [module.service-account-gke-node.iam_email], "roles/monitoring.metricWriter" = [module.service-account-gke-node.iam_email], diff --git a/tests/foundations/business_units/__init__.py b/tests/foundations/business_units/__init__.py new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/foundations/business_units/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/foundations/business_units/fixture/main.tf b/tests/foundations/business_units/fixture/main.tf new file mode 100644 index 00000000..7dfe870a --- /dev/null +++ b/tests/foundations/business_units/fixture/main.tf @@ -0,0 +1,23 @@ +/** + * 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. + */ + +module "test" { + source = "../../../../foundations/business-units" + billing_account_id = var.billing_account_id + organization_id = var.organization_id + prefix = var.prefix + root_node = var.root_node +} diff --git a/tests/foundations/business_units/fixture/variables.tf b/tests/foundations/business_units/fixture/variables.tf new file mode 100644 index 00000000..eeeb616d --- /dev/null +++ b/tests/foundations/business_units/fixture/variables.tf @@ -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 +# +# 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. + +variable "billing_account_id" { + type = string + default = "1234-5678-9012" +} + +variable "organization_id" { + type = string + default = "organizations/1234567890" +} + +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string + default = "test" +} + +variable "root_node" { + description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'." + type = string + default = "folders/1234567890" +} diff --git a/tests/foundations/business_units/test_plan.py b/tests/foundations/business_units/test_plan.py new file mode 100644 index 00000000..0e400b36 --- /dev/null +++ b/tests/foundations/business_units/test_plan.py @@ -0,0 +1,27 @@ +# 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_resources(e2e_plan_runner): + "Test that plan works and the numbers of resources is as expected." + modules, resources = e2e_plan_runner(FIXTURES_DIR) + assert len(modules) == 9 + assert len(resources) == 84 diff --git a/tests/modules/folders/fixture/main.tf b/tests/modules/folders/fixture/main.tf index 926675d5..e9a141f9 100644 --- a/tests/modules/folders/fixture/main.tf +++ b/tests/modules/folders/fixture/main.tf @@ -18,7 +18,7 @@ module "test" { source = "../../../../modules/folder" parent = "organizations/12345678" name = "folder-a" - iam_members = var.iam_members + iam = var.iam policy_boolean = var.policy_boolean policy_list = var.policy_list } diff --git a/tests/modules/folders/fixture/variables.tf b/tests/modules/folders/fixture/variables.tf index 1267bdd7..f1cebb93 100644 --- a/tests/modules/folders/fixture/variables.tf +++ b/tests/modules/folders/fixture/variables.tf @@ -14,7 +14,7 @@ * limitations under the License. */ -variable "iam_members" { +variable "iam" { type = map(list(string)) default = {} } diff --git a/tests/modules/folders/test_plan.py b/tests/modules/folders/test_plan.py index 73896ea7..160a6eaf 100644 --- a/tests/modules/folders/test_plan.py +++ b/tests/modules/folders/test_plan.py @@ -32,26 +32,25 @@ def test_folder(plan_runner): def test_iam(plan_runner): "Test folder resources with iam roles and members." - iam_members = '{"roles/owner" = ["user:a@b.com"] }' - _, resources = plan_runner(FIXTURES_DIR, - iam_members=iam_members) + iam = '{"roles/owner" = ["user:a@b.com"] }' + _, resources = plan_runner(FIXTURES_DIR, iam=iam) assert len(resources) == 2 + def test_iam_multiple_members(plan_runner): "Test folder resources with multiple iam members." - iam_members = '{"roles/owner" = ["user:a@b.com", "user:c@d.com"] }' - _, resources = plan_runner(FIXTURES_DIR, - iam_members=iam_members) + iam = '{"roles/owner" = ["user:a@b.com", "user:c@d.com"] }' + _, resources = plan_runner(FIXTURES_DIR, iam=iam) assert len(resources) == 2 + def test_iam_multiple_roles(plan_runner): "Test folder resources with multiple iam roles." - iam_members = ( - '{ ' - '"roles/owner" = ["user:a@b.com"], ' - '"roles/viewer" = ["user:c@d.com"] ' - '} ' + iam = ( + '{ ' + '"roles/owner" = ["user:a@b.com"], ' + '"roles/viewer" = ["user:c@d.com"] ' + '} ' ) - _, resources = plan_runner(FIXTURES_DIR, - iam_members=iam_members) + _, resources = plan_runner(FIXTURES_DIR, iam=iam) assert len(resources) == 3 From bf2319972491787a500cd9bd0d7fb6d8423b0c44 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 16:56:57 +0100 Subject: [PATCH 29/53] rename iam variables in artifact registry --- modules/artifact-registry/README.md | 4 ++-- modules/artifact-registry/main.tf | 2 +- modules/artifact-registry/variables.tf | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/artifact-registry/README.md b/modules/artifact-registry/README.md index bf0a82cc..49fd84c2 100644 --- a/modules/artifact-registry/README.md +++ b/modules/artifact-registry/README.md @@ -13,7 +13,7 @@ module "docker_artifact_registry" { location = "europe-west1" format = "DOCKER" id = "myregistry" - iam_members = { + iam = { "roles/artifactregistry.admin" = ["group:cicd@example.com"] } } @@ -28,7 +28,7 @@ module "docker_artifact_registry" { | project_id | Registry project id. | string | ✓ | | | *description* | An optional description for the repository | string | | Terraform-managed registry | | *format* | Repository format. One of DOCKER or UNSPECIFIED | string | | DOCKER | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(set(string)) | | {} | +| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *labels* | Labels to be attached to the registry. | map(string) | | {} | | *location* | Registry location. Use `gcloud beta artifacts locations list' to get valid values | string | | | diff --git a/modules/artifact-registry/main.tf b/modules/artifact-registry/main.tf index 7a7a07d2..9b687873 100644 --- a/modules/artifact-registry/main.tf +++ b/modules/artifact-registry/main.tf @@ -26,7 +26,7 @@ resource "google_artifact_registry_repository" "registry" { resource "google_artifact_registry_repository_iam_binding" "bindings" { provider = google-beta - for_each = var.iam_members + for_each = var.iam project = var.project_id location = google_artifact_registry_repository.registry.location repository = google_artifact_registry_repository.registry.name diff --git a/modules/artifact-registry/variables.tf b/modules/artifact-registry/variables.tf index 0726053a..b977f6c7 100644 --- a/modules/artifact-registry/variables.tf +++ b/modules/artifact-registry/variables.tf @@ -14,9 +14,9 @@ * limitations under the License. */ -variable "iam_members" { - description = "Map of member lists used to set authoritative bindings, keyed by role." - type = map(set(string)) +variable "iam" { + description = "IAM bindings in {ROLE => [MEMBERS]} format." + type = map(list(string)) default = {} } From 9d0db19c5ecf11d6497630b58b086fff5c6e4ee0 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 16:57:06 +0100 Subject: [PATCH 30/53] rename iam variables in gcs module --- modules/gcs/README.md | 8 ++++---- modules/gcs/main.tf | 2 +- modules/gcs/variables.tf | 6 +++--- tests/modules/gcs/fixture/main.tf | 2 +- tests/modules/gcs/fixture/variables.tf | 4 ++-- tests/modules/gcs/test_plan.py | 5 ++--- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/modules/gcs/README.md b/modules/gcs/README.md index 4d2ae30d..3c69da48 100644 --- a/modules/gcs/README.md +++ b/modules/gcs/README.md @@ -12,7 +12,7 @@ module "bucket" { project_id = "myproject" prefix = "test" name = "my-bucket" - iam_members = { + iam = { "roles/storage.admin" = ["group:storage@example.com"] } } @@ -26,7 +26,7 @@ module "bucket" { project_id = "myproject" prefix = "test" name = "my-bucket" - iam_members = { + iam = { "roles/storage.admin" = ["group:storage@example.com"] } encryption_keys = local.kms_key.self_link @@ -41,7 +41,7 @@ module "bucket" { project_id = "myproject" prefix = "test" name = "my-bucket" - iam_members = { + iam = { "roles/storage.admin" = ["group:storage@example.com"] } @@ -72,7 +72,7 @@ module "bucket" { | *logging_config* | Bucket logging configuration. | object({...}) | | null | | *prefix* | Prefix used to generate the bucket name. | string | | null | | *retention_policy* | Bucket retention policy. | object({...}) | | null | -| *storage_class* | Bucket storage class. | string | | MULTI_REGIONAL | +| *storage_class* | Bucket storage class. | string | | ... | | *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). | bool | | true | | *versioning* | Enable versioning, defaults to false. | bool | | false | diff --git a/modules/gcs/main.tf b/modules/gcs/main.tf index d9f866ae..b983b0a6 100644 --- a/modules/gcs/main.tf +++ b/modules/gcs/main.tf @@ -64,7 +64,7 @@ resource "google_storage_bucket" "bucket" { } resource "google_storage_bucket_iam_binding" "bindings" { - for_each = var.iam_members + for_each = var.iam bucket = google_storage_bucket.bucket.name role = each.key members = each.value diff --git a/modules/gcs/variables.tf b/modules/gcs/variables.tf index 7c8d800f..dac13b04 100644 --- a/modules/gcs/variables.tf +++ b/modules/gcs/variables.tf @@ -26,9 +26,9 @@ variable "force_destroy" { default = false } -variable "iam_members" { - description = "IAM members keyed by bucket name and role." - type = map(set(string)) +variable "iam" { + description = "IAM bindings in {ROLE => [MEMBERS]} format." + type = map(list(string)) default = {} } diff --git a/tests/modules/gcs/fixture/main.tf b/tests/modules/gcs/fixture/main.tf index a8b892b0..711196cb 100644 --- a/tests/modules/gcs/fixture/main.tf +++ b/tests/modules/gcs/fixture/main.tf @@ -19,7 +19,7 @@ module "test" { project_id = "my-project" uniform_bucket_level_access = var.uniform_bucket_level_access force_destroy = var.force_destroy - iam_members = var.iam_members + iam = var.iam labels = var.labels logging_config = var.logging_config name = "bucket-a" diff --git a/tests/modules/gcs/fixture/variables.tf b/tests/modules/gcs/fixture/variables.tf index dfc9a83a..c989a286 100644 --- a/tests/modules/gcs/fixture/variables.tf +++ b/tests/modules/gcs/fixture/variables.tf @@ -24,8 +24,8 @@ variable "force_destroy" { default = true } -variable "iam_members" { - type = map(set(string)) +variable "iam" { + type = map(list(string)) default = {} } diff --git a/tests/modules/gcs/test_plan.py b/tests/modules/gcs/test_plan.py index 8516b544..4749b901 100644 --- a/tests/modules/gcs/test_plan.py +++ b/tests/modules/gcs/test_plan.py @@ -55,7 +55,6 @@ def test_config_values(plan_runner): def test_iam(plan_runner): "Test bucket resources with iam roles and members." - iam_members = '{ "roles/storage.admin" = ["user:a@b.com"] }' - _, resources = plan_runner( - FIXTURES_DIR, iam_members=iam_members) + iam = '{ "roles/storage.admin" = ["user:a@b.com"] }' + _, resources = plan_runner(FIXTURES_DIR, iam=iam) assert len(resources) == 2 From 9ebff3ece88673c4160d76f1844d6b8fa90a281a Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 4 Nov 2020 17:00:22 +0100 Subject: [PATCH 31/53] update examples for gcs module variable rename --- data-solutions/gcs-to-bq-with-dataflow/main.tf | 8 ++++---- foundations/environments/main.tf | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data-solutions/gcs-to-bq-with-dataflow/main.tf b/data-solutions/gcs-to-bq-with-dataflow/main.tf index 41eca04a..916458de 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/main.tf +++ b/data-solutions/gcs-to-bq-with-dataflow/main.tf @@ -246,9 +246,7 @@ module "vm_example" { ############################################################################### module "kms-gcs" { - source = "../../modules/gcs" - project_id = module.project-service.project_id - prefix = module.project-service.project_id + source = "../../modules/gcs" for_each = { data = { members = { @@ -269,8 +267,10 @@ module "kms-gcs" { } } } + project_id = module.project-service.project_id + prefix = module.project-service.project_id name = each.key - iam_members = each.value.members + iam = each.value.members encryption_key = module.kms.keys.key-gcs.self_link force_destroy = true } diff --git a/foundations/environments/main.tf b/foundations/environments/main.tf index 542a3a15..48ac7568 100644 --- a/foundations/environments/main.tf +++ b/foundations/environments/main.tf @@ -72,7 +72,7 @@ module "tf-gcs-environments" { name = each.value prefix = "${var.prefix}-tf" location = var.gcs_location - iam_members = { + iam = { "roles/storage.objectAdmin" = [module.tf-service-accounts[each.value].iam_email] } } From 82672520317b8d448df9ff6e373e024d6457b2a1 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 5 Nov 2020 08:49:29 +0100 Subject: [PATCH 32/53] rename iam variable in net vpc module --- modules/net-vpc/README.md | 6 +++--- modules/net-vpc/main.tf | 2 +- modules/net-vpc/variables.tf | 4 ++-- networking/shared-vpc-gke/main.tf | 2 +- tests/modules/net_vpc/fixture/main.tf | 2 +- tests/modules/net_vpc/fixture/variables.tf | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md index d541d176..a000a474 100644 --- a/modules/net-vpc/README.md +++ b/modules/net-vpc/README.md @@ -86,7 +86,7 @@ module "vpc-host" { local.service_project_1.project_id, local.service_project_2.project_id ] - iam_members = { + iam = { "europe-west1/subnet-1" = { "roles/compute.networkUser" = [ local.service_project_1.cloudsvc_sa, @@ -110,13 +110,13 @@ module "vpc-host" { | *auto_create_subnetworks* | Set to true to create an auto mode subnet, defaults to custom mode. | bool | | false | | *delete_default_routes_on_create* | Set to true to delete the default routes at creation time. | bool | | false | | *description* | An optional description of this resource (triggers recreation on change). | string | | Terraform-managed. | -| *iam_members* | List of IAM members keyed by subnet 'region/name' and role. | map(map(list(string))) | | {} | +| *iam* | Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format. | map(map(list(string))) | | {} | | *log_config_defaults* | Default configuration for flow logs when enabled. | object({...}) | | ... | | *log_configs* | Map keyed by subnet 'region/name' of optional configurations for flow logs when enabled. | map(map(string)) | | {} | | *peering_config* | VPC peering configuration. | object({...}) | | null | | *peering_create_remote_end* | Skip creation of peering on the remote end when using peering_config | bool | | true | | *routes* | Network routes, keyed by name. | map(object({...})) | | {} | -| *routing_mode* | The network routing mode (default 'GLOBAL') | string | | GLOBAL | +| *routing_mode* | The network routing mode (default 'GLOBAL') | string | | ... | | *shared_vpc_host* | Enable shared VPC for this project. | bool | | false | | *shared_vpc_service_projects* | Shared VPC service projects to register with this host | list(string) | | [] | | *subnet_descriptions* | Optional map of subnet descriptions, keyed by subnet 'region/name'. | map(string) | | {} | diff --git a/modules/net-vpc/main.tf b/modules/net-vpc/main.tf index bad4f870..14800ef5 100644 --- a/modules/net-vpc/main.tf +++ b/modules/net-vpc/main.tf @@ -15,7 +15,7 @@ */ locals { - iam_members = var.iam_members == null ? {} : var.iam_members + iam_members = var.iam == null ? {} : var.iam subnet_iam_members = flatten([ for subnet, roles in local.iam_members : [ for role, members in roles : { diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf index 6c3ab855..485da879 100644 --- a/modules/net-vpc/variables.tf +++ b/modules/net-vpc/variables.tf @@ -32,8 +32,8 @@ variable "description" { default = "Terraform-managed." } -variable "iam_members" { - description = "List of IAM members keyed by subnet 'region/name' and role." +variable "iam" { + description = "Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format." type = map(map(list(string))) default = {} } diff --git a/networking/shared-vpc-gke/main.tf b/networking/shared-vpc-gke/main.tf index 05e34bc4..bee0d814 100644 --- a/networking/shared-vpc-gke/main.tf +++ b/networking/shared-vpc-gke/main.tf @@ -107,7 +107,7 @@ module "vpc-shared" { } } ] - iam_members = { + iam = { "${var.region}/gce" = { "roles/compute.networkUser" = concat(var.owners_gce, [ "serviceAccount:${module.project-svc-gce.service_accounts.cloud_services}", diff --git a/tests/modules/net_vpc/fixture/main.tf b/tests/modules/net_vpc/fixture/main.tf index 5ab2c4f8..03b74124 100644 --- a/tests/modules/net_vpc/fixture/main.tf +++ b/tests/modules/net_vpc/fixture/main.tf @@ -18,7 +18,7 @@ module "test" { source = "../../../../modules/net-vpc" project_id = var.project_id name = var.name - iam_members = var.iam_members + iam = var.iam log_configs = var.log_configs log_config_defaults = var.log_config_defaults peering_config = var.peering_config diff --git a/tests/modules/net_vpc/fixture/variables.tf b/tests/modules/net_vpc/fixture/variables.tf index 7388ad66..0a19ef07 100644 --- a/tests/modules/net_vpc/fixture/variables.tf +++ b/tests/modules/net_vpc/fixture/variables.tf @@ -29,7 +29,7 @@ variable "auto_create_subnetworks" { default = false } -variable "iam_members" { +variable "iam" { type = map(map(set(string))) default = null } From 2828b2c45514e74ce8fb112d57e993f0f099929b Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 5 Nov 2020 08:57:18 +0100 Subject: [PATCH 33/53] fix skipped service directory module test --- tests/modules/service_directory/test_plan.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/modules/service_directory/test_plan.py b/tests/modules/service_directory/test_plan.py index dd74e926..52b62abe 100644 --- a/tests/modules/service_directory/test_plan.py +++ b/tests/modules/service_directory/test_plan.py @@ -31,12 +31,12 @@ def test_resource_count(resources): assert len(resources) == 9 -def test_service_iam(resources): - "Test service IAM binding resources." +def test_iam(resources): + "Test IAM binding resources." bindings = [r['values'] for r in resources if r['type'] == 'google_service_directory_namespace_iam_binding'] assert len(bindings) == 1 - assert bindings[0]['role'] == 'roles/servicedirectory.editor' + assert bindings[0]['role'] == 'roles/servicedirectory.viewer' def test_services(resources): From 7e96e899d5d0644fdddccb9b9b1f393f57a7e5be Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 5 Nov 2020 08:57:50 +0100 Subject: [PATCH 34/53] rename folder module tests --- tests/modules/{folders => folder}/__init__.py | 0 tests/modules/{folders => folder}/fixture/main.tf | 0 tests/modules/{folders => folder}/fixture/variables.tf | 0 tests/modules/{folders => folder}/test_plan.py | 0 tests/modules/{folders => folder}/test_plan_org_policies.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename tests/modules/{folders => folder}/__init__.py (100%) rename tests/modules/{folders => folder}/fixture/main.tf (100%) rename tests/modules/{folders => folder}/fixture/variables.tf (100%) rename tests/modules/{folders => folder}/test_plan.py (100%) rename tests/modules/{folders => folder}/test_plan_org_policies.py (100%) diff --git a/tests/modules/folders/__init__.py b/tests/modules/folder/__init__.py similarity index 100% rename from tests/modules/folders/__init__.py rename to tests/modules/folder/__init__.py diff --git a/tests/modules/folders/fixture/main.tf b/tests/modules/folder/fixture/main.tf similarity index 100% rename from tests/modules/folders/fixture/main.tf rename to tests/modules/folder/fixture/main.tf diff --git a/tests/modules/folders/fixture/variables.tf b/tests/modules/folder/fixture/variables.tf similarity index 100% rename from tests/modules/folders/fixture/variables.tf rename to tests/modules/folder/fixture/variables.tf diff --git a/tests/modules/folders/test_plan.py b/tests/modules/folder/test_plan.py similarity index 100% rename from tests/modules/folders/test_plan.py rename to tests/modules/folder/test_plan.py diff --git a/tests/modules/folders/test_plan_org_policies.py b/tests/modules/folder/test_plan_org_policies.py similarity index 100% rename from tests/modules/folders/test_plan_org_policies.py rename to tests/modules/folder/test_plan_org_policies.py From 462a7023cd7b8d942927a4d56f87951546f6d046 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 5 Nov 2020 08:58:05 +0100 Subject: [PATCH 35/53] rename iam variable in source repo module --- modules/source-repository/README.md | 6 +-- modules/source-repository/main.tf | 2 +- modules/source-repository/variables.tf | 8 ++-- tests/modules/source_repository/__init__.py | 13 +++++++ .../modules/source_repository/fixture/main.tf | 22 +++++++++++ .../source_repository/fixture/variables.tf | 32 +++++++++++++++ tests/modules/source_repository/test_plan.py | 39 +++++++++++++++++++ 7 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 tests/modules/source_repository/__init__.py create mode 100644 tests/modules/source_repository/fixture/main.tf create mode 100644 tests/modules/source_repository/fixture/variables.tf create mode 100644 tests/modules/source_repository/test_plan.py diff --git a/modules/source-repository/README.md b/modules/source-repository/README.md index 9beae244..78a7e7d7 100644 --- a/modules/source-repository/README.md +++ b/modules/source-repository/README.md @@ -12,7 +12,7 @@ module "repo" { source e = "./modules/source-repository" project_id = "my-project" name = "my-repo" - iam_members = { + iam = { "roles/source.reader" = ["user:foo@example.com"] } } @@ -23,9 +23,9 @@ module "repo" { | name | description | type | required | default | |---|---|:---: |:---:|:---:| -| name | Repository topic name. | string | ✓ | | +| name | Repository name. | string | ✓ | | | project_id | Project used for resources. | string | ✓ | | -| *iam_members* | IAM members for each topic role. | map(set(string)) | | {} | +| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | ## Outputs diff --git a/modules/source-repository/main.tf b/modules/source-repository/main.tf index 71432e16..f7bea1b1 100644 --- a/modules/source-repository/main.tf +++ b/modules/source-repository/main.tf @@ -20,7 +20,7 @@ resource "google_sourcerepo_repository" "default" { } resource "google_sourcerepo_repository_iam_binding" "default" { - for_each = var.iam_members + for_each = var.iam project = var.project_id repository = google_sourcerepo_repository.default.name role = each.key diff --git a/modules/source-repository/variables.tf b/modules/source-repository/variables.tf index a7254370..9d5d0832 100644 --- a/modules/source-repository/variables.tf +++ b/modules/source-repository/variables.tf @@ -19,13 +19,13 @@ variable "project_id" { type = string } -variable "iam_members" { - description = "IAM members for each topic role." - type = map(set(string)) +variable "iam" { + description = "IAM bindings in {ROLE => [MEMBERS]} format." + type = map(list(string)) default = {} } variable "name" { - description = "Repository topic name." + description = "Repository name." type = string } diff --git a/tests/modules/source_repository/__init__.py b/tests/modules/source_repository/__init__.py new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/modules/source_repository/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/modules/source_repository/fixture/main.tf b/tests/modules/source_repository/fixture/main.tf new file mode 100644 index 00000000..14bb53d9 --- /dev/null +++ b/tests/modules/source_repository/fixture/main.tf @@ -0,0 +1,22 @@ +/** + * 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. + */ + +module "test" { + source = "../../../../modules/source-repository" + project_id = var.project_id + name = var.name + iam = var.iam +} diff --git a/tests/modules/source_repository/fixture/variables.tf b/tests/modules/source_repository/fixture/variables.tf new file mode 100644 index 00000000..dd58ca52 --- /dev/null +++ b/tests/modules/source_repository/fixture/variables.tf @@ -0,0 +1,32 @@ +/** + * 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. + */ + +variable "project_id" { + type = string + default = "test-project" +} + +variable "iam" { + type = map(list(string)) + default = { + "roles/source.reader" = ["foo@example.org"] + } +} + +variable "name" { + type = string + default = "test" +} diff --git a/tests/modules/source_repository/test_plan.py b/tests/modules/source_repository/test_plan.py new file mode 100644 index 00000000..88ff4a69 --- /dev/null +++ b/tests/modules/source_repository/test_plan.py @@ -0,0 +1,39 @@ +# 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +@pytest.fixture +def resources(plan_runner): + _, resources = plan_runner(FIXTURES_DIR) + return resources + + +def test_resource_count(resources): + "Test number of resources created." + assert len(resources) == 2 + + +def test_iam(resources): + "Test IAM binding resources." + bindings = [r['values'] for r in resources if r['type'] + == 'google_sourcerepo_repository_iam_binding'] + assert len(bindings) == 1 + assert bindings[0]['role'] == 'roles/source.reader' From 44f1828c337bb94beffcb831d140e5373c4cca1f Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 5 Nov 2020 09:10:49 +0100 Subject: [PATCH 36/53] rename iam variable in compute vm module --- modules/compute-vm/README.md | 2 +- modules/compute-vm/main.tf | 4 ++-- modules/compute-vm/variables.tf | 6 +++--- tests/modules/compute_vm/fixture/main.tf | 2 +- tests/modules/compute_vm/fixture/variables.tf | 2 +- tests/modules/compute_vm/test_plan.py | 4 ++-- tests/modules/compute_vm/test_plan_zones.py | 5 ++--- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/modules/compute-vm/README.md b/modules/compute-vm/README.md index 18aaf269..3771452c 100644 --- a/modules/compute-vm/README.md +++ b/modules/compute-vm/README.md @@ -184,7 +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. | object({...}) | | null | | *group* | Define this variable to create an instance group for instances. Disabled for template use. | object({...}) | | null | | *hostname* | Instance FQDN name. | string | | null | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use. | map(set(string)) | | {} | +| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *instance_count* | Number of instances to create (only for non-template usage). | number | | 1 | | *instance_type* | Instance type. | string | | f1-micro | | *labels* | Instance labels. | map(string) | | {} | diff --git a/modules/compute-vm/main.tf b/modules/compute-vm/main.tf index 45749790..782c4425 100644 --- a/modules/compute-vm/main.tf +++ b/modules/compute-vm/main.tf @@ -26,8 +26,8 @@ locals { "${pair[0]}-${pair[1]}" => { disk_name = pair[1], name = pair[0] } } iam_members = var.use_instance_template ? {} : { - for pair in setproduct(keys(var.iam_members), keys(local.names)) : - "${pair.0}/${pair.1}" => { role = pair.0, name = pair.1, members = var.iam_members[pair.0] } + for pair in setproduct(keys(var.iam), keys(local.names)) : + "${pair.0}/${pair.1}" => { role = pair.0, name = pair.1, members = var.iam[pair.0] } } names = ( var.use_instance_template ? { (var.name) = 0 } : { diff --git a/modules/compute-vm/variables.tf b/modules/compute-vm/variables.tf index 604a5766..a3aa80a2 100644 --- a/modules/compute-vm/variables.tf +++ b/modules/compute-vm/variables.tf @@ -90,9 +90,9 @@ variable "hostname" { default = null } -variable "iam_members" { - description = "Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use." - type = map(set(string)) +variable "iam" { + description = "IAM bindings in {ROLE => [MEMBERS]} format." + type = map(list(string)) default = {} } diff --git a/tests/modules/compute_vm/fixture/main.tf b/tests/modules/compute_vm/fixture/main.tf index fb3dd42c..749742d8 100644 --- a/tests/modules/compute_vm/fixture/main.tf +++ b/tests/modules/compute_vm/fixture/main.tf @@ -25,7 +25,7 @@ module "test" { instance_count = var.instance_count use_instance_template = var.use_instance_template group = var.group - iam_members = var.iam_members + iam = var.iam metadata = var.metadata metadata_list = var.metadata_list } diff --git a/tests/modules/compute_vm/fixture/variables.tf b/tests/modules/compute_vm/fixture/variables.tf index bc61f3d6..b1fd91fe 100644 --- a/tests/modules/compute_vm/fixture/variables.tf +++ b/tests/modules/compute_vm/fixture/variables.tf @@ -19,7 +19,7 @@ variable "group" { default = null } -variable "iam_members" { +variable "iam" { type = map(set(string)) default = {} } diff --git a/tests/modules/compute_vm/test_plan.py b/tests/modules/compute_vm/test_plan.py index 5cf1458d..698bf33a 100644 --- a/tests/modules/compute_vm/test_plan.py +++ b/tests/modules/compute_vm/test_plan.py @@ -56,12 +56,12 @@ def test_group(plan_runner): def test_iam(plan_runner): - iam_members = ( + iam = ( '{"roles/compute.instanceAdmin" = ["user:a@a.com", "user:b@a.com"],' '"roles/iam.serviceAccountUser" = ["user:a@a.com"]}' ) _, resources = plan_runner( - FIXTURES_DIR, instance_count=2, iam_members=iam_members) + FIXTURES_DIR, instance_count=2, iam=iam) assert len(resources) == 6 assert set(r['type'] for r in resources) == set([ 'google_compute_instance', 'google_compute_instance_iam_binding']) diff --git a/tests/modules/compute_vm/test_plan_zones.py b/tests/modules/compute_vm/test_plan_zones.py index 9aa1ae24..83e7898c 100644 --- a/tests/modules/compute_vm/test_plan_zones.py +++ b/tests/modules/compute_vm/test_plan_zones.py @@ -49,10 +49,9 @@ def test_group(plan_runner): def test_iam(plan_runner): - iam_members = '{"roles/a" = ["user:a@a.com"], "roles/b" = ["user:a@a.com"]}' + iam = '{"roles/a" = ["user:a@a.com"], "roles/b" = ["user:a@a.com"]}' _, resources = plan_runner(FIXTURES_DIR, instance_count=3, - iam_members=iam_members, - zones='["a", "b"]') + iam=iam, zones='["a", "b"]') iam_bindings = dict( (r['index'], r['values']['zone']) for r in resources if r['type'] == 'google_compute_instance_iam_binding' From cb54ff77a1e9e361fbeeb86110d4bfd219397c7b Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 5 Nov 2020 09:22:13 +0100 Subject: [PATCH 37/53] rename iam variable in service account module --- modules/iam-service-account/README.md | 2 +- modules/iam-service-account/main.tf | 2 +- modules/iam-service-account/variables.tf | 6 +++--- tests/modules/iam_service_account/fixture/main.tf | 2 +- tests/modules/iam_service_account/fixture/variables.tf | 2 +- tests/modules/iam_service_account/test_plan.py | 9 ++------- 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index 43ef5bb4..069d86a5 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -11,7 +11,7 @@ module "myproject-default-service-accounts" { name = "vm-default" generate_key = true # authoritative roles granted *on* the service accounts to other identities - iam_members = { + iam = { "roles/iam.serviceAccountUser" = ["user:foo@example.com"] } # non-authoritative roles granted *to* the service accounts on other resources diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf index 5fc7d266..953fef57 100644 --- a/modules/iam-service-account/main.tf +++ b/modules/iam-service-account/main.tf @@ -67,7 +67,7 @@ resource "google_service_account_key" "key" { } resource "google_service_account_iam_binding" "roles" { - for_each = var.iam_members + for_each = var.iam service_account_id = google_service_account.service_account.name role = each.key members = each.value diff --git a/modules/iam-service-account/variables.tf b/modules/iam-service-account/variables.tf index 524a820e..b4d5110e 100644 --- a/modules/iam-service-account/variables.tf +++ b/modules/iam-service-account/variables.tf @@ -20,9 +20,9 @@ variable "generate_key" { default = false } -variable "iam_members" { - description = "Map of members which are granted authoritative roles on the service account, keyed by role." - type = map(set(string)) +variable "iam" { + description = "IAM bindings on the service account in {ROLE => [MEMBERS]} format." + type = map(list(string)) default = {} } diff --git a/tests/modules/iam_service_account/fixture/main.tf b/tests/modules/iam_service_account/fixture/main.tf index be903b70..a3ec7a81 100644 --- a/tests/modules/iam_service_account/fixture/main.tf +++ b/tests/modules/iam_service_account/fixture/main.tf @@ -20,7 +20,7 @@ module "test" { name = "sa-one" prefix = var.prefix generate_key = var.generate_key - iam_members = var.iam_members + iam = var.iam iam_billing_roles = var.iam_billing_roles iam_folder_roles = var.iam_folder_roles iam_organization_roles = var.iam_organization_roles diff --git a/tests/modules/iam_service_account/fixture/variables.tf b/tests/modules/iam_service_account/fixture/variables.tf index 1c2933b0..50f8590a 100644 --- a/tests/modules/iam_service_account/fixture/variables.tf +++ b/tests/modules/iam_service_account/fixture/variables.tf @@ -19,7 +19,7 @@ variable "generate_key" { default = false } -variable "iam_members" { +variable "iam" { type = map(list(string)) default = {} } diff --git a/tests/modules/iam_service_account/test_plan.py b/tests/modules/iam_service_account/test_plan.py index a8a4d2cf..b6c46040 100644 --- a/tests/modules/iam_service_account/test_plan.py +++ b/tests/modules/iam_service_account/test_plan.py @@ -36,13 +36,8 @@ def test_resources(plan_runner): def test_iam_roles(plan_runner): "Test iam roles with one member." - variables = dict( - iam_members=( - '{' - '"roles/iam.serviceAccountUser" = ["user:a@b.com"] ' - '}') - ) - _, resources = plan_runner(FIXTURES_DIR, **variables) + iam=('{"roles/iam.serviceAccountUser" = ["user:a@b.com"]}') + _, resources = plan_runner(FIXTURES_DIR, iam=iam) assert len(resources) == 2 iam_resources = [r for r in resources if r['type'] != 'google_service_account'] From c33e68ecd0fb605cf20bf702ac7aa0a514e288af Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 5 Nov 2020 09:26:24 +0100 Subject: [PATCH 38/53] use list in service account module variable types --- modules/iam-service-account/README.md | 12 ++++++------ modules/iam-service-account/variables.tf | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index 069d86a5..6389a078 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -33,12 +33,12 @@ module "myproject-default-service-accounts" { | project_id | Project id where service account will be created. | string | ✓ | | | *display_name* | Display name of the service account to create. | string | | Terraform-managed. | | *generate_key* | Generate a key for service account. | bool | | false | -| *iam_billing_roles* | Project roles granted to the service account, by billing account id. | map(set(string)) | | {} | -| *iam_folder_roles* | Project roles granted to the service account, by folder id. | map(set(string)) | | {} | -| *iam_members* | Map of members which are granted authoritative roles on the service account, keyed by role. | map(set(string)) | | {} | -| *iam_organization_roles* | Project roles granted to the service account, by organization id. | map(set(string)) | | {} | -| *iam_project_roles* | Project roles granted to the service account, by project id. | map(set(string)) | | {} | -| *iam_storage_roles* | Storage roles granted to the service account, by bucket name. | map(set(string)) | | {} | +| *iam* | IAM bindings on the service account in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| *iam_billing_roles* | Project roles granted to the service account, by billing account id. | map(list(string)) | | {} | +| *iam_folder_roles* | Project roles granted to the service account, by folder id. | map(list(string)) | | {} | +| *iam_organization_roles* | Project roles granted to the service account, by organization id. | map(list(string)) | | {} | +| *iam_project_roles* | Project roles granted to the service account, by project id. | map(list(string)) | | {} | +| *iam_storage_roles* | Storage roles granted to the service account, by bucket name. | map(list(string)) | | {} | | *prefix* | Prefix applied to service account names. | string | | null | ## Outputs diff --git a/modules/iam-service-account/variables.tf b/modules/iam-service-account/variables.tf index b4d5110e..11463be6 100644 --- a/modules/iam-service-account/variables.tf +++ b/modules/iam-service-account/variables.tf @@ -28,31 +28,31 @@ variable "iam" { variable "iam_billing_roles" { description = "Project roles granted to the service account, by billing account id." - type = map(set(string)) + type = map(list(string)) default = {} } variable "iam_folder_roles" { description = "Project roles granted to the service account, by folder id." - type = map(set(string)) + type = map(list(string)) default = {} } variable "iam_organization_roles" { description = "Project roles granted to the service account, by organization id." - type = map(set(string)) + type = map(list(string)) default = {} } variable "iam_project_roles" { description = "Project roles granted to the service account, by project id." - type = map(set(string)) + type = map(list(string)) default = {} } variable "iam_storage_roles" { description = "Storage roles granted to the service account, by bucket name." - type = map(set(string)) + type = map(list(string)) default = {} } From ca931181fc127f9561c1b158722e32ba54aea805 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 5 Nov 2020 17:58:03 +0100 Subject: [PATCH 39/53] bump tftest version --- tests/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index 55e326c7..f233348b 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,3 @@ pytest>=4.6.0 PyYAML>=5.3 -tftest>=1.5.1 +tftest>=1.5.2 From 3a4938874bbd50d81e89812bd23e364926b9e05e Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 5 Nov 2020 21:28:34 +0100 Subject: [PATCH 40/53] rename iam variables in pubsub module --- .../asset-inventory-feed-remediation/main.tf | 2 +- modules/pubsub/README.md | 8 +-- modules/pubsub/main.tf | 4 +- modules/pubsub/variables.tf | 12 ++-- tests/modules/pubsub/__init__.py | 13 +++++ tests/modules/pubsub/fixture/main.tf | 34 ++++++++++++ tests/modules/pubsub/fixture/variables.tf | 20 +++++++ tests/modules/pubsub/test_plan.py | 55 +++++++++++++++++++ 8 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 tests/modules/pubsub/__init__.py create mode 100644 tests/modules/pubsub/fixture/main.tf create mode 100644 tests/modules/pubsub/fixture/variables.tf create mode 100644 tests/modules/pubsub/test_plan.py diff --git a/cloud-operations/asset-inventory-feed-remediation/main.tf b/cloud-operations/asset-inventory-feed-remediation/main.tf index 74e2bc2b..17f48697 100644 --- a/cloud-operations/asset-inventory-feed-remediation/main.tf +++ b/cloud-operations/asset-inventory-feed-remediation/main.tf @@ -63,7 +63,7 @@ module "pubsub" { project_id = module.project.project_id name = var.name subscriptions = { "${var.name}-default" = null } - iam_members = { + iam = { "roles/pubsub.publisher" = [ "serviceAccount:${module.project.service_accounts.robots.cloudasset}" ] diff --git a/modules/pubsub/README.md b/modules/pubsub/README.md index 974fe42c..16b92762 100644 --- a/modules/pubsub/README.md +++ b/modules/pubsub/README.md @@ -12,7 +12,7 @@ module "pubsub" { source = "./modules/pubsub" project_id = "my-project" name = "my-topic" - iam_members = { + iam = { "roles/pubsub.viewer" = ["group:foo@example.com"] "roles/pubsub.subscriber" = ["user:user1@example.com"] } @@ -76,7 +76,7 @@ module "pubsub" { test-1 = null test-1 = null } - subscription_iam_members = { + subscription_iam = { test-1 = { "roles/pubsub.subscriber" = ["user:user1@ludomagno.net"] } @@ -93,12 +93,12 @@ module "pubsub" { | project_id | Project used for resources. | string | ✓ | | | *dead_letter_configs* | Per-subscription dead letter policy configuration. | map(object({...})) | | {} | | *defaults* | Subscription defaults for options. | object({...}) | | ... | -| *iam_members* | IAM members for each topic role. | map(set(string)) | | {} | +| *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *kms_key* | KMS customer managed encryption key. | string | | null | | *labels* | Labels. | map(string) | | {} | | *push_configs* | Push subscription configurations. | map(object({...})) | | {} | | *regions* | List of regions used to set persistence policy. | list(string) | | [] | -| *subscription_iam_members* | IAM members for each subscription and role. | map(map(set(string))) | | {} | +| *subscription_iam* | IAM bindings for subscriptions in {SUBSCRIPTION => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | | *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. | map(object({...})) | | {} | ## Outputs diff --git a/modules/pubsub/main.tf b/modules/pubsub/main.tf index 31caa258..dd652e35 100644 --- a/modules/pubsub/main.tf +++ b/modules/pubsub/main.tf @@ -16,7 +16,7 @@ locals { sub_iam_members = flatten([ - for sub, roles in var.subscription_iam_members : [ + for sub, roles in var.subscription_iam : [ for role, members in roles : { sub = sub role = role @@ -50,7 +50,7 @@ resource "google_pubsub_topic" "default" { } resource "google_pubsub_topic_iam_binding" "default" { - for_each = var.iam_members + for_each = var.iam project = var.project_id topic = google_pubsub_topic.default.name role = each.key diff --git a/modules/pubsub/variables.tf b/modules/pubsub/variables.tf index e6b15083..dd68354b 100644 --- a/modules/pubsub/variables.tf +++ b/modules/pubsub/variables.tf @@ -39,9 +39,9 @@ variable "defaults" { } } -variable "iam_members" { - description = "IAM members for each topic role." - type = map(set(string)) +variable "iam" { + description = "IAM bindings for topic in {ROLE => [MEMBERS]} format." + type = map(list(string)) default = {} } @@ -101,8 +101,8 @@ variable "subscriptions" { default = {} } -variable "subscription_iam_members" { - description = "IAM members for each subscription and role." - type = map(map(set(string))) +variable "subscription_iam" { + description = "IAM bindings for subscriptions in {SUBSCRIPTION => {ROLE => [MEMBERS]}} format." + type = map(map(list(string))) default = {} } diff --git a/tests/modules/pubsub/__init__.py b/tests/modules/pubsub/__init__.py new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/modules/pubsub/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/modules/pubsub/fixture/main.tf b/tests/modules/pubsub/fixture/main.tf new file mode 100644 index 00000000..aa4468a6 --- /dev/null +++ b/tests/modules/pubsub/fixture/main.tf @@ -0,0 +1,34 @@ +/** + * 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. + */ + +module "test" { + source = "../../../../modules/pubsub" + project_id = "my-project" + regions = ["europe-west1"] + name = "test" + iam = { + "roles/pubsub.publisher" = ["user:me@example.com"] + } + subscriptions = { + test = null + } + subscription_iam = { + test = { + "roles/pubsub.subscriber" = ["user:me@example.com"] + } + } + labels = var.labels +} diff --git a/tests/modules/pubsub/fixture/variables.tf b/tests/modules/pubsub/fixture/variables.tf new file mode 100644 index 00000000..4e0e0396 --- /dev/null +++ b/tests/modules/pubsub/fixture/variables.tf @@ -0,0 +1,20 @@ +/** + * 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. + */ + +variable "labels" { + type = map(string) + default = {} +} diff --git a/tests/modules/pubsub/test_plan.py b/tests/modules/pubsub/test_plan.py new file mode 100644 index 00000000..424f481c --- /dev/null +++ b/tests/modules/pubsub/test_plan.py @@ -0,0 +1,55 @@ +# 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +@pytest.fixture +def resources(plan_runner): + _, resources = plan_runner(FIXTURES_DIR) + return resources + + +def test_resource_count(resources): + "Test number of resources created." + assert len(resources) == 4 + + +def test_iam(resources): + "Test IAM binding resources." + bindings = [r['values'] for r in resources if r['type'] + == 'google_pubsub_topic_iam_binding'] + assert len(bindings) == 1 + assert bindings[0]['role'] == 'roles/pubsub.publisher' + + +def test_subscriptions(resources): + "Test subscription resources." + subs = [r['values'] for r in resources if r['type'] + == 'google_pubsub_subscription'] + assert len(subs) == 1 + assert set(s['name'] for s in subs) == set(['test']) + + +def test_subscription_iam(resources): + "Test subscription IAM binding resources." + bindings = [r['values'] for r in resources if r['type'] + == 'google_pubsub_subscription_iam_binding'] + assert len(bindings) == 1 + assert set(b['role'] for b in bindings) == set(['roles/pubsub.subscriber']) From efc694ba011e436c0628f42f3d05d79af3baae70 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 6 Nov 2020 08:11:07 +0100 Subject: [PATCH 41/53] fix and streamline bigtable module, add tests, align to new iam variable name --- modules/bigtable-instance/README.md | 19 ++++---- modules/bigtable-instance/main.tf | 5 +- modules/bigtable-instance/outputs.tf | 8 ++-- modules/bigtable-instance/variables.tf | 24 +++++----- tests/modules/bigtable_instance/__init__.py | 13 +++++ .../modules/bigtable_instance/fixture/main.tf | 33 +++++++++++++ .../bigtable_instance/fixture/variables.tf | 20 ++++++++ tests/modules/bigtable_instance/test_plan.py | 47 +++++++++++++++++++ 8 files changed, 139 insertions(+), 30 deletions(-) create mode 100644 tests/modules/bigtable_instance/__init__.py create mode 100644 tests/modules/bigtable_instance/fixture/main.tf create mode 100644 tests/modules/bigtable_instance/fixture/variables.tf create mode 100644 tests/modules/bigtable_instance/test_plan.py diff --git a/modules/bigtable-instance/README.md b/modules/bigtable-instance/README.md index da332180..23aac694 100644 --- a/modules/bigtable-instance/README.md +++ b/modules/bigtable-instance/README.md @@ -13,22 +13,21 @@ This module allows managing a single BigTable instance, including access configu ```hcl -module "big-table-instance" { +module "bigtable-instance" { source = "./modules/bigtable-instance" project_id = "my-project" name = "instance" cluster_id = "instance" - instance_type = "PRODUCTION" + zone = "europe-west1-b" tables = { - test1 = { table_options = null }, - test2 = { table_options = { + test1 = null, + test2 = { split_keys = ["a", "b", "c"] column_family = null - } } } - iam_members = { - viewer = ["user:viewer@testdomain.com"] + iam = { + "roles/bigtable.user" = ["user:viewer@testdomain.com"] } } ``` @@ -44,12 +43,12 @@ module "big-table-instance" { | *cluster_id* | The ID of the Cloud Bigtable cluster. | string | | europe-west1 | | *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. | | | true | | *display_name* | The human-readable display name of the Bigtable instance. | | | null | -| *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. | map(set(string)) | | {} | -| *instance_type* | None | string | | DEVELOPMENT | +| *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| *instance_type* | (deprecated) The instance type to create. One of 'DEVELOPMENT' or 'PRODUCTION'. | string | | null | | *num_nodes* | The number of nodes in your Cloud Bigtable cluster. | number | | 1 | | *storage_type* | The storage type to use. | string | | SSD | | *table_options_defaults* | Default option of tables created in the BigTable instance. | object({...}) | | ... | -| *tables* | Tables to be created in the BigTable instance. | map(object({...})) | | {} | +| *tables* | Tables to be created in the BigTable instance, options can be null. | map(object({...})) | | {} | ## Outputs diff --git a/modules/bigtable-instance/main.tf b/modules/bigtable-instance/main.tf index 32c27bb5..f8660606 100644 --- a/modules/bigtable-instance/main.tf +++ b/modules/bigtable-instance/main.tf @@ -16,7 +16,7 @@ locals { 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 } } @@ -35,8 +35,7 @@ resource "google_bigtable_instance" "default" { } resource "google_bigtable_instance_iam_binding" "default" { - for_each = var.iam_members - + for_each = var.iam project = var.project_id instance = google_bigtable_instance.default.name role = each.key diff --git a/modules/bigtable-instance/outputs.tf b/modules/bigtable-instance/outputs.tf index 2012b5c6..4d7a5217 100644 --- a/modules/bigtable-instance/outputs.tf +++ b/modules/bigtable-instance/outputs.tf @@ -18,8 +18,8 @@ output "id" { description = "An identifier for the resource with format projects/{{project}}/instances/{{name}}." value = google_bigtable_instance.default.id depends_on = [ - google_bigtable_instance_iam_binding, - google_bigtable_table + google_bigtable_instance_iam_binding.default, + google_bigtable_table.default ] } @@ -27,8 +27,8 @@ output "instance" { description = "BigTable intance." value = google_bigtable_instance.default depends_on = [ - google_bigtable_instance_iam_binding, - google_bigtable_table + google_bigtable_instance_iam_binding.default, + google_bigtable_table.default ] } diff --git a/modules/bigtable-instance/variables.tf b/modules/bigtable-instance/variables.tf index 0e2db64f..662ac5b3 100644 --- a/modules/bigtable-instance/variables.tf +++ b/modules/bigtable-instance/variables.tf @@ -14,12 +14,6 @@ * limitations under the License. */ -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(set(string)) - default = {} -} - variable "cluster_id" { description = "The ID of the Cloud Bigtable cluster." type = string @@ -36,10 +30,16 @@ variable "display_name" { default = null } +variable "iam" { + description = "IAM bindings for topic in {ROLE => [MEMBERS]} format." + type = map(list(string)) + default = {} +} + 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 - default = "DEVELOPMENT" + default = null } variable "name" { @@ -65,12 +65,10 @@ variable "storage_type" { } 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({ - table_options = object({ - split_keys = list(string) - column_family = string - }) + split_keys = list(string) + column_family = string })) default = {} } diff --git a/tests/modules/bigtable_instance/__init__.py b/tests/modules/bigtable_instance/__init__.py new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/modules/bigtable_instance/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/modules/bigtable_instance/fixture/main.tf b/tests/modules/bigtable_instance/fixture/main.tf new file mode 100644 index 00000000..47aa2ed5 --- /dev/null +++ b/tests/modules/bigtable_instance/fixture/main.tf @@ -0,0 +1,33 @@ +/** + * 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. + */ + +module "test" { + source = "../../../../modules/bigtable-instance" + project_id = "my-project" + name = "test" + iam = { + "roles/bigtable.user" = ["user:me@example.com"] + } + tables = { + test-1 = null, + test-2 = { + split_keys = ["a", "b", "c"] + column_family = null + } + + } + zone = var.zone +} diff --git a/tests/modules/bigtable_instance/fixture/variables.tf b/tests/modules/bigtable_instance/fixture/variables.tf new file mode 100644 index 00000000..2c2d2d03 --- /dev/null +++ b/tests/modules/bigtable_instance/fixture/variables.tf @@ -0,0 +1,20 @@ +/** + * 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. + */ + +variable "zone" { + type = string + default = "europe-west1-b" +} diff --git a/tests/modules/bigtable_instance/test_plan.py b/tests/modules/bigtable_instance/test_plan.py new file mode 100644 index 00000000..875816ff --- /dev/null +++ b/tests/modules/bigtable_instance/test_plan.py @@ -0,0 +1,47 @@ +# 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +@pytest.fixture +def resources(plan_runner): + _, resources = plan_runner(FIXTURES_DIR) + return resources + + +def test_resource_count(resources): + "Test number of resources created." + assert len(resources) == 4 + + +def test_iam(resources): + "Test IAM binding resources." + bindings = [r['values'] for r in resources if r['type'] + == 'google_bigtable_instance_iam_binding'] + assert len(bindings) == 1 + assert bindings[0]['role'] == 'roles/bigtable.user' + + +def test_tables(resources): + "Test table resources." + subs = [r['values'] for r in resources if r['type'] + == 'google_bigtable_table'] + assert len(subs) == 2 + assert set(s['name'] for s in subs) == set(['test-1', 'test-2']) From 0d17007c9d93808a9ecb3273008b9f398965f01e Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 6 Nov 2020 08:18:57 +0100 Subject: [PATCH 42/53] new iam variable in endpoints module --- modules/endpoints/README.md | 13 +++---- modules/endpoints/main.tf | 2 +- modules/endpoints/variables.tf | 7 ++-- tests/modules/endpoints/__init__.py | 13 +++++++ tests/modules/endpoints/fixture/main.tf | 25 +++++++++++++ tests/modules/endpoints/fixture/openapi.yaml | 13 +++++++ tests/modules/endpoints/fixture/variables.tf | 20 ++++++++++ tests/modules/endpoints/test_plan.py | 39 ++++++++++++++++++++ 8 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 tests/modules/endpoints/__init__.py create mode 100644 tests/modules/endpoints/fixture/main.tf create mode 100644 tests/modules/endpoints/fixture/openapi.yaml create mode 100644 tests/modules/endpoints/fixture/variables.tf create mode 100644 tests/modules/endpoints/test_plan.py diff --git a/modules/endpoints/README.md b/modules/endpoints/README.md index adb98de5..6706c944 100644 --- a/modules/endpoints/README.md +++ b/modules/endpoints/README.md @@ -1,6 +1,6 @@ # Google Cloud Endpoints -This module allows simple management of ['Google Cloud Endpoints'](https://cloud.google.com/endpoints/) services. It supports creating ['OpenAPI'](https://cloud.google.com/endpoints/docs/openapi) or ['gRPC'](https://cloud.google.com/endpoints/docs/grpc/about-grpc) endpoints. +This module allows simple management of ['Google Cloud Endpoints'](https://cloud.google.com/endpoints/) services. It supports creating ['OpenAPI'](https://cloud.google.com/endpoints/docs/openapi) or ['gRPC'](https://cloud.google.com/endpoints/docs/grpc/about-grpc) endpoints. ## Examples @@ -12,24 +12,23 @@ module "endpoint" { project_id = "my-project" service_name = "YOUR-API.endpoints.YOUR-PROJECT-ID.cloud.goog" openapi_config = { "yaml_path" = "openapi.yaml" } - grpc_config = null - iam_members = { - "servicemanagement.serviceController" = ["serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com"] + iam = { + "servicemanagement.serviceController" = ["serviceAccount:123456890-compute@developer.gserviceaccount.com"] } } ``` -[Here](https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/endpoints/getting-started/openapi.yaml) you can find an example of an openapi.yaml file. Once created the endpoint, remember to activate the service at project level. +[Here](https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/endpoints/getting-started/openapi.yaml) you can find an example of an openapi.yaml file. Once created the endpoint, remember to activate the service at project level. ## Variables | name | description | type | required | default | |---|---|:---: |:---:|:---:| -| grpc_config | The configuration for a gRPC enpoint. Either this or openapi_config must be specified. | object({...}) | ✓ | | | openapi_config | The configuration for an OpenAPI endopoint. Either this or grpc_config must be specified. | object({...}) | ✓ | | | service_name | The name of the service. Usually of the form '$apiname.endpoints.$projectid.cloud.goog'. | string | ✓ | | -| *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. | map(set(string)) | | {} | +| *grpc_config* | The configuration for a gRPC enpoint. Either this or openapi_config must be specified. | object({...}) | | null | +| *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *project_id* | The project ID that the service belongs to. | string | | null | ## Outputs diff --git a/modules/endpoints/main.tf b/modules/endpoints/main.tf index 872b7107..782e61ff 100644 --- a/modules/endpoints/main.tf +++ b/modules/endpoints/main.tf @@ -23,7 +23,7 @@ resource "google_endpoints_service" "default" { } resource "google_endpoints_service_iam_binding" "default" { - for_each = var.iam_members + for_each = var.iam service_name = google_endpoints_service.default.service_name role = each.key members = each.value diff --git a/modules/endpoints/variables.tf b/modules/endpoints/variables.tf index acf23401..1d9286f7 100644 --- a/modules/endpoints/variables.tf +++ b/modules/endpoints/variables.tf @@ -20,12 +20,13 @@ variable "grpc_config" { yaml_path = string protoc_output_path = string }) + default = null } -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(set(string)) +variable "iam" { + description = "IAM bindings for topic in {ROLE => [MEMBERS]} format." + type = map(list(string)) default = {} } diff --git a/tests/modules/endpoints/__init__.py b/tests/modules/endpoints/__init__.py new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/modules/endpoints/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/modules/endpoints/fixture/main.tf b/tests/modules/endpoints/fixture/main.tf new file mode 100644 index 00000000..375fc7bd --- /dev/null +++ b/tests/modules/endpoints/fixture/main.tf @@ -0,0 +1,25 @@ +/** + * 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. + */ + +module "test" { + source = "../../../../modules/endpoints" + project_id = "my-project" + service_name = var.service_name + openapi_config = { "yaml_path" = "openapi.yaml" } + iam = { + "roles/servicemanagement.serviceController" = ["user:me@example.com"] + } +} diff --git a/tests/modules/endpoints/fixture/openapi.yaml b/tests/modules/endpoints/fixture/openapi.yaml new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/modules/endpoints/fixture/openapi.yaml @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/modules/endpoints/fixture/variables.tf b/tests/modules/endpoints/fixture/variables.tf new file mode 100644 index 00000000..40ffe31a --- /dev/null +++ b/tests/modules/endpoints/fixture/variables.tf @@ -0,0 +1,20 @@ +/** + * 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. + */ + +variable "service_name" { + type = string + default = "foo.endpoints.test.cloud.goog" +} diff --git a/tests/modules/endpoints/test_plan.py b/tests/modules/endpoints/test_plan.py new file mode 100644 index 00000000..84bcda7b --- /dev/null +++ b/tests/modules/endpoints/test_plan.py @@ -0,0 +1,39 @@ +# 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +@pytest.fixture +def resources(plan_runner): + _, resources = plan_runner(FIXTURES_DIR) + return resources + + +def test_resource_count(resources): + "Test number of resources created." + assert len(resources) == 2 + + +def test_iam(resources): + "Test IAM binding resources." + bindings = [r['values'] for r in resources if r['type'] + == 'google_endpoints_service_iam_binding'] + assert len(bindings) == 1 + assert bindings[0]['role'] == 'roles/servicemanagement.serviceController' From f72bea3f96ab3d6b1ac3e5ccc7fd09314bc76e19 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 6 Nov 2020 08:22:35 +0100 Subject: [PATCH 43/53] use new iam variable in container registry --- modules/container-registry/README.md | 4 +- modules/container-registry/main.tf | 2 +- modules/container-registry/variables.tf | 8 ++-- tests/modules/container_registry/__init__.py | 13 +++++++ .../container_registry/fixture/main.tf | 24 ++++++++++++ .../container_registry/fixture/variables.tf | 20 ++++++++++ tests/modules/container_registry/test_plan.py | 39 +++++++++++++++++++ 7 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 tests/modules/container_registry/__init__.py create mode 100644 tests/modules/container_registry/fixture/main.tf create mode 100644 tests/modules/container_registry/fixture/variables.tf create mode 100644 tests/modules/container_registry/test_plan.py diff --git a/modules/container-registry/README.md b/modules/container-registry/README.md index ffafa387..ac382ccc 100644 --- a/modules/container-registry/README.md +++ b/modules/container-registry/README.md @@ -9,7 +9,7 @@ module "container_registry" { source = "../../modules/container-registry" project_id = "myproject" location = "EU" - iam_members = { + iam = { "roles/storage.admin" = ["group:cicd@example.com"] } } @@ -21,7 +21,7 @@ module "container_registry" { | name | description | type | required | default | |---|---|:---: |:---:|:---:| | project_id | Registry project id. | string | ✓ | | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(set(string)) | | null | +| *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *location* | Registry location. Can be US, EU, ASIA or empty | string | | | ## Outputs diff --git a/modules/container-registry/main.tf b/modules/container-registry/main.tf index 7a84a281..7c750e96 100644 --- a/modules/container-registry/main.tf +++ b/modules/container-registry/main.tf @@ -20,7 +20,7 @@ resource "google_container_registry" "registry" { } resource "google_storage_bucket_iam_binding" "bindings" { - for_each = var.iam_members + for_each = var.iam bucket = google_container_registry.registry.id role = each.key members = each.value diff --git a/modules/container-registry/variables.tf b/modules/container-registry/variables.tf index 1e5ae3f6..72f3a87e 100644 --- a/modules/container-registry/variables.tf +++ b/modules/container-registry/variables.tf @@ -14,10 +14,10 @@ * limitations under the License. */ -variable "iam_members" { - description = "Map of member lists used to set authoritative bindings, keyed by role." - type = map(set(string)) - default = null +variable "iam" { + description = "IAM bindings for topic in {ROLE => [MEMBERS]} format." + type = map(list(string)) + default = {} } variable "location" { diff --git a/tests/modules/container_registry/__init__.py b/tests/modules/container_registry/__init__.py new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/modules/container_registry/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/modules/container_registry/fixture/main.tf b/tests/modules/container_registry/fixture/main.tf new file mode 100644 index 00000000..a9d6174b --- /dev/null +++ b/tests/modules/container_registry/fixture/main.tf @@ -0,0 +1,24 @@ +/** + * 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. + */ + +module "test" { + source = "../../../../modules/container-registry" + project_id = "my-project" + location = var.location + iam = { + "roles/storage.admin" = ["user:me@example.com"] + } +} diff --git a/tests/modules/container_registry/fixture/variables.tf b/tests/modules/container_registry/fixture/variables.tf new file mode 100644 index 00000000..f3939cee --- /dev/null +++ b/tests/modules/container_registry/fixture/variables.tf @@ -0,0 +1,20 @@ +/** + * 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. + */ + +variable "location" { + type = string + default = "EU" +} diff --git a/tests/modules/container_registry/test_plan.py b/tests/modules/container_registry/test_plan.py new file mode 100644 index 00000000..09a0caa5 --- /dev/null +++ b/tests/modules/container_registry/test_plan.py @@ -0,0 +1,39 @@ +# 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +@pytest.fixture +def resources(plan_runner): + _, resources = plan_runner(FIXTURES_DIR) + return resources + + +def test_resource_count(resources): + "Test number of resources created." + assert len(resources) == 2 + + +def test_iam(resources): + "Test IAM binding resources." + bindings = [r['values'] for r in resources if r['type'] + == 'google_storage_bucket_iam_binding'] + assert len(bindings) == 1 + assert bindings[0]['role'] == 'roles/storage.admin' From f1a3c3ebe834a239d28e1ff4a6efdb6519caf6a3 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 6 Nov 2020 08:26:50 +0100 Subject: [PATCH 44/53] use new iam variables in kms module --- modules/kms/README.md | 8 ++++---- modules/kms/main.tf | 4 ++-- modules/kms/variables.tf | 12 ++++++------ tests/modules/kms/fixture/main.tf | 4 ++-- tests/modules/kms/fixture/variables.tf | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/kms/README.md b/modules/kms/README.md index 98c5743e..67b98c18 100644 --- a/modules/kms/README.md +++ b/modules/kms/README.md @@ -16,7 +16,7 @@ In this module **no lifecycle blocks are set on resources to prevent destroy**, module "kms" { source = "../modules/kms" project_id = "my-project" - iam_members = { + iam = { "roles/owner" = ["user:user1@example.com"] } keyring = { location = "europe-west1", name = "test" } @@ -31,7 +31,7 @@ module "kms" { module "kms" { source = "../modules/kms" project_id = "my-project" - key_iam_members = { + key_iam = { key-a = { "roles/owner" = ["user:user1@example.com"] } @@ -72,8 +72,8 @@ module "kms" { |---|---|:---: |:---:|:---:| | keyring | Keyring attributes. | object({...}) | ✓ | | | project_id | Project id where the keyring will be created. | string | ✓ | | -| *iam_members* | Keyring IAM members. | map(set(string)) | | {} | -| *key_iam_members* | IAM members keyed by key name and role. | map(map(set(string))) | | {} | +| *iam* | Keyring IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| *key_iam* | Key IAM bindings for topic in {KEY => {ROLE => [MEMBERS]}} format. | map(map(list(string))) | | {} | | *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. | map(object({...})) | | {} | | *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. | object({...}) | | ... | | *keyring_create* | Set to false to manage keys and IAM bindings in an existing keyring. | bool | | true | diff --git a/modules/kms/main.tf b/modules/kms/main.tf index a6f4795f..ab3a61aa 100644 --- a/modules/kms/main.tf +++ b/modules/kms/main.tf @@ -16,7 +16,7 @@ locals { key_iam_members = flatten([ - for key, roles in var.key_iam_members : [ + for key, roles in var.key_iam : [ for role, members in roles : { key = key role = role @@ -51,7 +51,7 @@ resource "google_kms_key_ring" "default" { } resource "google_kms_key_ring_iam_binding" "default" { - for_each = var.iam_members + for_each = var.iam key_ring_id = local.keyring.self_link role = each.key members = each.value diff --git a/modules/kms/variables.tf b/modules/kms/variables.tf index 1f104cdf..f0586910 100644 --- a/modules/kms/variables.tf +++ b/modules/kms/variables.tf @@ -14,15 +14,15 @@ * limitations under the License. */ -variable "iam_members" { - description = "Keyring IAM members." - type = map(set(string)) +variable "iam" { + description = "Keyring IAM bindings for topic in {ROLE => [MEMBERS]} format." + type = map(list(string)) default = {} } -variable "key_iam_members" { - description = "IAM members keyed by key name and role." - type = map(map(set(string))) +variable "key_iam" { + description = "Key IAM bindings for topic in {KEY => {ROLE => [MEMBERS]}} format." + type = map(map(list(string))) default = {} } diff --git a/tests/modules/kms/fixture/main.tf b/tests/modules/kms/fixture/main.tf index 45fd119b..c58824f8 100644 --- a/tests/modules/kms/fixture/main.tf +++ b/tests/modules/kms/fixture/main.tf @@ -16,8 +16,8 @@ module "test" { source = "../../../../modules/kms" - iam_members = var.iam_members - key_iam_members = var.key_iam_members + iam = var.iam + key_iam = var.key_iam key_purpose = var.key_purpose key_purpose_defaults = var.key_purpose_defaults keyring = var.keyring diff --git a/tests/modules/kms/fixture/variables.tf b/tests/modules/kms/fixture/variables.tf index 04b77d84..65124e67 100644 --- a/tests/modules/kms/fixture/variables.tf +++ b/tests/modules/kms/fixture/variables.tf @@ -14,14 +14,14 @@ * limitations under the License. */ -variable "iam_members" { +variable "iam" { type = map(list(string)) default = { "roles/owner" = ["user:ludo@ludomagno.net"] } } -variable "key_iam_members" { +variable "key_iam" { type = map(map(list(string))) default = { key-a = { From 1423a823225e677e55b946d0fbf50e61ae2a884c Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 6 Nov 2020 08:28:42 +0100 Subject: [PATCH 45/53] update kms iam variables in examples --- data-solutions/cmek-via-centralized-kms/main.tf | 2 +- data-solutions/gcs-to-bq-with-dataflow/main.tf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data-solutions/cmek-via-centralized-kms/main.tf b/data-solutions/cmek-via-centralized-kms/main.tf index 94832905..6f7a388c 100644 --- a/data-solutions/cmek-via-centralized-kms/main.tf +++ b/data-solutions/cmek-via-centralized-kms/main.tf @@ -79,7 +79,7 @@ module "kms" { location = var.location } keys = { key-gce = null, key-gcs = null } - key_iam_members = { + key_iam = { key-gce = { "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ "serviceAccount:${module.project-service.service_accounts.robots.compute}", diff --git a/data-solutions/gcs-to-bq-with-dataflow/main.tf b/data-solutions/gcs-to-bq-with-dataflow/main.tf index 916458de..f9fda8aa 100644 --- a/data-solutions/gcs-to-bq-with-dataflow/main.tf +++ b/data-solutions/gcs-to-bq-with-dataflow/main.tf @@ -120,7 +120,7 @@ module "kms" { location = var.location } keys = { key-gce = null, key-gcs = null, key-bq = null } - key_iam_members = { + key_iam = { key-gce = { "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ "serviceAccount:${module.project-service.service_accounts.robots.compute}", @@ -150,7 +150,7 @@ module "kms-regional" { location = var.region } keys = { key-df = null } - key_iam_members = { + key_iam = { key-df = { "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ "serviceAccount:${module.project-service.service_accounts.robots.dataflow}", From b4fc8167a0ea9a39ed6449a01a938936eb8f8fee Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 6 Nov 2020 08:37:44 +0100 Subject: [PATCH 46/53] use new iam variable in cloud function module --- .../main.tf | 5 +++ modules/cloud-function/README.md | 4 +- modules/cloud-function/main.tf | 2 +- modules/cloud-function/variables.tf | 12 +++--- modules/endpoints/variables.tf | 1 - tests/modules/cloud_function/__init__.py | 13 +++++++ .../cloud_function/fixture/bundle/main.py | 13 +++++++ tests/modules/cloud_function/fixture/main.tf | 29 ++++++++++++++ .../cloud_function/fixture/variables.tf | 20 ++++++++++ tests/modules/cloud_function/test_plan.py | 39 +++++++++++++++++++ 10 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 tests/modules/cloud_function/__init__.py create mode 100644 tests/modules/cloud_function/fixture/bundle/main.py create mode 100644 tests/modules/cloud_function/fixture/main.tf create mode 100644 tests/modules/cloud_function/fixture/variables.tf create mode 100644 tests/modules/cloud_function/test_plan.py diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index 479afe71..9af8951b 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -17,6 +17,7 @@ ############################################################################### # Projects # ############################################################################### + module "project" { source = "../../modules/project" name = var.project_id @@ -46,6 +47,7 @@ module "service-account" { ############################################################################### # Pub/Sub # ############################################################################### + module "pubsub" { source = "../../modules/pubsub" project_id = module.project.project_id @@ -60,6 +62,7 @@ module "pubsub" { ############################################################################### # Cloud Function # ############################################################################### + module "cf" { source = "../../modules/cloud-function" project_id = module.project.project_id @@ -88,6 +91,7 @@ resource "random_pet" "random" { ############################################################################### # Cloud Scheduler # ############################################################################### + resource "google_app_engine_application" "app" { project = module.project.project_id location_id = var.location @@ -116,6 +120,7 @@ resource "google_cloud_scheduler_job" "job" { ############################################################################### # Bigquery # ############################################################################### + module "bq" { source = "../../modules/bigquery-dataset" project_id = module.project.project_id diff --git a/modules/cloud-function/README.md b/modules/cloud-function/README.md index 71e135e8..696154a1 100644 --- a/modules/cloud-function/README.md +++ b/modules/cloud-function/README.md @@ -63,7 +63,7 @@ module "cf-http" { source_dir = "my-cf-source-folder" output_path = "bundle.zip" } - iam_members = { + iam = { "roles/cloudfunctions.invoker" = ["allUsers"] } } @@ -136,7 +136,7 @@ module "cf-http" { | *bucket_config* | Enable and configure auto-created bucket. Set fields to null to use defaults. | object({...}) | | null | | *environment_variables* | Cloud function environment variables. | map(string) | | {} | | *function_config* | Cloud function configuration. | object({...}) | | ... | -| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use. | map(set(string)) | | {} | +| *iam* | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *ingress_settings* | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL and ALLOW_INTERNAL_ONLY. | string | | null | | *labels* | Resource labels | map(string) | | {} | | *prefix* | Optional prefix used for resource names. | string | | null | diff --git a/modules/cloud-function/main.tf b/modules/cloud-function/main.tf index 3b1818a1..a55fe3f5 100644 --- a/modules/cloud-function/main.tf +++ b/modules/cloud-function/main.tf @@ -95,7 +95,7 @@ resource "google_cloudfunctions_function" "function" { } resource "google_cloudfunctions_function_iam_binding" "default" { - for_each = var.iam_members + for_each = var.iam project = var.project_id region = var.region cloud_function = google_cloudfunctions_function.function.name diff --git a/modules/cloud-function/variables.tf b/modules/cloud-function/variables.tf index 427e3417..40b67c5b 100644 --- a/modules/cloud-function/variables.tf +++ b/modules/cloud-function/variables.tf @@ -42,12 +42,6 @@ variable "environment_variables" { default = {} } -variable "iam_members" { - description = "Map of member lists used to set authoritative bindings, keyed by role. Ignored for template use." - type = map(set(string)) - default = {} -} - variable "function_config" { description = "Cloud function configuration." type = object({ @@ -68,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" { description = "Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL and ALLOW_INTERNAL_ONLY." type = string diff --git a/modules/endpoints/variables.tf b/modules/endpoints/variables.tf index 1d9286f7..74695d8d 100644 --- a/modules/endpoints/variables.tf +++ b/modules/endpoints/variables.tf @@ -23,7 +23,6 @@ variable "grpc_config" { default = null } - variable "iam" { description = "IAM bindings for topic in {ROLE => [MEMBERS]} format." type = map(list(string)) diff --git a/tests/modules/cloud_function/__init__.py b/tests/modules/cloud_function/__init__.py new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/modules/cloud_function/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/modules/cloud_function/fixture/bundle/main.py b/tests/modules/cloud_function/fixture/bundle/main.py new file mode 100644 index 00000000..6913f02e --- /dev/null +++ b/tests/modules/cloud_function/fixture/bundle/main.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/modules/cloud_function/fixture/main.tf b/tests/modules/cloud_function/fixture/main.tf new file mode 100644 index 00000000..54dd23dd --- /dev/null +++ b/tests/modules/cloud_function/fixture/main.tf @@ -0,0 +1,29 @@ +/** + * 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. + */ + +module "test" { + source = "../../../../modules/cloud-function" + project_id = "my-project" + name = "test" + bucket_name = var.bucket_name + bundle_config = { + source_dir = "bundle" + output_path = "bundle.zip" + } + iam = { + "roles/cloudfunctions.invoker" = ["allUsers"] + } +} diff --git a/tests/modules/cloud_function/fixture/variables.tf b/tests/modules/cloud_function/fixture/variables.tf new file mode 100644 index 00000000..8d77df50 --- /dev/null +++ b/tests/modules/cloud_function/fixture/variables.tf @@ -0,0 +1,20 @@ +/** + * 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. + */ + +variable "bucket_name" { + type = string + default = "test" +} diff --git a/tests/modules/cloud_function/test_plan.py b/tests/modules/cloud_function/test_plan.py new file mode 100644 index 00000000..50ce14e8 --- /dev/null +++ b/tests/modules/cloud_function/test_plan.py @@ -0,0 +1,39 @@ +# 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +@pytest.fixture +def resources(plan_runner): + _, resources = plan_runner(FIXTURES_DIR) + return resources + + +def test_resource_count(resources): + "Test number of resources created." + assert len(resources) == 3 + + +def test_iam(resources): + "Test IAM binding resources." + bindings = [r['values'] for r in resources if r['type'] + == 'google_cloudfunctions_function_iam_binding'] + assert len(bindings) == 1 + assert bindings[0]['role'] == 'roles/cloudfunctions.invoker' From 2a604cc9784275b36caeb99183f79c04b93dd693 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 6 Nov 2020 08:42:49 +0100 Subject: [PATCH 47/53] enable tests for all environments in ci cloud build configuration --- .ci/cloudbuild.test.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.ci/cloudbuild.test.yaml b/.ci/cloudbuild.test.yaml index 75b90b92..d1945de5 100644 --- a/.ci/cloudbuild.test.yaml +++ b/.ci/cloudbuild.test.yaml @@ -40,6 +40,8 @@ steps: entrypoint: pytest args: - -vv + - tests/cloud_operations + - tests/data_solutions - tests/foundations - tests/networking env: From f9e4a1d614df331a1f67c352bdee9ef55612c685 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 6 Nov 2020 08:43:06 +0100 Subject: [PATCH 48/53] bump required tf version in modules using conditions --- modules/dns/versions.tf | 2 +- modules/gcs/versions.tf | 2 +- modules/net-vpc/versions.tf | 2 +- modules/project/versions.tf | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/dns/versions.tf b/modules/dns/versions.tf index 9ecd9e3b..635b41dd 100644 --- a/modules/dns/versions.tf +++ b/modules/dns/versions.tf @@ -15,7 +15,7 @@ */ terraform { - required_version = ">= 0.12.20" + required_version = ">= 0.13.0" required_providers { google = "~> 3.10" google-beta = "~> 3.20" diff --git a/modules/gcs/versions.tf b/modules/gcs/versions.tf index bc4c2a9d..2a088552 100644 --- a/modules/gcs/versions.tf +++ b/modules/gcs/versions.tf @@ -15,5 +15,5 @@ */ terraform { - required_version = ">= 0.12.6" + required_version = ">= 0.13.0" } diff --git a/modules/net-vpc/versions.tf b/modules/net-vpc/versions.tf index bc4c2a9d..2a088552 100644 --- a/modules/net-vpc/versions.tf +++ b/modules/net-vpc/versions.tf @@ -15,5 +15,5 @@ */ terraform { - required_version = ">= 0.12.6" + required_version = ">= 0.13.0" } diff --git a/modules/project/versions.tf b/modules/project/versions.tf index bc4c2a9d..2a088552 100644 --- a/modules/project/versions.tf +++ b/modules/project/versions.tf @@ -15,5 +15,5 @@ */ terraform { - required_version = ">= 0.12.6" + required_version = ">= 0.13.0" } From f89d78230459fbb444348b1774b2c3abcd0631ef Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 6 Nov 2020 08:45:46 +0100 Subject: [PATCH 49/53] remove version constraints from examples --- .../versions.tf | 17 ------------- .../cmek-via-centralized-kms/versions.tf | 17 ------------- .../gcs-to-bq-with-dataflow/versions.tf | 17 ------------- foundations/business-units/versions.tf | 19 -------------- foundations/environments/versions.tf | 17 ------------- networking/hub-and-spoke-peering/versions.tf | 25 ------------------- networking/ilb-next-hop/versions.tf | 17 ------------- .../onprem-google-access-dns/versions.tf | 17 ------------- networking/shared-vpc-gke/versions.tf | 17 ------------- 9 files changed, 163 deletions(-) delete mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/versions.tf delete mode 100644 data-solutions/cmek-via-centralized-kms/versions.tf delete mode 100644 data-solutions/gcs-to-bq-with-dataflow/versions.tf delete mode 100644 foundations/business-units/versions.tf delete mode 100644 foundations/environments/versions.tf delete mode 100644 networking/hub-and-spoke-peering/versions.tf delete mode 100644 networking/ilb-next-hop/versions.tf delete mode 100644 networking/onprem-google-access-dns/versions.tf delete mode 100644 networking/shared-vpc-gke/versions.tf diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/versions.tf b/cloud-operations/scheduled-asset-inventory-export-bq/versions.tf deleted file mode 100644 index 057095c0..00000000 --- a/cloud-operations/scheduled-asset-inventory-export-bq/versions.tf +++ /dev/null @@ -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" -} diff --git a/data-solutions/cmek-via-centralized-kms/versions.tf b/data-solutions/cmek-via-centralized-kms/versions.tf deleted file mode 100644 index 057095c0..00000000 --- a/data-solutions/cmek-via-centralized-kms/versions.tf +++ /dev/null @@ -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" -} diff --git a/data-solutions/gcs-to-bq-with-dataflow/versions.tf b/data-solutions/gcs-to-bq-with-dataflow/versions.tf deleted file mode 100644 index 057095c0..00000000 --- a/data-solutions/gcs-to-bq-with-dataflow/versions.tf +++ /dev/null @@ -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" -} diff --git a/foundations/business-units/versions.tf b/foundations/business-units/versions.tf deleted file mode 100644 index bc4c2a9d..00000000 --- a/foundations/business-units/versions.tf +++ /dev/null @@ -1,19 +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. - */ - -terraform { - required_version = ">= 0.12.6" -} diff --git a/foundations/environments/versions.tf b/foundations/environments/versions.tf deleted file mode 100644 index 057095c0..00000000 --- a/foundations/environments/versions.tf +++ /dev/null @@ -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" -} diff --git a/networking/hub-and-spoke-peering/versions.tf b/networking/hub-and-spoke-peering/versions.tf deleted file mode 100644 index c36e893b..00000000 --- a/networking/hub-and-spoke-peering/versions.tf +++ /dev/null @@ -1,25 +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.16" -} - -provider "google" { - version = "~> 3.3" -} - -provider "google-beta" { - version = "~> 3.3" -} diff --git a/networking/ilb-next-hop/versions.tf b/networking/ilb-next-hop/versions.tf deleted file mode 100644 index 057095c0..00000000 --- a/networking/ilb-next-hop/versions.tf +++ /dev/null @@ -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" -} diff --git a/networking/onprem-google-access-dns/versions.tf b/networking/onprem-google-access-dns/versions.tf deleted file mode 100644 index 057095c0..00000000 --- a/networking/onprem-google-access-dns/versions.tf +++ /dev/null @@ -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" -} diff --git a/networking/shared-vpc-gke/versions.tf b/networking/shared-vpc-gke/versions.tf deleted file mode 100644 index de5425c2..00000000 --- a/networking/shared-vpc-gke/versions.tf +++ /dev/null @@ -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" -} From c60e37d884e78ccb2a0455359604f6c2ea81a64c Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 6 Nov 2020 09:36:09 +0100 Subject: [PATCH 50/53] Update copyright in headers --- .../scheduled-asset-inventory-export-bq/backend.tf.sample | 2 +- modules/dns/main.tf | 2 +- modules/kms/outputs.tf | 2 +- modules/kms/variables.tf | 2 +- modules/net-cloudnat/outputs.tf | 2 +- modules/secret-manager/outputs.tf | 2 +- modules/secret-manager/variables.tf | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample b/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample index 61572d61..09644750 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample +++ b/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2019 Google LLC +# 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. diff --git a/modules/dns/main.tf b/modules/dns/main.tf index 7e6154a2..40a511cc 100644 --- a/modules/dns/main.tf +++ b/modules/dns/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. diff --git a/modules/kms/outputs.tf b/modules/kms/outputs.tf index d92190a5..a1ea1e26 100644 --- a/modules/kms/outputs.tf +++ b/modules/kms/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. diff --git a/modules/kms/variables.tf b/modules/kms/variables.tf index f0586910..c13bea28 100644 --- a/modules/kms/variables.tf +++ b/modules/kms/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. diff --git a/modules/net-cloudnat/outputs.tf b/modules/net-cloudnat/outputs.tf index 1cf94a55..15d0a649 100644 --- a/modules/net-cloudnat/outputs.tf +++ b/modules/net-cloudnat/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. diff --git a/modules/secret-manager/outputs.tf b/modules/secret-manager/outputs.tf index 78256e5c..781de2b2 100644 --- a/modules/secret-manager/outputs.tf +++ b/modules/secret-manager/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. diff --git a/modules/secret-manager/variables.tf b/modules/secret-manager/variables.tf index 0b8afef9..4f02c0b2 100644 --- a/modules/secret-manager/variables.tf +++ b/modules/secret-manager/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2018 Google LLC + * 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. From 8a672b1b130dcbb444bca0651650adf4efafc424 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 6 Nov 2020 09:36:32 +0100 Subject: [PATCH 51/53] Fix typo in pubsub README --- modules/pubsub/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/pubsub/README.md b/modules/pubsub/README.md index 16b92762..e708078e 100644 --- a/modules/pubsub/README.md +++ b/modules/pubsub/README.md @@ -26,7 +26,7 @@ Subscriptions are defined with the `subscriptions` variable, allowing optional c ```hcl module "pubsub" { source = "./modules/pubsub" - project_id = "my-project + project_id = "my-project" name = "my-topic" subscriptions = { test-pull = null @@ -50,7 +50,7 @@ Push subscriptions need extra configuration in the `push_configs` variable. ```hcl module "pubsub" { source = "./modules/pubsub" - project_id = "my-project + project_id = "my-project" name = "my-topic" subscriptions = { test-push = null @@ -70,7 +70,7 @@ module "pubsub" { ```hcl module "pubsub" { source = "./modules/pubsub" - project_id = "my-project + project_id = "my-project" name = "my-topic" subscriptions = { test-1 = null From 1df08caa7f58ddd3ca980d4088539d0498c08e3f Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 6 Nov 2020 09:43:12 +0100 Subject: [PATCH 52/53] Update READMEs with new variable names --- modules/dns/README.md | 2 +- modules/folder/README.md | 2 +- modules/folders-unit/README.md | 3 +-- modules/gcs/README.md | 2 +- modules/project/README.md | 4 ++-- modules/secret-manager/README.md | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/dns/README.md b/modules/dns/README.md index 2c00ebb1..780e9f0b 100644 --- a/modules/dns/README.md +++ b/modules/dns/README.md @@ -37,7 +37,7 @@ module "private-dns" { | *peer_network* | Peering network self link, only valid for 'peering' zone types. | string | | null | | *recordsets* | List of DNS record objects to manage. | list(object({...})) | | [] | | *service_directory_namespace* | Service directory namespace id (URL), only valid for 'service-directory' zone types. | string | | null | -| *type* | Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'. | string | | private | +| *type* | Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'. | string | | ... | | *zone_create* | Create zone. When set to false, uses a data source to reference existing zone. | bool | | true | ## Outputs diff --git a/modules/folder/README.md b/modules/folder/README.md index 5b15204c..5e85b49a 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -46,7 +46,7 @@ module "folder" { |---|---|:---: |:---:|:---:| | name | Folder name. | string | ✓ | | | parent | Parent in folders/folder_id or organizations/org_id format. | string | ✓ | | -| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(set(string)) | | null | +| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(set(string)) | | {} | | *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | | *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. | map(object({...})) | | {} | diff --git a/modules/folders-unit/README.md b/modules/folders-unit/README.md index 43c84ec0..9ebad644 100644 --- a/modules/folders-unit/README.md +++ b/modules/folders-unit/README.md @@ -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. | string | ✓ | | | *environments* | Unit environments short names. | map(string) | | ... | | *gcs_defaults* | Defaults use for the state GCS buckets. | map(string) | | ... | +| *iam* | IAM bindings for the top-level folder in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *iam_billing_config* | Grant billing user role to service accounts, defaults to granting on the billing account. | object({...}) | | ... | | *iam_enviroment_roles* | IAM roles granted to the environment service account on the environment sub-folder. | list(string) | | ... | -| *iam_members* | IAM members for roles applied on the unit folder. | map(list(string)) | | null | -| *iam_roles* | IAM roles applied on the unit folder. | list(string) | | null | | *iam_xpn_config* | Grant Shared VPC creation roles to service accounts, defaults to granting at folder level. | object({...}) | | ... | | *prefix* | Optional prefix used for GCS bucket names to ensure uniqueness. | string | | null | | *service_account_keys* | Generate and store service account keys in the state file. | bool | | false | diff --git a/modules/gcs/README.md b/modules/gcs/README.md index 3c69da48..33afea1f 100644 --- a/modules/gcs/README.md +++ b/modules/gcs/README.md @@ -66,7 +66,7 @@ module "bucket" { | project_id | Bucket project id. | string | ✓ | | | *encryption_key* | KMS key that will be used for encryption. | string | | null | | *force_destroy* | Optional map to set force destroy keyed by name, defaults to false. | bool | | false | -| *iam_members* | IAM members keyed by bucket name and role. | map(set(string)) | | {} | +| *iam* | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | *labels* | Labels to be attached to all buckets. | map(string) | | {} | | *location* | Bucket location. | string | | EU | | *logging_config* | Bucket logging configuration. | object({...}) | | null | diff --git a/modules/project/README.md b/modules/project/README.md index 61b62286..fcd8ca05 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -15,7 +15,7 @@ module "project" { "container.googleapis.com", "stackdriver.googleapis.com" ] - iam_members = { + iam = { "roles/container.hostServiceAgentUser" = [ "serviceAccount:${var.gke_service_account}" ] @@ -31,7 +31,7 @@ module "project" { name = "project-example" project_create = false - iam_additive_bindings = { + iam_additive = { "group:usergroup_watermlon_experimentation@lemonadeinc.io" = [ "roles/viewer", "roles/storage.objectAdmin" diff --git a/modules/secret-manager/README.md b/modules/secret-manager/README.md index b1617e62..7f237482 100644 --- a/modules/secret-manager/README.md +++ b/modules/secret-manager/README.md @@ -25,7 +25,7 @@ module "secret-manager" { ### Secret IAM bindings -IAM bindings can be set per secret in the same way as for most other modules supporting IAM, using the `iam_members` variable. +IAM bindings can be set per secret in the same way as for most other modules supporting IAM, using the `iam` variable. ```hcl module "secret-manager" { From ec6b1bc8ba4dfb6e3eea4e1a5917f49ffb2a0626 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 6 Nov 2020 10:14:30 +0100 Subject: [PATCH 53/53] Update references to renamed modules --- README.md | 2 +- modules/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 465f37d9..fa2d2864 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ The current list of modules supports most of the core foundational and networkin 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) - **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) diff --git a/modules/README.md b/modules/README.md index 722b5ee4..c9ab6f2d 100644 --- a/modules/README.md +++ b/modules/README.md @@ -10,11 +10,11 @@ Specific modules also offer support for non-authoritative bindings (e.g. `google ## Foundational modules -- [folders](./folders) +- [folder](./folder) - [log sinks](./logging-sinks) - [organization](./organization) - [project](./project) -- [service accounts](./iam-service-accounts) +- [service account](./iam-service-account) ## Networking modules