Skip to content

Commit

Permalink
feat: Add helper for assigning members to custom roles (terraform-goo…
Browse files Browse the repository at this point in the history
…gle-modules#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 <kunalkgupta@kunalkgupta-macbookpro3.roam.corp.google.com>
  • Loading branch information
kunalkg11 and Kunal Kumar Gupta authored Apr 9, 2020
1 parent 64722cd commit fc46920
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 3 deletions.
1 change: 1 addition & 0 deletions examples/custom_role_org/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
14 changes: 14 additions & 0 deletions examples/custom_role_project/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
2 changes: 2 additions & 0 deletions modules/custom_role_iam/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
```

Expand All @@ -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 |
Expand Down
22 changes: 22 additions & 0 deletions modules/custom_role_iam/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
*****************************************/
Expand All @@ -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
}
5 changes: 5 additions & 0 deletions modules/custom_role_iam/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
50 changes: 48 additions & 2 deletions test/integration/custom-role/controls/custom-role.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -55,12 +55,58 @@
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")
expect(data["includedPermissions"]).to include("iam.roles.delete")
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
4 changes: 3 additions & 1 deletion test/setup/iam.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down

0 comments on commit fc46920

Please sign in to comment.