From 084d39f46317c3ecd5015f7ebc9615debcdb4281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Gniewek-W=C4=99grzyn?= Date: Fri, 5 Jan 2024 12:01:09 +0100 Subject: [PATCH 1/4] feat: add support for dynamic tables --- README.md | 2 ++ examples/complete/main.tf | 21 +++++++++++++++++++++ locals.tf | 13 +++++++++++++ main.tf | 32 ++++++++++++++++++++++++++++++++ variables.tf | 22 ++++++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/README.md b/README.md index e25adb7..1b31aed 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ module "snowflake_role" { | [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 | | [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 | @@ -130,6 +131,7 @@ module "snowflake_role" { | [snowflake_account_grant.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/account_grant) | resource | | [snowflake_database_grant.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/database_grant) | resource | | [snowflake_external_table_grant.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/external_table_grant) | resource | +| [snowflake_grant_privileges_to_role.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_privileges_to_role) | resource | | [snowflake_role.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/role) | resource | | [snowflake_role_grants.granted_roles](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/role_grants) | resource | | [snowflake_role_grants.granted_to](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/role_grants) | resource | diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 2bd274a..60acc53 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -38,4 +38,25 @@ module "snowflake_role" { privileges = ["SELECT"] } ] + + dynamic_table_grants = [ + { + database_name = "LOGS_DB" + on_all = true + on_future = true + all_privileges = true + }, + { + database_name = "TEST_DB" + schema_name = "BRONZE" + on_all = true + all_privileges = true + }, + { + database_name = "TEST_DB" + schema_name = "SILVER" + dynamic_table_name = "EXAMPLE" + privileges = ["SELECT"] + }, + ] } diff --git a/locals.tf b/locals.tf index 2cd6ee9..3ce6838 100644 --- a/locals.tf +++ b/locals.tf @@ -58,4 +58,17 @@ locals { privilege = privilege } }]...) + + dynamic_table_grants = merge([for grant in var.dynamic_table_grants : { + for key, value in { "dynamic_table_name" = grant.dynamic_table_name, "on_all" = grant.on_all, "on_future" = grant.on_future } : + "${grant.database_name}/${coalesce(grant.schema_name, "all")}/${key == "dynamic_table_name" ? value : key}" => { + database_name = grant.database_name + schema_name = grant.schema_name + dynamic_table_name = key == "dynamic_table_name" ? value : null + on_future = key == "on_future" ? value : false + on_all = key == "on_all" ? value : false + privileges = grant.privileges + all_privileges = grant.all_privileges + } if(key == "dynamic_table_name" && value != null) || value == true + }]...) } diff --git a/main.tf b/main.tf index 53c4f70..95a1ed8 100644 --- a/main.tf +++ b/main.tf @@ -100,3 +100,35 @@ resource "snowflake_account_grant" "this" { with_grant_option = false } + +resource "snowflake_grant_privileges_to_role" "this" { + for_each = module.this.enabled ? local.dynamic_table_grants : {} + + privileges = each.value.privileges + all_privileges = each.value.all_privileges + role_name = one(snowflake_role.this[*].name) + + on_schema_object { + + object_type = each.value.dynamic_table_name != null ? "DYNAMIC TABLE" : null + object_name = each.value.dynamic_table_name != null ? join(".", [each.value.database_name, each.value.schema_name, each.value.dynamic_table_name]) : null + + dynamic "future" { + for_each = each.value.on_future ? [1] : [] + content { + object_type_plural = "DYNAMIC TABLES" + in_database = each.value.schema_name != null ? null : each.value.database_name + in_schema = each.value.schema_name != null ? join(".", [each.value.database_name, each.value.schema_name]) : null + } + } + + dynamic "all" { + for_each = each.value.on_all ? [1] : [] + content { + object_type_plural = "DYNAMIC TABLES" + in_database = each.value.schema_name != null ? null : each.value.database_name + in_schema = each.value.schema_name != null ? join(".", [each.value.database_name, each.value.schema_name]) : null + } + } + } +} diff --git a/variables.tf b/variables.tf index c25428f..234df39 100644 --- a/variables.tf +++ b/variables.tf @@ -110,6 +110,28 @@ variable "view_grants" { } } +variable "dynamic_table_grants" { + description = "Grants on a dynamic_table level" + type = 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) + })) + default = [] + validation { + condition = alltrue([for grant in var.dynamic_table_grants : !anytrue([grant.privileges == null && grant.all_privileges == null, grant.privileges != null && grant.all_privileges != null])]) + error_message = "Variable `dynamic_table_grants` fails validation - only one of `privilages` or `all_privilages` can be not null." + } + validation { + condition = alltrue([for grant in var.dynamic_table_grants : !alltrue([grant.dynamic_table_name != null, grant.on_future || grant.on_all])]) + error_message = "Variable `dynamic_table_grants` fails validation - when `dynamic_table_name` is set, `on_future` and `on_all` have to be false / not set." + } +} + variable "descriptor_name" { description = "Name of the descriptor used to form a resource name" type = string From 1168ae204cd98b293de04340834f51d43dff2f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Gniewek-W=C4=99grzyn?= Date: Fri, 5 Jan 2024 12:33:29 +0100 Subject: [PATCH 2/4] fix: review suggestions --- variables.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variables.tf b/variables.tf index 234df39..4e4550f 100644 --- a/variables.tf +++ b/variables.tf @@ -124,7 +124,7 @@ variable "dynamic_table_grants" { default = [] validation { condition = alltrue([for grant in var.dynamic_table_grants : !anytrue([grant.privileges == null && grant.all_privileges == null, grant.privileges != null && grant.all_privileges != null])]) - error_message = "Variable `dynamic_table_grants` fails validation - only one of `privilages` or `all_privilages` can be not null." + error_message = "Variable `dynamic_table_grants` fails validation - only one of `privileges` or `all_privileges` can be set." } validation { condition = alltrue([for grant in var.dynamic_table_grants : !alltrue([grant.dynamic_table_name != null, grant.on_future || grant.on_all])]) From 0c3476fb8714c3050d270c543a118b2e3c50a74c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Gniewek-W=C4=99grzyn?= Date: Fri, 5 Jan 2024 12:41:44 +0100 Subject: [PATCH 3/4] fix: rename resource as suggested in review --- main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.tf b/main.tf index 95a1ed8..796b65c 100644 --- a/main.tf +++ b/main.tf @@ -101,7 +101,7 @@ resource "snowflake_account_grant" "this" { with_grant_option = false } -resource "snowflake_grant_privileges_to_role" "this" { +resource "snowflake_grant_privileges_to_role" "dynamic_table" { for_each = module.this.enabled ? local.dynamic_table_grants : {} privileges = each.value.privileges From fff30a540d01b68df37c6b93caf639ba68063fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Gniewek-W=C4=99grzyn?= Date: Fri, 5 Jan 2024 12:46:42 +0100 Subject: [PATCH 4/4] docs: update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1b31aed..9372c6e 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ module "snowflake_role" { | [snowflake_account_grant.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/account_grant) | resource | | [snowflake_database_grant.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/database_grant) | resource | | [snowflake_external_table_grant.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/external_table_grant) | resource | -| [snowflake_grant_privileges_to_role.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_privileges_to_role) | resource | +| [snowflake_grant_privileges_to_role.dynamic_table](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_privileges_to_role) | resource | | [snowflake_role.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/role) | resource | | [snowflake_role_grants.granted_roles](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/role_grants) | resource | | [snowflake_role_grants.granted_to](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/role_grants) | resource |