Skip to content

Commit

Permalink
Add service-account module and its dependencies (ssm-secret, cross-pr…
Browse files Browse the repository at this point in the history
…oject-custom-role)
  • Loading branch information
Deniss Lapcenko committed Dec 6, 2022
1 parent 2ba604b commit fb8d109
Show file tree
Hide file tree
Showing 19 changed files with 1,275 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Local .terraform directories
**/.terraform/*
**/.terraform.lock.hcl

# .tfstate files
*.tfstate
*.tfstate.*


.idea/

# Crash log files
crash.log

Expand Down
31 changes: 31 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: double-quote-string-fixer
- id: check-ast #python validator
- id: check-builtin-literals #python
- id: pretty-format-json
args:
- "--autofix"
- "--no-sort-keys"
- id: check-json
- id: check-yaml
args:
- "--allow-multiple-documents"
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.77.0
hooks:
- id: terraform_fmt
- id: terraform_validate
args:
- --hook-config=--retry-once-with-cleanup=true
- id: terraform_docs
args:
- --hook-config=--add-to-existing-file=true
- --hook-config=--create-file-if-not-exist=true
- id: terraform_tflint
- id: terraform_checkov
41 changes: 41 additions & 0 deletions iam/cross-project-custom-role/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# cross_project_role

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements

No requirements.

## Providers

| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | 4.22.0 |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [google_project_iam_custom_role.custom_role](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_custom_role) | resource |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_permissions"></a> [permissions](#input\_permissions) | n/a | `list(string)` | n/a | yes |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | n/a | `any` | n/a | yes |
| <a name="input_projects"></a> [projects](#input\_projects) | n/a | `set(string)` | `[]` | no |
| <a name="input_role_description"></a> [role\_description](#input\_role\_description) | n/a | `string` | `""` | no |
| <a name="input_role_name"></a> [role\_name](#input\_role\_name) | n/a | `string` | n/a | yes |
| <a name="input_role_title"></a> [role\_title](#input\_role\_title) | n/a | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_role_name"></a> [role\_name](#output\_role\_name) | n/a |
| <a name="output_roles"></a> [roles](#output\_roles) | n/a |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
9 changes: 9 additions & 0 deletions iam/cross-project-custom-role/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
resource "google_project_iam_custom_role" "custom_role" {
for_each = coalesce(var.projects, toset([var.project_id]))

project = each.value
role_id = var.role_name
title = var.role_title
description = var.role_description
permissions = var.permissions
}
9 changes: 9 additions & 0 deletions iam/cross-project-custom-role/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "roles" {
value = {
for role in google_project_iam_custom_role.custom_role : "${role.project}" => role.name
}
}

output "role_name" {
value = google_project_iam_custom_role.custom_role[*]
}
22 changes: 22 additions & 0 deletions iam/cross-project-custom-role/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
variable "project_id" {}

variable "projects" {
type = set(string)
default = []
}

variable "permissions" {
type = list(string)
}

variable "role_name" {
type = string
}

variable "role_title" {
type = string
}

variable "role_description" {
default = ""
}
71 changes: 71 additions & 0 deletions iam/service-account/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# service_account

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements

No requirements.

## Providers

| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | 4.22.0 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_cross_project_role"></a> [cross\_project\_role](#module\_cross\_project\_role) | ../cross-project-custom-role | n/a |
| <a name="module_secret"></a> [secret](#module\_secret) | ../../ssm-secret | n/a |

## Resources

| Name | Type |
|------|------|
| [google_project_iam_member.role_set_membership](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource |
| [google_service_account.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource |
| [google_service_account_iam_policy.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account_iam_policy) | resource |
| [google_service_account_key.json_key](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account_key) | resource |
| [google_iam_policy.sa-policy](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/iam_policy) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_account_id"></a> [account\_id](#input\_account\_id) | n/a | `any` | n/a | yes |
| <a name="input_add_email_as_secret"></a> [add\_email\_as\_secret](#input\_add\_email\_as\_secret) | used in business apps for GKE Workload Identity | `bool` | `false` | no |
| <a name="input_create_custom_role"></a> [create\_custom\_role](#input\_create\_custom\_role) | n/a | `bool` | `false` | no |
| <a name="input_create_key"></a> [create\_key](#input\_create\_key) | n/a | `bool` | `false` | no |
| <a name="input_cross_project_permission_project_ids"></a> [cross\_project\_permission\_project\_ids](#input\_cross\_project\_permission\_project\_ids) | n/a | `list(string)` | `[]` | no |
| <a name="input_custom_role_name"></a> [custom\_role\_name](#input\_custom\_role\_name) | n/a | `string` | `""` | no |
| <a name="input_custom_role_title"></a> [custom\_role\_title](#input\_custom\_role\_title) | n/a | `string` | `""` | no |
| <a name="input_decode_key_before_storing"></a> [decode\_key\_before\_storing](#input\_decode\_key\_before\_storing) | n/a | `bool` | `false` | no |
| <a name="input_description"></a> [description](#input\_description) | n/a | `string` | `"Managed by terraform"` | no |
| <a name="input_display_name"></a> [display\_name](#input\_display\_name) | n/a | `string` | `""` | no |
| <a name="input_environment"></a> [environment](#input\_environment) | n/a | `string` | `""` | no |
| <a name="input_output_key"></a> [output\_key](#input\_output\_key) | dont | `bool` | `false` | no |
| <a name="input_permissions"></a> [permissions](#input\_permissions) | List of permissions that aren't contained in any of the available roles. If not provided - only roles list will be applied to the service account | `list(string)` | `[]` | no |
| <a name="input_policy_members"></a> [policy\_members](#input\_policy\_members) | n/a | `list(string)` | `[]` | no |
| <a name="input_policy_roles"></a> [policy\_roles](#input\_policy\_roles) | Roles to assign to users regarding this service account. Complements local.all\_users. | `list(string)` | <pre>[<br> "roles/iam.serviceAccountTokenCreator",<br> "roles/iam.serviceAccountKeyAdmin",<br> "roles/iam.serviceAccountUser"<br>]</pre> | no |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | n/a | `any` | n/a | yes |
| <a name="input_region"></a> [region](#input\_region) | n/a | `any` | n/a | yes |
| <a name="input_roles"></a> [roles](#input\_roles) | n/a | `set(string)` | `[]` | no |
| <a name="input_sa_disabled"></a> [sa\_disabled](#input\_sa\_disabled) | n/a | `bool` | `false` | no |
| <a name="input_secret_id"></a> [secret\_id](#input\_secret\_id) | n/a | `string` | `""` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_account_id"></a> [account\_id](#output\_account\_id) | n/a |
| <a name="output_assigned_roles"></a> [assigned\_roles](#output\_assigned\_roles) | n/a |
| <a name="output_custom_role"></a> [custom\_role](#output\_custom\_role) | n/a |
| <a name="output_custom_role_created"></a> [custom\_role\_created](#output\_custom\_role\_created) | n/a |
| <a name="output_email"></a> [email](#output\_email) | n/a |
| <a name="output_id"></a> [id](#output\_id) | n/a |
| <a name="output_name"></a> [name](#output\_name) | n/a |
| <a name="output_projects_affected"></a> [projects\_affected](#output\_projects\_affected) | n/a |
| <a name="output_secret_ids"></a> [secret\_ids](#output\_secret\_ids) | n/a |
| <a name="output_service_account_json_key"></a> [service\_account\_json\_key](#output\_service\_account\_json\_key) | n/a |
| <a name="output_users_allowed_access"></a> [users\_allowed\_access](#output\_users\_allowed\_access) | n/a |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
101 changes: 101 additions & 0 deletions iam/service-account/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
locals {
all_projects = distinct(concat(var.cross_project_permission_project_ids, tolist([var.project_id])))

proj_role_list = flatten([
for proj in local.all_projects : [
for role in setunion(var.roles, var.create_custom_role ? ["projects/${proj}/roles/${var.custom_role_name}"] : []) : {
project = proj
role = role
}
]
])

account_id = join("-", compact(tolist([var.environment, var.account_id])))
}

resource "google_service_account" "this" {
account_id = local.account_id

project = var.project_id
display_name = coalesce(var.display_name, var.account_id)
description = var.description

disabled = var.sa_disabled
}

resource "google_service_account_key" "json_key" {
count = var.create_key ? 1 : 0
service_account_id = google_service_account.this.account_id
}

module "cross_project_role" {
source = "../cross-project-custom-role"
count = var.create_custom_role ? 1 : 0

project_id = var.project_id

permissions = var.permissions
projects = local.all_projects
role_name = var.custom_role_name
role_title = var.custom_role_title
}

resource "google_project_iam_member" "role_set_membership" {
for_each = {
for entry in local.proj_role_list : "${entry.project}.${entry.role}" => entry
}

project = each.value.project
role = each.value.role
member = "serviceAccount:${google_service_account.this.email}"

depends_on = [module.cross_project_role, google_service_account.this]
}

data "google_iam_policy" "sa-policy" {
dynamic "binding" {
for_each = var.policy_roles

content {
role = binding.value
members = var.policy_members
}
}
}

resource "google_service_account_iam_policy" "this" {
count = length(var.policy_members) > 0 ? 1 : 0

service_account_id = google_service_account.this.name
policy_data = data.google_iam_policy.sa-policy.policy_data
}

locals {
secrets = [
var.add_email_as_secret ? {
id : "${var.account_id}-service-account",
data : google_service_account.this.email
} : null,

var.create_key ? {
id : "${var.account_id}-key"
data : var.decode_key_before_storing ? base64decode(google_service_account_key.json_key[0].private_key) : google_service_account_key.json_key[0].private_key
} : null
]
}

module "secret" {
source = "../../ssm-secret"

for_each = {
for secret in local.secrets : secret.id => secret if secret != null
}

environment = var.environment
project_id = var.project_id

secret_id = each.value.id
secret_data = each.value.data

depends_on = [google_service_account_key.json_key]
}
53 changes: 53 additions & 0 deletions iam/service-account/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

output "service_account_json_key" {
value = var.create_key && var.output_key ? google_service_account_key.json_key[0].private_key : ""
sensitive = true
}

output "account_id" {
value = local.account_id
}

output "email" {
value = google_service_account.this.email
}

output "id" {
value = google_service_account.this.id
}

output "name" {
value = google_service_account.this.name
}

output "users_allowed_access" {
value = length(var.policy_members) > 0 ? var.policy_members : ["warning: all users!"]
}

output "projects_affected" {
value = local.all_projects
}

output "assigned_roles" {
value = {
for proj in local.all_projects : "${proj}" => [
for entry in local.proj_role_list : entry.role if entry.project == proj
]
}
}

output "custom_role_created" {
value = length(var.permissions) > 0
}

output "secret_ids" {
value = try(flatten(module.secret.*.secret_id), [])
}

output "custom_role" {
value = length(var.permissions) == 0 ? [] : [{
name : var.custom_role_name
projects : length(var.permissions) > 0 ? var.cross_project_permission_project_ids : []
roles : var.permissions
}]
}
Loading

0 comments on commit fb8d109

Please sign in to comment.