# terraform-snowflake

## Overview

Terraform modules for managing Snowflake resources.

> [!WARNING]
> Breaking changes! Version 7.0.0 changes the provider requirements. Please refer to [the migration guide.](migration/upgrade_v7.md)

> [!WARNING]
> Breaking changes! Version 6.0.0 replaces a number of deprecated resources with new resource types. Please refer to [the migration guide.](migration/upgrade_v6.md)

> [!WARNING]
> Breaking changes! Version 5.0.0 replaces a number of deprecated resources with new resource types. Please refer to [the migration guide.](migration/upgrade_v5.md)

## Modules

### Database

The `database` module manages a Snowflake database and (optionally) one or more schemas within that database.

A default read-only, read-write and create-schema roles will be created for the database. Role names could be customized using module variables (see an example below).

For each schema managed by the module, a default read-only and read-write role will also be created for the schema. You can customize the name of the roles here too.
These roles will be granted to the read-only and read-write database roles respectively. You can disable creation of the dedicated roles for the schema, and you also can make a schema managed (you can find more details about managed schemas [here](https://docs.snowflake.com/en/sql-reference/sql/create-schema#optional-parameters), see the parameter WITH MANAGED ACCESS)

Roles can be created on the account and database levels. The preferred way is to create them on the database level (this is the default), however they can also be created on the account level for backwards compatibility.

#### Basic Usage

```hcl
module "database" {
  source = "git@github.com:theorchard/terraform-snowflake.git//modules/database?ref=X.X.X"

  database_name = "EXAMPLE"
}
```

#### Custom default role names

```hcl
module "database" {
  source = "git@github.com:theorchard/terraform-snowflake.git//modules/database?ref=X.X.X"

  database_name = "EXAMPLE"

  read_role_name          = "CUSTOM_DB_RO_ROLE_NAME"
  read_write_role_name    = "CUSTOM_DB_RW_ROLE_NAME"
  create_schema_role_name = "CUSTOM_DB_CREATE_SCHEMA_ROLE_NAME"
}
```

#### Account level roles (deprecated)

```hcl
module "database" {
  source = "../../modules/database"

  database_name = "EXAMPLE"

  enable_account_roles  = true
  enable_database_roles = false
}
```
#### Custom schema parameters

```hcl
module "database" {
  source = "../../modules/database"

  database_name = "EXAMPLE"

  schemas = [
    {
      name         = "EXAMPLE1_DISABLE_ROLES"
      create_roles = false
      is_managed   = true
    },
    {
      name                 = "EXAMPLE2_CUSTOM_NAMES"
      read_role_name       = "EXAMPLE2_CUSTOM_RO_ROLE_NAME"
      read_write_role_name = "EXAMPLE2_CUSTOM_RW_ROLE_NAME"
      data_retention_days  = 5
    },
    {
      name         = "EXAMPLE3_MANAGED_SCHEMA"
      is_managed   = true
    },
  ]
}
```

### Service User

The `service-user` module creates a Snowflake user suitable for programmatic (ie application) use, for use cases such as:

- ETLs
- APIs
- DBT

The purpose of this module is to standardise and simplify the configuration of service users.

A role will be created for the service user and attached to it at creation, and a default warehouse will be set based on whether read-only or read-write access to Snowflake is needed by the application.

Object access is provided to the user by passing **pre-existing** object access roles to the module. These get granted to the functional role, and in turn they get granted to the user.

If additional warehouses are required, these can also be passed to the module, however this is usually an edge case as the purpose of this module is to try and keep workloads on the most appropriate (correctly optimised) warehouse for the task.

#### Inputs

Minimal usage example. This will generate a user with access to a write-optimised warehouse and readwrite access to the QA schema in the FACTS DB:

``` hcl
module "service-user" {
  source            = "git@github.com:theorchard/terraform-snowflake.git//modules/service-user?ref=0.0.1"
  environment       = "QA"
  service_name      = "some-data-ingestion-job"
  read_only         = false
  rsa_public_key    = "public-key-goes-here"
  additional_roles  = ["FACTS_DB_QA_SCHEMA_READWRITE"]
}
```

Usage example for a user for a read-only workload. It will get read access to `ROYALTYACCOUNTING_DB` in QA as well as the additional `ACCOUNT_REPORTING_READONLY` role, and an additional warehouse is being granted too (example is purely illustrative):

``` hcl
module "service-user" {
  source                = "git@github.com:theorchard/terraform-snowflake.git//modules/service-user?ref=0.0.1"
  environment           = "QA"
  service_name          = "some-data-ingestion-job"
  read_only             = true
  rsa_public_key        = "public-key-goes-here"
  additional_roles      = ["ROYALTYACCOUNTING_DB_QA_SCHEMA_READ", "ACCOUNT_REPORTING_READONLY"]
  additional_warehouse  = ["QA_DBT_WAREHOUSE"]
}
```

#### Outputs

The module outputs the following:

1. `service_user` - the username to use to configure a snowflake connector in your code, which your service will use for making Snowflake queries.

2. `service_role` - the functional role created for the user, which has been granted the appropriate warehouse-access role, the passed object-access roles, and any additional warehouse-access roles passed.

3. `warehouse_name` - the service user's default warehouse, to be used by the service for Snowflake queries.

#### Resources

The module creates the following infrastructure components:

- [snowflake_role](https://registry.terraform.io/providers/snowflakedb/snowflake/latest/docs/resources/role)
- [snowflake_role_grants](https://registry.terraform.io/providers/snowflakedb/snowflake/latest/docs/resources/role_grants)
- [snowflake_role_ownership_grant](https://registry.terraform.io/providers/snowflakedb/snowflake/latest/docs/resources/role_ownership_grant)
- [snowflake_user](https://registry.terraform.io/providers/snowflakedb/snowflake/latest/docs/resources/user)
- [snowflake_user_ownership_grant](https://registry.terraform.io/providers/snowflakedb/snowflake/latest/docs/resources/user_ownership_grant)

See [the provider documentation](https://registry.terraform.io/providers/snowflakedb/snowflake/latest) for more information.

##### Ownership

The resources will automatically be owned by USERADMIN, and the roles will be granted to the user created and additionally to SYSADMIN, by convention.

### Compute Pool

The `compute_pool` module manages a Snowflake compute pool for use with Snowpark Container Services.

An access role (`{NAME}_COMPUTE_POOL_ACCESS`) is created and granted `MONITOR`, `OPERATE`, and `USAGE` privileges on the compute pool. This role is intended to be granted to functional roles that need to run services or jobs on the pool.

#### Basic Usage

```hcl
module "compute_pool" {
  source = "git@github.com:theorchard/terraform-snowflake.git//modules/compute_pool?ref=X.X.X"

  name            = "MY_COMPUTE_POOL"
  instance_family = "CPU_X64_S"
}
```

#### Custom node counts and suspend timeout

```hcl
module "compute_pool" {
  source = "git@github.com:theorchard/terraform-snowflake.git//modules/compute_pool?ref=X.X.X"

  name              = "MY_COMPUTE_POOL"
  instance_family   = "GPU_NV_S"
  min_nodes         = 1
  max_nodes         = 5
  auto_suspend_secs = 7200
  comment           = "GPU pool for ML inference workloads"
}
```

#### With team tag and custom timeouts

```hcl
module "compute_pool" {
  source = "git@github.com:theorchard/terraform-snowflake.git//modules/compute_pool?ref=X.X.X"

  name            = "MY_COMPUTE_POOL"
  instance_family = "CPU_X64_S"
  team            = "data-engineering"

  timeouts = {
    create = "2h"
    delete = "30m"
  }
}
```

#### Inputs

| Variable | Description | Type | Default | Required |
|---|---|---|---|---|
| `name` | Name of the compute pool. Uppercase letters, digits, and underscores only. | `string` | — | yes |
| `instance_family` | Machine type for pool nodes (e.g. `CPU_X64_S`, `GPU_NV_M`) | `string` | — | yes |
| `min_nodes` | Minimum number of nodes | `number` | `1` | no |
| `max_nodes` | Maximum number of nodes | `number` | `1` | no |
| `auto_resume` | Auto-resume pool when a service or job is submitted | `bool` | `true` | no |
| `auto_suspend_secs` | Seconds of inactivity before auto-suspend. Set to `0` to disable. | `number` | `3600` | no |
| `comment` | Compute pool description | `string` | `null` | no |
| `for_application` | Snowflake Native App name this pool is associated with | `string` | `null` | no |
| `team` | Team name to associate via tag | `string` | `""` | no |
| `team_tag_name` | Tag identifier for team association | `string` | `SNOWFLAKE_SETTINGS.TAGS.TEAM` | no |
| `timeouts` | Terraform operation timeouts (create, update, delete, read). Uses Go duration strings e.g. `"30m"`, `"1h"`. | `object` | `"1h"` for all | no |

#### Outputs

The module outputs the following:

1. `name` - the name of the compute pool.
2. `access_role_name` - the name of the access role to grant to functional roles.
