Skip to content

microsoftexpert/tf-mod-snowflake-database

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

❄️ Snowflake Database Terraform Module

Manages a keystone Snowflake database (snowflake_database.this) and the database roles scoped to its lifecycle (snowflake_database_role, for_each). Targets snowflakedb/snowflake ~> 2.17 (verified against provider v2.18.0).

Terraform Snowflake Provider Module Type Resources Posture


🧩 Overview

  • ❄️ Creates exactly one keystone snowflake_database.this β€” a standard (or transient) Snowflake database, with every account-inheritable parameter (retention, task defaults, log levels, Iceberg catalog/serialization settings) modeled as an explicit, deeply-typed variable.
  • 🧩 Creates zero or more snowflake_database_role children via for_each over var.database_roles, keyed by role name β€” a database role only ever makes sense inside exactly one database, so its lifecycle lives here, not in tf-mod-snowflake-role.
  • πŸ”Œ Optionally references a tf-mod-snowflake-external-volume instance's fully_qualified_name as this database's default Iceberg external volume β€” never creates the volume itself.
  • πŸ”’ Never accepts a grant, a credential, or an account/organization/role identifier as a variable β€” grants are handled entirely by tf-mod-snowflake-role-grant-bundle and tf-mod-snowflake-database-role-grant-bundle, and auth/role-context is the caller's provider configuration.
  • 🧱 Secure-by-default throughout: no implicit database roles, no forced retention override, no destructive PUBLIC-schema drop, no replication/failover unless explicitly configured.

πŸ’‘ Why it matters: every other data-platform module in this library β€” schemas, grant bundles, tags β€” ultimately anchors to a database's fully_qualified_name. Getting this one composite right (secure defaults, an honest type contract, no silent account-level overrides) means every module built on top of it inherits that same discipline for free.


❀️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!


πŸ—ΊοΈ Where this fits

graph TD
 EXTVOL["tf-mod-snowflake-external-volume"] -->|"external_volume (optional input)"| DB
 DB["tf-mod-snowflake-database (this module)"] --> KEYSTONE["snowflake_database.this"]
 DB -->|"fully_qualified_name"| SCHEMA["tf-mod-snowflake-schema"]
 DB -->|"fully_qualified_name"| ROLEGRANT["tf-mod-snowflake-role-grant-bundle"]
 DB -->|"database_roles map"| DBROLEGRANT["tf-mod-snowflake-database-role-grant-bundle"]

 style DB fill:#29B5E8,color:#ffffff
 style KEYSTONE fill:#11567F,color:#ffffff
 style EXTVOL fill:#EDEDED,color:#333333
 style SCHEMA fill:#EDEDED,color:#333333
 style ROLEGRANT fill:#EDEDED,color:#333333
 style DBROLEGRANT fill:#EDEDED,color:#333333
Loading

Validated via the Mermaid Chart MCP (validate_and_render_mermaid_diagram) before embedding β€” diagram type flowchart, valid: true.


🧬 What this builds

graph TD
 VARS["var.name, var.comment, var.is_transient,\nvar.data_retention_time_in_days,\nvar.replication,...(caller config)"] --> DB["snowflake_database.this"]
 ROLES["var.database_roles\n(map, for_each)"] --> CHILD["snowflake_database_role.children[each.key]"]
 DB -->|"fully_qualified_name (parent)"| CHILD
 DB --> OUT1["fully_qualified_name / name / id"]
 CHILD --> OUT2["database_roles map output"]

 style DB fill:#11567F,color:#ffffff
 style CHILD fill:#11567F,color:#ffffff
 style VARS fill:#EDEDED,color:#333333
 style ROLES fill:#EDEDED,color:#333333
 style OUT1 fill:#29B5E8,color:#ffffff
 style OUT2 fill:#29B5E8,color:#ffffff
Loading

Validated via the Mermaid Chart MCP β€” diagram type flowchart, valid: true.

Resource inventory

Resource Cardinality Role
snowflake_database.this Exactly 1 (keystone) The database itself
snowflake_database_role.children 0..N (for_each over var.database_roles) Database roles scoped to this database

βœ… Provider / Versions

Requirement Value
Terraform >= 1.12.0
snowflakedb/snowflake provider ~> 2.17 (Phase-1 grounding pulled and validated against v2.18.0)
Provider block Not present in this module β€” the caller's root configuration owns provider "snowflake" {}, the account identifier, and every credential

