diff --git a/README.md b/README.md
index 89c65cd..8c8c741 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,10 @@
-# Terraform Module Template
+# Snowflake Role Terraform Module
-
-> **Warning**:
-> This is a template document. Remember to **remove** all text in _italics_ and **update** Module name, Repo name and links/badges to the acual name of your GitHub repository/module!!!
-
-
-
-
-![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white)
-
+![Snowflake](https://img.shields.io/badge/-SNOWFLAKE-249edc?style=for-the-badge&logo=snowflake&logoColor=white)
![Terraform](https://img.shields.io/badge/terraform-%235835CC.svg?style=for-the-badge&logo=terraform&logoColor=white)
-
-![License](https://badgen.net/github/license/getindata/terraform-module-template/)
-![Release](https://badgen.net/github/release/getindata/terraform-module-template/)
+![License](https://badgen.net/github/license/getindata/terraform-snowflake-role/)
+![Release](https://badgen.net/github/release/getindata/terraform-snowflake-role/)
@@ -22,36 +13,60 @@
---
-_Brief Description of MODULE:_
+Terraform module for managing Snowflake Database roles.
-* _What it does_
-* _What technologies it uses_
-
-> **Warning**:
-> _When using "Invicton-Labs/deepmerge/null" module - pin `tflint` version to `v0.41.0` in [pre-commit.yaml](.github/workflows/pre-commit.yml) to avoid failing `tflint` checks_
+- Creates Snowflake database role with specific privileges on database and schemas.
+- Allows granting of privileges on future schemas in a database.
+- Allows granting of privileges on all existing schemas in a database.
+- Allows granting of privileges on specific schema objects like tables.
+- Supports granting of all privileges or specific ones based on the configuration.
+- Can be used to create a hierarchy of roles by assigning parent roles.
+- Can be used to grant roles to other roles.
## USAGE
-_Example usage of the module - terraform code snippet_
-
```terraform
-module "template" {
- source = "getindata/template/null"
- # version = "x.x.x"
-
- example_var = "foo"
+resource "snowflake_database" "this" {
+ name = "TEST_DB"
}
-```
-## NOTES
+resource "snowflake_schema" "this" {
+ database = snowflake_database.this.name
+ name = "BRONZE"
+}
-_Additional information that should be made public, for ex. how to solve known issues, additional descriptions/suggestions_
+module "snowflake_database_role" {
+ source = "../../"
+
+ database_name = snowflake_database.this.name
+ name = "TEST_DB_ROLE"
+
+
+ schema_grants = [
+ {
+ future_schemas_in_database = true
+ all_schemas_in_database = true
+ all_privileges = true
+ },
+ ]
+
+ schema_objects_grants = {
+ "TABLE" = [
+ {
+ all_privileges = true
+ on_future = true
+ on_all = true
+ schema_name = snowflake_schema.this.name
+ }
+ ]
+ }
+}
+```
## EXAMPLES
- [Simple](examples/simple) - Basic usage of the module
- [Complete](examples/complete) - Advanced usage of the module
-
@@ -63,13 +78,16 @@ _Additional information that should be made public, for ex. how to solve known i
|------|-------------|------|---------|:--------:|
| [additional\_tag\_map](#input\_additional\_tag\_map) | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration. | `map(string)` | `{}` | no |
| [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) | Database 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 | object({
all_privileges = optional(bool)
with_grant_option = optional(bool, false)
privileges = optional(list(string), null)
})
| `{}` | no |
+| [database\_name](#input\_database\_name) | The name of the database to create the role in | `string` | n/a | yes |
| [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` | `"resource-type"` | no |
+| [descriptor\_name](#input\_descriptor\_name) | Name of the descriptor used to form a resource name | `string` | `"snowflake-database-role"` | 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 |
-| [example\_var](#input\_example\_var) | Example variable passed into the module | `string` | n/a | yes |
+| [granted\_database\_roles](#input\_granted\_database\_roles) | Database Roles granted to this role | `list(string)` | `[]` | no |
| [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no |
| [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no |
| [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no |
@@ -77,9 +95,11 @@ _Additional information that should be made public, for ex. how to solve known i
| [labels\_as\_tags](#input\_labels\_as\_tags) | Set of labels (ID elements) to include as tags in the `tags` output.
Default is to include all labels.
Tags with empty values will not be included in the `tags` output.
Set to `[]` to suppress all generated tags.
**Notes:**
The value of the `name` tag, if included, will be the `id`, not the `name`.
Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be
changed in later chained modules. Attempts to change it will be silently ignored. | `set(string)` | [
"default"
]
| no |
| [name](#input\_name) | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no |
| [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 |
+| [parent\_database\_role](#input\_parent\_database\_role) | Fully qualified Parent Database Role name (`DB_NAME.ROLE_NAME`), to create parent-child relationship | `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 |
+| [schema\_grants](#input\_schema\_grants) | Grants on a schema level | list(object({
all_privileges = optional(bool)
with_grant_option = optional(bool, false)
privileges = optional(list(string), null)
all_schemas_in_database = optional(bool, false)
future_schemas_in_database = optional(bool, false)
schema_name = optional(string, null)
}))
| `[]` | no |
+| [schema\_objects\_grants](#input\_schema\_objects\_grants) | Grants on a schema object level
Example usage:
schema\_objects\_grants = {
"TABLE" = [
{
privileges = ["SELECT"]
object\_name = snowflake\_table.table\_1.name
schema\_name = snowflake\_schema.this.name
},
{
all\_privileges = true
object\_name = snowflake\_table.table\_2.name
schema\_name = snowflake\_schema.this.name
}
]
"ALERT" = [
{
all\_privileges = true
on\_future = true
on\_all = true
}
]
}
Note: If you don't provide a schema\_name, the grants will be created for all objects of that type in the database.
You can find a list of all object types [here](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_privileges_to_database_role#object_type) | map(list(object({
all_privileges = optional(bool)
with_grant_option = optional(bool)
privileges = optional(list(string))
object_name = optional(string)
on_all = optional(bool, false)
schema_name = optional(string)
on_future = optional(bool, false)
})))
| `{}` | no |
| [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no |
-| [sub\_resource](#input\_sub\_resource) | Some other resource that is part of stack/module | object({
descriptor_name = optional(string, "sub-resource-type")
example_var = string
})
| n/a | yes |
| [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 |
@@ -87,34 +107,39 @@ _Additional information that should be made public, for ex. how to solve known i
| Name | Source | Version |
|------|--------|---------|
-| [subresource\_label](#module\_subresource\_label) | cloudposse/label/null | 0.25.0 |
+| [role\_label](#module\_role\_label) | cloudposse/label/null | 0.25.0 |
| [this](#module\_this) | cloudposse/label/null | 0.25.0 |
## Outputs
| Name | Description |
|------|-------------|
-| [example\_output](#output\_example\_output) | Example output of the module |
+| [fully\_qualified\_name](#output\_fully\_qualified\_name) | Name of the database role in fully qualified format ("DB\_NAME"."ROLE\_NAME") |
+| [name](#output\_name) | Name of the database role |
## Providers
| Name | Version |
|------|---------|
-| [null](#provider\_null) | 3.1.1 |
+| [snowflake](#provider\_snowflake) | >= 0.87 |
## Requirements
| Name | Version |
|------|---------|
-| [terraform](#requirement\_terraform) | >= 1.3.0 |
-| [null](#requirement\_null) | 3.1.1 |
+| [terraform](#requirement\_terraform) | >= 1.3 |
+| [snowflake](#requirement\_snowflake) | >= 0.87 |
## Resources
| Name | Type |
|------|------|
-| [null_resource.output_input](https://registry.terraform.io/providers/hashicorp/null/3.1.1/docs/resources/resource) | resource |
-| [null_resource.subresource](https://registry.terraform.io/providers/hashicorp/null/3.1.1/docs/resources/resource) | resource |
+| [snowflake_database_role.this](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/database_role) | resource |
+| [snowflake_grant_database_role.granted_database_roles](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_database_role) | resource |
+| [snowflake_grant_database_role.parent_database_role](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_database_role) | resource |
+| [snowflake_grant_privileges_to_database_role.database_grants](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_privileges_to_database_role) | resource |
+| [snowflake_grant_privileges_to_database_role.schema_grants](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_privileges_to_database_role) | resource |
+| [snowflake_grant_privileges_to_database_role.schema_objects_grants](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_privileges_to_database_role) | resource |
## CONTRIBUTING
diff --git a/examples/complete/.env.dist b/examples/complete/.env.dist
index e69de29..275e5eb 100644
--- a/examples/complete/.env.dist
+++ b/examples/complete/.env.dist
@@ -0,0 +1,6 @@
+export SNOWFLAKE_PRIVATE_KEY_PATH=""
+export SNOWFLAKE_USER=""
+export SNOWFLAKE_ROLE=""
+export SNOWFLAKE_ACCOUNT=""
+export SNOWFLAKE_AUTHENTICATOR=""
+export SNOWFLAKE_REGION=""
diff --git a/examples/complete/README.md b/examples/complete/README.md
index 1c0282f..be3a884 100644
--- a/examples/complete/README.md
+++ b/examples/complete/README.md
@@ -1,13 +1,115 @@
# Complete Example
```terraform
-module "terraform_module_template" {
+resource "snowflake_database" "this" {
+ name = "TEST_DB"
+}
+
+resource "snowflake_schema" "this" {
+ database = snowflake_database.this.name
+ name = "BRONZE"
+}
+
+resource "snowflake_table" "table_1" {
+ database = snowflake_schema.this.database
+ schema = snowflake_schema.this.name
+ name = "TEST_TABLE_1"
+
+ column {
+ name = "identity"
+ type = "NUMBER(38,0)"
+ nullable = true
+
+ identity {
+ start_num = 1
+ step_num = 3
+ }
+ }
+}
+
+resource "snowflake_table" "table_2" {
+ database = snowflake_schema.this.database
+ schema = snowflake_schema.this.name
+ name = "TEST_TABLE_2"
+
+ column {
+ name = "identity"
+ type = "NUMBER(38,0)"
+ nullable = true
+
+ identity {
+ start_num = 1
+ step_num = 3
+ }
+ }
+}
+
+resource "snowflake_database_role" "db_role_1" {
+ database = snowflake_database.this.name
+ name = "DB_ROLE_1"
+}
+
+resource "snowflake_database_role" "db_role_2" {
+ database = snowflake_database.this.name
+ name = "DB_ROLE_2"
+}
+
+resource "snowflake_database_role" "db_role_3" {
+ database = snowflake_database.this.name
+ name = "DB_ROLE_3"
+}
+
+module "snowflake_database_role" {
source = "../../"
context = module.this.context
- example_var = "This is a example value."
- sub_resource = {
- example_var = "This is a example value of sub resource."
+ database_name = snowflake_database.this.name
+ name = "TEST_DB_ROLE"
+
+
+ parent_database_role = snowflake_database_role.db_role_1.name
+ granted_database_roles = [
+ snowflake_database_role.db_role_2.name,
+ snowflake_database_role.db_role_3.name
+ ]
+ database_grants = [
+ {
+ privileges = ["USAGE", "CREATE SCHEMA"]
+ },
+ ]
+
+ schema_grants = [
+ {
+ schema_name = snowflake_schema.this.name
+ privileges = ["USAGE"]
+ },
+ {
+ future_schemas_in_database = true
+ all_schemas_in_database = true
+ privileges = ["USAGE"]
+ },
+ ]
+
+ schema_objects_grants = {
+ "TABLE" = [
+ {
+ privileges = ["SELECT"]
+ object_name = snowflake_table.table_1.name
+ schema_name = snowflake_schema.this.name
+ },
+ {
+ all_privileges = true
+ object_name = snowflake_table.table_2.name
+ schema_name = snowflake_schema.this.name
+ }
+ ]
+ "ALERT" = [
+ {
+ all_privileges = true
+ on_future = true
+ on_all = true
+ }
+ ]
}
}
```
@@ -15,6 +117,6 @@ module "terraform_module_template" {
## Usage
```
terraform init
-terraform plan -var-file fixtures.tfvars -out tfplan
+terraform plan -out tfplan
terraform apply tfplan
```
diff --git a/examples/complete/fixtures.tfvars b/examples/complete/fixtures.tfvars
deleted file mode 100644
index 11358b0..0000000
--- a/examples/complete/fixtures.tfvars
+++ /dev/null
@@ -1,7 +0,0 @@
-descriptor_formats = {
-
-}
-
-tags = {
- Terraform = "True"
-}
diff --git a/examples/complete/main.tf b/examples/complete/main.tf
index bd89c41..06f5e97 100644
--- a/examples/complete/main.tf
+++ b/examples/complete/main.tf
@@ -1,9 +1,111 @@
-module "terraform_module_template" {
+resource "snowflake_database" "this" {
+ name = "TEST_DB"
+}
+
+resource "snowflake_schema" "this" {
+ database = snowflake_database.this.name
+ name = "BRONZE"
+}
+
+resource "snowflake_table" "table_1" {
+ database = snowflake_schema.this.database
+ schema = snowflake_schema.this.name
+ name = "TEST_TABLE_1"
+
+ column {
+ name = "identity"
+ type = "NUMBER(38,0)"
+ nullable = true
+
+ identity {
+ start_num = 1
+ step_num = 3
+ }
+ }
+}
+
+resource "snowflake_table" "table_2" {
+ database = snowflake_schema.this.database
+ schema = snowflake_schema.this.name
+ name = "TEST_TABLE_2"
+
+ column {
+ name = "identity"
+ type = "NUMBER(38,0)"
+ nullable = true
+
+ identity {
+ start_num = 1
+ step_num = 3
+ }
+ }
+}
+
+resource "snowflake_database_role" "db_role_1" {
+ database = snowflake_database.this.name
+ name = "DB_ROLE_1"
+}
+
+resource "snowflake_database_role" "db_role_2" {
+ database = snowflake_database.this.name
+ name = "DB_ROLE_2"
+}
+
+resource "snowflake_database_role" "db_role_3" {
+ database = snowflake_database.this.name
+ name = "DB_ROLE_3"
+}
+
+module "snowflake_database_role" {
source = "../../"
context = module.this.context
- example_var = "This is a example value."
- sub_resource = {
- example_var = "This is a example value of sub resource."
+ database_name = snowflake_database.this.name
+ name = "TEST_DB_ROLE"
+
+
+ parent_database_role = snowflake_database_role.db_role_1.name
+ granted_database_roles = [
+ snowflake_database_role.db_role_2.name,
+ snowflake_database_role.db_role_3.name
+ ]
+
+ database_grants = {
+ privileges = ["USAGE", "CREATE SCHEMA"]
+ }
+
+
+ schema_grants = [
+ {
+ schema_name = snowflake_schema.this.name
+ privileges = ["USAGE"]
+ },
+ {
+ future_schemas_in_database = true
+ all_schemas_in_database = true
+ privileges = ["USAGE"]
+ },
+ ]
+
+ schema_objects_grants = {
+ "TABLE" = [
+ {
+ privileges = ["SELECT"]
+ object_name = snowflake_table.table_1.name
+ schema_name = snowflake_schema.this.name
+ },
+ {
+ all_privileges = true
+ object_name = snowflake_table.table_2.name
+ schema_name = snowflake_schema.this.name
+ }
+ ]
+ "ALERT" = [
+ {
+ all_privileges = true
+ on_future = true
+ on_all = true
+ }
+ ]
}
}
diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf
index c2007a2..1b42ee5 100644
--- a/examples/complete/outputs.tf
+++ b/examples/complete/outputs.tf
@@ -1,4 +1,4 @@
-output "example_output" {
- description = "Example output of the module"
- value = module.terraform_module_template
+output "snowflake_database_role" {
+ description = "Snowflake database role outputs"
+ value = module.snowflake_database_role
}
diff --git a/examples/complete/providers.tf b/examples/complete/providers.tf
index c793099..d343f0d 100644
--- a/examples/complete/providers.tf
+++ b/examples/complete/providers.tf
@@ -1,3 +1 @@
-provider "null" {
- # Configuration options
-}
+provider "snowflake" {}
diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf
index daa4ce8..d0efb7a 100644
--- a/examples/complete/versions.tf
+++ b/examples/complete/versions.tf
@@ -1,10 +1,11 @@
terraform {
- required_version = ">= 1.3.0"
+ required_version = ">= 1.3"
required_providers {
- null = {
- source = "hashicorp/null"
- version = "3.1.1"
+ snowflake = {
+ source = "Snowflake-Labs/snowflake"
+ version = "0.87.2"
}
}
+
}
diff --git a/examples/simple/.env.dist b/examples/simple/.env.dist
index e69de29..275e5eb 100644
--- a/examples/simple/.env.dist
+++ b/examples/simple/.env.dist
@@ -0,0 +1,6 @@
+export SNOWFLAKE_PRIVATE_KEY_PATH=""
+export SNOWFLAKE_USER=""
+export SNOWFLAKE_ROLE=""
+export SNOWFLAKE_ACCOUNT=""
+export SNOWFLAKE_AUTHENTICATOR=""
+export SNOWFLAKE_REGION=""
diff --git a/examples/simple/README.md b/examples/simple/README.md
index bb30bd9..275f420 100644
--- a/examples/simple/README.md
+++ b/examples/simple/README.md
@@ -1,12 +1,39 @@
# Simple Example
```terraform
-module "terraform_module_template" {
+resource "snowflake_database" "this" {
+ name = "TEST_DB"
+}
+
+resource "snowflake_schema" "this" {
+ database = snowflake_database.this.name
+ name = "BRONZE"
+}
+
+module "snowflake_database_role" {
source = "../../"
- example_var = "This is a example value."
- sub_resource = {
- example_var = "This is a example value of sub resource."
+ database_name = snowflake_database.this.name
+ name = "TEST_DB_ROLE"
+
+
+ schema_grants = [
+ {
+ future_schemas_in_database = true
+ all_schemas_in_database = true
+ all_privileges = true
+ },
+ ]
+
+ schema_objects_grants = {
+ "TABLE" = [
+ {
+ all_privileges = true
+ on_future = true
+ on_all = true
+ schema_name = snowflake_schema.this.name
+ }
+ ]
}
}
```
diff --git a/examples/simple/main.tf b/examples/simple/main.tf
index c729d72..813f843 100644
--- a/examples/simple/main.tf
+++ b/examples/simple/main.tf
@@ -1,8 +1,34 @@
-module "terraform_module_template" {
+resource "snowflake_database" "this" {
+ name = "TEST_DB"
+}
+
+resource "snowflake_schema" "this" {
+ database = snowflake_database.this.name
+ name = "BRONZE"
+}
+
+module "snowflake_database_role" {
source = "../../"
- example_var = "This is a example value."
- sub_resource = {
- example_var = "This is a example value of sub resource."
+ database_name = snowflake_database.this.name
+ name = "TEST_DB_ROLE"
+
+ schema_grants = [
+ {
+ future_schemas_in_database = true
+ all_schemas_in_database = true
+ all_privileges = true
+ },
+ ]
+
+ schema_objects_grants = {
+ "TABLE" = [
+ {
+ all_privileges = true
+ on_future = true
+ on_all = true
+ schema_name = snowflake_schema.this.name
+ }
+ ]
}
}
diff --git a/examples/simple/outputs.tf b/examples/simple/outputs.tf
index c2007a2..1b42ee5 100644
--- a/examples/simple/outputs.tf
+++ b/examples/simple/outputs.tf
@@ -1,4 +1,4 @@
-output "example_output" {
- description = "Example output of the module"
- value = module.terraform_module_template
+output "snowflake_database_role" {
+ description = "Snowflake database role outputs"
+ value = module.snowflake_database_role
}
diff --git a/examples/simple/providers.tf b/examples/simple/providers.tf
index c793099..d343f0d 100644
--- a/examples/simple/providers.tf
+++ b/examples/simple/providers.tf
@@ -1,3 +1 @@
-provider "null" {
- # Configuration options
-}
+provider "snowflake" {}
diff --git a/examples/simple/versions.tf b/examples/simple/versions.tf
index daa4ce8..d0efb7a 100644
--- a/examples/simple/versions.tf
+++ b/examples/simple/versions.tf
@@ -1,10 +1,11 @@
terraform {
- required_version = ">= 1.3.0"
+ required_version = ">= 1.3"
required_providers {
- null = {
- source = "hashicorp/null"
- version = "3.1.1"
+ snowflake = {
+ source = "Snowflake-Labs/snowflake"
+ version = "0.87.2"
}
}
+
}
diff --git a/locals.tf b/locals.tf
index 5778a0d..48bca52 100644
--- a/locals.tf
+++ b/locals.tf
@@ -1,15 +1,82 @@
locals {
# Get a name from the descriptor. If not available, use default naming convention.
# Trim and replace function are used to avoid bare delimiters on both ends of the name and situation of adjacent delimiters.
- #
- # todo: Build a wrapper module around context module with name from descriptor feature
- name_from_descriptor = module.this.enabled ? trim(replace(
- lookup(module.this.descriptors, var.descriptor_name, module.this.id), "/${module.this.delimiter}${module.this.delimiter}+/", module.this.delimiter
- ), module.this.delimiter) : null
+ name_from_descriptor = module.role_label.enabled ? trim(replace(
+ lookup(module.role_label.descriptors, var.descriptor_name, module.role_label.id), "/${module.role_label.delimiter}${module.role_label.delimiter}+/", module.role_label.delimiter
+ ), module.role_label.delimiter) : null
- subresource_name_from_descriptor = module.subresource_label.enabled ? trim(replace(
- lookup(module.subresource_label.descriptors, var.sub_resource.descriptor_name, module.subresource_label.id), "/${module.subresource_label.delimiter}${module.subresource_label.delimiter}+/", module.subresource_label.delimiter
- ), module.subresource_label.delimiter) : null
+ database_role_name = "\"${one(snowflake_database_role.this[*].database)}\".\"${one(snowflake_database_role.this[*].name)}\""
- enabled = module.this.enabled
+ database_grants = var.database_grants.all_privileges == null && var.database_grants.privileges == null ? {} : {
+ var.database_grants.all_privileges == true ? "ALL" : "CUSTOM" = var.database_grants
+ }
+
+ schema_grants = {
+ for index, schema_grant in flatten([
+ for grant in var.schema_grants : grant.future_schemas_in_database && grant.all_schemas_in_database ? [
+ merge(
+ grant,
+ {
+ future_schemas_in_database = true,
+ all_schemas_in_database = false
+ }
+ ),
+ merge(
+ grant,
+ {
+ future_schemas_in_database = false,
+ all_schemas_in_database = true
+ }
+ )
+ ] : [grant]
+ ]) :
+ "${schema_grant.schema_name != null ? schema_grant.schema_name :
+ schema_grant.all_schemas_in_database != false ? "ALL_SCHEMAS" :
+ schema_grant.future_schemas_in_database != false ? "FUTURE_SCHEMAS" : ""
+ }_${schema_grant.all_privileges == true ? "ALL" : "CUSTOM"}_${index}" => schema_grant
+ }
+ schema_objects_grants = {
+ for index, grant in flatten([
+ for object_type, grants in var.schema_objects_grants : [
+ for grant in grants :
+ grant.on_all && grant.on_future ? [
+ merge(
+ grant,
+ {
+ object_type = "${object_type}S",
+ on_future = true,
+ on_all = false
+ }
+ ),
+ merge(
+ grant,
+ {
+ object_type = "${object_type}S",
+ on_future = false,
+ on_all = true
+ }
+ )
+ ] : [
+ merge(
+ grant,
+ {
+ object_type = grant.on_all || grant.on_future ? "${object_type}S" : object_type
+ }
+ )
+ ]
+ ]
+ ]) : "${
+ grant.object_type != null && grant.object_name != null ?
+ "_${grant.object_type}_${grant.object_name}_${grant.all_privileges == true ? "ALL" : "CUSTOM"}"
+ : ""
+ }${
+ grant.on_all != null && grant.on_all ?
+ "_ALL_${grant.object_type}${grant.schema_name != null ? "_${grant.schema_name}_${grant.all_privileges == true ? "ALL" : "CUSTOM"}" : ""}"
+ : ""
+ }${
+ grant.on_future != null && grant.on_future ?
+ "_FUTURE_${grant.object_type}${grant.schema_name != null ? "_${grant.schema_name}_${grant.all_privileges == true ? "ALL" : "CUSTOM"}" : ""}"
+ : ""
+ }" => grant
+ }
}
diff --git a/main.tf b/main.tf
index 740356a..78a9ea2 100644
--- a/main.tf
+++ b/main.tf
@@ -1,36 +1,88 @@
-# Example resource that outputs the input value and
-# echoes it's base64 encoded version locally
+module "role_label" {
+ source = "cloudposse/label/null"
+ version = "0.25.0"
+ context = module.this.context
-resource "null_resource" "output_input" {
- count = local.enabled ? 1 : 0
+ delimiter = coalesce(module.this.context.delimiter, "_")
+ regex_replace_chars = coalesce(module.this.context.regex_replace_chars, "/[^_a-zA-Z0-9]/")
+ label_value_case = coalesce(module.this.context.label_value_case, "upper")
+}
- triggers = {
- name = local.name_from_descriptor
- input = var.example_var
- }
+resource "snowflake_database_role" "this" {
+ count = module.this.enabled ? 1 : 0
- provisioner "local-exec" {
- command = "echo ${var.example_var} | base64"
- }
+ database = var.database_name
+ name = local.name_from_descriptor
+ comment = var.comment
}
-module "subresource_label" {
- source = "cloudposse/label/null"
- version = "0.25.0"
- context = module.this.context
+resource "snowflake_grant_database_role" "parent_database_role" {
+ count = module.this.enabled && var.parent_database_role != null ? 1 : 0
- attributes = ["sub"]
+ database_role_name = local.database_role_name
+ parent_database_role_name = "${one(snowflake_database_role.this[*].database)}.${var.parent_database_role}"
}
-resource "null_resource" "subresource" {
- count = local.enabled ? 1 : 0
+resource "snowflake_grant_database_role" "granted_database_roles" {
+ for_each = toset(module.this.enabled ? var.granted_database_roles : [])
+
+ database_role_name = each.value != null ? "${one(snowflake_database_role.this[*].database)}.${each.value}" : null
+ parent_database_role_name = local.database_role_name
+}
- triggers = {
- name = local.subresource_name_from_descriptor
- input = var.sub_resource.example_var
+resource "snowflake_grant_privileges_to_database_role" "database_grants" {
+ for_each = module.this.enabled ? local.database_grants : {}
+
+ all_privileges = each.value.all_privileges
+ privileges = each.value.privileges
+ with_grant_option = each.value.with_grant_option
+ database_role_name = local.database_role_name
+
+ on_database = one(snowflake_database_role.this[*].database)
+}
+
+resource "snowflake_grant_privileges_to_database_role" "schema_grants" {
+ for_each = module.this.enabled ? local.schema_grants : {}
+
+ all_privileges = each.value.all_privileges
+ privileges = each.value.privileges
+ with_grant_option = each.value.with_grant_option
+ database_role_name = local.database_role_name
+
+ on_schema {
+ all_schemas_in_database = each.value.all_schemas_in_database == true ? one(snowflake_database_role.this[*].database) : null
+ schema_name = each.value.schema_name != null ? "\"${one(snowflake_database_role.this[*].database)}\".\"${each.value.schema_name}\"" : null
+ future_schemas_in_database = each.value.future_schemas_in_database == true ? one(snowflake_database_role.this[*].database) : null
}
+}
+
+resource "snowflake_grant_privileges_to_database_role" "schema_objects_grants" {
+ for_each = module.this.enabled ? local.schema_objects_grants : {}
+
+ all_privileges = each.value.all_privileges
+ privileges = each.value.privileges
+ with_grant_option = each.value.with_grant_option
+ database_role_name = local.database_role_name
+
+ on_schema_object {
+ object_type = each.value.object_type != null && !try(each.value.on_all, false) && !try(each.value.on_future, false) ? each.value.object_type : null
+ object_name = each.value.object_name != null && !try(each.value.on_all, false) && !try(each.value.on_future, false) ? "\"${one(snowflake_database_role.this[*].database)}\".\"${each.value.schema_name}\".\"${each.value.object_name}\"" : null
+ dynamic "all" {
+ for_each = try(each.value.on_all, false) ? [1] : []
+ content {
+ object_type_plural = each.value.object_type
+ in_database = each.value.schema_name == null ? one(snowflake_database_role.this[*].database) : null
+ in_schema = each.value.schema_name != null ? "\"${one(snowflake_database_role.this[*].database)}\".\"${each.value.schema_name}\"" : null
+ }
+ }
- provisioner "local-exec" {
- command = "echo ${var.sub_resource.example_var} | base64"
+ dynamic "future" {
+ for_each = try(each.value.on_future, false) ? [1] : []
+ content {
+ object_type_plural = each.value.object_type
+ in_database = each.value.schema_name == null ? one(snowflake_database_role.this[*].database) : null
+ in_schema = each.value.schema_name != null ? "\"${one(snowflake_database_role.this[*].database)}\".\"${each.value.schema_name}\"" : null
+ }
+ }
}
}
diff --git a/outputs.tf b/outputs.tf
index b1bbafe..bb4da77 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -1,6 +1,9 @@
-# Example output from the module
+output "name" {
+ description = "Name of the database role"
+ value = one(snowflake_database_role.this[*].name)
+}
-output "example_output" {
- description = "Example output of the module"
- value = one(null_resource.output_input[*].id)
+output "fully_qualified_name" {
+ description = "Name of the database role in fully qualified format (\"DB_NAME\".\"ROLE_NAME\")"
+ value = local.database_role_name
}
diff --git a/variables.tf b/variables.tf
index 291a160..4b283ed 100644
--- a/variables.tf
+++ b/variables.tf
@@ -1,18 +1,114 @@
-variable "example_var" {
- description = "Example variable passed into the module"
+variable "database_name" {
+ description = "The name of the database to create the role in"
type = string
}
+variable "comment" {
+ description = "Database Role description"
+ type = string
+ default = null
+}
+
variable "descriptor_name" {
description = "Name of the descriptor used to form a resource name"
type = string
- default = "resource-type"
+ default = "snowflake-database-role"
}
-variable "sub_resource" {
- description = "Some other resource that is part of stack/module"
+variable "parent_database_role" {
+ description = "Fully qualified Parent Database Role name (`DB_NAME.ROLE_NAME`), to create parent-child relationship"
+ type = string
+ default = null
+}
+
+variable "granted_database_roles" {
+ description = "Database Roles granted to this role"
+ type = list(string)
+ default = []
+}
+variable "database_grants" {
+ description = "Grants on a database level"
type = object({
- descriptor_name = optional(string, "sub-resource-type")
- example_var = string
+ all_privileges = optional(bool)
+ with_grant_option = optional(bool, false)
+ privileges = optional(list(string), null)
})
+ default = {}
+
+ validation {
+ condition = var.database_grants == {} ? (var.database_grants.privileges != null) != (var.database_grants.all_privileges == true) : true
+ error_message = "Variable `database_grants` fails validation - only one of `privileges` or `all_privileges` can be set."
+ }
+}
+
+variable "schema_grants" {
+ description = "Grants on a schema level"
+ type = list(object({
+ all_privileges = optional(bool)
+ with_grant_option = optional(bool, false)
+ privileges = optional(list(string), null)
+ all_schemas_in_database = optional(bool, false)
+ future_schemas_in_database = optional(bool, false)
+ schema_name = optional(string, null)
+ }))
+ default = []
+ validation {
+ condition = alltrue([for grant in var.schema_grants : (grant.privileges != null) != (grant.all_privileges == true)])
+ error_message = "Variable `schema_grants` fails validation - only one of `privileges` or `all_privileges` can be set."
+ }
+}
+
+variable "schema_objects_grants" {
+ description = <