From 040d95d1187c014f90ed7eb7cdef062483701b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Gniewek-W=C4=99grzyn?= Date: Tue, 23 Jan 2024 09:46:35 +0100 Subject: [PATCH] feat: add possibility to set enable_multiple_grants --- README.md | 11 +++--- examples/complete/main.tf | 5 ++- locals.tf | 55 ++++++++++++++------------ main.tf | 82 +++++++++++++++++++++------------------ variables.tf | 61 +++++++++++++++++------------ 5 files changed, 120 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 9372c6e..109025f 100644 --- a/README.md +++ b/README.md @@ -71,14 +71,15 @@ module "snowflake_role" { | [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | | [comment](#input\_comment) | Role description | `string` | `null` | no | | [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional\_tag\_map, which are merged. | `any` |
{
"additional_tag_map": {},
"attributes": [],
"delimiter": null,
"descriptor_formats": {},
"enabled": true,
"environment": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"labels_as_tags": [
"unset"
],
"name": null,
"namespace": null,
"regex_replace_chars": null,
"stage": null,
"tags": {},
"tenant": null
}
| no | -| [database\_grants](#input\_database\_grants) | Grants on a database level |
list(object({
database_name = string
privileges = list(string)
}))
| `[]` | no | +| [database\_grants](#input\_database\_grants) | Grants on a database level |
list(object({
database_name = string
privileges = list(string)
enable_multiple_grants = optional(bool)
}))
| `[]` | no | | [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no | | [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.
Map of maps. Keys are names of descriptors. Values are maps of the form
`{
format = string
labels = list(string)
}`
(Type is `any` so the map values can later be enhanced to provide additional options.)
`format` is a Terraform format string to be passed to the `format()` function.
`labels` is a list of labels, in order, to pass to `format()` function.
Label values will be normalized before being passed to `format()` so they will be
identical to how they appear in `id`.
Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no | | [descriptor\_name](#input\_descriptor\_name) | Name of the descriptor used to form a resource name | `string` | `"snowflake-role"` | no | | [dynamic\_table\_grants](#input\_dynamic\_table\_grants) | Grants on a dynamic\_table level |
list(object({
database_name = string
schema_name = optional(string)
dynamic_table_name = optional(string)
on_future = optional(bool, false)
on_all = optional(bool, false)
all_privileges = optional(bool)
privileges = optional(list(string), null)
}))
| `[]` | no | +| [enable\_multiple\_grants](#input\_enable\_multiple\_grants) | When this is set to true, multiple grants of the same type can be created for all grants in the role. This will cause Terraform to not revoke grants applied to roles and objects outside Terraform | `bool` | `null` | no | | [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | | [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | -| [external\_table\_grants](#input\_external\_table\_grants) | Grants on a external table level |
list(object({
database_name = string
schema_name = string
external_table_name = optional(string)
on_future = optional(bool)
on_all = optional(bool)
privileges = list(string)
}))
| `[]` | no | +| [external\_table\_grants](#input\_external\_table\_grants) | Grants on a external table level |
list(object({
database_name = string
schema_name = string
external_table_name = optional(string)
on_future = optional(bool)
on_all = optional(bool)
privileges = list(string)
enable_multiple_grants = optional(bool)
}))
| `[]` | no | | [granted\_roles](#input\_granted\_roles) | Roles granted to this role | `list(string)` | `[]` | no | | [granted\_to\_roles](#input\_granted\_to\_roles) | Roles which this role is granted to | `list(string)` | `[]` | no | | [granted\_to\_users](#input\_granted\_to\_users) | Users which this role is granted to | `list(string)` | `[]` | no | @@ -91,12 +92,12 @@ module "snowflake_role" { | [namespace](#input\_namespace) | ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique | `string` | `null` | no | | [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no | | [role\_ownership\_grant](#input\_role\_ownership\_grant) | The name of the role to grant ownership | `string` | `null` | no | -| [schema\_grants](#input\_schema\_grants) | Grants on a schema level |
list(object({
database_name = string
schema_name = optional(string)
privileges = list(string)
on_all = optional(bool)
on_future = optional(bool)
}))
| `[]` | no | +| [schema\_grants](#input\_schema\_grants) | Grants on a schema level |
list(object({
database_name = string
schema_name = optional(string)
privileges = list(string)
on_all = optional(bool)
on_future = optional(bool)
enable_multiple_grants = optional(bool)
}))
| `[]` | no | | [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no | -| [table\_grants](#input\_table\_grants) | Grants on a table level |
list(object({
database_name = string
schema_name = string
table_name = optional(string)
on_future = optional(bool)
on_all = optional(bool)
privileges = list(string)
}))
| `[]` | no | +| [table\_grants](#input\_table\_grants) | Grants on a table level |
list(object({
database_name = string
schema_name = string
table_name = optional(string)
on_future = optional(bool)
on_all = optional(bool)
privileges = list(string)
enable_multiple_grants = optional(bool)
}))
| `[]` | no | | [tags](#input\_tags) | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`).
Neither the tag keys nor the tag values will be modified by this module. | `map(string)` | `{}` | no | | [tenant](#input\_tenant) | ID element \_(Rarely used, not included by default)\_. A customer identifier, indicating who this instance of a resource is for | `string` | `null` | no | -| [view\_grants](#input\_view\_grants) | Grants on a view level |
list(object({
database_name = string
schema_name = string
view_name = optional(string)
on_future = optional(bool)
on_all = optional(bool)
privileges = list(string)
}))
| `[]` | no | +| [view\_grants](#input\_view\_grants) | Grants on a view level |
list(object({
database_name = string
schema_name = string
view_name = optional(string)
on_future = optional(bool)
on_all = optional(bool)
privileges = list(string)
enable_multiple_grants = optional(bool)
}))
| `[]` | no | ## Modules diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 60acc53..2641985 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -8,8 +8,9 @@ module "snowflake_role" { database_grants = [ { - database_name = "LOGS_DB" - privileges = ["USAGE"] + database_name = "LOGS_DB" + privileges = ["USAGE"] + enable_multiple_grants = true } ] diff --git a/locals.tf b/locals.tf index 3ce6838..a83d60c 100644 --- a/locals.tf +++ b/locals.tf @@ -11,51 +11,56 @@ locals { database_grants = merge([for database_grant in var.database_grants : { for privilege in database_grant.privileges : "${database_grant.database_name}/${privilege}" => { - database_name = database_grant.database_name - privilege = privilege + database_name = database_grant.database_name + enable_multiple_grants = database_grant.enable_multiple_grants == null ? var.enable_multiple_grants : database_grant.enable_multiple_grants + privilege = privilege } }]...) schema_grants = merge([for schema_grant in var.schema_grants : { for privilege in schema_grant.privileges : "${schema_grant.database_name}/${coalesce(schema_grant.schema_name, schema_grant.on_future != null ? "on_future" : "on_all")}/${privilege}" => { - database_name = schema_grant.database_name - schema_name = schema_grant.schema_name - on_future = schema_grant.on_future - on_all = schema_grant.on_all - privilege = privilege + database_name = schema_grant.database_name + schema_name = schema_grant.schema_name + on_future = schema_grant.on_future + on_all = schema_grant.on_all + enable_multiple_grants = schema_grant.enable_multiple_grants == null ? var.enable_multiple_grants : schema_grant.enable_multiple_grants + privilege = privilege } }]...) table_grants = merge([for table_grant in var.table_grants : { for privilege in table_grant.privileges : "${table_grant.database_name}/${table_grant.schema_name}/${coalesce(table_grant.table_name, table_grant.on_future != null ? "on_future" : "on_all")}/${privilege}" => { - database_name = table_grant.database_name - schema_name = table_grant.schema_name - table_name = table_grant.table_name - on_future = table_grant.on_future - on_all = table_grant.on_all - privilege = privilege + database_name = table_grant.database_name + schema_name = table_grant.schema_name + table_name = table_grant.table_name + on_future = table_grant.on_future + on_all = table_grant.on_all + enable_multiple_grants = table_grant.enable_multiple_grants == null ? var.enable_multiple_grants : table_grant.enable_multiple_grants + privilege = privilege } }]...) external_table_grants = merge([for table_grant in var.external_table_grants : { for privilege in table_grant.privileges : "${table_grant.database_name}/${table_grant.schema_name}/${coalesce(table_grant.external_table_name, table_grant.on_future != null ? "on_future" : "on_all")}/${privilege}" => { - database_name = table_grant.database_name - schema_name = table_grant.schema_name - external_table_name = table_grant.external_table_name - on_future = table_grant.on_future - on_all = table_grant.on_all - privilege = privilege + database_name = table_grant.database_name + schema_name = table_grant.schema_name + external_table_name = table_grant.external_table_name + on_future = table_grant.on_future + on_all = table_grant.on_all + enable_multiple_grants = table_grant.enable_multiple_grants == null ? var.enable_multiple_grants : table_grant.enable_multiple_grants + privilege = privilege } }]...) view_grants = merge([for view_grant in var.view_grants : { for privilege in view_grant.privileges : "${view_grant.database_name}/${view_grant.schema_name}/${coalesce(view_grant.view_name, view_grant.on_future != null ? "on_future" : "on_all")}/${privilege}" => { - database_name = view_grant.database_name - schema_name = view_grant.schema_name - view_name = view_grant.view_name - on_future = view_grant.on_future - on_all = view_grant.on_all - privilege = privilege + database_name = view_grant.database_name + schema_name = view_grant.schema_name + view_name = view_grant.view_name + on_future = view_grant.on_future + on_all = view_grant.on_all + enable_multiple_grants = view_grant.enable_multiple_grants == null ? var.enable_multiple_grants : view_grant.enable_multiple_grants + privilege = privilege } }]...) diff --git a/main.tf b/main.tf index 796b65c..4e7fb8f 100644 --- a/main.tf +++ b/main.tf @@ -25,78 +25,86 @@ resource "snowflake_role_ownership_grant" "this" { resource "snowflake_role_grants" "granted_roles" { for_each = toset(module.this.enabled ? local.granted_roles : []) - role_name = each.value - roles = [one(snowflake_role.this[*].name)] + enable_multiple_grants = var.enable_multiple_grants + role_name = each.value + roles = [one(snowflake_role.this[*].name)] } resource "snowflake_role_grants" "granted_to" { count = module.this.enabled && (length(local.granted_to_roles) > 0 || length(local.granted_to_users) > 0) ? 1 : 0 - role_name = one(snowflake_role.this[*].name) - roles = local.granted_to_roles - users = local.granted_to_users + enable_multiple_grants = var.enable_multiple_grants + role_name = one(snowflake_role.this[*].name) + roles = local.granted_to_roles + users = local.granted_to_users } resource "snowflake_database_grant" "this" { for_each = module.this.enabled ? local.database_grants : {} - database_name = each.value.database_name - privilege = each.value.privilege - roles = [one(snowflake_role.this[*].name)] + enable_multiple_grants = each.value.enable_multiple_grants + database_name = each.value.database_name + privilege = each.value.privilege + roles = [one(snowflake_role.this[*].name)] } resource "snowflake_schema_grant" "this" { for_each = module.this.enabled ? local.schema_grants : {} - database_name = each.value.database_name - schema_name = each.value.schema_name - privilege = each.value.privilege - on_future = each.value.on_future - on_all = each.value.on_all - roles = [one(snowflake_role.this[*].name)] + enable_multiple_grants = each.value.enable_multiple_grants + database_name = each.value.database_name + schema_name = each.value.schema_name + privilege = each.value.privilege + on_future = each.value.on_future + on_all = each.value.on_all + roles = [one(snowflake_role.this[*].name)] } resource "snowflake_table_grant" "this" { for_each = module.this.enabled ? local.table_grants : {} - database_name = each.value.database_name - schema_name = each.value.schema_name - table_name = each.value.table_name - privilege = each.value.privilege - on_future = each.value.on_future - on_all = each.value.on_all - roles = [one(snowflake_role.this[*].name)] + enable_multiple_grants = each.value.enable_multiple_grants + database_name = each.value.database_name + schema_name = each.value.schema_name + table_name = each.value.table_name + privilege = each.value.privilege + on_future = each.value.on_future + on_all = each.value.on_all + roles = [one(snowflake_role.this[*].name)] } resource "snowflake_external_table_grant" "this" { for_each = module.this.enabled ? local.external_table_grants : {} - database_name = each.value.database_name - schema_name = each.value.schema_name - external_table_name = each.value.external_table_name - privilege = each.value.privilege - on_future = each.value.on_future - on_all = each.value.on_all - roles = [one(snowflake_role.this[*].name)] + enable_multiple_grants = each.value.enable_multiple_grants + database_name = each.value.database_name + schema_name = each.value.schema_name + external_table_name = each.value.external_table_name + privilege = each.value.privilege + on_future = each.value.on_future + on_all = each.value.on_all + roles = [one(snowflake_role.this[*].name)] } resource "snowflake_view_grant" "this" { for_each = module.this.enabled ? local.view_grants : {} - database_name = each.value.database_name - schema_name = each.value.schema_name - view_name = each.value.view_name - privilege = each.value.privilege - on_future = each.value.on_future - on_all = each.value.on_all - roles = [one(snowflake_role.this[*].name)] + enable_multiple_grants = each.value.enable_multiple_grants + database_name = each.value.database_name + schema_name = each.value.schema_name + view_name = each.value.view_name + privilege = each.value.privilege + on_future = each.value.on_future + on_all = each.value.on_all + roles = [one(snowflake_role.this[*].name)] } resource "snowflake_account_grant" "this" { for_each = toset(module.this.enabled ? var.account_grants : []) - privilege = each.value - roles = [one(snowflake_role.this[*].name)] + enable_multiple_grants = var.enable_multiple_grants + privilege = each.value + roles = [one(snowflake_role.this[*].name)] with_grant_option = false } diff --git a/variables.tf b/variables.tf index 4e4550f..f589d5c 100644 --- a/variables.tf +++ b/variables.tf @@ -4,6 +4,12 @@ variable "comment" { default = null } +variable "enable_multiple_grants" { + description = "When this is set to true, multiple grants of the same type can be created for all grants in the role. This will cause Terraform to not revoke grants applied to roles and objects outside Terraform" + type = bool + default = null +} + variable "role_ownership_grant" { description = "The name of the role to grant ownership" type = string @@ -37,8 +43,9 @@ variable "account_grants" { variable "database_grants" { description = "Grants on a database level" type = list(object({ - database_name = string - privileges = list(string) + database_name = string + privileges = list(string) + enable_multiple_grants = optional(bool) })) default = [] } @@ -46,11 +53,12 @@ variable "database_grants" { variable "schema_grants" { description = "Grants on a schema level" type = list(object({ - database_name = string - schema_name = optional(string) - privileges = list(string) - on_all = optional(bool) - on_future = optional(bool) + database_name = string + schema_name = optional(string) + privileges = list(string) + on_all = optional(bool) + on_future = optional(bool) + enable_multiple_grants = optional(bool) })) default = [] validation { @@ -62,12 +70,13 @@ variable "schema_grants" { variable "table_grants" { description = "Grants on a table level" type = list(object({ - database_name = string - schema_name = string - table_name = optional(string) - on_future = optional(bool) - on_all = optional(bool) - privileges = list(string) + database_name = string + schema_name = string + table_name = optional(string) + on_future = optional(bool) + on_all = optional(bool) + privileges = list(string) + enable_multiple_grants = optional(bool) })) default = [] validation { @@ -79,12 +88,13 @@ variable "table_grants" { variable "external_table_grants" { description = "Grants on a external table level" type = list(object({ - database_name = string - schema_name = string - external_table_name = optional(string) - on_future = optional(bool) - on_all = optional(bool) - privileges = list(string) + database_name = string + schema_name = string + external_table_name = optional(string) + on_future = optional(bool) + on_all = optional(bool) + privileges = list(string) + enable_multiple_grants = optional(bool) })) default = [] validation { @@ -96,12 +106,13 @@ variable "external_table_grants" { variable "view_grants" { description = "Grants on a view level" type = list(object({ - database_name = string - schema_name = string - view_name = optional(string) - on_future = optional(bool) - on_all = optional(bool) - privileges = list(string) + database_name = string + schema_name = string + view_name = optional(string) + on_future = optional(bool) + on_all = optional(bool) + privileges = list(string) + enable_multiple_grants = optional(bool) })) default = [] validation {