Schema notes that bite (resource-specific β€” see this module suite's cross-cutting notes for the provider-wide gotchas that apply to every module):

  • is_transient is force-new, and the provider does not detect external changes to database type β€” if a live database's transient/standard type diverges from this configuration outside Terraform, the fix is a manual destroy/recreate, not an in-place apply.
  • A database cannot be dropped while it still has network-rule/network-policy associations β€” destroying snowflake_database.this in that state fails at apply with a Snowflake-side error (098507 (2BP01)), not a Terraform validation error; unassign the association first (see the provider's ../guides/unassigning_policies).
  • database (on snowflake_database_role) accepts a fully_qualified_name directly β€” confirmed against the provider's own Example Usage, which passes snowflake_database.test_database.fully_qualified_name straight into database. This module relies on that pass-through for snowflake_database_role.children.database.
  • Both resources expose a provider-SDK timeouts {} meta-block (create/delete/read/update), confirmed present in the live schema for snowflake_database and snowflake_database_role. Per this module suite's universal-tail convention (comment only, never timeouts), this module does not expose it as a variable β€” this is a deliberate v1.0.0 scope decision, not an oversight.
  • The provider's own import guidance for snowflake_database references a with_managed_access field that does not exist anywhere in this resource's schema (it belongs to snowflake_schema) β€” apparent documentation copy/paste artifact upstream. Only is_transient needs to be set explicitly before import here; see πŸ” Troubleshooting.
  • replication is a Block List Max 1; its nested enable_to_account is a Block List Min 1 within that one block β€” adding/removing a replication target is a change to the single replication block as a whole, not an independent for_each resource.

πŸ”‘ Required Snowflake Privileges

ℹ️ Neither snowflake_database nor snowflake_database_role's own Terraform docs page carries a dedicated "required privileges" subsection (confirmed during Phase-1 grounding) β€” the list below is sourced from Snowflake's own CREATE DATABASE / CREATE DATABASE ROLE SQL reference, not the Terraform provider docs. Treat it as a least-privilege starting point, not an exhaustive, provider-guaranteed list.

  • CREATE DATABASE on the account β€” typically SYSADMIN, or a role explicitly granted CREATE DATABASE at the account level β€” to create the keystone snowflake_database.this.
  • CREATE DATABASE ROLE on the database (ownership of the database, or an explicit grant of this privilege) β€” to create each for_each child in snowflake_database_role.children.
  • If var.replication is populated: treats enabling replication as an ACCOUNTADMIN-or-delegated-organization-privilege action. Setting replication.ignore_edition_check = true bypasses an edition/compliance safeguard β€” route through Compliance/Legal before using it against a Business Critical (or higher) primary, per this organization's regulatory-flagging posture.

Snowflake Prerequisites

  • No preview_features_enabled flag required β€” both snowflake_database and snowflake_database_role are GA (subcategory: "Stable") as of provider v2.18.0.
  • If var.external_volume is set, the referenced external volume must already exist (created by a tf-mod-snowflake-external-volume instance) β€” this module does not create or check for it at plan time.
  • If var.replication is populated, every enable_to_account[*].account_identifier must already be a member of the same Snowflake organization ("<organization_name>"."<account_name>" format). Replicating from a Business Critical (or higher) primary to a lower-edition secondary, or to an account with no equivalent HIPAA/HITRUST business associate agreement, is blocked unless ignore_edition_check = true β€” confirm the compliance implications first.

πŸ“ Module Structure

tf-mod-snowflake-database/
β”œβ”€β”€ providers.tf # required_providers + required_version β€” no provider {} block
β”œβ”€β”€ variables.tf # name, database-level parameters, replication, database_roles map
β”œβ”€β”€ main.tf # snowflake_database.this + snowflake_database_role.children (for_each)
β”œβ”€β”€ outputs.tf # fully_qualified_name, name, id, database_roles map
β”œβ”€β”€ README.md # this file
β”œβ”€β”€ SCOPE.md # cross-module contract, privileges, prerequisites, gotchas
└── examples/ # runnable example call sites

βš™οΈ Quick Start

module "core_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name = "ANALYTICS"
}

The caller's root module configures provider "snowflake" {} (including any role alias) and authentication β€” this module never declares either. The call above produces a standard (non-transient) database, no explicit retention override (inherits the account default), no database roles, and no replication β€” the safe, empty-call default.


πŸ”Œ Cross-Module Contract

Consumes

Input Type Source module
external_volume string (optional, fully_qualified_name) tf-mod-snowflake-external-volume

Every other input (name, is_transient, retention/catalog/collation/serialization/log-level/ task-default parameters, replication, database_roles) is caller-authored configuration, not a sibling-module reference.

Emits

Output Description Consumed by
fully_qualified_name The database's fully-qualified identifier tf-mod-snowflake-schema (database input), tf-mod-snowflake-role-grant-bundle, tf-mod-snowflake-database-role-grant-bundle
name The database's bare name Documentation / display only
id The database's Terraform resource id Import / drift-detection tooling
database_roles Map of role name -> { fully_qualified_name, id }, keyed identically to var.database_roles tf-mod-snowflake-database-role-grant-bundle

πŸ“š Example Library

1 Β· Smallest possible call
module "core_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name = "ANALYTICS"
}

