From fc469204fb961de12763904b323fd2e23d9acac2 Mon Sep 17 00:00:00 2001 From: Kunal Kumar Gupta Date: Thu, 9 Apr 2020 15:31:06 -0500 Subject: [PATCH] feat: Add helper for assigning members to custom roles (#102) * Added bindings for custom roles * Added org policy role in project setup * Added org admin role for setup * Changed from iam_binding to iam_member * Fixed service account domain * Added members to custom role module Co-authored-by: Kunal Kumar Gupta --- examples/custom_role_org/main.tf | 1 + examples/custom_role_project/main.tf | 14 ++++++ modules/custom_role_iam/README.md | 2 + modules/custom_role_iam/main.tf | 22 ++++++++ modules/custom_role_iam/variables.tf | 5 ++ .../custom-role/controls/custom-role.rb | 50 ++++++++++++++++++- test/setup/iam.tf | 4 +- 7 files changed, 95 insertions(+), 3 deletions(-) diff --git a/examples/custom_role_org/main.tf b/examples/custom_role_org/main.tf index 900717c6..2b55c284 100644 --- a/examples/custom_role_org/main.tf +++ b/examples/custom_role_org/main.tf @@ -40,4 +40,5 @@ module "custom-roles-org" { role_id = "iamDeleter_${random_id.rand_custom_id.hex}" permissions = ["iam.roles.list", "iam.roles.delete"] description = "This is an organization level custom role." + members = ["group:test-gcp-org-admins@test.infra.cft.tips", "group:test-gcp-billing-admins@test.infra.cft.tips"] } diff --git a/examples/custom_role_project/main.tf b/examples/custom_role_project/main.tf index d6692649..90a283df 100644 --- a/examples/custom_role_project/main.tf +++ b/examples/custom_role_project/main.tf @@ -36,4 +36,18 @@ module "custom-role-project" { role_id = "iamDeleter" permissions = ["iam.roles.list", "iam.roles.delete"] description = "This is a project level custom role." + members = ["serviceAccount:custom-role-account-01@${var.project_id}.iam.gserviceaccount.com", "serviceAccount:custom-role-account-02@${var.project_id}.iam.gserviceaccount.com"] +} + +/****************************************** + Create service accounts to use as members + *****************************************/ +resource "google_service_account" "custom_role_account_01" { + account_id = "custom-role-account-01" + project = var.project_id +} + +resource "google_service_account" "custom_role_account_02" { + account_id = "custom-role-account-02" + project = var.project_id } diff --git a/modules/custom_role_iam/README.md b/modules/custom_role_iam/README.md index 5eaef91c..d62a58dd 100644 --- a/modules/custom_role_iam/README.md +++ b/modules/custom_role_iam/README.md @@ -14,6 +14,7 @@ module "custom-roles" { title = "Custom Role Unique Title" description = "Custom Role Description" permissions = ["iam.roles.list", "iam.roles.create", "iam.roles.delete"] + members = ["user:user01@domain.com", "group:group01@domain.com"] } ``` @@ -38,6 +39,7 @@ module "custom-roles" { | Name | Description | Type | Default | Required | |------|-------------|:----:|:-----:|:-----:| | description | Description of Custom role. | string | `""` | no | +| members | List of members to be added to custom role. | list(string) | n/a | yes | | 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 | diff --git a/modules/custom_role_iam/main.tf b/modules/custom_role_iam/main.tf index 8203d54d..df3bd6f6 100644 --- a/modules/custom_role_iam/main.tf +++ b/modules/custom_role_iam/main.tf @@ -31,6 +31,17 @@ resource "google_organization_iam_custom_role" "org-custom-role" { permissions = var.permissions } +/****************************************** + Assigning custom_role to member + *****************************************/ +resource "google_organization_iam_member" "custom_role_member" { + + for_each = var.target_level == "org" ? toset(var.members) : [] + org_id = var.target_id + role = "organizations/${var.target_id}/roles/${local.custom-role-output}" + member = each.key +} + /****************************************** Custom IAM Project Role *****************************************/ @@ -43,3 +54,14 @@ resource "google_project_iam_custom_role" "project-custom-role" { description = var.description permissions = var.permissions } + +/****************************************** + Assigning custom_role to member + *****************************************/ +resource "google_project_iam_member" "custom_role_member" { + + for_each = var.target_level == "project" ? toset(var.members) : [] + project = var.target_id + role = "projects/${var.target_id}/roles/${local.custom-role-output}" + member = each.key +} diff --git a/modules/custom_role_iam/variables.tf b/modules/custom_role_iam/variables.tf index b24cc256..abb12143 100644 --- a/modules/custom_role_iam/variables.tf +++ b/modules/custom_role_iam/variables.tf @@ -52,3 +52,8 @@ variable "target_level" { description = "String variable to denote if custom role being created is at project or organization level." default = "project" } + +variable "members" { + description = "List of members to be added to custom role." + type = list(string) +} diff --git a/test/integration/custom-role/controls/custom-role.rb b/test/integration/custom-role/controls/custom-role.rb index a2610336..cc607632 100644 --- a/test/integration/custom-role/controls/custom-role.rb +++ b/test/integration/custom-role/controls/custom-role.rb @@ -34,7 +34,7 @@ end end - describe "custom_role" do + describe "project_custom_role" do it "have role" do expect(data["description"]).to include("This is a project level custom role.") expect(data["includedPermissions"]).to include("iam.roles.list") @@ -55,7 +55,7 @@ end end - describe "custom_role" do + describe "organization_custom_role" do it "have role" do expect(data["description"]).to include("This is an organization level custom role.") expect(data["includedPermissions"]).to include("iam.roles.list") @@ -63,4 +63,50 @@ end end end + + describe command ("gcloud projects get-iam-policy #{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 "project_custom_role" do + it "is bound to" do + transformed_data={} + data['bindings'].each do |binding| + transformed_data.store(binding["role"],binding["members"]) + end + expect(transformed_data["projects/#{project_id}/roles/#{custom_role_id_project}"]).to include("serviceAccount:custom-role-account-01@#{project_id}.iam.gserviceaccount.com") + end + end + end + + describe command ("gcloud organizations get-iam-policy #{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 "organization_custom_role" do + it "is bound to" do + transformed_data={} + data['bindings'].each do |binding| + transformed_data.store(binding["role"],binding["members"]) + end + expect(transformed_data["organizations/#{org_id}/roles/#{custom_role_id_org}"]).to include("group:test-gcp-org-admins@test.infra.cft.tips") + end + end + end end diff --git a/test/setup/iam.tf b/test/setup/iam.tf index baace282..04a1a305 100644 --- a/test/setup/iam.tf +++ b/test/setup/iam.tf @@ -16,7 +16,9 @@ locals { int_required_org_roles = [ - "roles/iam.organizationRoleAdmin" + "roles/iam.organizationRoleAdmin", + "roles/orgpolicy.policyAdmin", + "roles/resourcemanager.organizationAdmin", ] int_required_proj_roles = [