diff --git a/.kitchen.yml b/.kitchen.yml index 36b5eeeb..31bb5e7a 100644 --- a/.kitchen.yml +++ b/.kitchen.yml @@ -97,3 +97,16 @@ suites: backend: local provisioner: name: terraform + + - name: custom-role + driver: + name: terraform + command_timeout: 1800 + root_module_directory: test/fixtures/custom-role + verifier: + name: terraform + systems: + - name: custom-role + backend: local + provisioner: + name: terraform diff --git a/build/int.cloudbuild.yaml b/build/int.cloudbuild.yaml index 9f93942e..8e361571 100644 --- a/build/int.cloudbuild.yaml +++ b/build/int.cloudbuild.yaml @@ -67,6 +67,28 @@ steps: name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' args: ['/bin/bash', '-c', 'source /usr/local/bin/task_helper_functions.sh && kitchen_do destroy billing-iam-local'] +# ----- SUITE custom-role-local + +- id: create custom-role-local + waitFor: + - prepare + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'source /usr/local/bin/task_helper_functions.sh && kitchen_do create custom-role-local'] +- id: converge custom-role-local + waitFor: + - create custom-role-local + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'source /usr/local/bin/task_helper_functions.sh && kitchen_do converge custom-role-local'] +- id: verify custom-role-local + waitFor: + - converge custom-role-local + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'source /usr/local/bin/task_helper_functions.sh && kitchen_do verify custom-role-local'] +- id: destroy custom-role-local + waitFor: + - verify custom-role-local + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'source /usr/local/bin/task_helper_functions.sh && kitchen_do destroy custom-role-local'] # ----- SUITE additive-local diff --git a/examples/custom_role_org/README.md b/examples/custom_role_org/README.md new file mode 100644 index 00000000..a8c305c5 --- /dev/null +++ b/examples/custom_role_org/README.md @@ -0,0 +1,19 @@ +# Organization Level Custom Role Example + +This example illustrates how to use the `custom_role_iam` submodule to create a custom role at the organization level. + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| org\_id | Variable for Organization ID. | string | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| role\_id | ID of the custom role created at organization level. | + + + diff --git a/examples/custom_role_org/main.tf b/examples/custom_role_org/main.tf new file mode 100644 index 00000000..bfc9b66b --- /dev/null +++ b/examples/custom_role_org/main.tf @@ -0,0 +1,42 @@ +/** + * Copyright 2019 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. + */ + +/****************************************** + Provider configuration + *****************************************/ +provider "google" { + version = "~> 3.3" +} + +provider "google-beta" { + version = "~> 3.3" +} + +resource "random_id" "rand_custom_id" { + byte_length = 2 +} + +/****************************************** + Module custom_role call + *****************************************/ +module "custom-roles-org" { + source = "../../modules/custom_role_iam/" + + target_level = "org" + target_id = var.org_id + role_id = "iamDeleter_${random_id.rand_custom_id.hex}" + permissions = ["iam.roles.list", "iam.roles.delete"] +} diff --git a/examples/custom_role_org/outputs.tf b/examples/custom_role_org/outputs.tf new file mode 100644 index 00000000..b5563183 --- /dev/null +++ b/examples/custom_role_org/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2019 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 "role_id" { + value = module.custom-roles-org.custom_role_id + description = "ID of the custom role created at organization level." +} diff --git a/examples/custom_role_org/variables.tf b/examples/custom_role_org/variables.tf new file mode 100644 index 00000000..532a0b55 --- /dev/null +++ b/examples/custom_role_org/variables.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2019 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 "org_id" { + type = string + description = "Variable for Organization ID." +} diff --git a/examples/custom_role_org/versions.tf b/examples/custom_role_org/versions.tf new file mode 100644 index 00000000..29704272 --- /dev/null +++ b/examples/custom_role_org/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 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" +} diff --git a/examples/custom_role_project/README.md b/examples/custom_role_project/README.md new file mode 100644 index 00000000..1d560e50 --- /dev/null +++ b/examples/custom_role_project/README.md @@ -0,0 +1,19 @@ +# Project Level Custom Role Example + +This example illustrates how to use the `custom_role_iam` submodule to create a custom role at the project level. + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| project\_id | Variable for Project ID. | string | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| role\_id | ID of the custom role created at project level. | + + + diff --git a/examples/custom_role_project/main.tf b/examples/custom_role_project/main.tf new file mode 100644 index 00000000..69bd1a94 --- /dev/null +++ b/examples/custom_role_project/main.tf @@ -0,0 +1,38 @@ +/** + * Copyright 2019 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. + */ + +/****************************************** + Provider configuration + *****************************************/ +provider "google" { + version = "~> 3.3" +} + +provider "google-beta" { + version = "~> 3.3" +} + +/****************************************** + Module custom_role call + *****************************************/ +module "custom-role-project" { + source = "../../modules/custom_role_iam/" + + target_level = "project" + target_id = var.project_id + role_id = "iamDeleter" + permissions = ["iam.roles.list", "iam.roles.delete"] +} diff --git a/examples/custom_role_project/outputs.tf b/examples/custom_role_project/outputs.tf new file mode 100644 index 00000000..c7587ee5 --- /dev/null +++ b/examples/custom_role_project/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2019 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 "role_id" { + value = module.custom-role-project.custom_role_id + description = "ID of the custom role created at project level." +} diff --git a/examples/custom_role_project/variables.tf b/examples/custom_role_project/variables.tf new file mode 100644 index 00000000..b144d95f --- /dev/null +++ b/examples/custom_role_project/variables.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2019 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 + description = "Variable for Project ID." +} diff --git a/examples/custom_role_project/versions.tf b/examples/custom_role_project/versions.tf new file mode 100644 index 00000000..29704272 --- /dev/null +++ b/examples/custom_role_project/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 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" +} diff --git a/modules/custom_role_iam/README.md b/modules/custom_role_iam/README.md new file mode 100644 index 00000000..13be7199 --- /dev/null +++ b/modules/custom_role_iam/README.md @@ -0,0 +1,54 @@ +# Module Custom Role IAM + +This optional module is used to create custom roles at organization or project level. + +## Usage - Custom Role at Organization Level + +```hcl +module "custom-roles" { + source = "terraform-google-modules/iam/google//modules/custom_role_iam" + + target_level = "org" + target_id = "123456789" + role_id = "custom_role_id" + title = "Custom Role Unique Title" + description = "Custom Role Description" + permissions = ["iam.roles.list", "iam.roles.create", "iam.roles.delete"] +} +``` + +## Usage - Custom Role at Project Level + +```hcl +module "custom-roles" { + source = "terraform-google-modules/iam/google//modules/custom_role_iam" + + target_level = "project" + target_id = "project_id_123" + role_id = "custom_role_id" + title = "Custom Role Unique Title" + description = "Custom Role Description" + permissions = ["iam.roles.list", "iam.roles.create", "iam.roles.delete"] +} +``` + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| description | Description of Custom role. | string | `""` | no | +| permissions | IAM permissions assigned to Custom Role. | list(string) | n/a | yes | +| role\_id | ID of the Custom Role. | string | n/a | yes | +| stage | The current launch stage of the role. Defaults to GA. | string | `"GA"` | no | +| target\_id | Variable for project or organization ID. | string | n/a | yes | +| target\_level | String variable to denote if custom role being created is at project or organization level. | string | `"project"` | no | +| title | Human-readable title of the Custom Role, defaults to role_id. | string | `""` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| custom\_role\_id | ID of the custom role created. | + + diff --git a/modules/custom_role_iam/main.tf b/modules/custom_role_iam/main.tf new file mode 100644 index 00000000..34e59399 --- /dev/null +++ b/modules/custom_role_iam/main.tf @@ -0,0 +1,43 @@ +/** + * Copyright 2019 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 { + custom-role-output = (var.target_level == "project") ? google_project_iam_custom_role.project-custom-role[0].role_id : google_organization_iam_custom_role.org-custom-role[0].role_id +} + +/****************************************** + Custom IAM Org Role + *****************************************/ +resource "google_organization_iam_custom_role" "org-custom-role" { + count = var.target_level == "org" ? 1 : 0 + + org_id = var.target_id + role_id = var.role_id + title = var.title == "" ? var.role_id : var.title + permissions = var.permissions +} + +/****************************************** + Custom IAM Project Role + *****************************************/ +resource "google_project_iam_custom_role" "project-custom-role" { + count = var.target_level == "project" ? 1 : 0 + + project = var.target_id + role_id = var.role_id + title = var.title == "" ? var.role_id : var.title + permissions = var.permissions +} diff --git a/modules/custom_role_iam/outputs.tf b/modules/custom_role_iam/outputs.tf new file mode 100644 index 00000000..f96fffea --- /dev/null +++ b/modules/custom_role_iam/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2019 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 "custom_role_id" { + value = local.custom-role-output + description = "ID of the custom role created." +} diff --git a/modules/custom_role_iam/variables.tf b/modules/custom_role_iam/variables.tf new file mode 100644 index 00000000..b24cc256 --- /dev/null +++ b/modules/custom_role_iam/variables.tf @@ -0,0 +1,54 @@ +/** + * Copyright 2019 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 "role_id" { + type = string + description = "ID of the Custom Role." +} + +variable "title" { + type = string + description = "Human-readable title of the Custom Role, defaults to role_id." + default = "" +} + +variable "permissions" { + type = list(string) + description = "IAM permissions assigned to Custom Role." +} + +variable "description" { + type = string + description = "Description of Custom role." + default = "" +} + +variable "stage" { + type = string + description = "The current launch stage of the role. Defaults to GA." + default = "GA" +} + +variable "target_id" { + type = string + description = "Variable for project or organization ID." +} + +variable "target_level" { + type = string + description = "String variable to denote if custom role being created is at project or organization level." + default = "project" +} diff --git a/test/fixtures/custom-role/main.tf b/test/fixtures/custom-role/main.tf new file mode 100644 index 00000000..8944993b --- /dev/null +++ b/test/fixtures/custom-role/main.tf @@ -0,0 +1,25 @@ +/** + * Copyright 2019 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 "create_custom_role_project" { + source = "../../../examples/custom_role_project" + project_id = var.project_id +} + +module "create_custom_role_org" { + source = "../../../examples/custom_role_org" + org_id = var.org_id +} diff --git a/test/fixtures/custom-role/outputs.tf b/test/fixtures/custom-role/outputs.tf new file mode 100644 index 00000000..ab69d524 --- /dev/null +++ b/test/fixtures/custom-role/outputs.tf @@ -0,0 +1,35 @@ +/** + * Copyright 2019 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 "project_id" { + value = var.project_id + description = "Project ID of the Custom Role." +} + +output "org_id" { + value = var.org_id + description = "Organization ID of the Custom Role." +} + +output "custom_role_id_project" { + value = module.create_custom_role_project.role_id + description = "ID of the custom role created at project level." +} + +output "custom_role_id_org" { + value = module.create_custom_role_org.role_id + description = "ID of the custom role created at organization level." +} diff --git a/test/fixtures/custom-role/variables.tf b/test/fixtures/custom-role/variables.tf new file mode 100644 index 00000000..54f62e97 --- /dev/null +++ b/test/fixtures/custom-role/variables.tf @@ -0,0 +1,25 @@ +/** + * Copyright 2019 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 + description = "Project ID of the Custom Role." +} + +variable "org_id" { + type = string + description = "Organization ID of the Custom Role." +} diff --git a/test/integration/custom-role/controls/custom-role.rb b/test/integration/custom-role/controls/custom-role.rb new file mode 100644 index 00000000..352dfcfc --- /dev/null +++ b/test/integration/custom-role/controls/custom-role.rb @@ -0,0 +1,64 @@ +# Copyright 2019 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. + +# GCP Custom Role + +custom_role_id_project = attribute('custom_role_id_project') +custom_role_id_org = attribute('custom_role_id_org') +project_id = attribute('project_id') +org_id = attribute('org_id') + +control "GCP Custom Role" do + title "Custom Role" + + describe command ("gcloud iam roles describe #{custom_role_id_project} --project #{project_id} --format=json") do + its(:exit_status) { should eq 0 } + its(:stderr) { should eq '' } + + let!(:data) do + if subject.exit_status == 0 + JSON.parse(subject.stdout) + else + {} + end + end + + describe "custom_role" do + it "have role" do + expect(data["includedPermissions"]).to include("iam.roles.list") + expect(data["includedPermissions"]).to include("iam.roles.delete") + end + end + end + + describe command ("gcloud iam roles describe #{custom_role_id_org} --organization #{org_id} --format=json") do + its(:exit_status) { should eq 0 } + its(:stderr) { should eq '' } + + let!(:data) do + if subject.exit_status == 0 + JSON.parse(subject.stdout) + else + {} + end + end + + describe "custom_role" do + it "have role" do + expect(data["includedPermissions"]).to include("iam.roles.list") + expect(data["includedPermissions"]).to include("iam.roles.delete") + end + end + end +end diff --git a/test/integration/custom-role/inspec.yml b/test/integration/custom-role/inspec.yml new file mode 100644 index 00000000..712ff912 --- /dev/null +++ b/test/integration/custom-role/inspec.yml @@ -0,0 +1,28 @@ +# Copyright 2019 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. + +name: custom-role +attributes: + - name: custom_role_id_project + required: true + type: string + - name: custom_role_id_org + required: true + type: string + - name: project_id + required: true + type: string + - name: org_id + required: true + type: string diff --git a/test/setup/iam.tf b/test/setup/iam.tf index 6f93ca5b..baace282 100644 --- a/test/setup/iam.tf +++ b/test/setup/iam.tf @@ -15,6 +15,10 @@ */ locals { + int_required_org_roles = [ + "roles/iam.organizationRoleAdmin" + ] + int_required_proj_roles = [ "roles/owner", "roles/resourcemanager.projectIamAdmin", @@ -52,6 +56,14 @@ resource "google_service_account" "int_test" { display_name = "iam-int-test" } +resource "google_organization_iam_member" "int_test_org" { + count = length(local.int_required_org_roles) + + org_id = var.org_id + role = local.int_required_org_roles[count.index] + member = "serviceAccount:${google_service_account.int_test.email}" +} + resource "google_project_iam_member" "int_test_project" { count = length(local.int_required_proj_roles)