πŸ’‘ Every house-secure default applies: is_transient = false, comment = null, data_retention_time_in_days = null (inherit account default), database_roles = {}, replication = null.

2 Β· Explicit data retention override
module "compliance_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name                        = "COMPLIANCE_ARCHIVE"
  data_retention_time_in_days = 90
  comment                     = "Extended Time Travel retention for compliance archival"
}

ℹ️ The account default is left untouched everywhere else β€” only this database's retention is raised above whatever the account inherits.

3 Β· Transient database
module "scratch_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name         = "ETL_SCRATCH"
  is_transient = true
  comment      = "Ephemeral ETL staging β€” no Fail-safe protection, cost-optimized"
}

⚠️ is_transient is force-new and the provider does not detect external drift on this field β€” if someone runs CREATE OR REPLACE DATABASE... TRANSIENT outside Terraform, no diff will surface; correcting it requires a manual destroy/recreate. Set it explicitly before importing an existing database to avoid a surprise diff on the next plan.

4 Β· Referencing an external volume
module "iceberg_data" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name            = "ICEBERG_DATA"
  external_volume = module.iceberg_ext_volume.fully_qualified_name
}

ℹ️ module.iceberg_ext_volume is a tf-mod-snowflake-external-volume instance, referenced here only by its documented fully_qualified_name output (per that module's own scaffold β€” it has not yet been code-authored in this library pass, so re-verify the output name once it has). This module never creates the volume itself; it must already exist.

5 Β· Database roles (a few, by name)
module "core_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name = "ANALYTICS"

  database_roles = {
    "ANALYST_RO" = { comment = "Read-only analyst access" }
    "ETL_WRITER" = { comment = "ETL pipeline write access" }
  }
}
6 Β· Database roles at scale (for_each, generated)
locals {
  # One database role per financial-services association's read-only analyst team.
  association_codes = ["ASSOC01", "ASSOC02", "ASSOC03", "ASSOC04", "ASSOC05"]
}

module "core_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name = "ANALYTICS"

  database_roles = {
    for code in local.association_codes :
    "${code}_ANALYST_RO" => { comment = "Read-only analyst access for ${code}" }
  }
}

πŸ’‘ Because database_roles is a map(object(...)) iterated with for_each (never count), adding a sixth association code to local.association_codes creates exactly one new snowflake_database_role and leaves the other five untouched.

7 Β· Task-default parameters overridden
module "pipeline_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name = "PIPELINE_ORCHESTRATION"

  user_task_managed_initial_warehouse_size      = "LARGE"
  user_task_timeout_ms                          = 3600000
  user_task_minimum_trigger_interval_in_seconds = 120
  suspend_task_after_num_failures               = 10
  task_auto_retry_attempts                      = 3
}

ℹ️ user_task_managed_initial_warehouse_size carries no validation {} block in this module β€” the resource's own schema does not enumerate its legal values inline (see πŸ“₯ Inputs / SCOPE.md). A typo surfaces as a Snowflake-side error at apply, not at validate.

8 Β· Serialization / log-level enums, explicit
module "iceberg_observability_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name = "ICEBERG_OBSERVABLE"

  storage_serialization_policy = "OPTIMIZED"
  log_level                    = "WARN"
  log_event_level              = "ERROR"
  trace_level                  = "ON_EVENT"
}

