# Snowcap > Snowflake infrastructure as code # Overview # `snowcap` - Snowflake infrastructure as code ## Brought to you by Datacoves Snowcap helps you provision, deploy, and secure resources in Snowflake. Datacoves takes it further: a managed DataOps platform for dbt and Airflow, deployable in your private cloud or available as SaaS. - **Private cloud or SaaS** - your data, your choice - **Managed dbt + Airflow** - production-ready from day one - **In-browser VS Code** - onboard developers in minutes - **Bring your own tools** - integrates with your existing stack, no lock-in - **AI-assisted development** - connect your organization's approved LLM (Anthropic, OpenAI, Azure, Gemini, and more) - **Built-in governance** - CI/CD, guardrails, and best practices included Snowcap is the power tools. Datacoves is the workshop. [Explore the platform →](https://datacoves.com) ______________________________________________________________________ Snowcap replaces tools like Terraform, Schemachange, or Permifrost. Deploy any Snowflake resource, including users, roles, schemas, databases, integrations, pipes, stages, functions, stored procedures, and more. Convert adhoc, bug-prone SQL management scripts into simple, repeatable configuration. Use with care Snowcap is a powerful tool that can create, modify, and drop resources across your entire Snowflake account. Always run `snowcap plan` to review changes before applying. Test thoroughly in a sandbox or development account before running against production. Snowcap is provided "as is" under the [Apache 2.0 license](https://github.com/datacoves/snowcap/blob/main/LICENSE) with no warranties. ## Snowcap is for - DevOps engineers looking to automate and manage Snowflake infrastructure. - Analytics engineers working with dbt who want to manage Snowflake resources without macros. - Data platform teams who need to reliably manage Snowflake with CI/CD. - Organizations that prefer a git-based workflow for infrastructure management. - Teams seeking to replace Terraform for Snowflake-related tasks. ## Key Features - **Declarative** » Generates the right SQL to make your config and account match - **Comprehensive** » Nearly every Snowflake resource is supported - **Flexible** » Write resource configuration in YAML or Python - **Fast** » Snowcap runs 50-90% faster than Terraform and Permifrost - **Migration-friendly** » Generate config automatically with the export CLI - **LLM-friendly** » [llms.txt](https://llmstxt.org/) support for AI-assisted development ## Contents - [Getting Started](https://snowcap.datacoves.com/getting-started/index.md) - Installation, authentication, and first config - [Snowflake Permissions](https://snowcap.datacoves.com/snowflake-permissions/index.md) - Service account setup for CI/CD - [Role-Based Access Control](https://snowcap.datacoves.com/role-based-access-control/index.md) - Best practices for managing permissions - [Tag-Based Masking Policies](https://snowcap.datacoves.com/masking-policies/index.md) - Column-level security with tags - [Row Access Policies](https://snowcap.datacoves.com/row-access-policies/index.md) - Row-level security - [YAML Configuration](https://snowcap.datacoves.com/yaml-configuration/index.md) - Variables, loops, and scope - [Secrets and Variables](https://snowcap.datacoves.com/secrets-and-variables/index.md) - Environment variables and templating - [GitHub Action](https://snowcap.datacoves.com/snowcap-github-action/index.md) - Automate deployments with CI/CD - [Export Existing Resources](https://snowcap.datacoves.com/export/index.md) - Generate config from your current Snowflake setup - [Command Line Interface](https://snowcap.datacoves.com/cli/index.md) - CLI commands and options ### Advanced Usage - [Python API](https://snowcap.datacoves.com/python-api/index.md) - Programmatic control with Python - [Blueprint](https://snowcap.datacoves.com/blueprint/index.md) - Python API reference and parameters - [Working With Resources](https://snowcap.datacoves.com/working-with-resources/index.md) - Resource classes and relationships # Getting Started # Getting Started **Requirements:** Python 3.10 or higher ## Run with uv (Recommended) If you have [uv](https://docs.astral.sh/uv/) installed, you can run snowcap directly without installation: ``` uvx snowcap plan --config snowcap.yml uvx snowcap apply --config snowcap.yml ``` Using uv throughout the docs Anywhere you see `snowcap ` in this documentation, you can use `uvx snowcap ` instead. ## Install from PyPI ``` # MacOS / Linux python -m venv .venv source .venv/bin/activate pip install snowcap # Windows python -m venv .venv .\.venv\Scripts\activate pip install snowcap ``` ## Quick Start: Create a Warehouse The simplest way to get started—define a single resource and deploy it: ``` # snowcap.yml warehouses: - name: analytics warehouse_size: xsmall auto_suspend: 60 ``` Create a `.env` file for credentials (add `.env` to `.gitignore`!): ``` # .env SNOWFLAKE_ACCOUNT=my-account SNOWFLAKE_USER=my-user SNOWFLAKE_PASSWORD=my-password SNOWFLAKE_ROLE=SYSADMIN ``` All Environment Variables | Variable | Description | | ---------------------------- | -------------------------------------------- | | `SNOWFLAKE_ACCOUNT` | Your Snowflake account identifier | | `SNOWFLAKE_USER` | Username | | `SNOWFLAKE_PASSWORD` | Password (for password auth) | | `SNOWFLAKE_ROLE` | Role to use | | `SNOWFLAKE_WAREHOUSE` | Warehouse to use (optional) | | `SNOWFLAKE_DATABASE` | Default database (optional) | | `SNOWFLAKE_SCHEMA` | Default schema (optional) | | `SNOWFLAKE_AUTHENTICATOR` | Authentication method (see below) | | `SNOWFLAKE_MFA_PASSCODE` | TOTP passcode from authenticator app | | `SNOWFLAKE_PRIVATE_KEY_PATH` | Path to private key file (for key-pair auth) | | `PRIVATE_KEY_PASSPHRASE` | Passphrase for encrypted private key | **Authenticator options:** | Value | Description | | ----------------------- | ------------------------------------- | | *(default)* | Username and password | | `SNOWFLAKE_JWT` | Key-pair authentication | | `externalbrowser` | SSO via web browser | | `oauth` | OAuth with access token | | `username_password_mfa` | Password with MFA (push notification) | Key-Pair Authentication For [key-pair auth](https://docs.snowflake.com/en/user-guide/key-pair-auth), use `SNOWFLAKE_JWT` instead of password: ``` # .env SNOWFLAKE_ACCOUNT=my-account SNOWFLAKE_USER=my-user SNOWFLAKE_ROLE=SECURITYADMIN SNOWFLAKE_PRIVATE_KEY_PATH=/path/to/private-key.pem SNOWFLAKE_AUTHENTICATOR=SNOWFLAKE_JWT ``` If your private key is encrypted, also set: ``` PRIVATE_KEY_PASSPHRASE=your-passphrase ``` Run snowcap: ``` # Load environment variables export $(cat .env | xargs) # Preview changes snowcap plan --config snowcap.yml # Apply changes snowcap apply --config snowcap.yml ``` Wrapper Scripts For production use, consider a wrapper script that validates environment variables before running snowcap. See [CLI Wrapper Scripts](https://snowcap.datacoves.com/cli/#wrapper-scripts) for an example. That's it. Snowcap compares your config to Snowflake and generates the SQL to make them match. ## Scaling Up: Directory Structure with Templates As your infrastructure grows, organize configs into directories and use templates for scalability: ``` snowcap/ ├── resources/ │ ├── databases.yml # Database definitions │ ├── schemas.yml # Schema definitions │ ├── warehouses.yml # Warehouse definitions │ ├── stages.yml # Stage definitions │ ├── users.yml # User definitions │ ├── roles__base.yml # Atomic privilege roles │ └── roles__functional.yml # Functional roles + grants │ └── object_templates/ # Auto-generate resources with for_each ├── database.yml ├── schema.yml └── warehouses.yml ``` **databases.yml** - Define your databases: ``` vars: - name: databases type: list default: - name: raw owner: loader - name: analytics owner: transformer - name: analytics_dev owner: transformer ``` **object_templates/database.yml** - Auto-generate databases, roles, and grants: ``` # Databases databases: - for_each: var.databases name: "{{ each.value.name }}" owner: "{{ each.value.owner }}" # Database roles roles: - for_each: var.databases name: "z_db__{{ each.value.name }}" # Database grants grants: - for_each: var.databases priv: USAGE on: "database {{ each.value.name }}" to: "z_db__{{ each.value.name }}" ``` **roles\_\_functional.yml** - Compose into functional roles: ``` roles: - name: analyst - name: loader - name: transformer role_grants: - to_role: analyst roles: - z_db__analytics - z_schema__marts - z_wh__querying - z_tables_views__select - to_role: transformer roles: - z_db__raw - z_db__analytics - z_wh__transforming ``` **Run snowcap:** ``` # Load environment variables from .env export $(cat .env | xargs) # Preview all changes snowcap plan --config ./snowcap/ # Apply all changes snowcap apply --config ./snowcap/ ``` Adding a new database? Just add one line to `databases.yml`—the template auto-creates the database, role, and grant. ## CLI Commands ``` snowcap --help # Commands: # apply Apply a resource config to a Snowflake account # connect Test the connection to Snowflake # export Generate a resource config for existing Snowflake resources # plan Compare a resource config to the current state of Snowflake ``` ## Optimizing Grant Fetching with ACCOUNT_USAGE For large manifests with many roles, Snowcap can use Snowflake's `ACCOUNT_USAGE` views to fetch all grant information in a single bulk query instead of running individual `SHOW GRANTS` commands per role. ### When to Enable ACCOUNT_USAGE This optimization is **disabled by default** and is most beneficial when: - Your manifest manages **50+ roles** with grants - You're seeing many `SHOW GRANTS TO ROLE` queries in the logs - The bulk query time (typically 30-60 seconds) is less than the cumulative time of individual queries For smaller manifests, the default behavior (per-role `SHOW GRANTS`) is typically faster. ### Enabling ACCOUNT_USAGE **Step 1:** Grant access to ACCOUNT_USAGE views: ``` GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE TO ROLE ; ``` Replace `` with the role you use for Snowcap (e.g., `SYSADMIN` or a custom deployment role). **Step 2:** Enable the option: **CLI flag:** ``` snowcap plan --config snowcap.yml --use-account-usage snowcap apply --config snowcap.yml --use-account-usage ``` **YAML config:** ``` # snowcap.yml use_account_usage: true ``` **Python API:** ``` bp = Blueprint( resources=[...], use_account_usage=True, ) ``` About ACCOUNT_USAGE Latency ACCOUNT_USAGE views have up to 2 hours of latency—data may not reflect very recent changes. This is acceptable for grants because: - **GRANT statements are idempotent**: Re-granting an existing privilege succeeds without error - **REVOKE has IF EXISTS semantics**: Revoking a non-existent grant won't fail - **Worst case**: The plan shows a grant change that's already applied, and re-applies it harmlessly If `IMPORTED PRIVILEGES` is not granted, Snowcap falls back automatically to `SHOW GRANTS` with a warning. ## Next Steps - [Export Existing Resources](https://snowcap.datacoves.com/export/index.md) - Generate config from your current Snowflake setup - [Python API](https://snowcap.datacoves.com/python-api/index.md) - Programmatic control with Python - [Working With Resources](https://snowcap.datacoves.com/working-with-resources/index.md) - Resource configuration options - [Role-Based Access Control](https://snowcap.datacoves.com/role-based-access-control/index.md) - Best practices for managing permissions - [Blueprint](https://snowcap.datacoves.com/blueprint/index.md) - Advanced deployment customization - [GitHub Action](https://snowcap.datacoves.com/snowcap-github-action/index.md) - Automate deployments with CI/CD # Snowflake Permissions # Snowflake Permissions Snowcap runs SQL on your behalf. Whatever role your session uses, that's the role Snowcap uses. It has no elevated access of its own. This means a human operator running `snowcap apply` is doing exactly what they could do by hand in a SQL worksheet, just with reproducibility and version control. The permissions question becomes more important with **service accounts**. When Snowcap runs in a CI/CD pipeline, it operates unattended under a dedicated user. That user's role determines the blast radius if something goes wrong, or if the account is ever compromised. For that reason, Snowflake recommends (and Snowcap supports) running pipelines under a purpose-built role rather than ACCOUNTADMIN. ## Human operators No special setup is required. Use whatever Snowflake role you already have. If your role can create a warehouse manually, `snowcap apply` can create it too. If your team wants consistent behavior across all operators (for example, to ensure no one accidentally sets account parameters through Snowcap), you can have everyone set `SNOWFLAKE_ROLE=SNOWCAP_ADMIN` in their local environment. That's an organizational choice, not a security requirement. ## Service accounts and CI/CD This is where role design matters. A service account used in CI/CD should hold the minimum privileges Snowcap needs and nothing more. ACCOUNTADMIN can do things well outside Snowcap's scope (modify billing configuration, alter SSO/SAML settings, change encryption key management, suspend the account), none of which your pipeline should ever need. The scripts below walk through creating a `SNOWCAP_ADMIN` role scoped to what Snowcap actually uses. ### Step 1: Core privileges The base privileges cover what most teams manage with Snowcap: databases, schemas, warehouses, roles, users, and grants. These map to Snowflake's standard admin roles (SYSADMIN, SECURITYADMIN, USERADMIN). ``` USE ROLE ACCOUNTADMIN; -- Create the role CREATE ROLE IF NOT EXISTS SNOWCAP_ADMIN; -- Infrastructure (SYSADMIN equivalent) GRANT CREATE DATABASE ON ACCOUNT TO ROLE SNOWCAP_ADMIN; GRANT CREATE WAREHOUSE ON ACCOUNT TO ROLE SNOWCAP_ADMIN; -- Identity and access control (SECURITYADMIN + USERADMIN equivalent) GRANT CREATE ROLE ON ACCOUNT TO ROLE SNOWCAP_ADMIN; GRANT CREATE USER ON ACCOUNT TO ROLE SNOWCAP_ADMIN; GRANT MANAGE GRANTS ON ACCOUNT TO ROLE SNOWCAP_ADMIN; -- Place the role in the standard hierarchy so SYSADMIN -- can see and manage objects that SNOWCAP_ADMIN creates GRANT ROLE SNOWCAP_ADMIN TO ROLE SYSADMIN; ``` > **On MANAGE GRANTS:** This privilege allows the holder to grant or revoke privileges on any object in the account, even objects it doesn't own. Snowcap needs it for ownership transfers and to manage grants on behalf of the roles it controls. It's effectively equivalent to SECURITYADMIN for access control purposes. Treat the service account credentials accordingly. ### Step 2: Additional privileges Add these based on which resources your configuration includes: ``` -- Integrations (storage, API, external access, etc.) GRANT CREATE INTEGRATION ON ACCOUNT TO ROLE SNOWCAP_ADMIN; -- Network policies and rules GRANT CREATE NETWORK POLICY ON ACCOUNT TO ROLE SNOWCAP_ADMIN; -- Snowpark Container Services (compute pools, image repositories, services) GRANT CREATE COMPUTE POOL ON ACCOUNT TO ROLE SNOWCAP_ADMIN; -- ACCOUNT_USAGE optimization for large deployments (50+ roles) GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE TO ROLE SNOWCAP_ADMIN; ``` Unlike the account-level privileges above, `CREATE SEMANTIC VIEW` is schema-scoped — grant it per schema where your configuration manages semantic view grants: ``` GRANT CREATE SEMANTIC VIEW ON SCHEMA somedb.someschema TO ROLE SNOWCAP_ADMIN; ``` Named key pairs ([UserKeyPair](https://snowcap.datacoves.com/resources/user_key_pair/index.md)) are managed with `ALTER USER`, which the owner of the user can already do. Grant this only for key pairs on users that Snowcap doesn't own: ``` GRANT MODIFY PROGRAMMATIC AUTHENTICATION METHODS ON USER someuser TO ROLE SNOWCAP_ADMIN; ``` `CREATE COMPUTE POOL` above only covers creating new compute pools. Snowcap can also manage object-level grants on existing SPCS resources (see [Grant](https://snowcap.datacoves.com/resources/grant/index.md)): | Resource | Grantable privileges | | ---------------- | ---------------------------------------------------- | | Compute pool | `USAGE`, `MONITOR`, `MODIFY`, `OPERATE`, `OWNERSHIP` | | Image repository | `READ`, `WRITE`, `OWNERSHIP` | | Service | `MONITOR`, `OPERATE`, `OWNERSHIP` | See [Optimizing Grant Fetching with ACCOUNT_USAGE](https://snowcap.datacoves.com/getting-started/#optimizing-grant-fetching-with-account_usage) for when the ACCOUNT_USAGE optimization is worth enabling. ### Step 3: Create the service user ``` -- Generate your key pair first: -- openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out snowcap_key.p8 -nocrypt -- openssl rsa -in snowcap_key.p8 -pubout -out snowcap_key.pub CREATE USER IF NOT EXISTS SNOWCAP_SVC TYPE = SERVICE RSA_PUBLIC_KEY = '' DEFAULT_ROLE = SNOWCAP_ADMIN COMMENT = 'Snowcap CI/CD service account'; GRANT ROLE SNOWCAP_ADMIN TO USER SNOWCAP_SVC; ``` Service users with `TYPE = SERVICE` cannot log into Snowsight and do not require MFA, which makes them appropriate for unattended automation. ## Resources that require ACCOUNTADMIN Three resource types in Snowcap are locked to ACCOUNTADMIN by Snowflake. No privilege can be granted that would allow a custom role to create or modify them. This is a platform constraint, not a Snowcap limitation. | Resource | Why ACCOUNTADMIN is required | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | | [ResourceMonitor](https://snowcap.datacoves.com/resources/resource_monitor/index.md) | `CREATE RESOURCE MONITOR` is not a grantable privilege | | [AccountParameter](https://snowcap.datacoves.com/resources/account_parameter/index.md) | `ALTER ACCOUNT SET` requires ACCOUNTADMIN for most parameters | | [FailoverGroup](https://snowcap.datacoves.com/resources/failover_group/index.md) / [ReplicationGroup](https://snowcap.datacoves.com/resources/replication_group/index.md) | Failover and replication management is restricted to ACCOUNTADMIN | If your configuration includes any of these, you have two options: **Option A:** Run the pipeline with ACCOUNTADMIN for the full resource set. Simpler, but the service account holds more privilege than it needs for everything else. **Option B:** Run the pipeline with SNOWCAP_ADMIN for all standard resources, and handle resource monitors, account parameters, and failover groups through a separate process (a restricted manual workflow, a separate pipeline step that uses ACCOUNTADMIN only for those resource types, etc.). Most teams choose Option B as they mature their setup. ## System roles and ORGADMIN Snowflake's built-in roles (ACCOUNTADMIN, SECURITYADMIN, USERADMIN, SYSADMIN, PUBLIC, ORGADMIN) are never created by Snowcap. You can reference them and grant them, but they have to already exist in the account. That last part matters for **ORGADMIN**, which is the one system role Snowflake does not enable everywhere. It is enabled in an organization's *primary* account only. In any other account the role simply does not exist, so a config that grants it fails at plan time: ``` role_grants: - to_user: someuser roles: - ORGADMIN # fails unless ORGADMIN is enabled in this account ``` ``` Role "ORGADMIN" not found. Referenced by: role grant to user "SOMEUSER" Note: ORGADMIN is only enabled in an organization's primary account. Snowcap cannot enable it for you, because it does not manage accounts. Enable it manually by running `ALTER ACCOUNT SET IS_ORG_ADMIN = TRUE` as ORGADMIN, from the primary account (or any account where ORGADMIN is already enabled), then re-run. Otherwise remove the reference. ``` ### Enabling ORGADMIN in another account This has to be done from an account that already holds the role, and it is not something Snowcap manages (see below): ``` USE ROLE ORGADMIN; ALTER ACCOUNT my_account SET IS_ORG_ADMIN = TRUE; ``` Two constraints worth knowing: - `ALTER ACCOUNT` here accepts only the **account name** form of the identifier, not the account locator. - ORGADMIN can be enabled in at most **eight accounts** per organization by default. Contact Snowflake Support if you need more. Once the role exists in the account, granting it is an ordinary role grant and Snowcap handles it like any other. ### Known limitation: Snowcap cannot enable ORGADMIN Snowcap has no resource for the `IS_ORG_ADMIN` account property. [AccountParameter](https://snowcap.datacoves.com/resources/account_parameter/index.md) is not a substitute: it emits `ALTER ACCOUNT SET = ` against the account in the current session, while enabling ORGADMIN requires `ALTER ACCOUNT SET IS_ORG_ADMIN = TRUE`, a different statement targeting a *named* account, run from a *different* account. Enable it out of band with the SQL above, then manage the grants declaratively. ## What ACCOUNTADMIN can do that SNOWCAP_ADMIN cannot This is the full list of ACCOUNTADMIN-exclusive capabilities that have nothing to do with Snowcap. It's useful context when explaining the custom role to a security team. - **Billing and usage:** view credit consumption, manage payment methods, access billing dashboards - **Encryption key management:** configure Tri-Secret Secure, switch between Snowflake-managed and customer-managed key hierarchies (AWS KMS, Azure Key Vault, GCP Cloud KMS) - **Federated authentication:** `ALTER ACCOUNT SET SAML_IDENTITY_PROVIDER`, SCIM integration setup; an ACCOUNTADMIN compromise could silently redirect authentication to an attacker-controlled identity provider - **Trust Center:** enable, disable, and configure compliance scanner packages (these can drive significant unexpected credit consumption if misconfigured) - **Account suspension:** suspend or resume the account itself - **Cross-account replication targets:** approve an account as a replication target on the receiving side None of these are actions a Snowcap pipeline should ever need to take. Keeping them out of the service account's reach is the main reason to use a custom role. ## Environment variables ``` SNOWFLAKE_ACCOUNT=- SNOWFLAKE_USER=SNOWCAP_SVC SNOWFLAKE_ROLE=SNOWCAP_ADMIN SNOWFLAKE_AUTHENTICATOR=SNOWFLAKE_JWT SNOWFLAKE_PRIVATE_KEY_PATH=/path/to/snowcap_key.p8 ``` See [Getting Started](https://snowcap.datacoves.com/getting-started/index.md) for the full list of supported authenticators. # Role-Based Access Control # Role-Based Access Control Pattern This guide describes a recommended pattern for managing Snowflake permissions using Snowcap. The pattern uses **composite roles** to provide fine-grained, maintainable access control. ## Overview Instead of granting privileges directly to users, this pattern creates a hierarchy of roles: 1. **Object Roles** - Grant specific privileges on individual objects (databases, schemas, warehouses, stages) 1. **Base/Composite Roles** - Combine multiple object roles into logical groupings 1. **Functional Roles** - End-user roles that combine base roles and are assigned to users ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ USERS │ │ noel, jose, svc_airflow │ └─────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ FUNCTIONAL ROLES │ │ analyst, loader, transformer_dbt │ └─────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ BASE / COMPOSITE ROLES │ │ z_base__analyst │ └─────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ OBJECT ROLES │ │ z_db__raw, z_schema__l1_loans, z_wh__wh_transforming, z_stage__... │ └─────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ SNOWFLAKE OBJECTS │ │ databases, schemas, warehouses, stages, tables │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ## Role Naming Convention Recommended, not required This naming convention is a recommendation to help organize roles. Snowcap does not enforce any specific naming pattern—use whatever works for your organization. Object roles use a `z_` prefix followed by the object type and name: | Role Type | Naming Pattern | Example | | ------------------ | ----------------------------- | -------------------------------- | | Database | `z_db__` | `z_db__raw` | | Schema | `z_schema__` | `z_schema__l1_loans` | | Warehouse | `z_wh__` | `z_wh__wh_transforming` | | Stage (read) | `z_stage____read` | `z_stage__raw__artifacts__read` | | Stage (write) | `z_stage____write` | `z_stage__raw__artifacts__write` | | Account privileges | `z_account__` | `z_account__create_database` | | Tables/Views | `z_tables_views__` | `z_tables_views__select` | | Base/Composite | `z_base__` | `z_base__analyst` | The `z_` prefix ensures these object roles sort to the bottom of role lists, making functional roles more visible. ## Directory Structure Organize your Snowcap configuration into logical files: ``` snowcap/ ├── resources/ │ ├── databases.yml # Database variables │ ├── schemas.yml # Schema variables │ ├── warehouses.yml # Warehouse variables │ ├── stages.yml # Stage definitions + roles + grants │ ├── roles__base.yml # Object-level roles + grants │ ├── roles__functional.yml # Functional roles + role hierarchy │ ├── users.yml # User-to-role assignments │ └── object_templates/ │ ├── database.yml # Template for databases + roles + grants │ ├── schema.yml # Template for schemas + roles + grants │ └── warehouses.yml # Template for warehouses + roles + grants ├── plan.sh ├── apply.sh └── .env.sample ``` ## Configuration Examples ### Define Variables (databases.yml) Define your resources as variables that templates will iterate over: ``` vars: - name: databases type: list default: - name: raw owner: loader max_data_extension_time_in_days: 10 - name: analytics owner: transformer_dbt max_data_extension_time_in_days: 30 - name: analytics_dev owner: transformer_dbt max_data_extension_time_in_days: 5 ``` ### Create Resources with Templates (object_templates/database.yml) Use `for_each` to create resources, roles, and grants automatically: ``` # Create databases databases: - for_each: var.databases name: "{{ each.value.name }}" owner: "{{ each.value.owner }}" max_data_extension_time_in_days: "{{ each.value.max_data_extension_time_in_days }}" # Create a role for each database roles: - for_each: var.databases name: "z_db__{{ each.value.name }}" # Grant USAGE on each database to its corresponding role grants: - for_each: var.databases priv: USAGE on: "database {{ each.value.name }}" to: "z_db__{{ each.value.name }}" ``` ### Schema Template (object_templates/schema.yml) ``` # Create schemas schemas: - for_each: var.schemas name: "{{ each.value.name.split('.')[1] }}" database: "{{ each.value.name.split('.')[0] }}" owner: "{{ each.value.get('owner', parent.owner) }}" # Create a role for each schema roles: - for_each: var.schemas name: "z_schema__{{ each.value.name.split('.')[1] }}" # Grant USAGE on each schema to its corresponding role grants: - for_each: var.schemas priv: USAGE on: "schema {{ each.value.name }}" to: "z_schema__{{ each.value.name.split('.')[1] }}" ``` ### Warehouse Template (object_templates/warehouses.yml) ``` # Create warehouses warehouses: - for_each: var.warehouses name: "{{ each.value.name }}" warehouse_size: "{{ each.value.size }}" auto_suspend: "{{ each.value.auto_suspend }}" auto_resume: true initially_suspended: true # Create a role for each warehouse roles: - for_each: var.warehouses name: "z_wh__{{ each.value.name }}" # Grant USAGE and MONITOR on each warehouse to its corresponding role grants: - for_each: var.warehouses priv: - USAGE - MONITOR on: "warehouse {{ each.value.name }}" to: "z_wh__{{ each.value.name }}" ``` ### Base Object Roles (roles\_\_base.yml) Define additional object-level roles and their grants: ``` roles: - name: z_account__create_database - name: z_db__analytics_dev__create_schema - name: z_schemas__db__raw - name: z_tables_views__select grants: # Grant CREATE DATABASE at account level - priv: "CREATE DATABASE" on: "ACCOUNT" to: z_account__create_database # Grant CREATE SCHEMA on a specific database - priv: "CREATE SCHEMA" on: "database analytics_dev" to: z_db__analytics_dev__create_schema # Grant USAGE on all current and future schemas in a database - priv: "USAGE" on: - "all schemas in database raw" - "future schemas in database raw" to: z_schemas__db__raw # Grant SELECT on all tables and views across databases - for_each: var.databases priv: "SELECT" on: - "all tables in database {{ each.value.name }}" - "all views in database {{ each.value.name }}" - "future tables in database {{ each.value.name }}" - "future views in database {{ each.value.name }}" to: z_tables_views__select ``` Database-level future grants can be silently ignored When future grants exist on the **same object type** at both the database and the schema level, Snowflake gives the schema-level grant precedence and [ignores the database-level grant](https://docs.snowflake.com/en/sql-reference/sql/grant-privilege#future-grants-on-database-or-schema-objects) for that schema. Objects created there never receive the privilege, and nothing fails — access is simply missing. This is easy to trip over with managed access schemas, where privilege management is centralized on the schema owner: the schema-level future grant that shadows the database-level one is often added later, by a different config or a different team. Managed access does not by itself disable database-level future grants (the one exception is future grants of `OWNERSHIP`, which Snowcap does not support), but it is where the conflict tends to appear. If your schemas use `managed_access: true`, declare the future grants at the schema level, in the same template that creates the schemas: ``` grants: - for_each: var.schemas priv: SELECT on: - "all tables in schema {{ each.value.name }}" - "all views in schema {{ each.value.name }}" - "future tables in schema {{ each.value.name }}" - "future views in schema {{ each.value.name }}" to: z_tables_views__select ``` `snowcap plan` warns when it finds database-level future grants in a database that contains managed access schemas, and when a schema-level future grant already shadows a database-level one. Inherited grants avoid this problem entirely Snowflake's [inherited grants](https://docs.snowflake.com/en/user-guide/inherited-grants-intro) replace an `ALL` + `FUTURE` pair with a single container-level grant covering every current and future object of a type. They are **not** subject to the precedence rule above: a database-level and a schema-level inherited grant both apply, and managed access schemas do not change that. ``` grants: - priv: SELECT on: INHERITED TABLES IN DATABASE sales_db to: analyst ``` See [Inherited grants](#inherited-grants) below for the full syntax, the account requirements, and how to migrate an existing `ALL` + `FUTURE` pair. ### Functional Roles and Hierarchy (roles\_\_functional.yml) Define functional roles and assemble the role hierarchy: ``` roles: # Base composite role - name: z_base__analyst # Functional roles (assigned to users) - name: analyst - name: loader - name: transformer_dbt role_grants: # Assemble the base analyst role from object roles - to_role: z_base__analyst roles: # Database access - z_db__raw - z_db__analytics # Schema access - z_schemas__db__raw - z_schema__l1_loans - z_schema__l2_loan_analytics # Warehouse access - z_wh__wh_transforming # Grant base role + SELECT privileges to analyst - to_role: analyst roles: - z_base__analyst - z_tables_views__select # Loader gets warehouse access for loading data - to_role: loader roles: - z_wh__wh_loading # Transformer gets elevated privileges - to_role: transformer_dbt roles: - z_account__create_database - z_db__raw - z_schemas__db__raw - z_wh__wh_transforming - z_tables_views__select ``` ### User Assignments (users.yml) Assign functional roles to users: ``` role_grants: # Human users - to_user: alice roles: - analyst - to_user: bob roles: - analyst - loader - transformer_dbt - securityadmin # Service accounts - to_user: svc_airbyte roles: - loader - to_user: svc_airflow roles: - loader - transformer_dbt ``` ### Stage Roles (stages.yml) Stages often need separate read and write roles: ``` stages: - name: raw.dbt_artifacts.artifacts type: internal owner: transformer_dbt directory: enable: true comment: Used to store dbt artifacts roles: - name: z_stage__raw__dbt_artifacts__artifacts__read - name: z_stage__raw__dbt_artifacts__artifacts__write grants: - priv: "READ" on: "stage raw.dbt_artifacts.artifacts" to: z_stage__raw__dbt_artifacts__artifacts__read - priv: - READ - WRITE on: "stage raw.dbt_artifacts.artifacts" to: z_stage__raw__dbt_artifacts__artifacts__write ``` ## Inherited Grants An [inherited grant](https://docs.snowflake.com/en/user-guide/inherited-grants-intro) is a single grant on a container — an account, database, or schema — that applies to every current **and future** object of a type inside it. One inherited grant replaces the `ALL` + `FUTURE` pair this pattern would otherwise need: ``` grants: # Instead of "all tables in ..." plus "future tables in ..." - priv: SELECT on: INHERITED TABLES IN DATABASE sales_db to: z_tables_views__r # Multiple privileges expand to one statement each - priv: [SELECT, INSERT, UPDATE, DELETE] on: INHERITED TABLES IN SCHEMA sales_db.us_west to: z_tables__rw # The account can only be the container of an inherited grant - priv: SELECT on: INHERITED TABLES IN ACCOUNT to: z_scanner # Or turn an existing grant on all objects into an inherited one - priv: SELECT on: "all tables in database sales_db" inherited: true to: z_tables_views__r ``` ### Why it matters for this pattern | | `ALL` + `FUTURE` | `INHERITED` | | -------------------------------------- | ------------------------------------------ | ----------- | | Covers objects created later | Only via the `FUTURE` half | Yes | | Shadowed by a schema-level grant | Yes, silently | No | | Compared against Snowflake on each run | No — `ALL` grants are reapplied every time | Yes | | Grant records created | One per object, plus one future grant | One | Because Snowflake reports an inherited grant back as a single durable record, `snowcap plan` can compare it against your config. Grants on all objects cannot be compared, so they are reapplied on every run. ### Requirements Inherited grants are a Snowflake preview feature, opted into with an account parameter. Snowcap manages that parameter like any other — declare it alongside the rest: ``` # account.yml account_parameters: - name: FEATURE_RBAC_INHERITED_GRANTS value: ENABLED ``` Snowcap applies the parameter before any inherited grant that depends on it, so a single `snowcap apply` can enable the preview and create the grants in one run. `ALTER ACCOUNT` requires `ACCOUNTADMIN`, which is the role Snowcap already uses for account parameters. The equivalent SQL, if you would rather set it outside of Snowcap: ``` ALTER ACCOUNT SET FEATURE_RBAC_INHERITED_GRANTS = 'ENABLED'; ``` Either way, `snowcap plan` fails with a clear message if your config declares inherited grants and neither the account nor the config has opted in. If preview features are turned off account-wide Preview access gates every preview feature at once and is [enabled by default for most accounts](https://docs.snowflake.com/en/release-notes/preview-features), so usually there is nothing to do. If it has been disabled, the parameter above will not take effect until an account admin re-enables it: ``` SELECT SYSTEM$GET_PREVIEW_ACCESS_STATUS(); -- check SELECT SYSTEM$ENABLE_PREVIEW_ACCESS(); -- enable ``` These are system function calls rather than resources, so Snowcap cannot manage them declaratively. It does detect the situation: if preview access is off, `snowcap plan` says so and points at the function to call, rather than suggesting the parameter that would not help. Creating one requires `MANAGE GRANTS` on the container, not just ownership of it. By default Snowcap issues grants as `SECURITYADMIN`. To delegate to a database or schema admin instead, name that role as the grant's owner: ``` grants: - priv: SELECT on: INHERITED TABLES IN DATABASE sales_db to: analyst owner: sales_db_admin # holds MANAGE GRANTS ON DATABASE sales_db ``` ### Migrating from ALL + FUTURE Snowflake recommends adding the inherited grant first and revoking the originals only once you have confirmed access is intact. A grant pair is safe to collapse when the privilege and the grantee are the same on both halves, and no object in the container needs to be excluded. If some objects need different access, keep the granular grants, or use [masking policies](https://snowcap.datacoves.com/masking-policies/index.md) and [row access policies](https://snowcap.datacoves.com/row-access-policies/index.md) for the exceptions. Snowcap will not revoke per-object grants that a declared inherited grant covers, so you can add the inherited grant and remove the old declarations in either order without an access gap. ### Limitations Snowflake does not allow inherited grants to be combined with `WITH GRANT OPTION`, to carry `OWNERSHIP`, to target shares or integrations, or to be granted on shared databases. Snowcap rejects these at plan time. `priv: ALL` is also rejected — list the privileges explicitly. Note that Snowflake's Information Schema does not currently account for inherited grants when deciding whether an object is visible to a role, so an object a role can only reach through one will not appear in `INFORMATION_SCHEMA` results. ## Running Snowcap ### Environment Setup Create a `.env` file with your Snowflake credentials. This example uses [key-pair authentication](https://snowcap.datacoves.com/getting-started/#quick-start-create-a-warehouse): ``` SNOWFLAKE_ACCOUNT=your-account SNOWFLAKE_USER=your-user SNOWFLAKE_ROLE=SECURITYADMIN SNOWFLAKE_PRIVATE_KEY_PATH=/path/to/rsa_key.p8 SNOWFLAKE_AUTHENTICATOR=SNOWFLAKE_JWT ``` See [Getting Started](https://snowcap.datacoves.com/getting-started/index.md) for all authentication options. ### Plan Script (plan.sh) ``` #!/bin/bash if [ -f .env ]; then export $(cat .env | xargs) else echo "File .env does not exist." exit 1 fi snowcap plan \ --config resources/ \ --sync_resources role,grant,role_grant ``` About `--sync_resources` By default, Snowcap only creates or updates resources—it never deletes anything. The `--sync_resources` flag enables **sync mode** for the specified resource types. This means resources of those types that exist in Snowflake but are **not** in your config will be **deleted**. In this example, `role,grant,role_grant` are synced, so any roles or grants in Snowflake that aren't defined in your config files will be removed. Use with caution. ### Apply Script (apply.sh) ``` #!/bin/bash if [ -f .env ]; then export $(cat .env | xargs) else echo "File .env does not exist." exit 1 fi snowcap apply \ --config resources/ \ --sync_resources role,grant,role_grant ``` ## Benefits of This Pattern 1. **Fine-grained control** - Each object has its own role, making it easy to grant or revoke access to specific resources. 1. **Composability** - Base roles combine object roles into logical groupings that can be reused across functional roles. 1. **Visibility** - The `z_` prefix keeps object roles organized and separate from user-facing functional roles. 1. **Maintainability** - Adding a new database, schema, or warehouse automatically creates the corresponding role and grant through templates. 1. **Auditability** - The role hierarchy clearly shows who has access to what resources. 1. **Separation of concerns** - Object roles handle "what can be accessed", functional roles handle "who can access it". ## Role Type Reference | Role Type | What it grants | Example privileges | | -------------- | -------------------------------- | ------------------ | | Database | Visibility of database existence | USAGE | | Schema | Visibility of schema existence | USAGE | | Warehouse | Access to compute resources | USAGE, MONITOR | | Stage (read) | Read from stage | READ | | Stage (write) | Write to stage | READ, WRITE | | Tables/Views | Query data | SELECT | | Account | Account-level operations | CREATE DATABASE | | Base/Composite | Combination of other roles | (via role_grants) | | Functional | End-user grouping | (via role_grants) | ## Design Decisions This section explains the reasoning behind the patterns recommended in this guide. ### Account-Level Roles vs Database Roles Snowflake offers two types of roles: | Type | Scope | Can Grant to Users | Included in Clones | | ------------------- | --------------------- | ------------------------------- | ------------------ | | Account-level roles | Global across account | Yes | No | | Database roles | Single database only | No (must grant to account role) | Yes | **We recommend account-level roles** for most use cases because: 1. **Unified management** - All roles defined in one place, version-controlled in your Snowcap config 1. **Cross-database access** - One role can grant access to multiple databases (e.g., `z_tables_views__select` across all databases) 1. **Direct user assignment** - Roles can be granted directly to users without an extra layer 1. **Simpler hierarchy** - One inheritance tree to reason about **Database roles are useful when:** - **Data sharing** - Database roles can be included in shares to external accounts; account roles cannot - **Database owner autonomy** - When a database owner needs to manage access independently Snowcap supports both. See [DatabaseRole](https://snowcap.datacoves.com/resources/database_role/index.md) for database role configuration. ### Why Not Grant Custom Roles to SYSADMIN? Snowflake's documentation suggests granting all custom roles to SYSADMIN so administrators can access all objects. We don't recommend this approach because: 1. **Violates least privilege** - SYSADMIN gains access to everything, even sensitive data it doesn't need 1. **Blurs responsibility** - SYSADMIN is meant for creating and managing objects, not accessing business data 1. **Complicates auditing** - When SYSADMIN can access everything, it's harder to track who accessed what and why 1. **PII/compliance concerns** - Regulatory requirements often mandate restricted access to sensitive data; granting SYSADMIN blanket access can violate these requirements Instead, we recommend: - Keep SYSADMIN focused on infrastructure (creating databases, warehouses, schemas) - Use functional roles for data access, granted only to users who need it - Grant SECURITYADMIN or a dedicated security role the ability to manage grants - If admins need data access, grant them the appropriate functional role explicitly ### Managed Access Schemas By default, object owners can grant privileges on objects they create. This can lead to ad-hoc grants that bypass your centralized RBAC. **Managed access schemas** restrict grant authority to the schema owner (or roles with MANAGE GRANTS): ``` schemas: - for_each: var.schemas name: "{{ each.value.name.split('.')[1] }}" database: "{{ each.value.name.split('.')[0] }}" owner: "{{ each.value.get('owner', parent.owner) }}" managed_access: true ``` With `managed_access: true`, even if an analyst creates a view, they cannot grant SELECT on it—only the schema owner can. This ensures all access flows through your defined role hierarchy. ### Cloned Databases (QA, blue-green, PR environments) Cloning a database does two different things to grants: | What | Happens to grants | | --------------------------------------- | ------------------------------------------------------ | | The database itself | **Not** copied — the clone starts with no grants on it | | Schemas, tables and other child objects | **Copied** — each keeps the grants its source had | So after `CREATE DATABASE BALBOA_QA CLONE BALBOA`, every `z_schema__` role already holds USAGE on the clone's copy of its schema, without anyone writing that down. Only the database-level grant is missing, which is why a clone is normally followed by a re-grant of `USAGE ON DATABASE` to `z_db__`. That is the behaviour you want — a role named for a schema keeps its meaning in every copy of that schema — but Snowcap does not know about it. With `--sync_resources grant`, those copied grants are remote state that no config declares, so a plan proposes dropping them. Applying that leaves roles with usage on the clone's database and no access to anything inside it. Declare them with a filtered loop over the schema list you already keep: ``` grants: - for_each: var.schemas where: "each.value.name.split('.')[0] == 'BALBOA'" priv: USAGE on: "schema BALBOA_QA.{{ each.value.name.split('.')[1] }}" to: "z_schema__{{ each.value.name.split('.')[1] }}" ``` The `where` keeps the block off schemas in databases that have no clone. Adding a schema to the source layer covers its clone automatically, so the two cannot drift apart. The clone's schemas themselves stay undeclared — the clone creates them, and Snowcap only needs to describe the access. **Do not reach for `all schemas in database` here.** It looks like less configuration, but it grants every role that holds it the entire clone. If roles are scoped by layer — an analyst role seeing L1 through L3 and a reporter role seeing only L3 — a database-wide grant silently flattens that distinction in the clone while leaving it intact in the source, which is the kind of gap that survives review precisely because the source still looks correct. ## See Also - [Grant](https://snowcap.datacoves.com/resources/grant/index.md) - [Role](https://snowcap.datacoves.com/resources/role/index.md) - [RoleGrant](https://snowcap.datacoves.com/resources/role_grant/index.md) - [DatabaseRole](https://snowcap.datacoves.com/resources/database_role/index.md) - [Blueprint](https://snowcap.datacoves.com/blueprint/index.md) # YAML Configuration # YAML Configuration Snowcap uses YAML files to define your Snowflake resources. This page covers the templating features that make YAML configurations powerful and reusable. ## Variables (vars) Variables let you parameterize your configuration, making it reusable across environments or dynamic based on input. ### Basic Usage Use double curly braces to reference variables: ``` # snowcap.yml databases: - name: "db_{{ var.environment }}" ``` Pass values via CLI: ``` snowcap plan --config snowcap.yml --vars '{"environment": "prod"}' ``` Or via environment variables (must start with `SNOWCAP_VAR_` and be uppercase): ``` export SNOWCAP_VAR_ENVIRONMENT="prod" snowcap plan --config snowcap.yml ``` For managing sensitive values like passwords and API keys, see [Secrets and Variables](https://snowcap.datacoves.com/secrets-and-variables/index.md). ### Defining Variables with Defaults Use the top-level `vars:` key to define expected variables with types and optional defaults: ``` vars: - name: environment type: string - name: warehouse_size type: string default: XSMALL databases: - name: "analytics_{{ var.environment }}" warehouses: - name: "wh_{{ var.environment }}" warehouse_size: "{{ var.warehouse_size }}" ``` ### Variable Types | Type | Description | | -------- | -------------------------------------- | | `string` | Text value | | `list` | Array of values (used with `for_each`) | | `int` | Integer number | | `bool` | Boolean (true/false) | ## Loops (for_each) The `for_each` directive creates multiple resources from a list. This is the key to DRY (Don't Repeat Yourself) configurations. ### Basic Example ``` vars: - name: databases type: list default: - name: raw owner: loader - name: analytics owner: transformer databases: - for_each: var.databases name: "{{ each.value.name }}" owner: "{{ each.value.owner }}" ``` This creates two databases: `raw` (owned by `loader`) and `analytics` (owned by `transformer`). ### Loop Variables Inside a `for_each` block, you have access to: | Variable | Description | | -------------------- | --------------------------------------- | | `each.value` | The current item in the list | | `each.value.` | Access a field of the current item | | `each.index` | The index of the current item (0-based) | ### Filtering a Loop (where) Add `where` to run a block over only part of a list. The expression is bare Jinja — no `{{ }}` — and items it evaluates falsy for are skipped: ``` grants: # The schema list also covers databases other than BALBOA, so narrow it - for_each: var.schemas where: "each.value.name.split('.')[0] == 'BALBOA'" priv: USAGE on: "schema BALBOA_QA.{{ each.value.name.split('.')[1] }}" to: "z_schema__{{ each.value.name.split('.')[1] }}" ``` This lets one list drive several blocks that each cover a subset of it, rather than maintaining a second list per subset. It is the usual way to grant on a clone of a database — a QA or blue-green copy — without restating every schema: the same `z_schema__` role reaches the copy, so per-schema roles keep their meaning and tiered roles stay tiered. ### Creating Roles and Grants A common pattern is creating a role and grant for each resource: ``` vars: - name: databases type: list default: - name: raw - name: analytics # Create a role for each database roles: - for_each: var.databases name: "z_db__{{ each.value.name }}" # Grant USAGE on each database to its corresponding role grants: - for_each: var.databases priv: USAGE on: "database {{ each.value.name }}" to: "z_db__{{ each.value.name }}" ``` ### Multiple Grants Per Iteration Use a list for the `on` parameter to create multiple grants: ``` grants: - for_each: var.databases priv: SELECT on: - "all tables in database {{ each.value.name }}" - "all views in database {{ each.value.name }}" - "future tables in database {{ each.value.name }}" - "future views in database {{ each.value.name }}" to: z_tables_views__select ``` ### String Manipulation Use Jinja filters and Python methods for string operations: ``` vars: - name: schemas type: list default: - name: RAW.FINANCE - name: RAW.MARKETING - name: ANALYTICS.REPORTS schemas: - for_each: var.schemas name: "{{ each.value.name.split('.')[1] }}" database: "{{ each.value.name.split('.')[0] }}" ``` ### Default Values Use Jinja's `default` filter for optional fields: ``` warehouses: - for_each: var.warehouses name: "{{ each.value.name }}" warehouse_size: "{{ each.value.size }}" auto_suspend: "{{ each.value.auto_suspend | default(60) }}" ``` Or use Python's `.get()` method: ``` schemas: - for_each: var.schemas owner: "{{ each.value.get('owner', 'SYSADMIN') }}" ``` ### Parent Attributes Access parent resource attributes with `parent`: ``` schemas: - for_each: var.schemas name: "{{ each.value.name.split('.')[1] }}" database: "{{ each.value.name.split('.')[0] }}" owner: "{{ each.value.get('owner', parent.owner) }}" ``` ## Scope Experimental The scope feature is experimental and may change in future versions. ### What Scope Does By default, Snowcap can manage any resource in your Snowflake account. The `scope` parameter **restricts** Snowcap to only manage resources within a specific database or schema. **Why use scope?** | Benefit | Description | | ------------------- | -------------------------------------------------------------------- | | **Safety** | Prevents accidentally modifying resources outside your intended area | | **Reusability** | Same config can be applied to different databases/schemas | | **Team separation** | Different teams manage their own schemas without affecting others | | **Focused configs** | Smaller, focused configs instead of one giant account-wide config | ### How It Works When you set a scope: 1. **Resources outside the scope cause an error** - If you try to create an account-level resource (like a warehouse) in a schema-scoped config, Snowcap will reject it 1. **Resources are automatically placed in the scope** - Tables, views, etc. without explicit database/schema are assigned to the configured scope 1. **Sync mode only affects resources in scope** - Using `--sync_resources` won't delete resources outside your scope ### Database Scope Limits Snowcap to resources within a single database: ``` scope: DATABASE database: RAW # These are allowed (database-level or below) schemas: - name: FINANCE - name: LEGAL tables: - name: products schema: FINANCE columns: - name: product data_type: string # This would ERROR - warehouses are account-level, not database-level # warehouses: # - name: my_warehouse ``` ### Schema Scope Limits Snowcap to resources within a single schema: ``` scope: SCHEMA database: DEV schema: SALESFORCE # These are allowed (schema-level) tables: - name: products columns: - name: product data_type: string tags: - name: cost_center allowed_values: ["finance", "engineering"] # This would ERROR - schemas are database-level, not schema-level # schemas: # - name: another_schema ``` ### Reusable Configs with CLI Overrides The real power of scope is **reusability**. Define a config once and apply it to different targets: ``` # dev_schema.yml scope: SCHEMA database: DEV tables: - name: staging_orders columns: - name: id data_type: int - name: data data_type: variant views: - name: v_orders as_: "SELECT * FROM staging_orders" ``` Apply to different engineer schemas: ``` snowcap apply --config dev_schema.yml --schema=SCH_ALICE snowcap apply --config dev_schema.yml --schema=SCH_BOB snowcap apply --config dev_schema.yml --schema=SCH_CAROL ``` Each engineer gets identical tables and views in their own schema. ### Combining Scope with Variables Use variables to apply the same schema structure to different environments: ``` # finance.yml scope: SCHEMA database: "ANALYTICS_{{ var.env }}" schema: FINANCE tables: - name: revenue columns: - name: date data_type: date - name: amount data_type: number ``` ``` # Deploy to staging snowcap apply --config finance.yml --vars '{"env": "STAGE"}' # Deploy to production snowcap apply --config finance.yml --vars '{"env": "PROD"}' ``` This creates `ANALYTICS_STAGE.FINANCE.revenue` and `ANALYTICS_PROD.FINANCE.revenue`. ## See Also - [Getting Started](https://snowcap.datacoves.com/getting-started/index.md) - Basic setup and first config - [Secrets and Variables](https://snowcap.datacoves.com/secrets-and-variables/index.md) - Managing sensitive values with environment variables - [Role-Based Access Control](https://snowcap.datacoves.com/role-based-access-control/index.md) - Real-world YAML patterns - [Blueprint](https://snowcap.datacoves.com/blueprint/index.md) - Python API reference # Secrets and Variables # Secrets and Environment Variables Snowcap provides a secure way to manage sensitive values like passwords, API keys, and tokens without storing them in your configuration files or repository. ## How It Works Snowcap automatically loads environment variables prefixed with `SNOWCAP_VAR_` and makes them available in your YAML configurations using Jinja2 templating syntax. **Pattern:** 1. Set environment variable: `SNOWCAP_VAR_MY_SECRET="sensitive-value"` 1. Reference in YAML: `{{ var.my_secret }}` The variable name is converted to lowercase when referenced. For example: - `SNOWCAP_VAR_DB_PASSWORD` → `{{ var.db_password }}` - `SNOWCAP_VAR_API_KEY` → `{{ var.api_key }}` ## Using .env Files For local development, you can store environment variables in a `.env` file and source it before running Snowcap. ### Create a .env file ``` # .env (add to .gitignore!) SNOWCAP_VAR_DB_PASSWORD="your-secret-password" SNOWCAP_VAR_API_KEY="your-api-key" SNOWCAP_VAR_OAUTH_TOKEN="your-oauth-token" ``` ### Load and run Snowcap Use `export` with a subshell to load the variables: ``` export $(cat .env | xargs) snowcap plan --config snowcap.yml ``` Or use a tool like [direnv](#using-direnv) for automatic loading. Important Always add `.env` to your `.gitignore` file to prevent accidentally committing secrets to your repository. ``` # .gitignore .env .env.* ``` ## Using direnv [direnv](https://direnv.net/) is a shell extension that automatically loads environment variables when you enter a directory. ### Setup 1. Install direnv: ``` # macOS brew install direnv # Ubuntu/Debian sudo apt install direnv ``` 1. Add to your shell (e.g., `~/.zshrc` or `~/.bashrc`): ``` eval "$(direnv hook zsh)" # or bash ``` 1. Create a `.envrc` file in your project: ``` # .envrc export SNOWCAP_VAR_DB_PASSWORD="your-secret-password" export SNOWCAP_VAR_API_KEY="your-api-key" ``` 1. Allow the directory: ``` direnv allow ``` Now environment variables are automatically loaded when you `cd` into the project directory. ## YAML Configuration Examples ### Secrets ``` secrets: # Generic secret (API key) - name: external_api_key secret_type: GENERIC_STRING secret_string: "{{ var.api_key }}" comment: API key for external service # Password secret - name: database_credentials secret_type: PASSWORD username: "{{ var.db_username }}" password: "{{ var.db_password }}" comment: Credentials for external database # OAuth secret - name: oauth_secret secret_type: OAUTH2 api_authentication: my_security_integration oauth_refresh_token: "{{ var.oauth_token }}" oauth_refresh_token_expiry_time: 2049-01-06 20:00:00 ``` ### Other Resources Environment variables work with any resource field: ``` users: - name: service_account password: "{{ var.service_account_password }}" default_role: SERVICE_ROLE ``` ## CI/CD Integration ### GitHub Actions Use GitHub Secrets to securely pass environment variables: ``` # .github/workflows/snowcap.yml name: Snowcap Deploy on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run Snowcap uses: datacoves/snowcap-action@v1 with: config: snowcap.yml command: apply env: SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }} SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }} SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }} SNOWCAP_VAR_API_KEY: ${{ secrets.API_KEY }} SNOWCAP_VAR_DB_PASSWORD: ${{ secrets.DB_PASSWORD }} ``` ### GitLab CI ``` # .gitlab-ci.yml snowcap-deploy: image: python:3.11 script: - pip install snowcap - snowcap apply --config snowcap.yml variables: SNOWCAP_VAR_API_KEY: $API_KEY SNOWCAP_VAR_DB_PASSWORD: $DB_PASSWORD ``` ### Azure DevOps ``` # azure-pipelines.yml steps: - script: | pip install snowcap snowcap apply --config snowcap.yml env: SNOWCAP_VAR_API_KEY: $(API_KEY) SNOWCAP_VAR_DB_PASSWORD: $(DB_PASSWORD) ``` ## Best Practices 1. **Never commit secrets** - Always use environment variables for sensitive values and add `.env` files to `.gitignore`. 1. **Use secret managers in CI/CD** - GitHub Secrets, GitLab CI Variables, Azure Key Vault, AWS Secrets Manager, etc. 1. **Rotate secrets regularly** - Update tokens and passwords periodically. 1. **Use different secrets per environment** - Production, staging, and development should have separate credentials. 1. **Limit secret scope** - Only expose secrets to the services and pipelines that need them. ## Troubleshooting ### Variable not found If you get an error that a variable is not defined: 1. Verify the environment variable is set: ``` echo $SNOWCAP_VAR_MY_SECRET ``` 1. Check the prefix is correct (`SNOWCAP_VAR_` in uppercase) 1. Remember the variable name is lowercased in templates: 1. `SNOWCAP_VAR_MY_SECRET` → `{{ var.my_secret }}` ### Testing variable substitution Use `snowcap plan` to preview the configuration before applying: ``` source .env && snowcap plan --config snowcap.yml ``` This will show you the resolved configuration without making changes to Snowflake. # GitHub Action # GitHub Action Automate Snowflake deployments with GitHub Actions using a review-then-apply workflow. ## Workflow Pattern The recommended pattern is **plan on PR, apply on merge**: 1. **Pull Request opened** → Run `snowcap plan` to show what changes will be made 1. **Reviewers** → See the planned changes in the PR, approve or request changes 1. **PR merged to main** → Run `snowcap apply` to execute the changes This prevents accidental changes - nothing is applied to Snowflake until the PR is reviewed and merged. ## Authentication GitHub Actions require **key-pair authentication** since service accounts can't use passwords or MFA. Set up key-pair auth in Snowflake: 1. [Generate a key pair](https://docs.snowflake.com/en/user-guide/key-pair-auth#generate-the-private-key) 1. Assign the public key to your service user 1. Store the private key as a GitHub secret ## Example Workflow ``` # .github/workflows/snowcap.yml name: Snowcap on: pull_request: paths: - 'snowcap/**' push: branches: [main] paths: - 'snowcap/**' jobs: plan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' - run: pip install snowcap - name: Write private key run: echo "${{ secrets.SNOWFLAKE_PRIVATE_KEY }}" > /tmp/rsa_key.pem - name: Plan changes run: snowcap plan --config ./snowcap/ env: SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }} SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }} SNOWFLAKE_PRIVATE_KEY_PATH: /tmp/rsa_key.pem SNOWFLAKE_AUTHENTICATOR: SNOWFLAKE_JWT SNOWFLAKE_ROLE: ${{ secrets.SNOWFLAKE_ROLE }} apply: if: github.event_name == 'push' && github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' - run: pip install snowcap - name: Write private key run: echo "${{ secrets.SNOWFLAKE_PRIVATE_KEY }}" > /tmp/rsa_key.pem - name: Apply changes run: snowcap apply --config ./snowcap/ env: SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }} SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }} SNOWFLAKE_PRIVATE_KEY_PATH: /tmp/rsa_key.pem SNOWFLAKE_AUTHENTICATOR: SNOWFLAKE_JWT SNOWFLAKE_ROLE: ${{ secrets.SNOWFLAKE_ROLE }} ``` ## Configure Secrets Go to your GitHub repository **Settings → Secrets and variables → Actions** and add: | Secret | Description | | ----------------------- | --------------------------------------------------- | | `SNOWFLAKE_ACCOUNT` | Your Snowflake account identifier | | `SNOWFLAKE_USER` | Service account username | | `SNOWFLAKE_PRIVATE_KEY` | Contents of your private key file (PEM format) | | `SNOWFLAKE_ROLE` | Role to use for deployments (e.g., `SECURITYADMIN`) | ## How It Works 1. **Developer** creates a branch and modifies files in `snowcap/` 1. **Opens PR** → GitHub runs `snowcap plan`, showing planned changes 1. **Reviewer** approves the PR after reviewing the plan output 1. **Merge to main** → GitHub runs `snowcap apply`, changes are made to Snowflake # Export # Export Existing Resources Already have a Snowflake environment? Generate config from your existing setup: ``` snowcap export \ --resource=warehouse,role,grant \ --out=snowcap.yml ``` ## Export Specific Resource Types Export only the resources you need: ``` # Export warehouses only snowcap export --resource=warehouse --out=warehouses.yml # Export roles and grants snowcap export --resource=role,grant --out=rbac.yml # Export all supported resources snowcap export --resource=all --out=snowcap.yml ``` ## Output Format The exported YAML can be used directly with `snowcap plan` and `snowcap apply`: ``` # Exported warehouses.yml warehouses: - name: ANALYTICS warehouse_size: XSMALL auto_suspend: 60 auto_resume: true - name: LOADING warehouse_size: SMALL auto_suspend: 300 auto_resume: true ``` ## Workflow: Migrate from Manual Management 1. Export your current Snowflake configuration: ``` snowcap export --resource=all --out=snowcap.yml ``` 1. Review and organize the exported config into separate files if needed 1. Add the config to version control: ``` git add snowcap.yml git commit -m "Import existing Snowflake configuration" ``` 1. From now on, manage changes through Snowcap: ``` # Edit snowcap.yml, then: snowcap plan --config snowcap.yml snowcap apply --config snowcap.yml ``` # Advanced # Python API For programmatic control, use the Python API directly instead of the CLI. ## Basic Example ``` import os import snowflake.connector from snowcap.blueprint import Blueprint, print_plan from snowcap.resources import Grant, Role, Warehouse # Configure resources by instantiating Python objects role = Role(name="transformer") warehouse = Warehouse( name="transforming", warehouse_size="large", auto_suspend=60, ) usage_grant = Grant(priv="usage", to=role, on=warehouse) # Connect to Snowflake connection_params = { "account": os.environ["SNOWFLAKE_ACCOUNT"], "user": os.environ["SNOWFLAKE_USER"], "password": os.environ["SNOWFLAKE_PASSWORD"], "role": "SYSADMIN", } session = snowflake.connector.connect(**connection_params) # Create a Blueprint and pass your resources into it bp = Blueprint(resources=[ role, warehouse, usage_grant, ]) # Generate a plan (like Terraform) plan = bp.plan(session) print_plan(plan) # Apply changes to Snowflake bp.apply(session, plan) ``` ## Plan Output The `print_plan()` function displays changes that will be made: ``` » snowcap » Plan: 3 to add, 0 to change, 0 to destroy. + urn::ABCD123:warehouse/transforming { + name = "transforming" + owner = "SYSADMIN" + warehouse_type = "STANDARD" + warehouse_size = "LARGE" ... } + urn::ABCD123:role/transformer { + name = "transformer" + owner = "USERADMIN" } + urn::ABCD123:grant/TRANSFORMER?priv=USAGE&on=warehouse/TRANSFORMING { + priv = "USAGE" + on = "transforming" + to = TRANSFORMER } ``` ## Apply Output The `apply()` function executes the SQL commands: ``` [SNOWCAP_USER:SYSADMIN] > USE SECONDARY ROLES ALL [SNOWCAP_USER:SYSADMIN] > CREATE WAREHOUSE TRANSFORMING warehouse_type = STANDARD ... [SNOWCAP_USER:SYSADMIN] > USE ROLE USERADMIN [SNOWCAP_USER:USERADMIN] > CREATE ROLE TRANSFORMER [SNOWCAP_USER:USERADMIN] > USE ROLE SYSADMIN [SNOWCAP_USER:SYSADMIN] > GRANT USAGE ON WAREHOUSE transforming TO TRANSFORMER ``` ## Loading from Environment Use [python-dotenv](https://pypi.org/project/python-dotenv/) to load credentials from a `.env` file: ``` from dotenv import load_dotenv load_dotenv() # Now os.environ has values from .env ``` ## Next Steps - [Blueprint](https://snowcap.datacoves.com/blueprint/index.md) - Advanced deployment customization - [Working With Resources](https://snowcap.datacoves.com/working-with-resources/index.md) - Resource configuration options # Blueprint A Blueprint is the core engine that compares your configuration to Snowflake and generates the SQL to make them match. It validates resources, generates execution plans, and applies changes. ## YAML vs Python **Most users should use YAML with the CLI.** When you run `snowcap plan` or `snowcap apply`, a Blueprint is created automatically from your YAML files. This approach is: - Declarative and easy to read - Version-controlled in git - No Python knowledge required **The Python API is for advanced use cases:** - Building custom tooling or automation - Integrating Snowcap into other Python applications - Dynamic resource generation from external data (APIs, databases) - Complex logic that YAML templates can't express - Testing and CI/CD pipelines ## Python API Example ``` from snowcap.blueprint import Blueprint from snowcap.resources import Database, Schema bp = Blueprint( run_mode='create-or-update', resources=[ Database('my_database'), Schema('my_schema', database='my_database'), ], allowlist=["database", "schema"], dry_run=False, ) plan = bp.plan(session) bp.apply(session, plan) ``` For a complete Python example, see [Python API](https://snowcap.datacoves.com/python-api/index.md). ## Blueprint Parameters ### run_mode Defines how the blueprint interacts with the Snowflake account. | Value | Description | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------- | | `create-or-update` | *(default)* Resources are created or updated, never deleted | | `sync` | Snowflake is updated to match the blueprint exactly. **Will delete resources not in config.** Must be used with `allowlist`. | ### resources List of resources to manage. ``` resources=[ Database('my_database'), Schema('my_schema', database='my_database'), ] ``` ### allowlist Limits which resource types the blueprint can manage. Required when using `sync` mode. ``` allowlist=["database", "schema", "role"] ``` ### dry_run When `True`, `apply()` returns SQL commands without executing them. ``` dry_run=True ``` ### vars A dictionary of variable values for templating. ``` vars={ "environment": "prod", "owner": "analytics_team", } ``` ### vars_spec Defines expected variables with types and optional defaults. ``` vars_spec=[ {"name": "environment", "type": "string"}, {"name": "size", "type": "string", "default": "XSMALL"}, ] ``` ### scope, database, schema Limits Snowcap to managing resources within a specific database or schema. ``` scope="DATABASE", database="RAW", ``` ### use_account_usage Controls whether Snowcap uses `SNOWFLAKE.ACCOUNT_USAGE` views for fetching grants. Defaults to `False`. ``` use_account_usage=False # default ``` When enabled, Snowcap fetches all grants with a single bulk query to `ACCOUNT_USAGE.GRANTS_TO_ROLES` instead of per-role `SHOW GRANTS` commands. This can significantly reduce the number of queries for accounts with many roles. **When to enable:** - Your manifest manages **50+ roles** with grants - You're seeing many `SHOW GRANTS TO ROLE` queries in the logs - The bulk query time (typically 30-60 seconds) is less than the cumulative time of individual `SHOW GRANTS` commands **When to keep disabled (default):** - Smaller manifests with fewer roles - You want faster apply times for simple configurations - Your account has many grants but your manifest only references a few roles Requires `IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE`. If unavailable, Snowcap falls back to `SHOW GRANTS` automatically. See [Getting Started - Optimizing Grant Fetching](https://snowcap.datacoves.com/getting-started/#optimizing-grant-fetching-with-account_usage) for setup instructions. ## Methods ### plan(session) Compares your configuration to Snowflake and returns a list of changes needed. ``` plan = bp.plan(session) ``` **Parameters:** - `session` - Snowflake connection object **Returns:** - `list[ResourceChange]` - Changes needed to reach desired state ### apply(session, plan) Executes SQL commands to apply the plan to Snowflake. ``` results = bp.apply(session, plan) ``` **Parameters:** - `session` - Snowflake connection object - `plan` *(optional)* - List of changes. If not provided, generates a plan automatically. **Returns:** - `list[str]` - SQL commands that were executed ### add(resource) Adds resources to the blueprint after initialization. ``` bp.add(Database('another_db')) bp.add(role1, role2, role3) bp.add([schema1, schema2]) ``` ## Using Variables in Python ``` from snowcap.blueprint import Blueprint from snowcap.resources import Database from snowcap import var # Reference a variable db = Database(name=var.db_name) # Or use Jinja-style syntax in strings db = Database(name="db_{{ var.environment }}") # Pass values when creating the blueprint bp = Blueprint( resources=[db], vars={"db_name": "analytics", "environment": "prod"}, ) ``` ## See Also - [YAML Configuration](https://snowcap.datacoves.com/yaml-configuration/index.md) - Variables, loops, and scope in YAML - [Python API](https://snowcap.datacoves.com/python-api/index.md) - Complete Python example - [Working With Resources](https://snowcap.datacoves.com/working-with-resources/index.md) - Resource classes and relationships # Working with Resources In the Snowcap framework, anything in Snowflake that can be created with a `CREATE` statement has a corresponding Python class, such as `Warehouse`, `Database`, `Role`, etc. These act as simple wrappers around configuration with built-in runtime type checking. ## Introduction to Resources Resources in Snowcap are designed to be intuitive and straightforward. They encapsulate the configuration of Snowflake objects, ensuring that the properties and relationships between these objects are clearly defined and maintained. ### Instantiation and Configuration Resources can be instantiated directly in Python with their respective parameters. For example, creating a user or a warehouse involves simply passing the required parameters to the class constructor: ### Resource Interactions - **Passing Resources**: Resources can be passed directly to other resources to establish relationships or configurations. This can be done by passing the resource instance itself or by referencing its name. - **Pass by Instance**: Directly passing the resource instance ensures that the reference is clear and direct. - **Pass by Name**: Sometimes it's more convenient or necessary to pass resources by their name, especially when dealing with serialization or configurations that require names as strings. ### Combining Resources Combining resources refers to the practice of grouping multiple related resources into a coherent structure or configuration. This can be particularly useful in complex setups where multiple resources need to interact closely with each other. ### Containers Resources can be organized into containers that reflect their hierarchical relationship in Snowflake, such as a `Database` containing multiple `Schemas`, which in turn contain other objects like `Tables` or `Views`. - **Recommended Method**: Using keyword arguments (`kwargs`) to pass resources ensures clarity and readability. - **Advanced Method**: The `.add` method or using nested identifiers like "database.schema.table" can be used for more complex or dynamic configurations. ### Quoted Identifiers Snowcap attempts to infer the need for quoted identifiers in SQL statements. It is generally recommended to avoid manually quoting identifiers unless absolutely necessary, as Snowcap handles most of the common cases automatically. ## Advanced Configuration - **Manual Registration of Dependencies**: While Snowcap manages dependencies between resources automatically, there are rare cases where manual intervention might be necessary. - **Avoid Circular Dependencies**: Design your resource dependencies to avoid circular references, which can lead to errors or undefined behaviors. - **Using `Resource.requires(...)`**: This method can be used to explicitly define dependencies if needed, though it is typically not required. - **Name Qualification**: In complex setups, fully qualifying resource names can help avoid ambiguity and ensure that SQL operations are performed on the correct objects. By understanding and utilizing these concepts, you can effectively manage and orchestrate Snowflake resources using the Snowcap framework, making your data infrastructure robust, scalable, and maintainable. # Resources # AccountParameter [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/alter-account) | Snowcap CLI label: `account_parameter` An account parameter in Snowflake that allows you to set or alter account-level parameters. For a complete list of available parameters, see the [Snowflake Parameters Reference](https://docs.snowflake.com/en/sql-reference/parameters). ## Examples ### YAML ``` account_parameters: - name: TIMEZONE value: America/New_York - name: STATEMENT_TIMEOUT_IN_SECONDS value: 3600 ``` ### Python ``` account_parameter = AccountParameter( name="TIMEZONE", value="America/New_York", ) ``` ## Fields - `name` (string, required) - The name of the account parameter. See the [Snowflake Parameters Reference](https://docs.snowflake.com/en/sql-reference/parameters) for valid parameter names. - `value` (Any, required) - The value to set for the account parameter. **Note:** Requires ACCOUNTADMIN role. This privilege cannot be granted to other roles. See [Snowflake Permissions](https://snowcap.datacoves.com/snowflake-permissions/#resources-that-require-accountadmin) for details. # AggregationPolicy [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-aggregation-policy) | Snowcap CLI label: `aggregation_policy` Represents an aggregation policy in Snowflake, which defines constraints on aggregation operations. ## Examples ### YAML ``` aggregation_policies: - name: some_aggregation_policy body: AGGREGATION_CONSTRAINT(MIN_GROUP_SIZE => 5) owner: SYSADMIN ``` ### Python ``` aggregation_policy = AggregationPolicy( name="some_aggregation_policy", body="AGGREGATION_CONSTRAINT(MIN_GROUP_SIZE => 5)", owner="SYSADMIN" ) ``` ## Fields - `name` (string, required) - The name of the aggregation policy. - `body` (string, required) - The SQL expression defining the aggregation constraint. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the aggregation policy. Defaults to "SYSADMIN". # Alert [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-alert) | Snowcap CLI label: `alert` Alerts trigger notifications when certain conditions are met. ## Examples ### YAML ``` alerts: - name: some_alert warehouse: some_warehouse schedule: USING CRON * * * * * condition: SELECT COUNT(*) FROM some_table then: CALL SYSTEM$SEND_EMAIL('example@example.com', 'Alert Triggered', 'The alert condition was met.') ``` ### Python ``` alert = Alert( name="some_alert", warehouse="some_warehouse", schedule="USING CRON * * * * *", condition="SELECT COUNT(*) FROM some_table", then="CALL SYSTEM$SEND_EMAIL('example@example.com', 'Alert Triggered', 'The alert condition was met.')", ) ``` ## Fields - `name` (string, required) - The name of the alert. - `warehouse` (string or [Warehouse](https://snowcap.datacoves.com/resources/warehouse/index.md)) - The name of the warehouse to run the query on. - `schedule` (string) - The schedule for the alert to run on. - `condition` (string) - The condition for the alert to trigger on. - `then` (string) - The query to run when the alert triggers. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the alert. Defaults to "SYSADMIN". - `comment` (string) - A comment for the alert. Defaults to None. - `tags` (dict) - Tags for the alert. Defaults to None. # APIAuthenticationSecurityIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-security-integration) | Snowcap CLI label: `api_authentication_security_integration` Manages API authentication security integrations in Snowflake, allowing for secure API access management. ## Examples ### YAML ``` security_integrations: - name: some_api_authentication_security_integration type: api_authentication auth_type: OAUTH2 oauth_token_endpoint: https://example.com/oauth/token oauth_client_auth_method: CLIENT_SECRET_POST oauth_client_id: your_client_id oauth_client_secret: your_client_secret oauth_grant: client_credentials oauth_access_token_validity: 3600 oauth_allowed_scopes: [read, write] enabled: true comment: Integration for external API authentication. ``` ### Python ``` api_auth_integration = APIAuthenticationSecurityIntegration( name="some_api_authentication_security_integration", auth_type="OAUTH2", oauth_token_endpoint="https://example.com/oauth/token", oauth_client_auth_method="CLIENT_SECRET_POST", oauth_client_id="your_client_id", oauth_client_secret="your_client_secret", oauth_grant="client_credentials", oauth_access_token_validity=3600, oauth_allowed_scopes=["read", "write"], enabled=True, comment="Integration for external API authentication." ) ``` ## Fields - `name` (string, required) - The unique name of the security integration. - `auth_type` (string) - The type of authentication used, typically 'OAUTH2'. Defaults to 'OAUTH2'. - `oauth_token_endpoint` (string) - The endpoint URL for obtaining OAuth tokens. - `oauth_client_auth_method` (string) - The method used for client authentication, such as 'CLIENT_SECRET_POST'. - `oauth_client_id` (string) - The client identifier for OAuth. - `oauth_client_secret` (string) - The client secret for OAuth. - `oauth_grant` (string) - The OAuth grant type. - `oauth_access_token_validity` (int) - The validity period of the OAuth access token in seconds. Defaults to 0. - `oauth_allowed_scopes` (list) - A list of allowed scopes for the OAuth tokens. - `enabled` (bool) - Indicates if the security integration is enabled. Defaults to True. - `comment` (string) - An optional comment about the security integration. # APIIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-api-integration) | Snowcap CLI label: `api_integration` Manages API integrations in Snowflake, allowing external services to interact with Snowflake resources securely. This class supports creating, replacing, and checking the existence of API integrations across multiple cloud providers and Git HTTPS providers. ## Supported `api_provider` values | `api_provider` | Required fields | Used for | | -------------------------------------------------------------------------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `AWS_API_GATEWAY`, `AWS_PRIVATE_API_GATEWAY`, `AWS_GOV_API_GATEWAY`, `AWS_GOV_PRIVATE_API_GATEWAY` | `api_aws_role_arn` | External functions calling AWS API Gateway | | `AZURE_API_MANAGEMENT` | `azure_tenant_id`, `azure_ad_application_id` | External functions calling Azure API Management | | `GOOGLE_API_GATEWAY` | `google_audience` | External functions calling Google API Gateway | | `GIT_HTTPS_API` | (none beyond `api_allowed_prefixes`) | Snowflake [Git repositories](https://snowcap.datacoves.com/resources/git_repository/index.md) connecting to GitHub/GitLab/Bitbucket/etc. | ## Examples ### YAML — AWS API Gateway ``` api_integrations: - name: some_api_integration api_provider: AWS_API_GATEWAY api_aws_role_arn: "arn:aws:iam::123456789012:role/MyRole" enabled: true api_allowed_prefixes: ["/prod/", "/dev/"] api_blocked_prefixes: ["/test/"] api_key: "ABCD1234" comment: "Example AWS API integration" ``` ### YAML — GitHub (used by GitRepository) ``` api_integrations: - name: github_api_integration api_provider: GIT_HTTPS_API api_allowed_prefixes: ["https://github.com/some-org/"] enabled: true comment: "GitHub integration for git repos" ``` ### Python ``` api_integration = APIIntegration( name="some_api_integration", api_provider="AWS_API_GATEWAY", api_aws_role_arn="arn:aws:iam::123456789012:role/MyRole", enabled=True, api_allowed_prefixes=["/prod/", "/dev/"], api_blocked_prefixes=["/test/"], api_key="ABCD1234", comment="Example API integration", ) ``` ## Fields - `name` (string, required) - The unique name of the API integration. - `api_provider` (string or ApiProvider, required) - The provider of the API service. See table above for supported values. - `api_aws_role_arn` (string) - The AWS IAM role ARN. Required for AWS providers; omit for AZURE/GOOGLE/GIT_HTTPS_API. - `azure_tenant_id` (string) - Azure AD tenant ID. Required for `AZURE_API_MANAGEMENT`. - `azure_ad_application_id` (string) - Azure AD application registration ID. Required for `AZURE_API_MANAGEMENT`. - `google_audience` (string) - GCP audience identifier. Required for `GOOGLE_API_GATEWAY`. - `api_key` (string) - Optional API key used for authentication. - `api_allowed_prefixes` (list) - The list of allowed prefixes for the API endpoints. - `api_blocked_prefixes` (list) - The list of blocked prefixes for the API endpoints. - `enabled` (bool, required) - Specifies if the API integration is enabled. Defaults to TRUE. - `comment` (string) - A comment or description for the API integration. ## Granting on an integration Snowflake's `GRANT USAGE ON INTEGRATION ` SQL is valid for any subtype. In YAML you may use either the concrete subtype (`on: api integration `) — preferred — or the generic umbrella (`on: integration `): ``` grants: - priv: USAGE on: api integration github_api_integration # preferred — explicit subtype to: some_role - priv: USAGE on: integration github_api_integration # also supported (umbrella) to: another_role ``` # AuthenticationPolicy [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-authentication-policy) | Snowcap CLI label: `authentication_policy` Defines the rules and constraints for authentication within the system, ensuring they meet specific security standards. ## Examples ### Python ``` authentication_policy = AuthenticationPolicy( name="some_authentication_policy", authentication_methods=["PASSWORD", "SAML", "PROGRAMMATIC_ACCESS_TOKEN"], mfa_authentication_methods=["PASSWORD"], mfa_enrollment="REQUIRED", client_types=["SNOWFLAKE_UI"], security_integrations=["ALL"], pat_policy={ "network_policy_evaluation": "ENFORCED_NOT_REQUIRED", "default_expiry_in_days": 30, "max_expiry_in_days": 180, "require_role_restriction_for_service_users": False, }, comment="Policy for secure authentication." ) ``` ### YAML ``` authentication_policies: - name: some_authentication_policy authentication_methods: - PASSWORD - SAML - PROGRAMMATIC_ACCESS_TOKEN mfa_authentication_methods: - PASSWORD mfa_enrollment: REQUIRED client_types: - SNOWFLAKE_UI security_integrations: - ALL pat_policy: network_policy_evaluation: ENFORCED_NOT_REQUIRED default_expiry_in_days: 30 max_expiry_in_days: 180 require_role_restriction_for_service_users: false comment: Policy for secure authentication. ``` ## Fields - `name` (string, required) - The name of the authentication policy. - `authentication_methods` (list) - A list of allowed authentication methods. - `mfa_authentication_methods` (list) - A list of authentication methods that enforce multi-factor authentication (MFA). - `mfa_enrollment` (string) - Determines whether a user must enroll in multi-factor authentication. Defaults to OPTIONAL. - `client_types` (list) - A list of clients that can authenticate with Snowflake. - `security_integrations` (list) - A list of security integrations the authentication policy is associated with. - `pat_policy` (dict) - Controls programmatic access token issuance: network_policy_evaluation, default_expiry_in_days, max_expiry_in_days, and require_role_restriction_for_service_users must all be given or all omitted; declaring exactly the Snowflake defaults (ENFORCED_REQUIRED, 15, 365, TRUE) compares as unset. - `comment` (string) - A comment or description for the authentication policy. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the authentication policy. Defaults to SECURITYADMIN. # AzureStorageIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-storage-integration) | Snowcap CLI label: `azure_storage_integration` Represents an Azure storage integration in Snowflake, which allows Snowflake to access external cloud storage using Azure credentials. ## Examples ### YAML ``` azure_storage_integrations: - name: some_azure_storage_integration enabled: true azure_tenant_id: some_tenant_id storage_allowed_locations: - azure://somebucket/somepath/ storage_blocked_locations: - azure://someotherbucket/somepath/ comment: This is an Azure storage integration. ``` ### Python ``` azure_storage_integration = AzureStorageIntegration( name="some_azure_storage_integration", enabled=True, azure_tenant_id="some_tenant_id", storage_allowed_locations=["azure://somebucket/somepath/"], storage_blocked_locations=["azure://someotherbucket/somepath/"], comment="This is an Azure storage integration." ) ``` ## Fields - `name` (string, required) - The name of the storage integration. - `enabled` (bool, required) - Specifies whether the storage integration is enabled. - `azure_tenant_id` (string, required) - The Azure tenant ID associated with the storage integration. - `storage_allowed_locations` (list) - The cloud storage locations that are allowed. - `storage_blocked_locations` (list) - The cloud storage locations that are blocked. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the storage integration. Defaults to "ACCOUNTADMIN". - `comment` (string) - A comment about the storage integration. # ComputePool [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-compute-pool) | Snowcap CLI label: `compute_pool` A compute pool is a group of compute resources in Snowflake that can be used to execute SQL queries. ## Examples ### YAML ``` compute_pools: - name: some_compute_pool owner: ACCOUNTADMIN min_nodes: 2 max_nodes: 10 instance_family: CPU_X64_S auto_resume: true initially_suspended: false auto_suspend_secs: 1800 comment: Example compute pool ``` ### Python ``` compute_pool = ComputePool( name="some_compute_pool", owner="ACCOUNTADMIN", min_nodes=2, max_nodes=10, instance_family="CPU_X64_S", auto_resume=True, initially_suspended=False, auto_suspend_secs=1800, comment="Example compute pool" ) ``` ## Fields - `name` (string, required) - The unique name of the compute pool. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the compute pool. Defaults to "ACCOUNTADMIN". - `min_nodes` (int) - The minimum number of nodes in the compute pool. - `max_nodes` (int) - The maximum number of nodes in the compute pool. - `instance_family` (string or InstanceFamily) - The family of instances to use for the compute nodes. - `auto_resume` (bool) - Whether the compute pool should automatically resume when queries are submitted. Defaults to True. - `initially_suspended` (bool) - Whether the compute pool should start in a suspended state. - `auto_suspend_secs` (int) - The number of seconds of inactivity after which the compute pool should automatically suspend. Defaults to 3600. - `comment` (string) - An optional comment about the compute pool. **Note:** Requires CREATE COMPUTE POOL privilege on the account. See [Snowflake Permissions](https://snowcap.datacoves.com/snowflake-permissions/#step-2-additional-privileges) for setup instructions. # CortexSearchService [Snowflake Documentation](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-search/cortex-search-overview) | Snowcap CLI label: `cortex_search_service` A Cortex Search Service is a schema-scoped Snowflake AI service that exposes semantic + lexical search over a base table or view. Snowcap supports granting access to existing services declaratively. The service itself (the `CREATE CORTEX SEARCH SERVICE ... AS ` body, embedding model, `target_lag`, attribute set, etc.) is **not** modeled as a concrete resource — create it via DDL or dbt, then manage who can call it through `grants:`. ## Examples ### YAML ``` grants: # Required to call SNOWFLAKE.CORTEX.SEARCH_PREVIEW() against the service. - priv: USAGE on: cortex search service somedb.someschema.transcript_search to: customer_support # Required for get_ai_observability_events() / Cortex Search request logs. - priv: MONITOR on: cortex search service somedb.someschema.transcript_search to: search_observability_role # Schema-scope privilege to allow a role to create new services. - priv: CREATE CORTEX SEARCH SERVICE on: schema somedb.someschema to: search_author_role ``` ### Python ``` # Grant USAGE grant = Grant( priv="USAGE", on_cortex_search_service="somedb.someschema.transcript_search", to="customer_support", ) # Grant MONITOR grant = Grant( priv="MONITOR", on_cortex_search_service="somedb.someschema.transcript_search", to="search_observability_role", ) ``` ## Privileges | Privilege | Purpose | | ----------- | ---------------------------------------------------------------- | | `USAGE` | Call `SNOWFLAKE.CORTEX.SEARCH_PREVIEW(...)` against the service. | | `MONITOR` | Read request logs via `get_ai_observability_events(...)`. | | `OWNERSHIP` | Standard ownership semantics — drop, alter, transfer. | | `ALL` | Convenience: expand to all of the above. | The schema-scope privilege `CREATE CORTEX SEARCH SERVICE` is part of [Grant](https://snowcap.datacoves.com/resources/grant/index.md) under [SchemaPriv] — see the schema-privileges example above. ## Minimal example: full Cortex access for a developer role A common goal is "let this role use Cortex Code in Snowsight, call Cortex AI SQL functions, and query our Cortex Search Service." Three pieces stack together: ``` # 1. Account-level privilege for Cortex AI SQL (AI_COMPLETE, AI_FILTER, # SUMMARIZE, embeddings, etc.). Granted to PUBLIC by default — declare it # explicitly so access survives a future PUBLIC revoke. grants: - priv: USE AI FUNCTIONS on: ACCOUNT to: dbt_developer # 2. (Optional) USAGE on the search service itself - priv: USAGE on: cortex search service db_dev.cortex.faq_search to: dbt_developer # 3. Database-role grants on the SNOWFLAKE shared database. COPILOT_USER is # required for the Cortex Code pane in Snowsight. CORTEX_USER (or # CORTEX_AGENT_USER) is required for Cortex AI SQL functions and Cortex # Code's underlying calls. database_role_grants: - database_role: SNOWFLAKE.COPILOT_USER roles: - dbt_developer - database_role: SNOWFLAKE.CORTEX_USER roles: - dbt_developer ``` ### Gotchas - `SNOWFLAKE.CORTEX_USER` is granted to `PUBLIC` by default, so a role inherits it transitively unless your account has revoked that default. `SNOWFLAKE.COPILOT_USER` is **not** granted to `PUBLIC` — without an explicit grant the Cortex Code pane is hidden in Snowsight. - Declaring a `database_role_grants` entry for a role that is already granted to another grantee (e.g. `ACCOUNTADMIN`) requires snowcap ≥ the release containing the multi-grantee fetch fix. Earlier versions emit a spurious `UpdateResource(to_role: ACCOUNTADMIN → )` diff instead of a clean create. - `USE AI FUNCTIONS ON ACCOUNT` is the account privilege, separate from the `SNOWFLAKE.CORTEX_USER` database role. Both are typically required for Cortex AI SQL calls; missing either produces a "Function requires X privilege" error at runtime. - Querying a search service also requires `USAGE` on its parent database and schema. If those are absent the call fails before reaching the service-level USAGE check. ## See also - [Snowflake — Cortex Search overview](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-search/cortex-search-overview) - [Snowflake — Cortex Code access control](https://docs.snowflake.com/en/user-guide/cortex-code/cortex-code-snowsight#access-control-requirements) - [Snowflake — Cortex AI SQL required privileges](https://docs.snowflake.com/en/user-guide/snowflake-cortex/aisql#required-privileges) - [Snowflake — Cortex Search Monitor / logs](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-search/cortex-search-monitor) - [DatabaseRole](https://snowcap.datacoves.com/resources/database_role/index.md) — for granting `SNOWFLAKE.*` database roles - [Grant](https://snowcap.datacoves.com/resources/grant/index.md) — for the underlying grant resource and YAML schema # Database [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-database) | Snowcap CLI label: `database` Represents a database in Snowflake. ## Examples ### YAML ``` databases: - name: some_database transient: true owner: SYSADMIN data_retention_time_in_days: 7 max_data_extension_time_in_days: 28 default_ddl_collation: utf8 tags: project: research priority: high comment: This is a database. ``` You can add schemas to a database using the `schemas` field: ``` databases: - name: some_database schemas: - name: another_schema ``` Or by referencing the database name: ``` databases: - name: some_database schemas: - name: another_schema database: some_database ``` ### Python ``` database = Database( name="some_database", transient=True, owner="SYSADMIN", data_retention_time_in_days=7, max_data_extension_time_in_days=28, default_ddl_collation="utf8", tags={"project": "research", "priority": "high"}, comment="This is a database." ) ``` A database can contain schemas. In Python, you can add a schema to a database in several ways: By database name: ``` sch = Schema( name="some_schema", database="my_test_db", ) ``` By database object: ``` db = Database(name="my_test_db") sch = Schema( name="some_schema", database=db, ) ``` Or using the `add` method: ``` db = Database(name="my_test_db") sch = Schema(name="some_schema") db.add(sch) ``` ## Fields - `name` (string, required) - The name of the database. - `transient` (bool) - Specifies if the database is transient. Defaults to False. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the database. Defaults to "SYSADMIN". - `data_retention_time_in_days` (int) - The number of days to retain data for Time Travel. Uses Snowflake default if not specified. - `max_data_extension_time_in_days` (int) - The maximum number of days to extend data retention beyond `data_retention_time_in_days`. Uses Snowflake default if not specified. - `external_volume` (string) - The external volume to use for Iceberg tables. - `catalog` (string) - The catalog integration for Iceberg tables. - `default_ddl_collation` (string) - The default collation for DDL statements. - `tags` (dict) - A dictionary of tags associated with the database. - `comment` (string) - A comment describing the database. # DatabaseRole [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-database-role) | Snowcap CLI label: `database_role` A database role in Snowflake is a collection of privileges that can be assigned to users or other roles within a specific database context. It is used to manage access control and permissions at the database level. ## Examples ### YAML ``` database_roles: - name: some_database_role database: some_database owner: USERADMIN tags: department: finance comment: This role is for database-specific access control. ``` ### Python ``` database_role = DatabaseRole( name="some_database_role", database="some_database", owner="USERADMIN", tags={"department": "finance"}, comment="This role is for database-specific access control." ) ``` ## Fields - `name` (string, required) - The name of the database role. - `database` (string) - The database this role is associated with. This is derived from the fully qualified name. - `owner` (string) - The owner of the database role. Defaults to "USERADMIN". - `tags` (dict) - Tags associated with the database role. - `comment` (string) - A comment about the database role. # DatabaseRoleGrant [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/grant-database-role) | Snowcap CLI label: `database_role_grant` Represents a grant of a database role to another role or database role in Snowflake. ## Examples ### Python ``` # Grant to Database Role: role_grant = DatabaseRoleGrant(database_role="somedb.somerole", to_database_role="somedb.someotherrole") role_grant = DatabaseRoleGrant(database_role="somedb.somerole", to=DatabaseRole(database="somedb", name="someotherrole")) # Grant to Role: role_grant = DatabaseRoleGrant(database_role="somedb.somerole", to_role="somerole") role_grant = DatabaseRoleGrant(database_role="somedb.somerole", to=Role(name="somerole")) ``` ### YAML ``` database_role_grants: - database_role: somedb.somerole to_database_role: somedb.someotherrole - database_role: somedb.somerole to_role: somerole ``` ## Fields - `database_role` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md), required) - The database role to be granted. - `to_role` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The role to which the database role is granted. - `to_database_role` (string or [User](https://snowcap.datacoves.com/resources/user/index.md)) - The database role to which the database role is granted. # DbtProject [Snowflake Documentation](https://docs.snowflake.com/en/user-guide/data-engineering/dbt-projects-on-snowflake-access-control) | Snowcap CLI label: `dbt_project` A dbt project object (part of *dbt Projects on Snowflake*) is a schema-scoped object that packages a dbt project so it can be executed with `EXECUTE DBT PROJECT`. Snowcap supports granting access to existing dbt project objects declaratively. The object itself (`CREATE DBT PROJECT ... FROM ` with its profiles/target config) is **not** modeled as a concrete resource — create it via a Snowflake Workspace or DDL, then manage who can execute and monitor it through `grants:`. ## Examples ### YAML ``` grants: # Required to EXECUTE DBT PROJECT and to list/retrieve the project's files. - priv: USAGE on: dbt project somedb.someschema.analytics_dbt to: transformer_role # Required to view the project (details + run history) in Snowsight. - priv: MONITOR on: dbt project somedb.someschema.analytics_dbt to: analytics_observer # Schema-scope privilege to allow a role to create new dbt project objects # (e.g. deploying from a Workspace). - priv: CREATE DBT PROJECT on: schema somedb.someschema to: dbt_author_role ``` ### Python ``` # Grant USAGE (execute the project + read its files) grant = Grant( priv="USAGE", on_dbt_project="somedb.someschema.analytics_dbt", to="transformer_role", ) # Grant MONITOR (Snowsight project details + run history) grant = Grant( priv="MONITOR", on_dbt_project="somedb.someschema.analytics_dbt", to="analytics_observer", ) ``` ## Privileges | Privilege | Purpose | | ----------- | ---------------------------------------------------------------------------- | | `USAGE` | Execute the dbt project (`EXECUTE DBT PROJECT`) and list/retrieve its files. | | `MONITOR` | View the project's details and run history in Snowsight. | | `OWNERSHIP` | Full control of the object. Exclusive to a single role (like tasks). | | `ALL` | All privileges above. | The schema-scope privilege `CREATE DBT PROJECT` (see [Schema](https://snowcap.datacoves.com/resources/schema/index.md) grants) lets a role create dbt project objects in that schema. ## Minimal example Two roles, least privilege: one runs the project, one only watches it in Snowsight. Neither owns it — ownership stays with the deploying role. ``` grants: - priv: USAGE on: dbt project analytics.transforms.daily_models to: dbt_runner - priv: MONITOR on: dbt project analytics.transforms.daily_models to: analytics_viewer ``` > Note: in a managed-access schema, granting these privileges is restricted to the schema owner or a role with `MANAGE GRANTS` — the same constraint that applies to tasks. # DynamicTable [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table) | Snowcap CLI label: `dynamic_table` Represents a dynamic table in Snowflake, which can be configured to refresh automatically, fully, or incrementally, and initialized on creation or on a schedule. ## Examples ### YAML ``` dynamic_tables: - name: some_dynamic_table columns: - name: id - name: data target_lag: 1 HOUR warehouse: some_warehouse refresh_mode: AUTO initialize: ON_CREATE as_: SELECT id, data FROM source_table comment: This is a sample dynamic table owner: SYSADMIN ``` ### Python ``` dynamic_table = DynamicTable( name="some_dynamic_table", columns=[{"name": "id"}, {"name": "data"}], target_lag="1 HOUR", warehouse="some_warehouse", refresh_mode="AUTO", initialize="ON_CREATE", as_="SELECT id, data FROM source_table", comment="This is a sample dynamic table", owner="SYSADMIN" ) ``` ## Fields - `name` (string, required) - The name of the dynamic table. - `columns` (list, required) - A list of dicts defining the structure of the table. - `target_lag` (string) - The acceptable lag (delay) for data in the table. Defaults to "DOWNSTREAM". - `warehouse` (string or [Warehouse](https://snowcap.datacoves.com/resources/warehouse/index.md), required) - The warehouse where the table is stored. - `as_` (string, required) - The query used to populate the table. - `refresh_mode` (string or RefreshMode) - The mode of refreshing the table (AUTO, FULL, INCREMENTAL). - `initialize` (string or InitializeBehavior) - The behavior when the table is initialized (ON_CREATE, ON_SCHEDULE). - `comment` (string) - An optional comment for the table. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the table. Defaults to "SYSADMIN". # EmailNotificationIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-notification-integration) | Snowcap CLI label: `email_notification_integration` Manages the configuration for email-based notification integrations within Snowflake. This integration allows specifying recipients who will receive notifications via email. ## Examples ### YAML ``` email_notification_integrations: - name: some_email_notification_integration enabled: true allowed_recipients: - user1@example.com - user2@example.com comment: "Example email notification integration" ``` ### Python ``` email_notification_integration = EmailNotificationIntegration( name="some_email_notification_integration", enabled=True, allowed_recipients=["user1@example.com", "user2@example.com"], comment="Example email notification integration" ) ``` ## Fields - `name` (string, required) - The name of the email notification integration. - `enabled` (bool, required) - Specifies whether the notification integration is enabled. - `allowed_recipients` (list) - A list of email addresses that are allowed to receive notifications. - `comment` (string) - An optional comment about the notification integration. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the notification integration. Defaults to "ACCOUNTADMIN". # EventTable [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-event-table) | Snowcap CLI label: `event_table` An event table captures events, including logged messages from functions and procedures. ## Examples ### YAML ``` event_tables: - name: some_event_table cluster_by: - timestamp - user_id data_retention_time_in_days: 365 max_data_extension_time_in_days: 30 change_tracking: true default_ddl_collation: utf8 copy_grants: true comment: This is a sample event table. tags: department: analytics ``` ### Python ``` event_table = EventTable( name="some_event_table", cluster_by=["timestamp", "user_id"], data_retention_time_in_days=365, max_data_extension_time_in_days=30, change_tracking=True, default_ddl_collation="utf8", copy_grants=True, comment="This is a sample event table.", tags={"department": "analytics"} ) ``` ## Fields - `name` (string, required) - The name of the event table. - `cluster_by` (list) - The expressions to cluster data by. - `data_retention_time_in_days` (int) - The number of days to retain data. - `max_data_extension_time_in_days` (int) - The maximum number of days to extend data retention. - `change_tracking` (bool) - Specifies whether change tracking is enabled. Defaults to False. - `default_ddl_collation` (string) - The default collation for DDL operations. - `copy_grants` (bool) - Specifies whether to copy grants. Defaults to False. - `comment` (string) - A comment for the event table. - `tags` (dict) - Tags associated with the event table. # ExternalAccessIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-external-access-integration) | Snowcap CLI label: `external_access_integration` External Access Integrations enable code within functions and stored procedures to utilize secrets and establish connections with external networks. This resource configures the rules and secrets that can be accessed by such code. ## Examples ### YAML ``` external_access_integrations: - name: some_external_access_integration allowed_network_rules: - rule1 - rule2 enabled: true ``` ### Python ``` external_access_integration = ExternalAccessIntegration( name="some_external_access_integration", allowed_network_rules=["rule1", "rule2"], enabled=True ) ``` ## Fields - `name` (string, required) - The name of the external access integration. - `allowed_network_rules` (list, required) - [NetworkRules](https://snowcap.datacoves.com/resources/network_rule/index.md) that are allowed for this integration. - `allowed_api_authentication_integrations` (list) - API authentication integrations that are allowed. - `allowed_authentication_secrets` (list) - Authentication secrets that are allowed. - `enabled` (bool) - Specifies if the integration is enabled. Defaults to True. - `comment` (string) - An optional comment about the integration. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the external access integration. Defaults to "ACCOUNTADMIN". # ExternalStage [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-stage) | Snowcap CLI label: `external_stage` Manages external stages in Snowflake, which are used to reference external storage locations. ## Examples ### YAML ``` stages: - name: some_external_stage type: external url: https://example.com/storage owner: SYSADMIN storage_integration: some_integration ``` ### Python ``` external_stage = ExternalStage( name="some_external_stage", url="https://example.com/storage", owner="SYSADMIN", storage_integration="some_integration" ) ``` ## Fields - `name` (string, required) - The name of the external stage. - `url` (string, required) - The URL pointing to the external storage location. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the external stage. Defaults to "SYSADMIN". - `storage_integration` (string) - The name of the storage integration used with this stage. - `credentials` (dict) - The credentials for accessing the external storage, if required. - `encryption` (dict) - The encryption settings used for data stored in the external location. - `directory` (dict) - Settings related to directory handling in the external storage. - `tags` (dict) - Tags associated with the external stage. - `comment` (string) - A comment about the external stage. # FailoverGroup [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-failover-group) | Snowcap CLI label: `failover_group` Represents a failover group in Snowflake, which is a collection of databases, shares, and other resources that can be failed over together to a secondary account in case of a disaster recovery scenario. ## Examples ### YAML ``` failover_groups: - name: some_failover_group object_types: - DATABASES - ROLES allowed_accounts: - org1.account1 - org2.account2 allowed_databases: - db1 - db2 allowed_shares: - share1 - share2 allowed_integration_types: - SECURITY INTEGRATIONS - API INTEGRATIONS ignore_edition_check: true replication_schedule: "USING CRON 0 0 * * * UTC" owner: ACCOUNTADMIN ``` ### Python ``` failover_group = FailoverGroup( name="some_failover_group", object_types=["DATABASES", "ROLES"], allowed_accounts=["org1.account1", "org2.account2"], allowed_databases=["db1", "db2"], allowed_shares=["share1", "share2"], allowed_integration_types=["SECURITY INTEGRATIONS", "API INTEGRATIONS"], ignore_edition_check=True, replication_schedule="USING CRON 0 0 * * * UTC", owner="ACCOUNTADMIN" ) ``` ## Fields - `name` (string, required) - The name of the failover group. - `object_types` (list) - The types of objects included in the failover group. Can include string or ObjectType. - `allowed_accounts` (list, required) - The accounts that are allowed to be part of the failover group. - `allowed_databases` (list) - The databases that are allowed to be part of the failover group. - `allowed_shares` (list) - The shares that are allowed to be part of the failover group. - `allowed_integration_types` (list) - The integration types that are allowed in the failover group. Can include string or IntegrationTypes. - `ignore_edition_check` (bool) - Specifies whether to ignore the edition check. Defaults to None. - `replication_schedule` (string) - The schedule for replication. Defaults to None. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the failover group. Defaults to "ACCOUNTADMIN". **Note:** Requires ACCOUNTADMIN role. This privilege cannot be granted to other roles. See [Snowflake Permissions](https://snowcap.datacoves.com/snowflake-permissions/#resources-that-require-accountadmin) for details. # GCSStorageIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-storage-integration) | Snowcap CLI label: `gcs_storage_integration` Manages the integration of Google Cloud Storage (GCS) as an external stage for storing data. ## Examples ### YAML ``` gcs_storage_integrations: - name: some_gcs_storage_integration enabled: true storage_allowed_locations: - 'gcs://bucket/path/' storage_blocked_locations: - 'gcs://bucket/blocked_path/' ``` ### Python ``` gcs_storage_integration = GCSStorageIntegration( name="some_gcs_storage_integration", enabled=True, storage_allowed_locations=['gcs://bucket/path/'], storage_blocked_locations=['gcs://bucket/blocked_path/'] ) ``` ## Fields - `name` (string, required) - The name of the storage integration. - `enabled` (bool, required) - Specifies whether the storage integration is enabled. - `storage_allowed_locations` (list) - A list of allowed GCS locations for data storage. - `storage_blocked_locations` (list) - A list of blocked GCS locations for data storage. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the storage integration. Defaults to 'ACCOUNTADMIN'. - `comment` (string) - An optional comment about the storage integration. # GenericSecret [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret) | Snowcap CLI label: `generic_secret` A Secret defines a set of sensitive data that can be used for authentication or other purposes. This class defines a generic secret. ## Examples ### YAML For sensitive values, use environment variables to avoid storing secrets in your repository. Set environment variables prefixed with `SNOWCAP_VAR_` and reference them using `{{ var.variable_name }}` syntax. ``` # Set in your environment or .env file (loaded before running snowcap) export SNOWCAP_VAR_API_KEY="your-secret-api-key" ``` ``` secrets: - name: some_secret secret_type: GENERIC_STRING secret_string: "{{ var.api_key }}" comment: API key for external service owner: SYSADMIN ``` ### Python ``` import os secret = GenericSecret( name="some_secret", secret_string=os.environ.get("API_KEY"), comment="API key for external service", owner="SYSADMIN", ) ``` ## Fields - `name` (string, required) - The name of the secret. - `secret_string` (string) - The secret string. - `comment` (string) - A comment for the secret. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the secret. Defaults to SYSADMIN. # GitRepository [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-git-repository) | Snowcap CLI label: `git_repository` A Git Repository in Snowflake represents an externally hosted Git repository (GitHub, GitLab, Bitbucket, etc.) that has been registered for use with Snowflake's Git integration. Once registered, files in the repository can be referenced via stage syntax in `COPY`, `EXECUTE IMMEDIATE`, and other commands. A git repository depends on an [APIIntegration](https://snowcap.datacoves.com/resources/api_integration/index.md) whose `api_allowed_prefixes` covers the repository's `origin` URL, and optionally on a [Secret](https://snowcap.datacoves.com/resources/generic_secret/index.md) (for private repos) referenced via `git_credentials`. ## Examples ### YAML ``` git_repositories: - name: some_git_repository database: some_db schema: some_schema origin: https://github.com/some-org/some-repo.git api_integration: some_api_integration git_credentials: some_secret comment: Example git repository ``` ### Python ``` git_repository = GitRepository( name="some_git_repository", database="some_db", schema="some_schema", origin="https://github.com/some-org/some-repo.git", api_integration="some_api_integration", git_credentials="some_secret", comment="Example git repository", ) ``` ## Fields - `name` (string, required) - The name of the git repository. - `origin` (string, required) - The URL of the externally hosted Git repository (e.g., `https://github.com/some-org/some-repo.git`). - `api_integration` (string, required) - The name of the API integration object Snowflake will use to interact with the repository. The API integration's `api_allowed_prefixes` must include the `origin` URL. - `git_credentials` (string) - The name of a [Secret](https://snowcap.datacoves.com/resources/generic_secret/index.md) holding credentials for accessing a private repository. Optional for public repos. - `comment` (string) - A comment for the git repository. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the git repository. Defaults to `SYSADMIN`. ## Grants Snowcap supports `READ`, `WRITE`, and `OWNERSHIP` privileges on git repositories: ``` grants: - priv: READ on: git repository some_db.some_schema.some_git_repository to: some_role ``` # GlueCatalogIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-catalog-integration) | Snowcap CLI label: `glue_catalog_integration` Manages the integration of AWS Glue as a catalog in Snowflake, supporting the ICEBERG table format. ## Examples ### YAML ``` catalog_integrations: - name: some_catalog_integration table_format: ICEBERG glue_aws_role_arn: arn:aws:iam::123456789012:role/SnowflakeAccess glue_catalog_id: some_glue_catalog_id catalog_namespace: some_namespace enabled: true glue_region: us-west-2 comment: Integration for AWS Glue with Snowflake. ``` ### Python ``` glue_catalog_integration = GlueCatalogIntegration( name="some_catalog_integration", table_format="ICEBERG", glue_aws_role_arn="arn:aws:iam::123456789012:role/SnowflakeAccess", glue_catalog_id="some_glue_catalog_id", catalog_namespace="some_namespace", enabled=True, glue_region="us-west-2", comment="Integration for AWS Glue with Snowflake." ) ``` ## Fields - `name` (string, required) - The name of the catalog integration. - `table_format` (string or CatalogTableFormat, required) - The format of the table, defaults to ICEBERG. - `glue_aws_role_arn` (string, required) - The ARN for the AWS role to assume. - `glue_catalog_id` (string, required) - The Glue catalog ID. - `catalog_namespace` (string, required) - The namespace of the catalog. - `enabled` (bool, required) - Specifies whether the catalog integration is enabled. - `glue_region` (string) - The AWS region of the Glue catalog. Defaults to None. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the catalog integration. Defaults to "ACCOUNTADMIN". - `comment` (string) - An optional comment describing the catalog integration. # Grant [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/grant-privilege) | Snowcap CLI label: `grant` The `Grant` resource represents a privilege grant, a future grant, an inherited grant, or a grant of privileges on all resources of a specified type to a role in Snowflake. ## Examples ### YAML #### Object Grants ``` grants: # Global privileges - priv: CREATE WAREHOUSE on: ACCOUNT to: somerole # Single privilege on a table - priv: SELECT on: table some_table to: some_role # Multiple privileges on a table - priv: - SELECT - INSERT on: table some_table to: some_role grant_option: true # Schema privileges - priv: USAGE on: schema somedb.someschema to: some_role # Warehouse privileges - priv: USAGE on: warehouse some_warehouse to: some_role # Apps: USAGE on a Streamlit app so a role can open and run it - priv: USAGE on: streamlit somedb.someschema.some_streamlit to: app_viewer_role # AI: account-level privilege for Cortex AI SQL functions - priv: USE AI FUNCTIONS on: ACCOUNT to: cortex_user_role # AI: USAGE on a Cortex Search Service to call SNOWFLAKE.CORTEX.SEARCH_PREVIEW - priv: USAGE on: cortex search service somedb.someschema.someservice to: search_consumer_role # AI: MONITOR on a Cortex Search Service to query observability logs - priv: MONITOR on: cortex search service somedb.someschema.someservice to: search_observability_role # AI: schema-scope privilege to allow a role to create semantic views - priv: CREATE SEMANTIC VIEW on: schema somedb.someschema to: semantic_view_author_role # AI: SELECT on a Semantic View to query it via Cortex Analyst - priv: SELECT on: semantic view somedb.someschema.somesv to: semantic_view_consumer_role # Data Engineering: USAGE on a dbt project object to EXECUTE DBT PROJECT - priv: USAGE on: dbt project somedb.someschema.analytics_dbt to: transformer_role # Data Engineering: MONITOR on a dbt project for Snowsight run history - priv: MONITOR on: dbt project somedb.someschema.analytics_dbt to: analytics_observer # AI: USAGE on an MCP Server so MCP clients can call its tools. # Snowflake reports grants on these with granted_on 'CORTEX_AGENT_SERVER', # which Snowcap accepts as a synonym; the DDL grammar only takes MCP SERVER. - priv: USAGE on: mcp server somedb.someschema.someserver to: mcp_client_role # IMPORTED PRIVILEGES on a shared database (see SharedDatabase) - priv: IMPORTED PRIVILEGES on_database: gong to: gong_r # SPCS: USAGE on a compute pool - priv: usage on_compute_pool: ds_gpu_pool to_role: data_engineer # SPCS: READ on an image repository - priv: read on_image_repository: sandbox.spcs.vllm_repo to_role: data_engineer # SPCS: MONITOR on a service - priv: monitor on_service: sandbox.spcs.lora_service to_role: data_engineer ``` #### Future Grants ``` grants: - priv: - SELECT - INSERT on: future tables in schema someschema to: somerole # Multiple future grants - priv: SELECT on: - future tables in schema someschema - future views in schema someschema to: somerole # AI: future semantic views in schema - priv: SELECT on: future semantic views in schema somedb.someschema to: somerole ``` #### Grants on All Resources ``` grants: - priv: - SELECT - INSERT on: all tables in schema someschema to: somerole # Multiple "all" grants - priv: SELECT on: - all tables in schema someschema - all views in schema someschema to: somerole # AI: all semantic views in schema - priv: SELECT on: all semantic views in schema somedb.someschema to: somerole ``` #### Inherited Grants An inherited grant is a single grant on a container that covers every current **and future** object of a type inside it, replacing an `all` + `future` pair. ``` grants: - priv: SELECT on: inherited tables in schema somedb.someschema to: somerole # Multiple privileges expand to one statement each - priv: - SELECT - INSERT on: inherited tables in database somedb to: somerole # The account can only be the container of an inherited grant - priv: SELECT on: inherited tables in account to: somerole # Or turn a grant on all objects into an inherited one - priv: SELECT on: all tables in database somedb inherited: true to: somerole # Delegate to a role holding MANAGE GRANTS on the container - priv: SELECT on: inherited tables in database sales_db to: analyst owner: sales_db_admin ``` ### Python #### Object Grants ``` # Global Privileges: grant = Grant(priv="CREATE WAREHOUSE", on="ACCOUNT", to="somerole") # Warehouse Privileges: grant = Grant(priv="OPERATE", on=Warehouse(name="foo"), to="somerole") grant = Grant(priv="OPERATE", on_warehouse="foo", to="somerole") # Schema Privileges: grant = Grant(priv="CREATE TABLE", on=Schema(name="foo"), to="somerole") grant = Grant(priv="CREATE TABLE", on_schema="foo", to="somerole") # Table Privileges: grant = Grant(priv=["SELECT", "INSERT", "DELETE"], on_table="sometable", to="somerole") # MCP Server Privileges: grant = Grant(priv="USAGE", on_mcp_server="someserver", to="mcp_client_role") # IMPORTED PRIVILEGES on a shared database (see SharedDatabase): grant = Grant(priv="IMPORTED PRIVILEGES", on_database="gong", to="gong_r") # Snowpark Container Services (SPCS) Privileges: grant = Grant(priv="USAGE", on_compute_pool="ds_gpu_pool", to="data_engineer") grant = Grant(priv="READ", on_image_repository="sandbox.spcs.vllm_repo", to="data_engineer") grant = Grant(priv="MONITOR", on_service="sandbox.spcs.lora_service", to="data_engineer") ``` #### Future Grants ``` # Database Object Privileges: future_grant = Grant( priv="CREATE TABLE", on=["FUTURE", "SCHEMAS", Database(name="somedb")], to="somerole", ) future_grant = Grant( priv="CREATE TABLE", on="future schemas in database somedb", to="somerole", ) # Schema Object Privileges: future_grant = Grant( priv=["SELECT", "INSERT"], on=["future", "tables", "in", Schema(name="someschema")], to="somerole", ) future_grant = Grant( priv="READ", on="future image repositories in schema someschema", to="somerole", ) ``` #### Grants on All Resources ``` # Schema Privileges: grant_on_all = Grant( priv="CREATE TABLE", on="all schemas in database somedb", to="somerole", ) grant_on_all = Grant( priv="CREATE VIEW", on=["all", "schemas", Database(name="somedb")], to="somerole", ) # Schema Object Privileges: grant_on_all = Grant( priv=["SELECT", "INSERT"], on="all tables in schema someschema", to="somerole", ) grant_on_all = Grant( priv="SELECT", on="ALL VIEWS IN DATABASE SOMEDB", to="somerole", ) ``` #### Inherited Grants ``` inherited_grant = Grant( priv="SELECT", on="INHERITED TABLES IN SCHEMA somedb.someschema", to="somerole", ) inherited_grant = Grant( priv="SELECT", on=["INHERITED", "TABLES", Database(name="somedb")], to="somerole", ) # The account can only be the container of an inherited grant inherited_grant = Grant(priv="SELECT", on="INHERITED TABLES IN ACCOUNT", to="somerole") # Or turn a grant on all objects into an inherited one inherited_grant = Grant( priv="SELECT", on="ALL TABLES IN DATABASE somedb", inherited=True, to="somerole", ) ``` ## Fields - **`priv`** (`string` or `list`, required):\ The privilege(s) to grant. Examples include `"SELECT"`, `"INSERT"`, `"CREATE TABLE"`. - **`on`** (`string` or Resource, required): The resource on which the privilege is granted. Examples: - `"ACCOUNT"` - for account-level privileges - `"table my_table"` - for table privileges - `"schema my_db.my_schema"` - for schema privileges - `"warehouse my_wh"` - for warehouse privileges - `"database my_db"` - for database privileges - `"semantic view my_db.my_schema.my_sv"` - for semantic view privileges - `"compute pool my_pool"` - for compute pool privileges - `"image repository my_db.my_schema.my_repo"` - for image repository privileges - `"service my_db.my_schema.my_service"` - for service privileges - `"future tables in schema my_schema"` - for future grants - `"all tables in database my_db"` - for grants on all existing objects - `"inherited tables in database my_db"` - for inherited grants, covering existing and future objects - `"inherited tables in account"` - inherited grants are the only kind that can be scoped to the account - **`to`** (`string` or [Role](https://snowcap.datacoves.com/resources/role/index.md), required):\ The role to which the privileges are granted. - **`grant_option`** (`bool`, optional):\ Specifies whether the grantee can grant the privileges to other roles. Defaults to `false`. - **`owner`** (`string` or [Role](https://snowcap.datacoves.com/resources/role/index.md), optional):\ The owner role of the grant. Defaults to `"SYSADMIN"`. Grants are issued as `SECURITYADMIN`; for inherited grants, an explicit owner names the role holding `MANAGE GRANTS` on the container and is used to issue the grant instead. - **`inherited`** (`bool`, optional):\ Turns a grant on all objects in a container into an inherited grant, which also covers objects created later. Defaults to `false`. **Note:** Inherited grants are a Snowflake preview feature, opted into with an account parameter. Snowcap manages it with an [AccountParameter](https://snowcap.datacoves.com/resources/account_parameter/index.md), applied before any inherited grant that depends on it: ``` account_parameters: - name: FEATURE_RBAC_INHERITED_GRANTS value: ENABLED ``` `snowcap plan` fails with a clear message if neither the account nor the config has opted in. Snowflake does not allow inherited grants to be combined with `WITH GRANT OPTION`, to carry `OWNERSHIP`, or to target shares and integrations; `priv: ALL` is not supported either, so list privileges explicitly. See [Managing access with inherited grants](https://docs.snowflake.com/en/user-guide/inherited-grants-intro). **Note:** `IMPORTED PRIVILEGES` is only valid on a [SharedDatabase](https://snowcap.datacoves.com/resources/shared_database/index.md) (a database created `FROM SHARE`). It cannot be granted `WITH GRANT OPTION` and can only be granted to account roles, not database roles. Snowflake's `SHOW GRANTS` reports it as `USAGE` on shared databases — snowcap's fetch logic handles this quirk transparently. One `IMPORTED PRIVILEGES` grant also fans out in `SHOW GRANTS` into a row per object the share exposes — every view, function, procedure, schema, database role, class, tag and image repository in the database, which on the `SNOWFLAKE` database is several hundred rows. Those rows are never in your config, so `--sync_resources grant` treats them as covered by the declared grant rather than revoking them, the same way it treats the per-object grants produced by an `ALL` or `INHERITED` grant. # HybridTable [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-hybrid-table) | Snowcap CLI label: `hybrid_table` A hybrid table is a Snowflake table type that is optimized for hybrid transactional and operational workloads that require low latency and high throughput on small random point reads and writes. ## Examples ### YAML ``` hybrid_tables: - name: some_hybrid_table columns: - name: id data_type: INT constraint: PRIMARY KEY - name: name data_type: VARCHAR(100) - name: status data_type: VARCHAR(20) indexes: - name: idx_name columns: - name - name: idx_status columns: - status include: - created_at cluster_by: - id owner: SYSADMIN comment: This is a hybrid table. ``` ### Python ``` hybrid_table = HybridTable( name="some_hybrid_table", columns=[ Column(name="id", data_type="INT", constraint="PRIMARY KEY"), Column(name="name", data_type="VARCHAR(100)"), Column(name="status", data_type="VARCHAR(20)"), ], indexes=[ {"name": "idx_name", "columns": ["name"]}, {"name": "idx_status", "columns": ["status"], "include": ["created_at"]} ], cluster_by=["id"], owner="SYSADMIN", comment="This is a hybrid table." ) ``` ## Fields - `name` (string, required) - The name of the hybrid table. - `columns` (list, required) - The columns of the hybrid table. - `constraints` (list) - Table-level constraints (PRIMARY KEY, FOREIGN KEY). - `indexes` (list) - Index definitions. Each index is a dict with `name`, `columns`, and optional `include`. - `cluster_by` (list) - Clustering keys for the hybrid table. - `tags` (dict) - Tags associated with the hybrid table. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the hybrid table. Defaults to "SYSADMIN". - `comment` (string) - A comment for the hybrid table. # IcebergRestCatalogIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-catalog-integration-rest) | Snowcap CLI label: `iceberg_rest_catalog_integration` Manages an Apache Iceberg REST catalog integration in Snowflake. This is the right choice for AWS S3 Tables (federated catalogs reachable via the Glue Iceberg REST endpoint) and any other Iceberg REST-compatible catalog. ## Examples ### YAML ``` catalog_integrations: - name: ci_s3_tables_dev catalog_source: ICEBERG_REST catalog_namespace: my_namespace rest_config: catalog_uri: https://glue.us-east-1.amazonaws.com/iceberg catalog_api_type: AWS_GLUE catalog_name: '123456789012:s3tablescatalog/my_table_bucket' access_delegation_mode: VENDED_CREDENTIALS rest_authentication: type: SIGV4 sigv4_iam_role: arn:aws:iam::123456789012:role/snowflake-s3-tables-read sigv4_signing_region: us-east-1 enabled: true ``` ### Python ``` catalog = IcebergRestCatalogIntegration( name="ci_s3_tables_dev", catalog_namespace="my_namespace", rest_config={ "catalog_uri": "https://glue.us-east-1.amazonaws.com/iceberg", "catalog_api_type": "AWS_GLUE", "catalog_name": "123456789012:s3tablescatalog/my_table_bucket", "access_delegation_mode": "VENDED_CREDENTIALS", }, rest_authentication={ "type": "SIGV4", "sigv4_iam_role": "arn:aws:iam::123456789012:role/snowflake-s3-tables-read", "sigv4_signing_region": "us-east-1", }, enabled=True, ) ``` ### Minimal end-to-end example: AWS S3 Tables behind Lake Formation This is the configuration we run in production at Building Controls & Solutions for our Epicor P21 → S3 Tables → Snowflake pipeline. It documents the four AWS-side pieces a Snowflake reader needs *outside* Snowcap, and the two Snowcap resources that bind them to Snowflake. **The pieces (AWS side, configured once per environment):** 1. **S3 Tables bucket** — e.g., `bcs-iceberg-raw-prd`. Created via `aws s3tables create-table-bucket`. 1. **Lake Formation S3 Tables integration** — enabling it auto-creates a *federated* Glue catalog named `s3tablescatalog/` under the bucket-owner account. This is what Snowflake's REST catalog will talk to over HTTPS. 1. **Lake Formation grants** — the IAM role below needs at minimum `DESCRIBE` on the federated catalog and `SELECT` (and `DESCRIBE`) on every namespace/table you want Snowflake to read. 1. **Cross-account IAM role** (e.g., `snowflake-s3-tables-read`) — Snowflake's account assumes this via SIGV4. It needs `glue:GetCatalog`, `glue:GetDatabase*`, `glue:GetTable*`, `lakeformation:GetDataAccess`, and `s3tables:Get*`/`s3tables:List*` on the bucket, with a trust policy that lets the Snowflake account assume it (use the `STORAGE_AWS_EXTERNAL_ID` Snowflake gives you after `CREATE STORAGE INTEGRATION`). **Snowcap side (declarative):** ``` # catalog_integrations: tells Snowflake where Iceberg *metadata* lives. # CATALOG_NAME is the federated `:s3tablescatalog/` form # that Lake Formation auto-creates — note this DOES NOT work with the # legacy GlueCatalogIntegration / CATALOG_SOURCE=GLUE path; you must use # ICEBERG_REST + CATALOG_API_TYPE=AWS_GLUE for S3 Tables. catalog_integrations: - name: ci_p21_iceberg_prd catalog_source: ICEBERG_REST table_format: ICEBERG catalog_namespace: p21 rest_config: catalog_uri: https://glue.us-east-1.amazonaws.com/iceberg catalog_api_type: AWS_GLUE catalog_name: '123456789012:s3tablescatalog/bcs-iceberg-raw-prd' access_delegation_mode: VENDED_CREDENTIALS rest_authentication: type: SIGV4 sigv4_iam_role: arn:aws:iam::123456789012:role/snowflake-s3-tables-read sigv4_signing_region: us-east-1 enabled: true comment: 'P21 raw Iceberg tables (PRD) - S3 Tables federated catalog via ICEBERG_REST.' # storage_integrations: tells Snowflake where the Iceberg *data files* live. # Bucket-level allow so any namespace (p21, spire, future sources) under # the same bucket is reachable without per-namespace edits. The catalog # integration's CATALOG_NAMESPACE controls which tables are actually exposed. storage_integrations: - name: si_p21_raw_prd storage_provider: S3 enabled: true storage_aws_role_arn: arn:aws:iam::123456789012:role/snowflake-s3-tables-read storage_allowed_locations: - 's3://bcs-iceberg-raw-prd/' storage_aws_object_acl: bucket-owner-full-control comment: 'Snowflake read access to PRD raw Iceberg bucket (all namespaces).' ``` **Using it from Snowflake** (post-deploy, in a SQL worksheet — these statements are not managed by Snowcap): ``` CREATE OR REPLACE ICEBERG TABLE raw_prd.p21.oe_hdr CATALOG = 'CI_P21_ICEBERG_PRD' -- catalog_integration name, uppercased EXTERNAL_VOLUME = 'SI_P21_RAW_PRD' -- storage_integration name, uppercased CATALOG_TABLE_NAME = 'oe_hdr'; -- table inside namespace `p21` ``` **Verifying the integration is reachable** before pointing tables at it: ``` DESC CATALOG INTEGRATION ci_p21_iceberg_prd; SELECT SYSTEM$VERIFY_CATALOG_INTEGRATION('CI_P21_ICEBERG_PRD'); ``` **Gotchas we hit during onboarding:** - `CATALOG_SOURCE = GLUE` (the legacy `GlueCatalogIntegration` path) rejects the federated `:s3tablescatalog/` form for `GLUE_CATALOG_ID` with SQL compilation error 22023/1008. S3 Tables *must* go through `ICEBERG_REST` with `CATALOG_API_TYPE = AWS_GLUE` — that's why this resource exists. - Lake Formation grants are easy to forget. Without `DESCRIBE` on the federated catalog and `SELECT` on the namespace, `DESC CATALOG INTEGRATION` succeeds but `CREATE ICEBERG TABLE ... FROM CATALOG` fails with a vague 403. - `access_delegation_mode: VENDED_CREDENTIALS` is required if the writer (e.g., a `pyiceberg-rest` loader on EC2) relies on Lake Formation to vend temporary S3 credentials; without it the catalog returns data-file URIs the SIGV4 role can't read. - The Glue Iceberg REST endpoint is regional — match `sigv4_signing_region` to the S3 Tables bucket region. ## Fields - `name` (string, required) - The name of the catalog integration. - `rest_config` (dict, required) - Iceberg REST configuration. Required key: `catalog_uri`. Optional keys: `catalog_api_type`, `catalog_name`, `warehouse`, `prefix`, `access_delegation_mode`. - `rest_authentication` (dict, required) - Authentication block. Required key: `type` (one of `SIGV4`, `OAUTH`, `BEARER`, `NONE`). Auth-specific fields: `sigv4_iam_role`, `sigv4_signing_region`, `sigv4_external_id` (SIGV4); `oauth_client_id`, `oauth_client_secret`, `oauth_token_uri`, `oauth_allowed_scopes` (OAUTH); `bearer_token` (BEARER). - `catalog_namespace` (string) - Default namespace for tables referencing this catalog. - `enabled` (bool) - Whether the integration is enabled. Defaults to True. - `refresh_interval_seconds` (int) - Optional metadata refresh interval. - `table_format` (string or CatalogTableFormat) - Table format. Only `ICEBERG` is supported. Defaults to `ICEBERG`. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the catalog integration. Defaults to "ACCOUNTADMIN". - `comment` (string) - An optional comment describing the catalog integration. # ImageRepository [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-image-repository) | Snowcap CLI label: `image_repository` An image repository in Snowflake is a storage unit within a schema that allows for the management of OCIv2-compliant container images. ## Examples ### YAML ``` image_repositories: - name: some_image_repository ``` ### Python ``` image_repository = ImageRepository( name="some_image_repository", ) ``` ## Fields - `name` (string, required) - The unique identifier for the image repository within the schema. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the image repository. Defaults to "SYSADMIN". # InternalStage [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-stage) | Snowcap CLI label: `internal_stage` Represents an internal stage in Snowflake, which is a named location used to store data files that will be loaded into or unloaded from Snowflake tables. ## Examples ### YAML ``` stages: - name: some_internal_stage type: internal owner: SYSADMIN encryption: type: SNOWFLAKE_SSE directory: enable: true tags: department: finance comment: Data loading stage ``` ### Python ``` internal_stage = InternalStage( name="some_internal_stage", owner="SYSADMIN", encryption={"type": "SNOWFLAKE_SSE"}, directory={"enable": True}, tags={"department": "finance"}, comment="Data loading stage" ) ``` ## Fields - `name` (string, required) - The name of the internal stage. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the internal stage. Defaults to "SYSADMIN". - `encryption` (dict) - A dictionary specifying encryption settings. - `directory` (dict) - A dictionary specifying directory usage settings. - `tags` (dict) - A dictionary of tags associated with the internal stage. - `comment` (string) - A comment for the internal stage. # JavascriptUDF [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-function) | Snowcap CLI label: `javascript_udf` A Javascript user-defined function (UDF) in Snowflake allows you to define a function using the JavaScript programming language. ## Examples ### YAML ``` functions: - name: some_function language: javascript returns: STRING as_: function(x) { return x.toUpperCase(); } args: - name: x data_type: STRING comment: Converts a string to uppercase ``` ### Python ``` js_udf = JavascriptUDF( name="some_function", returns="STRING", as_="function(x) { return x.toUpperCase(); }", args=[{"name": "x", "data_type": "STRING"}], comment="Converts a string to uppercase", ) ``` ## Fields - `name` (string, required) - The name of the function. - `returns` (string or DataType, required) - The data type of the function's return value. - `as_` (string, required) - The JavaScript code to execute when the function is called. - `args` (list) - The arguments that the function takes. - `comment` (string) - A comment for the function. - `copy_grants` (bool) - Specifies whether to retain the access privileges from the original function when a new function is created using CREATE OR REPLACE FUNCTION. Defaults to False. - `external_access_integrations` (list) - External integrations accessible by the function. - `handler` (string) - The entry point for the function within the JavaScript code. - `imports` (list) - The list of JavaScript files to import. - `null_handling` (string or NullHandling) - How the function handles NULL input. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the function. Defaults to "SYSADMIN". - `packages` (list) - The list of npm packages that the function depends on. - `runtime_version` (string) - The JavaScript runtime version to use. - `secrets` (dict of string to string) - Key-value pairs of secrets available to the function. - `secure` (bool) - Specifies whether the function is secure. Defaults to False. - `volatility` (string or Volatility) - The volatility of the function. # JSONFileFormat [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-file-format) | Snowcap CLI label: `json_file_format` A JSON file format in Snowflake. ## Examples ### YAML ``` file_formats: - name: some_json_file_format type: JSON owner: SYSADMIN compression: AUTO date_format: AUTO time_format: AUTO timestamp_format: AUTO binary_format: HEX trim_space: false null_if: - NULL file_extension: json enable_octal: false allow_duplicate: false strip_outer_array: false strip_null_values: false replace_invalid_characters: false ignore_utf8_errors: false skip_byte_order_mark: true comment: This is a JSON file format. ``` ### Python ``` file_format = JSONFileFormat( name="some_json_file_format", owner="SYSADMIN", compression="AUTO", date_format="AUTO", time_format="AUTO", timestamp_format="AUTO", binary_format=BinaryFormat.HEX, trim_space=False, null_if=["NULL"], file_extension="json", enable_octal=False, allow_duplicate=False, strip_outer_array=False, strip_null_values=False, replace_invalid_characters=False, ignore_utf8_errors=False, skip_byte_order_mark=True, comment="This is a JSON file format." ) ``` ## Fields - `name` (string, required) - The name of the file format. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the file format. Defaults to "SYSADMIN". - `compression` (string) - The compression type for the file format. Defaults to "AUTO". - `date_format` (string) - The format used for date values. Defaults to "AUTO". - `time_format` (string) - The format used for time values. Defaults to "AUTO". - `timestamp_format` (string) - The format used for timestamp values. Defaults to "AUTO". - `binary_format` (BinaryFormat) - The format used for binary data. Defaults to HEX. - `trim_space` (bool) - Whether to trim spaces. Defaults to False. - `null_if` (list) - A list of strings to be interpreted as NULL. - `file_extension` (string) - The file extension used for files of this format. - `enable_octal` (bool) - Whether to enable octal values. Defaults to False. - `allow_duplicate` (bool) - Whether to allow duplicate keys. Defaults to False. - `strip_outer_array` (bool) - Whether to strip the outer array. Defaults to False. - `strip_null_values` (bool) - Whether to strip null values. Defaults to False. - `replace_invalid_characters` (bool) - Whether to replace invalid characters. Defaults to False. - `ignore_utf8_errors` (bool) - Whether to ignore UTF-8 errors. Defaults to False. - `skip_byte_order_mark` (bool) - Whether to skip the byte order mark. Defaults to True. - `comment` (string) - A comment for the file format. # MaskingPolicy [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-masking-policy) | Snowcap CLI label: `masking_policy` Represents a masking policy for column-level data protection in Snowflake. Masking policies define how data is transformed when accessed by users based on their roles or other conditions. ## Examples ### YAML ``` masking_policies: - name: governance.public.mask_pii_string args: - name: val data_type: VARCHAR returns: VARCHAR body: |- CASE WHEN IS_ROLE_IN_SESSION('Z_UNMASK__PII') THEN val ELSE '***MASKED***' END comment: Masks PII string data ``` ### Python ``` from snowcap.resources import MaskingPolicy policy = MaskingPolicy( name="governance.public.mask_pii_string", args=[{"name": "val", "data_type": "VARCHAR"}], returns="VARCHAR", body=""" CASE WHEN IS_ROLE_IN_SESSION('Z_UNMASK__PII') THEN val ELSE '***MASKED***' END """, comment="Masks PII string data", ) ``` ## Fields - `name` (string, required) - The fully qualified name of the masking policy (e.g., `db.schema.policy_name`). - `args` (list, required) - List of arguments for the policy. Each argument must have `name` and `data_type` fields. At least one argument is required. - `returns` (string, required) - The return data type of the masking policy. Must match the data type of the first argument. - `body` (string, required) - The SQL expression that defines the masking logic. Typically uses CASE expressions with role-based conditions. - `comment` (string) - A comment or description for the masking policy. - `exempt_other_policies` (bool) - Whether this policy exempts other policies from being applied. Defaults to False. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The role that owns the masking policy. Defaults to "SYSADMIN". **Note:** Masking policies require Enterprise Edition or higher. # MaterializedView [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-materialized-view) | Snowcap CLI label: `materialized_view` A Materialized View in Snowflake is a database object that contains the results of a query. It is physically stored and automatically updated as data changes, providing faster access to data. ## Examples ### YAML ``` materialized_views: - name: some_materialized_view owner: SYSADMIN secure: true as_: SELECT * FROM some_table ``` ### Python ``` materialized_view = MaterializedView( name="some_materialized_view", owner="SYSADMIN", secure=True, as_="SELECT * FROM some_table", ) ``` ## Fields - `name` (string, required) - The name of the materialized view. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the materialized view. Defaults to "SYSADMIN". - `secure` (bool) - Specifies if the materialized view is secure. Defaults to False. - `columns` (list) - A list of dictionaries specifying column definitions. - `tags` (dict) - Tags associated with the materialized view. - `copy_grants` (bool) - Specifies if grants should be copied from the source. Defaults to False. - `comment` (string) - A comment for the materialized view. - `cluster_by` (list) - A list of expressions defining the clustering of the materialized view. - `as_` (string, required) - The SELECT statement used to populate the materialized view. # MCPServer [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-mcp-server) | Snowcap CLI label: `mcp_server` Represents a Snowflake-managed MCP (Model Context Protocol) server, which exposes a set of tools, resources, and prompts to MCP clients over a Snowflake-hosted endpoint. MCP servers are a generally available Snowflake feature. Snowflake has no ALTER MCP SERVER command, so a change to specification is applied by dropping and recreating the server with CREATE OR REPLACE. This drops any grants on the MCP server; grants managed by snowcap are automatically re-created in the same apply, but externally-managed grants must be re-applied manually. The plan output warns when a specification change will trigger this. Renaming an MCP server is not supported; changing name creates a new resource instead of altering the existing one. The specification is canonicalized (re-serialized as sorted YAML) before it is stored or compared, so semantically identical specs written with different formatting, key order, tool order, or JSON vs. YAML syntax normalize to the same value. Tools are reordered by name. This uses YAML 1.1 scalar rules, so unquoted words like yes/no/on/off are read as booleans; quote them (e.g. "yes") if you intend a string. Unquoted date/timestamp-like scalars (e.g. 2024-01-01) are read as dates and re-serialized as plain strings, matching how the same value round-trips through DESC MCP SERVER's JSON. Comments in the input are not preserved by canonicalization. ## Examples ### Python ``` mcp_server = MCPServer( name="some_mcp_server", specification=""" tools: - name: query_data type: SYSTEM_EXECUTE_SQL """, owner="SYSADMIN", ) ``` ### YAML ``` mcp_servers: - name: some_mcp_server specification: | tools: - name: query_data type: SYSTEM_EXECUTE_SQL owner: SYSADMIN ``` ## Fields - `name` (string, required) - The name of the MCP server. - `specification` (string, required) - A YAML or JSON document describing the MCP server's tools, resources, and prompts. Canonicalized to sorted YAML with tools sorted by name; an absent top-level version key defaults to 1. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The role that owns the MCP server. Defaults to "SYSADMIN". # NetworkPolicy [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-network-policy) | Snowcap CLI label: `network_policy` A Network Policy in Snowflake defines a set of network rules and IP addresses that are allowed or blocked from accessing a Snowflake account. This helps in managing network traffic and securing access based on network policies. ## Examples ### YAML ``` network_policies: - name: some_network_policy allowed_network_rule_list: - rule1 - rule2 blocked_network_rule_list: - rule3 allowed_ip_list: ["192.168.1.1", "192.168.1.2"] blocked_ip_list: ["10.0.0.1"] comment: "Example network policy" ``` ### Python ``` network_policy = NetworkPolicy( name="some_network_policy", allowed_network_rule_list=[NetworkRule(name="rule1"), NetworkRule(name="rule2")], blocked_network_rule_list=[NetworkRule(name="rule3")], allowed_ip_list=["192.168.1.1", "192.168.1.2"], blocked_ip_list=["10.0.0.1"], comment="Example network policy" ) ``` ## Fields - `name` (string, required) - The name of the network policy. - `allowed_network_rule_list` (list) - A list of allowed network rules. - `blocked_network_rule_list` (list) - A list of blocked network rules. - `allowed_ip_list` (list) - A list of allowed IP addresses. - `blocked_ip_list` (list) - A list of blocked IP addresses. - `comment` (string) - A comment about the network policy. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the network policy. Defaults to "SECURITYADMIN". # NetworkRule [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-network-rule) | Snowcap CLI label: `network_rule` A Network Rule in Snowflake defines a set of network addresses, such as IP addresses or hostnames, that can be allowed or denied access to a Snowflake account. This helps in managing network traffic and securing access based on network policies. ## Examples ### YAML ``` network_rules: - name: some_network_rule type: IPV4 value_list: ["192.168.1.1", "192.168.1.2"] mode: INGRESS comment: "Example network rule" ``` ### Python ``` network_rule = NetworkRule( name="some_network_rule", type="IPV4", value_list=["192.168.1.1", "192.168.1.2"], mode="INGRESS", comment="Example network rule" ) ``` ## Fields - `name` (string, required) - The name of the network rule. - `type` (string or NetworkIdentifierType, required) - The type of network identifier. Defaults to IPV4. - `value_list` (list) - A list of values associated with the network rule. - `mode` (string or NetworkRuleMode) - The mode of the network rule. Defaults to INGRESS. - `comment` (string) - A comment about the network rule. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the network rule. Defaults to "SYSADMIN". # OAuthSecret [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret) | Snowcap CLI label: `oauth_secret` A Secret defines a set of sensitive data that can be used for authentication or other purposes. This class defines an OAuth secret. ## Examples ### YAML For sensitive values like tokens, use environment variables to avoid storing secrets in your repository. Set environment variables prefixed with `SNOWCAP_VAR_` and reference them using `{{ var.variable_name }}` syntax. ``` # Set in your environment or .env file (loaded before running snowcap) export SNOWCAP_VAR_OAUTH_REFRESH_TOKEN="your-secret-token" ``` ``` secrets: # OAuth with client credentials flow: - name: some_secret secret_type: OAUTH2 api_authentication: some_security_integration oauth_scopes: - scope1 - scope2 comment: OAuth secret for API access owner: SYSADMIN # OAuth with authorization code grant flow (using environment variable): - name: another_secret secret_type: OAUTH2 api_authentication: some_security_integration oauth_refresh_token: "{{ var.oauth_refresh_token }}" oauth_refresh_token_expiry_time: 2049-01-06 20:00:00 comment: OAuth secret with refresh token owner: SYSADMIN ``` ### Python ``` import os # OAuth with client credentials flow: secret = OAuthSecret( name="some_secret", api_authentication="some_security_integration", oauth_scopes=["scope1", "scope2"], comment="OAuth secret for API access", owner="SYSADMIN", ) # OAuth with authorization code grant flow (token from environment): secret = OAuthSecret( name="another_secret", api_authentication="some_security_integration", oauth_refresh_token=os.environ.get("OAUTH_REFRESH_TOKEN"), oauth_refresh_token_expiry_time="2049-01-06 20:00:00", comment="OAuth secret with refresh token", owner="SYSADMIN", ) ``` ## Fields - `name` (string, required) - The name of the secret. - `api_authentication` (string) - The security integration name for API authentication. - `oauth_scopes` (list) - The OAuth scopes for the secret. - `oauth_refresh_token` (string) - The OAuth refresh token. - `oauth_refresh_token_expiry_time` (string) - The expiry time of the OAuth refresh token. - `comment` (string) - A comment for the secret. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the secret. Defaults to SYSADMIN. # ObjectStoreCatalogIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-catalog-integration) | Snowcap CLI label: `object_store_catalog_integration` Manages the integration of an object store as a catalog in Snowflake, supporting the ICEBERG table format. ## Examples ### YAML ``` catalog_integrations: - name: some_catalog_integration table_format: ICEBERG enabled: true comment: Integration for object storage. ``` ### Python ``` object_store_catalog_integration = ObjectStoreCatalogIntegration( name="some_catalog_integration", table_format="ICEBERG", enabled=True, comment="Integration for object storage." ) ``` ## Fields - `name` (string, required) - The name of the catalog integration. - `table_format` (string or CatalogTableFormat, required) - The format of the table, defaults to ICEBERG. - `enabled` (bool) - Specifies whether the catalog integration is enabled. Defaults to True. - `comment` (string) - An optional comment describing the catalog integration. # PackagesPolicy [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-packages-policy) | Snowcap CLI label: `packages_policy` A Packages Policy defines a set of rules for allowed and blocked packages that are applied to user-defined functions and stored procedures. ## Examples ### YAML ``` packages_policy: - name: some_packages_policy allowlist: - numpy - pandas blocklist: - os - sys comment: Policy for data processing packages. ``` ### Python ``` packages_policy = PackagesPolicy( name="some_packages_policy", allowlist=["numpy", "pandas"], blocklist=["os", "sys"], comment="Policy for data processing packages." ) ``` ## Fields - `name` (string, required) - The name of the packages policy. - `language` (string or Language) - The programming language for the packages. Defaults to PYTHON. - `allowlist` (list) - A list of package specifications that are explicitly allowed. - `blocklist` (list) - A list of package specifications that are explicitly blocked. - `additional_creation_blocklist` (list) - A list of package specifications that are blocked during creation. - `comment` (string) - A comment or description for the packages policy. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the packages policy. Defaults to SYSADMIN. # ParquetFileFormat [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-file-format) | Snowcap CLI label: `parquet_file_format` A Parquet file format in Snowflake. ## Examples ### YAML ``` file_formats: - name: some_file_format type: PARQUET owner: SYSADMIN compression: AUTO binary_as_text: true trim_space: false replace_invalid_characters: false null_if: - NULL comment: This is a Parquet file format. ``` ### Python ``` file_format = ParquetFileFormat( name="some_file_format", owner="SYSADMIN", compression="AUTO", binary_as_text=True, trim_space=False, replace_invalid_characters=False, null_if=["NULL"], comment="This is a Parquet file format." ) ``` ## Fields - `name` (string, required) - The name of the file format. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the file format. Defaults to "SYSADMIN". - `compression` (string) - The compression type for the file format. Defaults to "AUTO". - `binary_as_text` (bool) - Whether to interpret binary data as text. Defaults to True. - `trim_space` (bool) - Whether to trim spaces. Defaults to False. - `replace_invalid_characters` (bool) - Whether to replace invalid characters. Defaults to False. - `null_if` (list) - A list of strings to be interpreted as NULL. - `comment` (string) - A comment for the file format. # PasswordPolicy [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-password-policy) | Snowcap CLI label: `password_policy` Defines the rules and constraints for creating and managing passwords within the system, ensuring they meet specific security standards. ## Examples ### YAML ``` password_policies: - name: some_password_policy password_min_length: 10 password_max_length: 128 password_min_upper_case_chars: 2 password_min_lower_case_chars: 2 password_min_numeric_chars: 2 password_min_special_chars: 1 password_min_age_days: 1 password_max_age_days: 60 password_max_retries: 3 password_lockout_time_mins: 30 password_history: 5 comment: Strict policy for admin accounts owner: SYSADMIN ``` ### Python ``` password_policy = PasswordPolicy( name="some_password_policy", password_min_length=10, password_max_length=128, password_min_upper_case_chars=2, password_min_lower_case_chars=2, password_min_numeric_chars=2, password_min_special_chars=1, password_min_age_days=1, password_max_age_days=60, password_max_retries=3, password_lockout_time_mins=30, password_history=5, comment="Strict policy for admin accounts.", owner="SYSADMIN" ) ``` ## Fields - `name` (string, required) - The name of the password policy. - `password_min_length` (int) - The minimum length of the password. Defaults to 8. - `password_max_length` (int) - The maximum length of the password. Defaults to 256. - `password_min_upper_case_chars` (int) - The minimum number of uppercase characters in the password. Defaults to 1. - `password_min_lower_case_chars` (int) - The minimum number of lowercase characters in the password. Defaults to 1. - `password_min_numeric_chars` (int) - The minimum number of numeric characters in the password. Defaults to 1. - `password_min_special_chars` (int) - The minimum number of special characters in the password. Defaults to 0. - `password_min_age_days` (int) - The minimum age of the password in days. Defaults to 0. - `password_max_age_days` (int) - The maximum age of the password in days. Defaults to 90. - `password_max_retries` (int) - The maximum number of login retries before the account is locked. Defaults to 5. - `password_lockout_time_mins` (int) - The time in minutes an account remains locked after exceeding retry limit. Defaults to 15. - `password_history` (int) - The number of unique new passwords before an old password can be reused. - `comment` (string) - A comment about the password policy. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the password policy. Defaults to "SYSADMIN". # PasswordSecret [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret) | Snowcap CLI label: `password_secret` A Secret defines a set of sensitive data that can be used for authentication or other purposes. This class defines a password secret. ## Examples ### YAML For sensitive values like passwords, use environment variables to avoid storing secrets in your repository. Set environment variables prefixed with `SNOWCAP_VAR_` and reference them using `{{ var.variable_name }}` syntax. ``` # Set in your environment or .env file (loaded before running snowcap) export SNOWCAP_VAR_DB_USERNAME="service_account" export SNOWCAP_VAR_DB_PASSWORD="your-secret-password" ``` ``` secrets: - name: some_secret secret_type: PASSWORD username: "{{ var.db_username }}" password: "{{ var.db_password }}" comment: Credentials for external database owner: SYSADMIN ``` ### Python ``` import os secret = PasswordSecret( name="some_secret", username=os.environ.get("DB_USERNAME"), password=os.environ.get("DB_PASSWORD"), comment="Credentials for external database", owner="SYSADMIN", ) ``` ## Fields - `name` (string, required) - The name of the secret. - `username` (string) - The username for the secret. - `password` (string) - The password for the secret. - `comment` (string) - A comment for the secret. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the secret. Defaults to SYSADMIN. # Pipe [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-pipe) | Snowcap CLI label: `pipe` Represents a data ingestion pipeline in Snowflake, which automates the loading of data into tables. ## Examples ### YAML ``` pipes: - name: some_pipe as_: COPY INTO some_table FROM @%some_stage owner: SYSADMIN auto_ingest: true error_integration: some_integration aws_sns_topic: some_topic integration: some_integration comment: This is a sample pipe ``` ### Python ``` pipe = Pipe( name="some_pipe", as_="COPY INTO some_table FROM @%some_stage", owner="SYSADMIN", auto_ingest=True, error_integration="some_integration", aws_sns_topic="some_topic", integration="some_integration", comment="This is a sample pipe" ) ``` ## Fields - `name` (string, required) - The name of the pipe. - `as_` (string, required) - The SQL statement that defines the data loading operation. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the pipe. Defaults to "SYSADMIN". - `auto_ingest` (bool) - Specifies if the pipe automatically ingests data when files are added to the stage. Defaults to None. - `error_integration` (string) - The name of the integration used for error notifications. Defaults to None. - `aws_sns_topic` (string) - The AWS SNS topic where notifications are sent. Defaults to None. - `integration` (string) - The integration used for data loading. Defaults to None. - `comment` (string) - A comment for the pipe. Defaults to None. # PythonStoredProcedure [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-procedure) | Snowcap CLI label: `python_stored_procedure` Represents a Python stored procedure in Snowflake, allowing for the execution of Python code within the Snowflake environment. ## Examples ### YAML ``` procedures: - name: some_procedure language: python args: [] returns: STRING runtime_version: "3.8" packages: - snowflake-snowpark-python handler: process_data as_: "def process_data(): return 'Hello, World!'" comment: A simple procedure copy_grants: false execute_as: CALLER null_handling: CALLED_ON_NULL_INPUT owner: SYSADMIN secure: false ``` ### Python ``` procedure = PythonStoredProcedure( name="some_procedure", args=[], returns="STRING", runtime_version="3.8", packages=["snowflake-snowpark-python"], handler="process_data", as_="def process_data(): return 'Hello, World!'", comment="A simple procedure", copy_grants=False, execute_as="CALLER", external_access_integrations=None, imports=None, null_handling="CALLED_ON_NULL_INPUT", owner="SYSADMIN", secure=False ) ``` ## Fields - `name` (str, required) - The name of the procedure. - `args` (list) - The arguments of the procedure. - `returns` (DataType) - The data type of the return value. - `runtime_version` (str, required) - The Python runtime version. - `packages` (list) - The list of packages required by the procedure. - `handler` (str, required) - The handler function for the procedure. - `as_` (str) - The procedure definition. - `comment` (str) - A comment about the procedure. Defaults to "user-defined procedure". - `copy_grants` (bool) - Whether to copy grants. Defaults to False. - `execute_as` (ExecutionRights) - The execution rights. Defaults to ExecutionRights.CALLER. - `external_access_integrations` (list) - External access integrations if any. - `imports` (list) - Files to import. - `null_handling` (NullHandling) - How nulls are handled. Defaults to NullHandling.CALLED_ON_NULL_INPUT. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the procedure. Defaults to "SYSADMIN". - `secure` (bool) - Whether the procedure is secure. Defaults to False. # PythonUDF [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-function) | Snowcap CLI label: `python_udf` A Python user-defined function (UDF) in Snowflake allows users to define their own custom functions in Python. These functions can be used to perform operations that are not available as standard SQL functions. ## Examples ### YAML ``` functions: - name: some_python_udf language: python returns: string runtime_version: "3.8" handler: process_data args: - name: input_data data_type: string as_: process_data_function comment: This function processes data. copy_grants: false external_access_integrations: - s3_integration imports: - pandas - numpy null_handling: CALLED_ON_NULL_INPUT owner: SYSADMIN packages: - pandas - numpy secrets: api_key: secret_value secure: false volatility: IMMUTABLE ``` ### Python ``` python_udf = PythonUDF( name="some_python_udf", returns="string", runtime_version="3.8", handler="process_data", args=[{"name": "input_data", "data_type": "string"}], as_="process_data_function", comment="This function processes data.", copy_grants=False, external_access_integrations=["s3_integration"], imports=["pandas", "numpy"], null_handling="CALLED_ON_NULL_INPUT", owner="SYSADMIN", packages=["pandas", "numpy"], secrets={"api_key": "secret_value"}, secure=False, volatility="IMMUTABLE" ) ``` ## Fields - `name` (string, required) - The name of the function. - `returns` (string, required) - The data type of the function's return value. - `runtime_version` (string, required) - The version of the Python runtime to use. - `handler` (string, required) - The name of the method to call in the Python script. - `args` (list, required) - A list of arguments that the function takes. - `as_` (string) - The Python code to execute when the function is called. - `comment` (string) - A comment for the function. - `copy_grants` (bool) - Whether to copy grants from the existing function. Defaults to False. - `external_access_integrations` (list) - List of external integrations accessible by the function. - `imports` (list) - List of modules to import in the function. - `null_handling` (NullHandling) - Specifies how NULL values are handled by the function. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the function. Defaults to "SYSADMIN". - `packages` (list) - List of Python packages that the function can use. - `secrets` (dict) - Secrets that can be accessed by the function. - `secure` (bool) - Whether the function is secure. Defaults to False. - `volatility` (string or Volatility) - The volatility of the function. # ReplicationGroup [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-replication-group) | Snowcap CLI label: `replication_group` A replication group in Snowflake. ## Examples ### YAML ``` replication_groups: - name: some_replication_group object_types: - DATABASES allowed_accounts: - account1 - account2 ``` ### Python ``` replication_group = ReplicationGroup( name="some_replication_group", object_types=["DATABASES"], allowed_accounts=["account1", "account2"], ) ``` ## Fields - `name` (string, required) - The name of the replication group. - `object_types` (list, required) - The object types to be replicated. - `allowed_accounts` (list, required) - The accounts allowed to replicate. - `allowed_databases` (list) - The databases allowed to replicate. - `allowed_shares` (list) - The shares allowed to replicate. - `allowed_integration_types` (list) - The integration types allowed to replicate. - `ignore_edition_check` (bool) - Whether to ignore the edition check. - `replication_schedule` (string) - The replication schedule. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the replication group. Defaults to "SYSADMIN". **Note:** Requires ACCOUNTADMIN role. This privilege cannot be granted to other roles. See [Snowflake Permissions](https://snowcap.datacoves.com/snowflake-permissions/#resources-that-require-accountadmin) for details. # ResourceMonitor [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-resource-monitor) | Snowcap CLI label: `resource_monitor` Manages the monitoring of resource usage within an account. ## Examples ### YAML ``` resource_monitors: - name: some_resource_monitor credit_quota: 1000 frequency: DAILY start_timestamp: "2049-01-01 00:00" end_timestamp: "2049-12-31 23:59" notify_users: - user1 - user2 ``` ### Python ``` resource_monitor = ResourceMonitor( name="some_resource_monitor", credit_quota=1000, frequency="DAILY", start_timestamp="2049-01-01 00:00", end_timestamp="2049-12-31 23:59", notify_users=["user1", "user2"] ) ``` ## Fields - `name` (string, required) - The name of the resource monitor. - `credit_quota` (int) - The amount of credits that can be used within the monitoring period. - `frequency` (string or ResourceMonitorFrequency) - The interval at which the credit usage resets. Valid values: `MONTHLY`, `DAILY`, `WEEKLY`, `YEARLY`, `NEVER`. Defaults to `MONTHLY` when `start_timestamp` is set. - `start_timestamp` (string) - The start time for the monitoring period (e.g., `"2024-01-01 00:00"`). - `end_timestamp` (string) - The end time for the monitoring period. - `notify_users` (list) - A list of user names to notify when thresholds are reached. **Note:** Resource monitors can only be owned by ACCOUNTADMIN. # Role [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-role) | Snowcap CLI label: `role` A role in Snowflake defines a set of access controls and permissions. ## Examples ### YAML ``` roles: - name: some_role owner: USERADMIN comment: This is a sample role. ``` ### Python ``` role = Role( name="some_role", owner="USERADMIN", comment="This is a sample role.", ) ``` ## Fields - `name` (string, required) - The name of the role. - `owner` (string) - The owner of the role. Defaults to "USERADMIN". - `tags` (dict) - Tags associated with the role. - `comment` (string) - A comment for the role. # RoleGrant [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/grant-role) | Snowcap CLI label: `role_grant` Represents a grant of a role to another role or user in Snowflake. ## Examples ### YAML ``` role_grants: # Grant multiple roles to a role - to_role: analyst roles: - z_db__raw - z_db__analytics - z_wh__transforming # Grant multiple roles to a user - to_user: jane_doe roles: - analyst - developer ``` ### Python ``` # Grant to Role: role_grant = RoleGrant(role="somerole", to_role="someotherrole") # Grant to User: role_grant = RoleGrant(role="somerole", to_user="someuser") ``` ## Fields - `role` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md), required) - The role to be granted. - `to_role` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The role to receive the grant. - `to_user` (string or [User](https://snowcap.datacoves.com/resources/user/index.md)) - The user to receive the grant. **Note:** You must specify either `to_role` or `to_user`, but not both. In YAML, you can also use the shorthand syntax with `roles` (list) to grant multiple roles at once. # RowAccessPolicy [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-row-access-policy) | Snowcap CLI label: `row_access_policy` Represents a row access policy for row-level security in Snowflake. Row access policies define which rows are visible to users based on conditions, typically checking role membership. ## Examples ### YAML ``` row_access_policies: - name: governance.policies.rap_sales_region args: - name: region data_type: VARCHAR body: |- CURRENT_ROLE() IN ('ADMIN', 'SALES_MANAGER') OR region = CURRENT_USER() comment: Users can only see rows for their assigned region ``` ### Python ``` from snowcap.resources import RowAccessPolicy policy = RowAccessPolicy( name="governance.policies.rap_sales_region", args=[{"name": "region", "data_type": "VARCHAR"}], body=""" CURRENT_ROLE() IN ('ADMIN', 'SALES_MANAGER') OR region = CURRENT_USER() """, comment="Users can only see rows for their assigned region", ) ``` ## Fields - `name` (string, required) - The fully qualified name of the row access policy (e.g., `db.schema.policy_name`). - `args` (list, required) - List of arguments for the policy. Each argument must have `name` and `data_type` fields. These correspond to columns that will be passed when the policy is attached to a table. At least one argument is required. - `body` (string, required) - A SQL expression that returns BOOLEAN. When TRUE, the row is visible; when FALSE, it is filtered out. Typically uses `IS_ROLE_IN_SESSION()` to check role membership. - `comment` (string) - A comment or description for the row access policy. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The role that owns the row access policy. Defaults to "SYSADMIN". **Note:** Row access policies require Enterprise Edition or higher. ## Attaching to Tables After creating a row access policy, attach it to tables using `ALTER TABLE`: ``` ALTER TABLE my_table ADD ROW ACCESS POLICY governance.policies.rap_sales_region ON (region); ``` See [Row Access Policies](https://snowcap.datacoves.com/row-access-policies/index.md) for a recommended pattern using role-based filtering with dbt integration. ## See Also - [Row Access Policies Guide](https://snowcap.datacoves.com/row-access-policies/index.md) - [MaskingPolicy](https://snowcap.datacoves.com/resources/masking-policy/index.md) # S3StorageIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-storage-integration) | Snowcap CLI label: `s3storage_integration` Manages the integration of Snowflake with AWS S3 storage. ## Examples ### YAML ``` s3_storage_integrations: - name: some_s3_storage_integration enabled: true storage_aws_role_arn: "arn:aws:iam::123456789012:role/MyS3AccessRole" storage_allowed_locations: - "s3://mybucket/myfolder/" storage_blocked_locations: - "s3://mybucket/myblockedfolder/" storage_aws_object_acl: "bucket-owner-full-control" comment: "This is a sample S3 storage integration." ``` ### Python ``` s3_storage_integration = S3StorageIntegration( name="some_s3_storage_integration", enabled=True, storage_aws_role_arn="arn:aws:iam::123456789012:role/MyS3AccessRole", storage_allowed_locations=["s3://mybucket/myfolder/"], storage_blocked_locations=["s3://mybucket/myblockedfolder/"], storage_aws_object_acl="bucket-owner-full-control", comment="This is a sample S3 storage integration." ) ``` ## Fields - `name` (string, required) - The name of the storage integration. - `enabled` (bool, required) - Whether the storage integration is enabled. Defaults to True. - `storage_aws_role_arn` (string, required) - The AWS IAM role ARN to access the S3 bucket. - `storage_allowed_locations` (list, required) - A list of allowed locations for storage in the format 's3:////'. - `storage_blocked_locations` (list) - A list of blocked locations for storage in the format 's3:////'. Defaults to an empty list. - `storage_aws_object_acl` (string) - The ACL policy for objects stored in S3. Defaults to 'bucket-owner-full-control'. - `type` (string) - The type of storage integration. Defaults to 'EXTERNAL_STAGE'. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the storage integration. Defaults to 'ACCOUNTADMIN'. - `comment` (string) - An optional comment about the storage integration. # Schema [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-schema) | Snowcap CLI label: `schema` Represents a schema in Snowflake, which is a logical grouping of database objects such as tables, views, and stored procedures. Schemas are used to organize and manage such objects within a database. ## Examples ### YAML ``` schemas: - name: some_schema transient: true managed_access: true data_retention_time_in_days: 7 max_data_extension_time_in_days: 28 default_ddl_collation: utf8 tags: project: analytics owner: SYSADMIN comment: Schema for analytics project. ``` ### Python ``` schema = Schema( name="some_schema", transient=True, managed_access=True, data_retention_time_in_days=7, max_data_extension_time_in_days=28, default_ddl_collation="utf8", tags={"project": "analytics"}, owner="SYSADMIN", comment="Schema for analytics project." ) ``` ## Fields - `name` (string, required) - The name of the schema. - `transient` (bool) - Specifies if the schema is transient. Defaults to False. - `managed_access` (bool) - Specifies if the schema has managed access. Defaults to False. - `data_retention_time_in_days` (int) - The number of days to retain data for Time Travel. Inherits from database if not specified. - `max_data_extension_time_in_days` (int) - The maximum number of days to extend data retention. Inherits from database if not specified. - `default_ddl_collation` (string) - The default DDL collation setting. Inherits from database if not specified. - `tags` (dict) - Tags associated with the schema. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the schema. Defaults to "SYSADMIN". - `comment` (string) - A comment about the schema. # Secret [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret) | Snowcap CLI label: `secret` A Secret defines a set of sensitive data that can be used for authentication or other purposes. ## Examples For sensitive values, use environment variables to avoid storing secrets in your repository. Set environment variables prefixed with `SNOWCAP_VAR_` and reference them using `{{ var.variable_name }}` syntax. For specific secret types, see: - [GenericSecret](https://snowcap.datacoves.com/resources/generic_secret/index.md) - For generic string secrets - [PasswordSecret](https://snowcap.datacoves.com/resources/password_secret/index.md) - For username/password credentials - [OAuthSecret](https://snowcap.datacoves.com/resources/oauth_secret/index.md) - For OAuth2 authentication ### YAML ``` # Set in your environment or .env file export SNOWCAP_VAR_OAUTH_TOKEN="your-secret-token" ``` ``` secrets: - name: some_secret type: OAUTH2 api_authentication: some_security_integration oauth_scopes: - scope1 - scope2 oauth_refresh_token: "{{ var.oauth_token }}" oauth_refresh_token_expiry_time: 2049-01-06 20:00:00 comment: OAuth secret for API access owner: SYSADMIN ``` ### Python ``` import os secret = Secret( name="some_secret", type="OAUTH2", api_authentication="some_security_integration", oauth_scopes=["scope1", "scope2"], oauth_refresh_token=os.environ.get("OAUTH_TOKEN"), oauth_refresh_token_expiry_time="2049-01-06 20:00:00", comment="OAuth secret for API access", owner="SYSADMIN", ) ``` ## Fields - `name` (string, required) - The name of the secret. - `type` (string or SecretType, required) - The type of the secret. - `api_authentication` (string) - The security integration name for API authentication. - `oauth_scopes` (list) - The OAuth scopes for the secret. - `oauth_refresh_token` (string) - The OAuth refresh token. - `oauth_refresh_token_expiry_time` (string) - The expiry time of the OAuth refresh token. - `username` (string) - The username for the secret. - `password` (string) - The password for the secret. - `secret_string` (string) - The secret string. - `comment` (string) - A comment for the secret. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the secret. Defaults to SYSADMIN. # SemanticView [Snowflake Documentation](https://docs.snowflake.com/en/user-guide/views-semantic/overview) | Snowcap CLI label: `semantic_view` A Semantic View is a schema-scoped Snowflake object that defines business metrics, dimensions, and relationships over one or more tables, used by Cortex Analyst and other consumers to query data in business terms. Snowcap supports granting access to existing semantic views declaratively. The semantic view itself (the `CREATE SEMANTIC VIEW ... TABLES (...) FACTS (...) DIMENSIONS (...) METRICS (...)` body) is **not** modeled as a concrete resource — create it via DDL or dbt, then manage who can query it through `grants:`. ## Examples ### YAML ``` grants: # Required to query the semantic view (e.g. via Cortex Analyst). - priv: SELECT on: semantic view somedb.someschema.sales_metrics to: analyst_role # Required to inspect the semantic view's structure via the information schema. - priv: REFERENCES on: semantic view somedb.someschema.sales_metrics to: analyst_role # Required for get_ai_observability_events() / Cortex Analyst request logs. - priv: MONITOR on: semantic view somedb.someschema.sales_metrics to: search_observability_role # Schema-scope privilege to allow a role to create new semantic views. - priv: CREATE SEMANTIC VIEW on: schema somedb.someschema to: semantic_view_author_role ``` ### Python ``` # Grant SELECT grant = Grant( priv="SELECT", on_semantic_view="somedb.someschema.sales_metrics", to="analyst_role", ) # Grant REFERENCES grant = Grant( priv="REFERENCES", on_semantic_view="somedb.someschema.sales_metrics", to="analyst_role", ) # Grant MONITOR grant = Grant( priv="MONITOR", on_semantic_view="somedb.someschema.sales_metrics", to="search_observability_role", ) ``` ## Privileges | Privilege | Purpose | | ------------ | ------------------------------------------------------------------------------------------- | | `SELECT` | Query the semantic view — sufficient on its own, without `SELECT` on the underlying tables. | | `REFERENCES` | Inspect the semantic view's structure via the information schema. | | `MONITOR` | Read Cortex Analyst request logs via `get_ai_observability_events(...)`. | | `OWNERSHIP` | Standard ownership semantics — drop, alter, transfer. | | `ALL` | Convenience: expand to all of the above. | The schema-scope privilege `CREATE SEMANTIC VIEW` is part of [Grant](https://snowcap.datacoves.com/resources/grant/index.md) under `SchemaPriv` — see the schema-privileges example above. ## See also - [Snowflake — Semantic views overview](https://docs.snowflake.com/en/user-guide/views-semantic/overview) - [Grant](https://snowcap.datacoves.com/resources/grant/index.md) — for the underlying grant resource and YAML schema # Sequence [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-sequence) | Snowcap CLI label: `sequence` Manages the creation and configuration of sequences in Snowflake, which are objects that generate numeric values according to a specified sequence. ## Examples ### YAML ``` sequences: - name: some_sequence owner: SYSADMIN start: 100 increment: 10 comment: This is a sample sequence. ``` ### Python ``` sequence = Sequence( name="some_sequence", owner="SYSADMIN", start=100, increment=10, comment="This is a sample sequence." ) ``` ## Fields - `name` (string, required) - The name of the sequence. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the sequence. Defaults to "SYSADMIN". - `start` (int) - The starting value of the sequence. - `increment` (int) - The value by which the sequence is incremented. - `comment` (string) - A comment for the sequence. # Service [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-service) | Snowcap CLI label: `service` Service is a managed resource in Snowflake that allows users to run instances of their applications as a collection of containers on a compute pool. Each service instance can handle incoming traffic with the help of a load balancer if multiple instances are run. ## Examples ### YAML ``` services: - name: some_service compute_pool: some_compute_pool stage: @tutorial_stage yaml_file_stage_path: echo_spec.yaml specification: FROM SPECIFICATION $$some_specification$$ external_access_integrations: - some_integration auto_resume: true min_instances: 1 max_instances: 2 query_warehouse: some_warehouse tags: key: value comment: This is a sample service. ``` ### Python ``` service = Service( name="some_service", compute_pool="some_compute_pool", stage="@tutorial_stage", yaml_file_stage_path="echo_spec.yaml", specification="FROM SPECIFICATION $$some_specification$$", external_access_integrations=["some_integration"], auto_resume=True, min_instances=1, max_instances=2, query_warehouse="some_warehouse", tags={"key": "value"}, comment="This is a sample service." ) ``` ## Fields - `name` (string, required) - The unique identifier for the service within the schema. - `compute_pool` (string or [ComputePool](https://snowcap.datacoves.com/resources/compute_pool/index.md), required) - The compute pool on which the service runs. - `stage` (string) - The Snowflake internal stage where the specification file is stored. - `yaml_file_stage_path` (string) - The path to the service specification file on the stage. - `specification` (string) - The service specification as a string. - `external_access_integrations` (list) - The names of external access integrations for the service. - `auto_resume` (bool) - Specifies whether to automatically resume the service when a function or ingress is called. Defaults to True. - `min_instances` (int) - The minimum number of service instances to run. - `max_instances` (int) - The maximum number of service instances to run. - `query_warehouse` (string or [Warehouse](https://snowcap.datacoves.com/resources/warehouse/index.md)) - The warehouse to use if a service container connects to Snowflake to execute a query. - `tags` (dict) - Tags associated with the service. - `comment` (string) - A comment for the service. # SessionPolicy [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-session-policy) | Snowcap CLI label: `session_policy` Manages session policies in Snowflake, which define timeout settings for user sessions to enhance security. ## Examples ### YAML ``` session_policies: - name: some_session_policy session_idle_timeout_mins: 30 session_ui_idle_timeout_mins: 10 comment: Policy for standard users. ``` ### Python ``` session_policy = SessionPolicy( name="some_session_policy", session_idle_timeout_mins=30, session_ui_idle_timeout_mins=10, comment="Policy for standard users." ) ``` ## Fields - `name` (string, required) - The name of the session policy. - `session_idle_timeout_mins` (int) - The maximum time in minutes a programmatic session (e.g., driver or connector) can remain idle before termination. - `session_ui_idle_timeout_mins` (int) - The maximum time in minutes a Snowflake UI session can remain idle before termination. - `comment` (string) - A description or comment about the session policy. # Share [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-share) | Snowcap CLI label: `share` Represents a share resource in Snowflake, which allows sharing data across Snowflake accounts. ## Examples ### YAML ``` shares: - name: some_share comment: This is a snowflake share. ``` ### Python ``` share = Share( name="some_share", comment="This is a snowflake share." ) ``` ## Fields - `name` (string, required) - The name of the share. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the share. Defaults to "ACCOUNTADMIN". - `comment` (string) - A comment about the share. # SharedDatabase [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-database) | Snowcap CLI label: `shared database` A `SharedDatabase` is the consumer side of a Snowflake share or Marketplace listing: `CREATE DATABASE FROM SHARE .`. It's a polymorphic sibling of [Database](https://snowcap.datacoves.com/resources/database/index.md) — declared under the same `databases:` key, distinguished by the presence of `from_share`. Because Snowflake replicates the provider's schemas, tables, and other objects into the consumer account, shared databases are read-only: snowcap cannot add schemas, tags, or params to them the way it can for a regular `Database`. ## Examples ### YAML ``` databases: - name: gong from_share: provider_account.share_name ``` ### Python ``` shared_database = SharedDatabase( name="gong", from_share="provider_account.share_name", ) ``` ## Fields - `name` (string, required) - The name of the database. - `from_share` (string, required) - The `.` the database is created from. Changing this on an existing shared database is not supported by `plan`/`apply` — it errors at plan time. Drop and recreate the database manually instead. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - Pinned to `"ACCOUNTADMIN"`. Snowflake prevents `GRANT OWNERSHIP` on an imported database, so a custom owner is rejected at plan time. Omit the field. ## Full example: importing a Gong share A common pattern is importing a Marketplace or direct share, then handing a scoped role access to it: ``` databases: - name: gong from_share: provider_account.share_name roles: - name: gong_r grants: - priv: IMPORTED PRIVILEGES on_database: gong to: gong_r role_grants: - role: gong_r to_role: data_engineer ``` Which is equivalent to: ``` CREATE DATABASE gong FROM SHARE provider_account.share_name; CREATE ROLE gong_r; GRANT IMPORTED PRIVILEGES ON DATABASE gong TO ROLE gong_r; GRANT ROLE gong_r TO ROLE data_engineer; ``` ### Gotchas - Shared databases are read-only — snowcap does not manage schemas, params, or tags on them. - `CREATE DATABASE ... FROM SHARE` requires the account-level `IMPORT SHARE` privilege, which only `ACCOUNTADMIN` holds by default. Snowcap runs the creation as `ACCOUNTADMIN`. - Ownership of a shared database cannot change: imported databases are read-only in the consumer account, and Snowflake prevents `GRANT OWNERSHIP` on them. Snowcap pins `owner` to `ACCOUNTADMIN` and does not track it for drift. - `IMPORTED PRIVILEGES` is the only privilege that can be granted on a shared database. It cannot be granted `WITH GRANT OPTION`, and it can only be granted to account roles, not database roles. - Snowflake's `SHOW GRANTS` reports `IMPORTED PRIVILEGES` grants on shared databases as `USAGE` — snowcap's fetch logic accounts for this quirk, so `plan`/`apply` still converge correctly. - Changing `from_share` on an existing shared database is not supported by `plan`/`apply` — it errors at plan time. Drop and recreate the database manually if you need to point it at a different share. ## See also - [Database](https://snowcap.datacoves.com/resources/database/index.md) — the non-shared sibling resource - [Grant](https://snowcap.datacoves.com/resources/grant/index.md) — for the `IMPORTED PRIVILEGES` grant shown above # SnowflakeCustomOAuthSecurityIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-oauth-snowflake) | Snowcap CLI label: `snowflake_custom_oauth_security_integration` A security integration in Snowflake for a custom OAuth client that Snowflake itself issues and manages. Unlike the partner integrations, Snowflake generates the OAuth client_id and client_secret for you; retrieve them with SYSTEM$SHOW_OAUTH_CLIENT_SECRETS, they cannot be set through this resource. ACCOUNTADMIN, ORGADMIN, GLOBALORGADMIN, and SECURITYADMIN are always blocked by Snowflake and must not be listed in blocked_roles_list. Every property except oauth_client_type is applied with ALTER SECURITY INTEGRATION and never recreates the integration. oauth_client_type cannot be changed after creation: snowcap fails the plan rather than recreating the integration, because recreation would rotate the Snowflake-issued client_id/client_secret and break live OAuth clients. To change it, recreate the integration manually and migrate clients. oauth_alternate_redirect_uris is CREATE-ONLY in snowcap: it is not fetchable, so editing it in YAML after creation produces no diff and no error. Changes must be applied out-of-band with ALTER SECURITY INTEGRATION SET OAUTH_ALTERNATE_REDIRECT_URIS = (...). ## Examples ### YAML ``` security_integrations: - name: claude_mcp_oauth enabled: true oauth_client_type: CONFIDENTIAL oauth_redirect_uri: https://claude.ai/api/mcp/auth_callback oauth_issue_refresh_tokens: true oauth_refresh_token_validity: 7776000 oauth_use_secondary_roles: NONE oauth_enforce_pkce: true blocked_roles_list: - SYSADMIN comment: OAuth client for the Claude MCP connector ``` ### Python ``` claude_mcp_oauth = SnowflakeCustomOAuthSecurityIntegration( name="claude_mcp_oauth", enabled=True, oauth_client_type="CONFIDENTIAL", oauth_redirect_uri="https://claude.ai/api/mcp/auth_callback", oauth_issue_refresh_tokens=True, oauth_refresh_token_validity=7776000, oauth_use_secondary_roles="NONE", oauth_enforce_pkce=True, blocked_roles_list=["SYSADMIN"], comment="OAuth client for the Claude MCP connector" ) ``` ## Fields - `name` (string, required) - The name of the security integration. - `enabled` (bool) - Specifies if the security integration is enabled. Defaults to True. - `oauth_client_type` (string or OAuthClientType, required) - The type of OAuth client. Supported values are 'CONFIDENTIAL' and 'PUBLIC'. Cannot be changed after creation. - `oauth_redirect_uri` (string, required) - The redirect URI the client uses to complete the OAuth flow. - `oauth_alternate_redirect_uris` (list) - Additional allowed redirect URIs, set at creation only. - `oauth_issue_refresh_tokens` (bool) - Indicates if refresh tokens should be issued. Defaults to True. - `oauth_refresh_token_validity` (int) - The validity period of the refresh token in seconds. Defaults to 7776000. - `oauth_use_secondary_roles` (string or OAuthUseSecondaryRoles) - Whether secondary roles are activated for OAuth sessions. Supported values are 'IMPLICIT' and 'NONE'. Defaults to 'NONE'. - `oauth_enforce_pkce` (bool) - Requires clients to use PKCE during the OAuth flow. Defaults to False. - `network_policy` (string) - The network policy enforced for requests made with this integration's tokens. - `pre_authorized_roles_list` (list) - Roles granted access without displaying a consent screen to the user. - `blocked_roles_list` (list) - Roles that are not allowed to use this integration. - `comment` (string) - A comment about the security integration. # SnowflakePartnerOAuthSecurityIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-security-integration) | Snowcap CLI label: `snowflake_partner_oauth_security_integration` A security integration in Snowflake designed to manage external OAuth clients for authentication purposes. This integration supports specific OAuth clients such as Looker, Tableau Desktop, and Tableau Server. ## Examples ### YAML ``` security_integrations: - name: some_security_integration enabled: true oauth_client: LOOKER oauth_client_secret: secret123 oauth_redirect_uri: https://example.com/oauth/callback oauth_issue_refresh_tokens: true oauth_refresh_token_validity: 7776000 comment: Integration for Looker OAuth ``` ### Python ``` snowflake_partner_oauth_security_integration = SnowflakePartnerOAuthSecurityIntegration( name="some_security_integration", enabled=True, oauth_client="LOOKER", oauth_client_secret="secret123", oauth_redirect_uri="https://example.com/oauth/callback", oauth_issue_refresh_tokens=True, oauth_refresh_token_validity=7776000, comment="Integration for Looker OAuth" ) ``` ## Fields - `name` (string, required) - The name of the security integration. - `enabled` (bool) - Specifies if the security integration is enabled. Defaults to True. - `oauth_client` (string or OAuthClient) - The OAuth client used for authentication. Supported clients are 'LOOKER', 'TABLEAU_DESKTOP', and 'TABLEAU_SERVER'. - `oauth_client_secret` (string) - The secret associated with the OAuth client. - `oauth_redirect_uri` (string) - The redirect URI configured for the OAuth client. - `oauth_issue_refresh_tokens` (bool) - Indicates if refresh tokens should be issued. Defaults to True. - `oauth_refresh_token_validity` (int) - The validity period of the refresh token in seconds. - `comment` (string) - A comment about the security integration. # SnowservicesOAuthSecurityIntegration [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-security-integration) | Snowcap CLI label: `snowservices_oauth_security_integration` Manages OAuth security integrations for Snowservices in Snowflake, allowing external authentication mechanisms. ## Examples ### YAML ``` snowservices_oauth: - name: some_security_integration enabled: true comment: Integration for external OAuth services. ``` ### Python ``` snowservices_oauth = SnowservicesOAuthSecurityIntegration( name="some_security_integration", enabled=True, comment="Integration for external OAuth services." ) ``` ## Fields - `name` (string, required) - The name of the security integration. - `enabled` (bool) - Specifies if the security integration is enabled. Defaults to True. - `comment` (string) - A comment about the security integration. # StageStream [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-stream) | Snowcap CLI label: `stage_stream` Represents a stream on a stage in Snowflake, which allows for capturing data changes on the stage. ## Examples ### YAML ``` streams: - name: some_stream on_stage: some_stage owner: SYSADMIN copy_grants: true comment: This is a sample stream. ``` ### Python ``` stream = StageStream( name="some_stream", on_stage="some_stage", owner="SYSADMIN", copy_grants=True, comment="This is a sample stream." ) ``` ## Fields - `name` (string, required) - The name of the stream. - `on_stage` (string, required) - The name of the stage the stream is based on. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The role that owns the stream. Defaults to "SYSADMIN". - `copy_grants` (bool) - Whether to copy grants from the source stage to the stream. - `comment` (string) - An optional description for the stream. # Streamlit [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-streamlit) | Snowcap CLI label: `streamlit` Represents a Streamlit app in Snowflake, which is a schema-scoped resource for creating interactive applications using Python code. ## Examples ### YAML ``` streamlits: # From a stage - name: my_db.my_schema.my_streamlit from: "@my_stage" main_file: app.py title: My Streamlit App query_warehouse: my_warehouse comment: A sample Streamlit app from a stage owner: SYSADMIN tags: project: demo # From a Git repository - name: my_streamlit from: https://github.com/user/repo.git version: main main_file: app.py title: Repo Streamlit App ``` ### Python ``` # Creating a Streamlit app from a stage streamlit_stage = Streamlit( name="my_db.my_schema.my_streamlit", from_="@my_stage", main_file="app.py", title="My Streamlit App", query_warehouse="my_warehouse", comment="A sample Streamlit app from a stage", tags={"project": "demo"} ) # Creating a Streamlit app from a Git repository streamlit_repo = Streamlit( name="my_streamlit", from_="https://github.com/user/repo.git", version="main", main_file="app.py", title="Repo Streamlit App", owner="SYSADMIN" ) ``` ## Fields - `name` (string, required) - The name of the Streamlit app. Can be a fully qualified name (e.g., "database.schema.app_name"). - `from_` (string, required) - The source of the Streamlit app. This can be either a stage (e.g., '@mystage') or a repository URL (e.g., 'https://github.com/user/repo.git'). - `version` (string) - The version or branch of the repository to use. Only applicable if from\_ is a repository URL. - `main_file` (string) - The name of the main Python file for the Streamlit app (e.g., 'app.py'). - `title` (string) - The display title of the Streamlit app. - `query_warehouse` (string) - The name of the warehouse to use for queries in the app. - `comment` (string) - A comment or description for the Streamlit app. - `owner` (string or Role) - The role that owns the Streamlit app. Defaults to "SYSADMIN". - `tags` (dict) - A dictionary of tags to associate with the Streamlit app. ## Granting access A Streamlit app is a schema-scoped object. Grant `USAGE` on the app to let a role open and run it — this is the whole access story for viewers when the app uses owner's rights (all app queries execute as the app owner, so viewers need no privileges on the underlying tables): ``` grants: # Let a viewer role open and run the app. - priv: USAGE on: streamlit my_db.my_schema.my_streamlit to: app_viewer_role # Schema-scope privilege to allow a role to create Streamlit apps. - priv: CREATE STREAMLIT on: schema my_db.my_schema to: app_developer_role ``` ``` # Grant USAGE so a role can open the app grant = Grant( priv="USAGE", on_streamlit="my_db.my_schema.my_streamlit", to="app_viewer_role", ) ``` | Privilege | Purpose | | ----------- | ---------------------------------------------------------------------------------------------------------------------------- | | `USAGE` | Open, view, and run the Streamlit app (and `DESCRIBE` it). | | `OWNERSHIP` | Full control. Set at create/deploy time — Snowflake does not support transferring streamlit ownership via `GRANT OWNERSHIP`. | | `ALL` | All privileges above. | The schema-scope privilege `CREATE STREAMLIT` lets a role create apps in that schema; creating an app with a `ROOT_LOCATION` stage also needs `CREATE STAGE`. # Table [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-table) | Snowcap CLI label: `table` A table in Snowflake. ## Examples ### YAML ``` tables: - name: some_table columns: - name: col1 data_type: STRING owner: SYSADMIN ``` ### Python ``` table = Table( name="some_table", columns=[{"name": "col1", "data_type": "STRING"}], owner="SYSADMIN", ) ``` ## Fields - `name` (string, required) - The name of the table. - `columns` (list, required) - The columns of the table. - `constraints` (list) - The constraints of the table. - `transient` (bool) - Whether the table is transient. - `cluster_by` (list) - The clustering keys for the table. - `enable_schema_evolution` (bool) - Whether schema evolution is enabled. Defaults to False. - `data_retention_time_in_days` (int) - The data retention time in days. - `max_data_extension_time_in_days` (int) - The maximum data extension time in days. - `change_tracking` (bool) - Whether change tracking is enabled. Defaults to False. - `default_ddl_collation` (string) - The default DDL collation. - `copy_grants` (bool) - Whether to copy grants. Defaults to False. - `row_access_policy` (dict) - The row access policy. - `tags` (dict) - The tags for the table. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the table. Defaults to SYSADMIN. - `comment` (string) - A comment for the table. # TableStream [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-stream) | Snowcap CLI label: `table_stream` Represents a stream on a table in Snowflake, which allows for change data capture on the table. ## Examples ### YAML ``` streams: - name: some_stream on_table: some_table owner: SYSADMIN copy_grants: true at: TIMESTAMP: "2022-01-01 00:00:00" before: STREAM: some_other_stream append_only: false show_initial_rows: true comment: This is a sample stream. ``` ### Python ``` stream = TableStream( name="some_stream", on_table="some_table", owner="SYSADMIN", copy_grants=True, at={"TIMESTAMP": "2022-01-01 00:00:00"}, before={"STREAM": "some_other_stream"}, append_only=False, show_initial_rows=True, comment="This is a sample stream." ) ``` ## Fields - `name` (string, required) - The name of the stream. - `on_table` (string, required) - The name of the table the stream is based on. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The role that owns the stream. Defaults to "SYSADMIN". - `copy_grants` (bool) - Whether to copy grants from the source table to the stream. - `at` (dict) - A dictionary specifying the point in time for the stream to start, using keys like TIMESTAMP, OFFSET, STATEMENT, or STREAM. - `before` (dict) - A dictionary specifying the point in time for the stream to start, similar to 'at' but defining a point before the specified time. - `append_only` (bool) - If set to True, the stream records only append operations. - `show_initial_rows` (bool) - If set to True, the stream includes the initial rows of the table at the time of stream creation. - `comment` (string) - An optional description for the stream. # TagMaskingPolicyReference [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/alter-tag) | Snowcap CLI label: `tag_masking_policy_reference` Associates a masking policy with a tag. When a tag with an associated masking policy is applied to a column, the masking policy is automatically enforced on that column. This provides a scalable way to apply data protection policies across your organization by simply tagging columns with sensitive data. ## Examples ### YAML ``` tag_masking_policy_references: - tag_name: governance.public.pii masking_policy_name: governance.public.mask_pii_string ``` ### Python ``` from snowcap.resources import TagMaskingPolicyReference ref = TagMaskingPolicyReference( tag_name="governance.public.pii", masking_policy_name="governance.public.mask_pii_string", ) ``` ## SQL Generated When creating this resource, Snowcap generates: ``` ALTER TAG governance.public.pii SET MASKING POLICY governance.public.mask_pii_string; ``` When removing this resource: ``` ALTER TAG governance.public.pii UNSET MASKING POLICY governance.public.mask_pii_string; ``` ## Fields - `tag_name` (string, required) - The fully qualified name of the tag (e.g., `governance.public.pii`). - `masking_policy_name` (string, required) - The fully qualified name of the masking policy (e.g., `governance.public.mask_pii_string`). ## Notes - Both the tag and masking policy must exist before creating this reference. - A tag can have multiple masking policies associated with it (for different data types). - The masking policy's signature (input type) should match the data type of columns where the tag will be applied. - This feature requires Enterprise Edition or higher. # Tag [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-tag) | Snowcap CLI label: `tag` Represents a tag in Snowflake, which can be used to label various resources for better management and categorization. ## Examples ### YAML ``` tags: - name: governance.public.pii comment: Personally Identifiable Information - name: governance.public.cost_center comment: Cost center for billing allowed_values: - finance - engineering - sales # With auto-propagation - name: governance.public.auto_pii comment: Auto-propagating PII tag allowed_values: - sensitive - highly_sensitive propagate: ON_DEPENDENCY_AND_DATA_MOVEMENT on_conflict: ALLOWED_VALUES_SEQUENCE ``` ### Python ``` from snowcap.resources import Tag tag = Tag( name="governance.public.pii", comment="Personally Identifiable Information", ) # With auto-propagation tag = Tag( name="governance.public.auto_pii", allowed_values=["sensitive", "highly_sensitive"], propagate="ON_DEPENDENCY_AND_DATA_MOVEMENT", on_conflict="ALLOWED_VALUES_SEQUENCE", ) ``` ## Fields - `name` (string, required) - The fully qualified name of the tag (e.g., `db.schema.tag_name`). - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the tag. Defaults to "SYSADMIN". - `allowed_values` (list) - A list of allowed values for the tag. If specified, only these values can be assigned when applying the tag. - `propagate` (string) - Configures automatic tag propagation (Enterprise Edition+). Values: - `ON_DEPENDENCY_AND_DATA_MOVEMENT` - Propagates for both dependencies and data movement - `ON_DEPENDENCY` - Propagates only for object dependencies - `ON_DATA_MOVEMENT` - Propagates only for data lineage scenarios - `on_conflict` (string) - Behavior when propagated tag values conflict. Use `ALLOWED_VALUES_SEQUENCE` to use the first allowed value, or specify a custom string like `'CONFLICT'`. - `comment` (string) - A comment or description for the tag. **Note:** Tags require Enterprise Edition or higher. # Task [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-task) | Snowcap CLI label: `task` Represents a scheduled task in Snowflake that performs a specified SQL statement at a recurring interval. ## Examples ### YAML ``` tasks: - name: some_task warehouse: some_warehouse schedule: USING CRON 0 9 * * * UTC state: SUSPENDED as_: SELECT 1 ``` ### Python ``` task = Task( name="some_task", warehouse="some_warehouse", schedule="USING CRON 0 9 * * * UTC", state="SUSPENDED", as_="SELECT 1" ) ``` ## Fields - `name` (string, required) - The name of the task. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the task. Defaults to "SYSADMIN". - `warehouse` (string or [Warehouse](https://snowcap.datacoves.com/resources/warehouse/index.md)) - The warehouse used by the task. Cannot be set if `user_task_managed_initial_warehouse_size` is specified. - `user_task_managed_initial_warehouse_size` (string or WarehouseSize) - The initial warehouse size for serverless tasks. If neither this nor `warehouse` is set, defaults to MEDIUM for serverless execution. - `schedule` (string) - The schedule on which the task runs (e.g., `"USING CRON 0 9 * * * UTC"` or `"1 MINUTE"`). - `config` (string) - Configuration settings for the task in JSON format. - `allow_overlapping_execution` (bool) - Whether the task can have overlapping executions. Defaults to False. - `user_task_timeout_ms` (int) - The timeout in milliseconds after which the task is aborted. - `suspend_task_after_num_failures` (int) - The number of consecutive failures after which the task is suspended. Defaults to 10 for root tasks. - `error_integration` (string) - The notification integration used for error handling. - `copy_grants` (bool) - Whether to copy grants when replacing the task. - `comment` (string) - A comment for the task. - `after` (list) - A list of predecessor tasks that must complete before this task runs. Used for task DAGs. - `when` (string) - A conditional expression (e.g., `SYSTEM$STREAM_HAS_DATA('mystream')`) that determines when the task runs. - `as_` (string) - The SQL statement that the task executes. - `state` (string or TaskState) - The initial state of the task. Defaults to SUSPENDED. # User [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-user) | Snowcap CLI label: `user` A user in Snowflake. **Note:** `RSA_PUBLIC_KEY_FP` and `RSA_PUBLIC_KEY_2_FP` are read-only fingerprint fields computed by Snowflake and cannot be managed through Snowcap. **Note:** `rsa_public_key` and `rsa_public_key_2` are Snowflake's legacy key-pair properties. Named key pairs, which support role restriction, expiration, and rotation with a grace period, are declared with a `key_pairs:` list on the user or as standalone [UserKeyPair](https://snowcap.datacoves.com/resources/user_key_pair/index.md) resources. Rotating on the legacy properties is the two-step flow Snowflake documents: set `rsa_public_key_2` to the new key and apply, move clients over, then set `rsa_public_key` to the new key and apply again. Both keys are read back from Snowflake, so each step settles to an empty plan. Keys may be given with or without their PEM delimiters. Removing the retired key is the one step Snowcap cannot do for you: an empty or absent value means "not managed" everywhere in Snowcap, so deleting `rsa_public_key_2` from your config plans nothing and leaves the old key live. Retire it with `ALTER USER someuser UNSET RSA_PUBLIC_KEY_2`. [Named key pairs](https://snowcap.datacoves.com/resources/user_key_pair/index.md) have no such gap — rotation retires the prior key on a timer you set. ## Examples ### YAML ``` users: - name: some_user owner: USERADMIN email: some.user@example.com type: PERSON ``` ### Python ``` user = User( name="some_user", owner="USERADMIN", email="some.user@example.com", type="PERSON", ) ``` ## Fields - `name` (string, required) - The name of the user. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner of the user. Defaults to "USERADMIN". - `password` (string) - The password of the user. - `login_name` (string) - The login name of the user. Defaults to the name in uppercase. - `display_name` (string) - The display name of the user. Defaults to the name in lowercase. - `first_name` (string) - The first name of the user. - `middle_name` (string) - The middle name of the user. - `last_name` (string) - The last name of the user. - `email` (string) - The email of the user. - `must_change_password` (bool) - Whether the user must change their password. Defaults to False. - `disabled` (bool) - Whether the user is disabled. Defaults to False. - `days_to_expiry` (int) - The number of days until the user's password expires. - `mins_to_unlock` (int) - The number of minutes until the user's account is unlocked. - `default_warehouse` (string) - The default warehouse for the user. - `default_namespace` (string) - The default namespace for the user. - `default_role` (string) - The default role for the user. - `default_secondary_roles` (list) - The default secondary roles for the user. Use `[]` for NONE or `['ALL']` for ALL. - `mins_to_bypass_mfa` (int) - The number of minutes until the user can bypass Multi-Factor Authentication. - `rsa_public_key` (string) - The RSA public key for the user. - `rsa_public_key_2` (string) - The RSA public key for the user. - `comment` (string) - A comment for the user. - `network_policy` (string) - The network policy for the user. - `allowed_interfaces` (list) - The allowed interfaces for the user. - `workload_identity` (string) - The workload identity for the user. - `type` (string or UserType) - The type of the user. Defaults to "NULL". - `tags` (dict) - Tags for the user. # UserKeyPair [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/alter-user-add-key-pair) | Snowcap CLI label: `user_key_pair` A named key pair registered for a user, used for key-pair authentication. Named key pairs are the recommended alternative to the legacy `rsa_public_key` and `rsa_public_key_2` user properties: a user can hold up to 10 of them, and each one can carry its own role restriction, expiration, and comment. Snowflake never returns the public key itself, only its SHA-256 fingerprint, so Snowcap compares the fingerprint of the configured key against the one Snowflake reports. ## Key storage A public key is not a secret and is safe to commit with your Snowcap configuration. Keeping it in version control makes rotations reviewable and preserves the key-to-user association. Never put the matching private key in Snowcap configuration or commit it to a repository; keep private keys encrypted in a secret manager or similarly protected storage. If your organization treats public-key identity as sensitive metadata, inject the public key through Snowcap's existing variable support instead: ``` user_key_pairs: - name: my_key user: some_user public_key: "{{ var.snowflake_public_key }}" ``` For local use, `SNOWCAP_VAR_SNOWFLAKE_PUBLIC_KEY` may be loaded from a gitignored `.env` file. This is an organizational policy choice, not a requirement for protecting the public key. See [Secrets and Environment Variables](https://snowcap.datacoves.com/secrets-and-variables/index.md). ## Examples ### YAML Key pairs can be declared on their own: ``` user_key_pairs: - name: my_key user: some_user public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgK... comment: primary workload key ``` ...or inline on the user that owns them: ``` users: - name: some_user type: SERVICE key_pairs: - name: my_key public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgK... - name: scoped_key public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgK... role_restriction: some_role days_to_expiry: 90 ``` ### Python ``` key_pair = UserKeyPair( name="my_key", user="some_user", public_key="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgK...", comment="primary workload key", ) ``` ## Rotating a key Change `public_key` and apply. Snowcap plans `ALTER USER ... ROTATE KEY PAIR`, which replaces the stored key while keeping the key pair's name, role restriction, expiration, and comment: ``` user_key_pairs: - name: my_key user: some_user public_key: # was: ``` The prior key stays valid for a grace period so clients that haven't picked up the new key keep authenticating — 24 hours by default. Snowflake retains it under a generated `_ROTATED_` name until it expires, and Snowcap leaves it alone rather than removing it early. The plan says so explicitly when it contains a rotation. **Responding to a leaked private key.** Set `expire_rotated_key_pair_after_hours: 0` so the rotation revokes the old key immediately instead of leaving it usable for a day: ``` user_key_pairs: - name: my_key user: some_user public_key: expire_rotated_key_pair_after_hours: 0 ``` The field describes how the *next* rotation behaves, so changing it on its own plans nothing. Rotated-out keys count against the per-user limit of 10 until they expire. ## Fields - `name` (string, required) - The name of the key pair. `PUBLIC_KEY_1` and `PUBLIC_KEY_2` are reserved by Snowflake for the legacy user properties and cannot be used. - `user` (string or [User](https://snowcap.datacoves.com/resources/user/index.md), required) - The user the key pair is registered for. - `public_key` (string, required) - The public key, with or without PEM delimiters. RSA keys and EC keys on the P-256, P-384, and P-521 curves are supported. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The role that manages the user, and therefore the role Snowcap runs create and update statements as. Defaults to "USERADMIN". - `role_restriction` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The role a session authenticated with this key pair is restricted to. The role must already be granted to the user. - `days_to_expiry` (int) - The number of days the key pair can be used for authentication. Must be 1 or greater. Defaults to no expiration. - `expire_rotated_key_pair_after_hours` (int) - How many hours the prior key stays valid after a rotation. `0` revokes it immediately. Defaults to Snowflake's 24 hours. Only applies to a rotation, never to registering a key pair. - `disabled` (bool) - Whether the key pair is disabled. A disabled key pair keeps its metadata but cannot authenticate. Defaults to False. - `comment` (string) - A comment for the key pair. ## Notes - Managing key pairs requires `OWNERSHIP` of the user or the `MODIFY PROGRAMMATIC AUTHENTICATION METHODS` privilege on it. - Snowflake does not report which role manages a key pair. If sync mode removes a key pair after its block has been deleted from config, Snowcap cannot recover a custom `owner` and falls back to `USERADMIN` or the connection role. Ensure that role can modify the user, or remove the key pair before deleting its config block. - `role_restriction` and `days_to_expiry` are fixed when the key pair is registered. Snowflake offers no way to change them, so Snowcap fails the plan and tells you to remove the key pair, apply, and add it back. That covers a changed `role_restriction`, and an expiration added to or dropped from an existing key pair. A change to the *length* of an existing expiration is not detected, because Snowflake reports an absolute expiration timestamp rather than the relative value that was registered. - A key pair past its expiration reports as expired rather than disabled, which Snowcap does not treat as drift on `disabled`. # View [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-view) | Snowcap CLI label: `view` Represents a view in Snowflake, which is a virtual table created by a stored query on the data. Views are used to simplify complex queries, improve security, or enhance performance. ## Examples ### YAML ``` views: - name: some_view owner: SYSADMIN secure: true as_: SELECT * FROM some_table ``` ### Python ``` view = View( name="some_view", owner="SYSADMIN", secure=True, as_="SELECT * FROM some_table" ) ``` ## Fields - `name` (string, required) - The name of the view. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The owner role of the view. Defaults to "SYSADMIN". - `secure` (bool) - Specifies if the view is secure. - `volatile` (bool) - Specifies if the view is volatile. - `recursive` (bool) - Specifies if the view is recursive. - `columns` (list) - A list of dictionaries specifying column details. - `tags` (dict) - A dictionary of tags associated with the view. - `change_tracking` (bool) - Specifies if change tracking is enabled. - `copy_grants` (bool) - Specifies if grants should be copied from the base table. - `comment` (string) - A comment for the view. - `as_` (string) - The SELECT statement defining the view. # ViewStream [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-stream) | Snowcap CLI label: `view_stream` Represents a stream on a view in Snowflake, allowing for real-time data processing and querying. This stream can be configured with various options such as time travel, append-only mode, and initial row visibility. ## Examples ### YAML ``` streams: - name: some_stream on_view: some_view owner: SYSADMIN copy_grants: true at: TIMESTAMP: "2022-01-01 00:00:00" before: STREAM: some_other_stream append_only: false show_initial_rows: true comment: This is a sample stream on a view. ``` ### Python ``` view_stream = ViewStream( name="some_stream", on_view="some_view", owner="SYSADMIN", copy_grants=True, at={"TIMESTAMP": "2022-01-01 00:00:00"}, before={"STREAM": "some_other_stream"}, append_only=False, show_initial_rows=True, comment="This is a sample stream on a view." ) ``` ## Fields - `name` (string, required) - The name of the stream. - `on_view` (string, required) - The name of the view the stream is based on. - `owner` (string or [Role](https://snowcap.datacoves.com/resources/role/index.md)) - The role that owns the stream. Defaults to 'SYSADMIN'. - `copy_grants` (bool) - Whether to copy grants from the view to the stream. - `at` (dict) - A dictionary specifying the point in time for the stream to start, using keys like TIMESTAMP, OFFSET, STATEMENT, or STREAM. - `before` (dict) - A dictionary specifying the point in time for the stream to start, similar to 'at' but defining a point before the specified time. - `append_only` (bool) - If set to True, the stream records only append operations. - `show_initial_rows` (bool) - If set to True, the stream includes the initial rows of the view at the time of stream creation. - `comment` (string) - An optional description for the stream. # Warehouse [Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/sql/create-warehouse) | Snowcap CLI label: `warehouse` A virtual warehouse, often referred to simply as a "warehouse", is a cluster of compute resources in Snowflake. It provides the necessary CPU, memory, and temporary storage to execute SQL SELECT statements, perform DML operations such as INSERT, UPDATE, DELETE, and manage data loading and unloading. ## Examples ### Python ``` warehouse = Warehouse( name="some_warehouse", owner="SYSADMIN", warehouse_type="STANDARD", warehouse_size="XSMALL", generation="2", resource_constraint="STANDARD_GEN_2", max_cluster_count=10, min_cluster_count=1, scaling_policy="STANDARD", auto_suspend=600, auto_resume=True, initially_suspended=False, resource_monitor=None, comment="This is a test warehouse", enable_query_acceleration=False, query_acceleration_max_scale_factor=1, max_concurrency_level=8, statement_queued_timeout_in_seconds=0, statement_timeout_in_seconds=172800, tags={"env": "test"}, ) ``` An adaptive warehouse sets max_query_performance_level instead of warehouse_size and cluster/scaling properties: ``` adaptive_warehouse = Warehouse( name="some_adaptive_warehouse", warehouse_type="ADAPTIVE", max_query_performance_level="LARGE", ) ``` ### YAML ``` warehouses: - name: some_warehouse owner: SYSADMIN warehouse_type: STANDARD warehouse_size: XSMALL generation: "2" resource_constraint: STANDARD_GEN_2 max_cluster_count: 10 min_cluster_count: 1 scaling_policy: STANDARD auto_suspend: 600 auto_resume: true initially_suspended: false resource_monitor: null comment: This is a test warehouse enable_query_acceleration: false query_acceleration_max_scale_factor: 1 max_concurrency_level: 8 statement_queued_timeout_in_seconds: 0 statement_timeout_in_seconds: 172800 tags: env: test ``` An adaptive warehouse in yaml: ``` warehouses: - name: some_adaptive_warehouse warehouse_type: ADAPTIVE max_query_performance_level: LARGE ``` ## Fields - `name` (string, required) - The name of the warehouse. - `owner` (string) - The owner of the warehouse. Defaults to "SYSADMIN". - `warehouse_type` (string or [WarehouseType](https://snowcap.datacoves.com/resources/warehouse/warehouse_type.md)) - The type of the warehouse: STANDARD, SNOWPARK-OPTIMIZED, or ADAPTIVE. Defaults to STANDARD. ADAPTIVE warehouses do not support warehouse_size, min_cluster_count, max_cluster_count, scaling_policy, auto_suspend, auto_resume, initially_suspended, enable_query_acceleration, query_acceleration_max_scale_factor, resource_constraint, or generation. - `warehouse_size` (string or [WarehouseSize](https://snowcap.datacoves.com/resources/warehouse/warehouse_size.md)) - The size of the warehouse which defines the compute and storage capacity. - `generation` (string or [WarehouseGeneration](https://snowcap.datacoves.com/resources/warehouse/warehouse_generation.md)) - The standard warehouse generation, either "1" or "2". - `resource_constraint` (string or [WarehouseResourceConstraint](https://snowcap.datacoves.com/resources/warehouse/warehouse_resource_constraint.md)) - The warehouse resource constraint, either STANDARD_GEN_1/2 for standard warehouses or MEMORY\_\* for Snowpark-optimized warehouses. - `max_query_performance_level` (string or [WarehouseSize](https://snowcap.datacoves.com/resources/warehouse/warehouse_size.md)) - The maximum size an ADAPTIVE warehouse may scale to: XSMALL, SMALL, MEDIUM, LARGE, XLARGE, XXLARGE, XXXLARGE, or X4LARGE. Only valid for ADAPTIVE warehouses; Snowflake defaults to XLARGE if omitted. - `max_cluster_count` (int) - The maximum number of clusters for the warehouse. - `min_cluster_count` (int) - The minimum number of clusters for the warehouse. - `scaling_policy` (string or [WarehouseScalingPolicy](https://snowcap.datacoves.com/resources/warehouse/warehouse_scaling_policy.md)) - The policy that defines how the warehouse scales. - `auto_suspend` (int) - The time in seconds of inactivity after which the warehouse is automatically suspended. - `auto_resume` (bool) - Whether the warehouse should automatically resume when queries are submitted. - `initially_suspended` (bool) - Whether the warehouse should start in a suspended state. - `resource_monitor` (string or [ResourceMonitor](https://snowcap.datacoves.com/resources/resource_monitor/index.md)) - The resource monitor that tracks the warehouse's credit usage and other metrics. - `comment` (string) - A comment about the warehouse. - `enable_query_acceleration` (bool) - Whether query acceleration is enabled to improve performance. If omitted, Snowflake's default applies. - `query_acceleration_max_scale_factor` (int) - The maximum scale factor for query acceleration. If omitted, Snowflake's default applies. - `max_concurrency_level` (int) - The maximum number of concurrent queries that the warehouse can handle. - `statement_queued_timeout_in_seconds` (int) - The time in seconds a statement can be queued before it times out. - `statement_timeout_in_seconds` (int) - The time in seconds a statement can run before it times out. - `tags` (dict) - Tags for the warehouse.