ℹ️ trace_level's legal values (ALWAYS, ON_EVENT, PROPAGATE, OFF) are a different set than log_level/log_event_level's (TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF) β€” each has its own validation {} block; a value from the wrong set fails at terraform validate.

9 Β· Replication + failover
module "primary_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name = "CORE_LEDGER"

  replication = {
    enable_to_account = [
      { account_identifier = "\"casey_ORG\".\"casey_ACCOUNT_DR\"", with_failover = true }
    ]
    ignore_edition_check = null
  }
}

πŸ”’ Enabling replication/failover is an elevated, organization-scoped operation β€” treats it as an ACCOUNTADMIN-or-delegated-organization-privilege action (see πŸ”‘ Required Snowflake Privileges). ignore_edition_check is left null here; setting it to true bypasses a Business-Critical-edition/BAA safeguard and requires Compliance/Legal sign-off first β€” never flip it without that review.

10 Β· Dropping the PUBLIC schema on creation
module "no_public_schema_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name                           = "LOCKED_DOWN_DB"
  drop_public_schema_on_creation = true
}

⚠️ drop_public_schema_on_creation defaults to false (house secure default β€” a destructive, creation-time-only option must never default to on). Only effective at creation; toggling it later on an existing database has no effect.

11 Β· Case-sensitivity / diagnostic toggles
module "legacy_migrated_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name                           = "LEGACY_MIGRATED"
  quoted_identifiers_ignore_case = true
  replace_invalid_characters     = true
  enable_console_output          = true
}

ℹ️ quoted_identifiers_ignore_case in particular is worth setting explicitly before importing an existing database β€” import does not always populate every optional field from live state, and this one changes case-folding behavior for every quoted identifier in the database.

12 Β· Metadata / governance fields
module "governed_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name                  = "GOVERNED_ANALYTICS"
  comment               = "Owned by Data Governance β€” see Confluence DG-142"
  catalog               = "OPEN_CATALOG_REF"
  default_ddl_collation = "en_US"
}
13 Β· πŸ—οΈ End-to-end composition

Wires this module's fully_qualified_name into tf-mod-snowflake-schema's database input, and one entry of its database_roles output into tf-mod-snowflake-database-role-grant-bundle's grant map, exactly as documented in this module's own SCOPE.md Emits table.

module "core_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"

  name = "ANALYTICS"

  database_roles = {
    "ANALYST_RO" = { comment = "Read-only analyst access" }
    "ETL_WRITER" = { comment = "ETL pipeline write access" }
  }
}

module "reporting_schema" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-schema.git?ref=v1.0.0"

  database = module.core_database.fully_qualified_name
  name     = "REPORTING"
}

module "analyst_ro_grants" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database-role-grant-bundle.git?ref=v1.0.0"

  database_role_fully_qualified_name = module.core_database.database_roles["ANALYST_RO"].fully_qualified_name

  grants = {
    "reporting_schema_usage" = {
      on_schema  = { schema_name = module.reporting_schema.fully_qualified_name }
      privileges = ["USAGE"]
    }
  }
}

ℹ️ tf-mod-snowflake-schema's database input is code-verified against its own variables.tf in this library. tf-mod-snowflake-database-role-grant-bundle's database_role_fully_qualified_name / grants shape is sourced from that module's own SCOPE.md (that module has not yet been authored in this library) β€” re-verify field names once that module's variables.tf exists.


πŸ“₯ Inputs

Keystone (snowflake_database.this): name (required); is_transient, comment, data_retention_time_in_days, max_data_extension_time_in_days, external_volume, catalog, default_ddl_collation, storage_serialization_policy, replace_invalid_characters, log_level, log_event_level, trace_level, suspend_task_after_num_failures, task_auto_retry_attempts, user_task_managed_initial_warehouse_size, user_task_timeout_ms, user_task_minimum_trigger_interval_in_seconds, quoted_identifiers_ignore_case, enable_console_output, drop_public_schema_on_creation, replication (all optional).

Children: database_roles (optional map(object({ comment })), default {}).

Full object schemas
variable "name" {
  type = string # required
}

variable "is_transient" {
  type    = bool
  default = false
}

variable "comment" {
  type    = string
  default = null
}

variable "data_retention_time_in_days" {
  type    = number
  default = null
}

variable "max_data_extension_time_in_days" {
  type    = number
  default = null
}

variable "external_volume" {
  type    = string # fully_qualified_name of a tf-mod-snowflake-external-volume instance
  default = null
}

variable "catalog" {
  type    = string
  default = null
}

variable "default_ddl_collation" {
  type    = string
  default = null
}

variable "storage_serialization_policy" {
  type    = string # COMPATIBLE | OPTIMIZED
  default = null
}

variable "replace_invalid_characters" {
  type    = bool
  default = null
}

variable "log_level" {
  type    = string # TRACE | DEBUG | INFO | WARN | ERROR | FATAL | OFF
  default = null
}

variable "log_event_level" {
  type    = string # TRACE | DEBUG | INFO | WARN | ERROR | FATAL | OFF
  default = null
}

variable "trace_level" {
  type    = string # ALWAYS | ON_EVENT | PROPAGATE | OFF
  default = null
}

variable "suspend_task_after_num_failures" {
  type    = number
  default = null
}

variable "task_auto_retry_attempts" {
  type    = number
  default = null
}

variable "user_task_managed_initial_warehouse_size" {
  type    = string # unvalidated β€” see SCOPE.md / README Β§6
  default = null
}

variable "user_task_timeout_ms" {
  type    = number
  default = null
}

variable "user_task_minimum_trigger_interval_in_seconds" {
  type    = number
  default = null
}

variable "quoted_identifiers_ignore_case" {
  type    = bool
  default = null
}

variable "enable_console_output" {
  type    = bool
  default = null
}

variable "drop_public_schema_on_creation" {
  type    = bool
  default = false
}

variable "replication" {
  type = object({
    enable_to_account = list(object({
      account_identifier = string
      with_failover      = optional(bool, false)
    }))
    ignore_edition_check = optional(bool, null)
  })
  default = null
}

variable "database_roles" {
  type = map(object({
    comment = optional(string, null)
  }))
  default = {}
}

🧾 Outputs

Output Description Sensitive?
fully_qualified_name The database's fully-qualified identifier β€” the safe cross-reference for sibling modules No
name The database's bare name No
id The database's Terraform resource id No
database_roles Map of role name -> { fully_qualified_name, id }, keyed identically to var.database_roles No

🧠 Architecture Notes

  • is_transient is force-new and drift-blind. Changing it in configuration destroys and recreates snowflake_database.this and every database_roles child beneath it; a live out-of-band type change produces no plan diff at all (see βœ… Schema notes that bite). Set it deliberately, and set it explicitly before any terraform import.
  • database_roles is keyed by role name, never a synthetic index. for_each (never count) means adding or removing one entry from the map creates or destroys exactly that one snowflake_database_role, leaving every sibling role's state untouched β€” this is the property the whole map(object(...)) + for_each pattern exists to guarantee.
  • replication is rendered as a dynamic block wrapping a nested dynamic block. The outer dynamic "replication" is gated on var.replication != null; the inner dynamic "enable_to_account" is keyed internally by account_identifier so that reordering entries doesn't necessarily force-replace unrelated ones β€” but toggling replication itself on/off always touches the single replication block as a whole (see SCOPE.md Provider gotchas).
  • snowflake_database_role.children.database always references snowflake_database.this.fully_qualified_name, confirmed accepted directly by the provider's own Example Usage β€” never a bare name, and never a caller-supplied variable, since this module is the database's own owner.
  • No secret-bearing field exists on either resource β€” nothing in this module's variable schema needs sensitive = true.

🧱 Design Principles

This module follows this module suite's Secure-by-default convention. Applied here:

Concern Secure default in this module Opt-out
Object comment comment = null Caller sets it explicitly
Data retention data_retention_time_in_days = null (inherit account default, never forced to 0) Caller sets an explicit value
Database type is_transient = false (keeps Fail-safe protection) Caller sets true explicitly
Destructive schema drop drop_public_schema_on_creation = false Caller sets true explicitly, creation-time only
Role hierarchy database_roles = {} (no implicit roles) Caller populates the map explicitly, per role
Replication replication = null (no cross-account replication) Caller populates the full object explicitly
Failover replication.enable_to_account[*].with_failover = false Caller sets true per account entry
Edition-check bypass replication.ignore_edition_check = null (check enforced) Caller sets true explicitly, after Compliance review
Grant scope Not modeled here at all β€” see tf-mod-snowflake-role-grant-bundle / tf-mod-snowflake-database-role-grant-bundle N/A
Secrets-bearing resources Not applicable β€” neither resource accepts credential material N/A

Worked example. The empty call in Example 1 β€”

module "core_database" {
  source = "git::https://github.com/microsoftexpert/tf-mod-snowflake-database.git?ref=v1.0.0"
  name   = "ANALYTICS"
}

β€” produces a standard, non-transient database with no explicit retention override, no database roles, and no replication. Nothing is destroyed on creation, nothing replicates to another account, and nothing grants itself to anyone. A caller who wants the transient, cost-optimized variant, or cross-account replication, has to type that explicitly β€” and that line reads as a deliberate, reviewable decision in the diff, exactly as the warehouse module's worked example demonstrates for auto_suspend.


πŸš€ Runbook

cd C:\GitHubCode\newsnowflakemodules\tf-mod-snowflake-database
terraform init -backend=false
terraform validate
terraform fmt -check

This module is plan-only in this authoring pipeline β€” no terraform apply is ever run here. A human applies from a governed CI pipeline, pinning source = "...?ref=v1.0.0" (never a branch).


πŸ§ͺ Testing

  • terraform init -backend=false / validate / fmt -check prove type correctness and HCL syntax β€” every object schema parses, every validation {} block's condition is syntactically valid, and formatting matches canonical terraform fmt output.
  • What these three commands cannot prove: whether the executing role actually holds CREATE DATABASE / CREATE DATABASE ROLE, whether a referenced external_volume actually exists, whether replication.enable_to_account[*].account_identifier resolves to a real organization member account, or whether a live account's edition permits the replication configured in Example 9. Those are only exercised by a real terraform plan/apply against a live Snowflake account, which this authoring pipeline deliberately never performs.

πŸ’¬ Example Output

$ terraform output

database_roles = {
 "ANALYST_RO" = {
 "fully_qualified_name" = "\"ANALYTICS\".\"ANALYST_RO\""
 "id" = "ANALYTICS\"|\"ANALYST_RO"
 }
 "ETL_WRITER" = {
 "fully_qualified_name" = "\"ANALYTICS\".\"ETL_WRITER\""
 "id" = "ANALYTICS\"|\"ETL_WRITER"
 }
}
fully_qualified_name = "\"ANALYTICS\""
id = "ANALYTICS"
name = "ANALYTICS"

πŸ” Troubleshooting

Symptom Cause Fix
terraform destroy fails with 098507 (2BP01) The database (or an object inside it) still has a network rule/network policy association Unassign the association first β€” see the provider's ../guides/unassigning_policies guide β€” then retry the destroy
Plan shows no diff even though the live database's transient/standard type was changed manually The provider does not detect external drift on is_transient Manually reconcile: terraform destroy + recreate, or terraform state rm + re-import against corrected configuration
Import leaves is_transient (or other optional fields) unset in the freshly-generated configuration Provider import does not always populate every optional field from live state Set is_transient (and any field you know the live value of) explicitly in configuration before the first post-import plan, per the provider's own migration-guide note
Following the provider's snowflake_database import note about with_managed_access produces a Terraform error ("no argument named with_managed_access") Upstream documentation artifact β€” with_managed_access belongs to snowflake_schema, not snowflake_database Ignore that part of the provider's import note for this resource; only is_transient needs to be set explicitly before import
terraform validate rejects a name or database_roles key with a ` /./"` character This module's own validation {} blocks catch the provider's documented identifier-parsing limitation before apply
apply fails with a Snowflake-side error on user_task_managed_initial_warehouse_size This field has no validation {} block in this module (legal values aren't enumerated in the resource's own schema) Confirm the exact warehouse-size string against Snowflake's USER_TASK_MANAGED_INITIAL_WAREHOUSE_SIZE parameter docs before setting it
Replication apply fails citing an edition/BAA restriction The primary account's edition or BAA status doesn't meet the secondary account's requirements Confirm with Compliance/Legal before setting ignore_edition_check = true β€” do not bypass this check unreviewed

πŸ”— Related Docs

  • Provider resource docs: snowflake_database, snowflake_database_role
  • Sibling modules: tf-mod-snowflake-schema, tf-mod-snowflake-external-volume, tf-mod-snowflake-role-grant-bundle, tf-mod-snowflake-database-role-grant-bundle
  • This module's SCOPE.md

About

No description or website provided.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages