Snowflake Intelligence via REST API: how developers build with it, not just use it
Written by
Arman Babayan
The first two episodes of True North showed Snowflake Intelligence from the front: plain-English questions, instant charts, and a contract workflow completed in a single conversation. Impressive, but if you are a developer or architect, the question that matters is different. Can I build with this, or am I locked into a chat window?
Episode 3 answers that. Everything you see in the chat interface is available programmatically through Snowflake's Cortex REST API. That means you can embed the same intelligence in your own products, internal tools, and automated workflows, with full visibility into how every answer is produced.
A note on naming: at Snowflake Summit 2026, Snowflake Intelligence was renamed CoWork. The demo was recorded under the original name. The API patterns shown apply to CoWork and the Cortex Agents it runs on. For what changed at Summit, read why your Snowflake agents give wrong answers on good data.
Key takeaways
Every capability in the Snowflake Intelligence (now CoWork) interface is accessible through the Cortex REST API.
Conversation threads are first-class API objects, so multi-turn context works in your own applications.
Agent requests are structured JSON, including which tools the agent may use, which lets you enforce boundaries in code.
Responses expose the full reasoning trace: generated SQL, the thinking chain, the chart specification, and the final answer.
The Agents panel in Snowflake gives admins a live view of every session and execution step.
Watch the demo
From the chat interface to the API
For this episode we used Insomnia, a REST API client, to call Snowflake directly. No custom application, no SDK wrapper, just HTTP requests against your account. That is deliberate: if you can do it in Insomnia, you can do it from any backend, workflow engine, or integration platform your team already runs.
What we demonstrated
1. Listing and creating conversation threads
The chat interface remembers what you asked, what came back, and what actions were taken. At the API level, that memory is a thread. We listed existing threads and created new ones, treating them as first-class objects. For developers, this is the foundation for any multi-turn experience: a patient services portal, an internal operations assistant, or an automated renewal flow that needs context across several steps.
2. Sending agent prompts with tool constraints in structured JSON
A request to the agent is not just a text string. It is a structured JSON payload carrying the prompt, the thread it belongs to, and the tools the agent is allowed to use. That last part matters in healthcare. You might allow read-only analytics in a member-facing tool while reserving write actions, like the contract generation in Episode 2, for an internal workflow with tighter controls. Tool constraints let you express those boundaries in code, on top of the Snowflake roles and policies already governing the data.
3. Inspecting the full reasoning trace
This is the part that resonates most with technical teams. The API response does not just return an answer. It returns the steps behind it: the SQL the agent generated, the thinking chain it followed, the chart specification it built before rendering, and the final answer composed from all of it. That is real observability. You can see not only what the agent said, but how and why. For applications where auditability is non-negotiable, this is a requirement, and it comes natively at the API level with no extra instrumentation.
4. Monitoring sessions from the Agents panel
Finally, we switched to the Agents panel in Snowflake, where every session, prompt, and execution step is visible in one place. When agents run across multiple teams and applications, this is how data and IT leaders keep oversight: what ran, when, under which role, and with what outcome.
What this unlocks for builders
Episodes 1 and 2 made the case for business users. Episode 3 makes the case for builders. Each capability shown in the interface becomes a building block you can compose:
A patient or member portal that answers coverage questions in plain English, restricted to read-only tools.
An automated contract renewal flow triggered by expiration events, with a human approval step before any document is issued.
An internal operations tool that surfaces service gaps weekly and posts ranked recommendations to the teams that own them.
Evaluation pipelines that replay known questions and compare generated SQL and answers against expected results before each release.
Because everything runs inside Snowflake, you inherit the security, governance, and compliance controls you already rely on. You are extending a platform you trust, not adding a new data path to secure.
Production checklist for healthcare teams
Moving from a demo to production is mostly about the layers around the agent:
Accurate definitions. A governed semantic layer so every metric means the same thing to every agent and every user. This is the core of AI-Ready Data Governance.
Scoped identity and tools. Dedicated roles per use case, least-privilege grants, and tool constraints that match each workflow's risk.
Evaluation before trust. A set of known questions with expected answers, run against every change to the semantic model or agent configuration.
Ongoing ownership. Someone has to keep definitions, permissions, and quality checks current as the business changes. That is what Platform Team as a Service covers.
Where to start
If you are planning to embed Snowflake agents in a product or workflow, the fastest safe start is a fixed-scope assessment through our Snowflake consulting practice. We review your data, definitions, and access model, identify which use cases are ready, and design the guardrails before you build. If the platform needs groundwork first, we deliver it as an AI-Ready Snowflake Platform. For industry context, see our Healthcare & Pharma page.
Episode 3: everything is programmable, with full tool control, reasoning transparency, and operational oversight.
Learn more about Snowflake from top experts
Join data leaders who get Snowflake insights and updates delivered straight to their inbox.
Thanks for joining us!
We’ll keep you posted with fresh updates and resources.
Oops! Something went wrong while submitting the form.
Insights
Learnings for data leaders
Blog
5 min read
Snowflake Intelligence for healthcare: from expired contract to signed PDF in one conversation
Snowflake Intelligence (now CoWork) goes from analytics to action: finding expired payer contracts, surfacing coverage gaps, and generating a new contract PDF in plain English.
Analytics tells you what is happening. In healthcare operations, the expensive part is what happens next. A contracts manager spots a batch of expired UnitedHealthcare contracts. Someone pulls the patient records. Someone else reviews the service breakdown. A third person drafts a new contract in a separate system, cross-checks the coverage, formats the document, and routes it for approval. One simple insight turns into a multi-day, multi-team process.
Episode 2 of True North, Snowstack's healthcare series on Snowflake Intelligence, asks a different question: what if everything after the insight happened in the same conversation that surfaced it?
A note on naming: at Snowflake Summit 2026, Snowflake Intelligence was renamed CoWork. The demo was recorded under the original name. Everything shown applies to CoWork, and existing deployments migrated automatically.
Key takeaways
Snowflake Intelligence (now CoWork) can execute workflows, not just answer questions, when it is given the right tools.
In the demo, one conversation moved from expired contracts, to a single patient's coverage, to 14 unsubscribed services ranked by copay, to a newly generated contract PDF.
The unsubscribed-services list is effectively a ready-made coverage opportunity list for revenue cycle teams.
Write actions raise the bar on governance: accurate definitions, scoped permissions, and an audit trail are prerequisites, not extras.
Humans stay in the loop. The agent removes the mechanical work, not the decision.
Watch the demo
From insight to execution
Episode 1 showed that plain-English questions on governed healthcare data return accurate, visualized answers in seconds. That alone removes a lot of friction. But the bigger gap in most organizations is not between a question and an answer. It is between an answer and the action it should trigger. Analytics happens in one tool, execution in another, and the space in between is filled with emails, spreadsheets, and manual re-keying.
This episode closes that gap end to end.
The demo, step by step
1. Filtering expired UnitedHealthcare contracts by tier and expiration date
The workflow starts with one instruction: show expired UnitedHealthcare contracts, filtered by tier and expiration date. The agent queried the contracts data, applied the filters, and returned a clean list. Normally that is a filtered SQL query or a report that may or may not exist. Here it is a sentence, and it is only the starting point.
2. Reviewing one patient's full active contract and services
From the expired list, we drilled into a single patient: show this patient's full active contract and service breakdown. The agent returned coverage tier, active services, copay values, and contract terms in one readable response. This mirrors how a contracts manager actually thinks: find the problem at the population level, then investigate at the individual level, without switching systems or rewriting a query.
3. Identifying 14 unsubscribed services, ranked by copay
Next, we asked which services this patient was not subscribed to, ranked by copay. The answer: 14 services, highest value first. That is a prioritized coverage opportunity list, showing the services most worth adding to a renewed contract, ordered by revenue impact. A revenue cycle team would usually commission this as separate analysis. Here it emerged mid-conversation as the natural next step.
4. One instruction: create the contract, add services, generate the PDF
Then the step that changes the category. We issued a single instruction to create a new contract for the patient, add the selected services, and generate the legal document. The agent created the contract record, attached the services, and produced a PDF ready for review and signature. From expired contract to new document, without leaving the conversation.
How actions like this work in Snowflake
Querying is only one of the tools an agent can use. In Snowflake, agents can also be given custom tools, such as stored procedures that insert records or render documents, alongside the analytics tooling that turns questions into SQL. The agent decides which tool fits each step, and every tool runs inside your Snowflake account under the role the agent is allowed to use.
That design is what makes write actions safe enough to consider in healthcare. It is also why governance matters more here than anywhere else in the series. An agent that can create a contract must be working from correct coverage data, consistent service definitions, and permissions scoped to exactly what it should touch. That foundation is the work behind AI-Ready Data Governance: source-to-report reconciliation, freshness checks, lineage, and a governed semantic layer, so the agent acts on numbers you would sign off on yourself.
What this means for healthcare operations
Faster renewals, fewer coverage gaps. Expired contracts are caught and replaced in one sitting instead of drifting for weeks.
Revenue you can see. Unsubscribed services ranked by copay give revenue cycle teams a concrete, prioritized list to act on.
Less manual handoff. Querying, cross-referencing, and document generation stop bouncing between teams and tools.
A clean audit trail. Because every step runs in Snowflake, the queries, actions, and outputs are traceable, which matters for compliance reviews.
None of this removes people from the process. The contracts manager still decides. Clinical and finance teams still review the output. The agent handles the mechanical work that slows them down. For more on how we apply this to providers, payers, and pharma, see our Healthcare & Pharma page.
Where to start
Agents that take action should be scoped carefully: which workflows, which tools, which data, and which approvals. A fixed-scope assessment through our Snowflake consulting practice maps your highest-value workflows, checks whether the underlying data is accurate enough to act on, and defines the guardrails before anything goes live. Once agents are in production, Platform Team as a Service keeps definitions, permissions, and data quality current as your contracts and services change.
Snowflake Intelligence for healthcare: ask your patient data questions in plain English
A live healthcare demo of Snowflake Intelligence (now CoWork): plain-English questions on patient, insurer, and copay data, answered in seconds with automatic charts.
Every healthcare organization runs on a steady stream of small, operational questions. Which insurance company covers the most patients? Which add-on services carry the highest copay? Which imaging service almost nobody uses, and is that a pricing problem or an access problem? None of these are hard questions. The data to answer them already sits in Snowflake. Yet in most organizations, getting the answer still means writing SQL, hunting for the right dashboard, or opening a ticket and waiting two days.
This post is the written companion to Episode 1 of True North, Snowstack's three-part healthcare series on Snowflake Intelligence. We connected it to a live Patients Management dataset, asked plain-English questions, and recorded exactly what came back.
A note on naming: at Snowflake Summit 2026, Snowflake Intelligence was renamed CoWork. The demo was recorded under the original name. Everything shown applies to CoWork, and existing deployments migrated automatically. For what changed at Summit and why agent accuracy now depends on context, read why your Snowflake agents give wrong answers on good data.
Key takeaways
Snowflake Intelligence (now CoWork) lets non-technical healthcare teams query governed Snowflake data in plain English, with no SQL and no BI tool in the loop.
It picks the right visualization on its own: bar charts for comparisons, pie charts for distributions.
In the demo, insurer coverage, top copay services, and the least-used imaging service were each surfaced in seconds.
Answer quality depends on the semantic layer underneath. Clean data is not enough; the agent needs consistent business definitions.
The fastest safe path to production is an assessment of your data, definitions, and governance before rollout.
Watch the demo
What Snowflake Intelligence actually does
It is easy to dismiss this category as a chatbot bolted onto a database. That undersells it. Snowflake Intelligence is an agent that runs inside your Snowflake account. When you ask a question, it works out which data is relevant, generates and runs the query, interprets the result, and decides how to present it: a single number, a ranked list, or a chart.
Three things make it different from a generic AI assistant:
It runs where the data lives. Queries execute inside Snowflake, under your existing roles and access policies. Nothing is exported to a separate tool.
It reasons over business meaning, not just tables. When a semantic model describes what your metrics and entities mean, the agent uses those definitions instead of guessing from column names.
It shows its work. Every answer is backed by generated SQL you can inspect, which matters a great deal in a regulated industry. We go deep on this in Episode 3.
The demo: a live Patients Management dataset
The dataset covers patients, their insurance companies, subscribed services, copay values, and imaging services. It is the kind of operational data that sits at the center of any provider or payer. We asked questions the way an operations manager would, with no schema explanation and no preamble.
1. Insurance companies and patient counts, in seconds
The first question: which insurance companies are in the system, and how many patients does each cover? In a traditional setup, that is a GROUP BY query, trivial for an analyst and out of reach for everyone else. Snowflake Intelligence returned a ranked answer in seconds, along with a bar chart it generated without being asked. It recognized that a ranked comparison across entities is best read visually.
2. Automatic charts, with no prompting
As the conversation continued, the agent kept making sensible visualization choices on its own: bar charts for comparisons, pie charts for distributions. Anyone who has spent an afternoon configuring axes and chart types in a BI tool will appreciate how much friction disappears when the person asking never has to think about the format.
3. The top 5 extra services, ranked by copay
Next, a revenue cycle question: which five additional services carry the highest copay? One sentence produced a ranked list ready to act on. In practice, this is the insight that informs service promotion, payer negotiations, and capacity planning, and it usually arrives days after someone first asks for it.
4. The least popular imaging service, and why it matters
The most useful question in the demo was about what deserves attention. We asked which imaging service had the lowest uptake. The answer itself is a single row, but it turns a vague feeling ("imaging seems to underperform") into a specific lead. Low uptake can point to pricing, scheduling, coverage tiers, or how the service is communicated to patients. Knowing exactly which service to investigate is the difference between data and a decision.
Why the answers were right: the semantic layer
A demo like this looks effortless, and that is exactly why it deserves a closer look. An agent that reads a schema perfectly can still answer confidently and incorrectly if it does not know what the data means. Does "patient count" include inactive members? Is copay stored per visit or per plan year? Which table is the source of truth for coverage?
Those definitions live in a governed semantic layer, alongside data quality checks, freshness monitoring, and lineage. That is the work behind AI-Ready Data Governance, and it is what turns an impressive demo into answers a clinical or finance leader can actually rely on. In healthcare, a wrong number is not just embarrassing. It can drive the wrong coverage or staffing decision.
What this means for healthcare teams
Faster decisions. Clinical operations, revenue cycle, and service managers answer their own questions in real time instead of waiting in a queue.
Broader access without weaker governance. The people who most need data are rarely the ones who write SQL. Plain-English access closes that gap while queries still run under Snowflake roles and policies.
Fewer ad hoc tickets. Every self-served question is one less item in the data team's backlog, freeing engineers for platform work that actually needs them.
One version of the truth. Answers come from live, governed data, not a stale export or a dashboard built for a different purpose.
It is not a replacement for data engineering, and it does not retire every dashboard. Scheduled executive reporting still benefits from a fixed, shared view. Where conversational analytics shines is exploratory, ad hoc work, which is exactly where most healthcare teams lose the most time. See how we approach this for providers, payers, and pharma on our Healthcare & Pharma page.
Where to start
Most healthcare teams are closer to this than they think. The data is usually already in Snowflake. What is missing is the layer that makes an agent accurate: consistent definitions, quality checks, and access controls tuned for PHI. Our Snowflake consulting engagements start with a fixed-scope assessment that tells you which questions an agent can answer reliably today, what needs fixing first, and what a production rollout looks like. If your platform itself needs groundwork, we build it as an AI-Ready Snowflake Platform.
Snowflake Intelligence is Snowflake's agent for asking questions of your enterprise data in plain English. It finds the relevant data, generates and runs SQL, and returns answers as numbers, lists, or charts. At Snowflake Summit 2026 it was renamed CoWork, and existing deployments migrated automatically.
Yes. CoWork is the new name for Snowflake Intelligence, announced at Snowflake Summit 2026. The product lineage is the same: a work agent that answers questions across structured and unstructured data inside your Snowflake account.
No. Users ask questions in plain English and the agent generates the SQL. Technical teams can still inspect every query it ran, which is important for auditability in regulated environments.
Queries run inside Snowflake under your existing roles, masking policies, and access controls, so the agent only sees what the user is allowed to see. Safe use still depends on how those controls and your semantic layer are configured, which is the focus of AI-Ready Data Governance.
Because it knows your schema but not your business definitions. Without a governed semantic layer, terms like patient count or copay can be interpreted incorrectly. A fixed-scope assessment through Snowflake consulting shows which questions an agent can answer reliably today.
Blog
5 min read
From zero to production: a comprehensive guide to managing Snowflake with Terraform
Manual clicks don’t scale. As Snowflake environments grow, managing them through the UI or ad-hoc scripts quickly leads to drift, blind spots, and compliance risks. What starts as a quick fix often becomes a challenge that slows delivery and exposes the business to security gaps.
Manual clicks don’t scale. As Snowflake environments grow, managing them through the UI or ad-hoc scripts quickly leads to drift, blind spots, and compliance risks. What starts as a quick fix often becomes a challenge that slows delivery and exposes the business to security gaps.
Infrastructure as Code with Terraform solves these challenges by bringing software engineering discipline to Snowflake management. Using Terraform’s declarative language, engineers define the desired state of their Snowflake environment, track changes with version control, and apply them consistently across environments. Terraform communicates with Snowflake’s APIs through the official snowflakedb/snowflake provider, translating configuration into the SQL statements and API calls that keep your platform aligned and secure.
This guide provides a complete walkthrough of how to manage Snowflake with Terraform. From provisioning core objects like databases, warehouses, and schemas to building scalable role hierarchies and implementing advanced governance policies such as dynamic data masking.
Section 1: bootstrapping Terraform for secure Snowflake automation
The initial setup of the connection between Terraform and Snowflake is the most critical phase of the entire process. A secure and correctly configured foundation is paramount for reliable and safe automation. This section focuses on establishing this connection using production-oriented best practices, specifically tailored for non-interactive, automated workflows typical of CI/CD pipelines.
1.1 The principle of least privilege: the terraform service role
Terraform should not operate using a personal user account. Instead, a dedicated service user must be created specifically for Terraform automation. Before any Terraform code can be executed, a one-time manual bootstrapping process must be performed within the Snowflake UI or via SnowSQL. This involves using the ACCOUNTADMIN role to create the dedicated service user and a high-level role for Terraform's initial operations.
The following SQL statements will create a TERRAFORM_SVC service user and grant it the necessary
-- Use the highest-level role to create users and grant system roles
USE ROLE ACCOUNTADMIN;
-- Create a dedicated service user for Terraform
-- The RSA_PUBLIC_KEY will be set in the next step
CREATE USER TERRAFORM_SVC
TYPE = SERVICE
COMMENT = 'Service user for managing Snowflake infrastructure via Terraform.' RSA_PUBLIC_KEY = '<YOUR_PUBLIC_KEY_CONTENT_HERE>';
-- Grant the necessary system roles to the Terraform service user
GRANT ROLE SYSADMIN TO USER TERRAFORM_SVC;
GRANT ROLE SECURITYADMIN TO USER TERRAFORM_SVC;
Granting SYSADMIN and SECURITYADMIN to the service user is a necessary starting point for the infrastructure management. The SYSADMIN role holds the privileges required to create and manage account-level objects like databases and warehouses. The SECURITYADMIN role is required for managing security principals, including users, roles, and grants.
1.2 Authentication: the key to automation
The choice of authentication method is important. The Snowflake provider supports several authentication mechanisms, including basic password, OAuth, and key-pair authentication. For any automated workflow, especially within a CI/CD context, key-pair authentication is the industry-standard and recommended approach.
A CI/CD pipeline, such as one running in GitHub Actions, is a non-interactive environment. Basic password authentication is a significant security risk and not recommended. This leaves key-pair authentication as the only method that is both highly secure, as it avoids transmitting passwords, and fully automatable.
The following table provides a comparative overview of the primary authentication methods available in the Snowflake provider, reinforcing the recommendation for key-pair authentication in production automation scenarios.
Low. Exposes credentials in state or environment variables.
Low. Requires secure secret management; often blocked by MFA.
OAuth
User-delegated access for third-party applications
High. Token-based, short-lived credentials.
Medium. Complex to set up for non-interactive server-to-server flows.
Key-Pair
Recommended for Automation. Service accounts, CI/CD pipelines.
High. Asymmetric cryptography; no passwords transmitted.
High. Designed for secure, non-interactive authentication.
To implement key-pair authentication, an RSA key pair must be generated. The following openssl commands will create a 2048-bit private key in the required PKCS#8 format and its corresponding public key:
Bash
# Navigate to a secure directory, such as ~/.ssh
cd ~/.ssh
# Generate an unencrypted 2048-bit RSA private key in PKCS#8 format
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out snowflake_terraform_key.p8 -nocrypt
# Extract the public key from the private key
openssl rsa -in snowflake_terraform_key.p8 -pubout -out snowflake_terraform_key.pub
After generating the keys, the content of the public key file (snowflake_terraform_key.pub), including the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers, must be copied and pasted into the ALTER USER statement from the previous step to associate it with the TERRAFORM_SVC user. For enhanced security, the private key itself can be encrypted with a passphrase. The Snowflake provider supports this by using the private_key_passphrase argument in the provider configuration.
1.3 Provider configuration: connecting Terraform to Snowflake
With the service user created and the key-pair generated, the final step is to configure the Snowflake provider in the Terraform project. This is typically done in a providers.tf file.
The foundational configuration requires defining the snowflakedb/snowflake provider and setting the connection parameters.
terraform {
required_providers {
snowflake = {
source = "snowflakedb/snowflake" version = ">= 2.8.0"// Best practice: pin to a major version to avoid breaking changes }
}
}
provider "snowflake" {
organization_name = var.snowflake_org_name
account_name = var.snowflake_account_name
user = var.snowflake_user // e.g., "TERRAFORM_SVC" role = "SYSADMIN"// Default role for the provider's operations authenticator = "SNOWFLAKE_JWT" private_key = var.snowflake_private_key
}
It is critical that sensitive values, especially the private_key, are never hardcoded in configuration files. The recommended approach is to define them as input variables marked as sensitive = true and supply their values through secure mechanisms like environment variables (e.g., TF_VAR_snowflake_private_key) or integration with a secrets management tool like GitHub Secrets or AWS Secrets Manager.
A common source of initial connection failures is the incorrect identification of the organization_name and account_name. These values can be retrieved with certainty by executing the following SQL queries in the Snowflake UI: SELECT CURRENT_ORGANIZATION_NAME(); and SELECT CURRENT_ACCOUNT_NAME();. Providing these simple but effective commands can prevent significant user frustration.
For more mature IaC implementations that strictly adhere to the principle of least privilege, Terraform supports the use of aliased providers. This powerful pattern allows for the definition of multiple provider configurations within the same project, each assuming a different role. This mirrors Snowflake's own best practices, where object creation (SYSADMIN) is separated from security management (SECURITYADMIN).
The following example demonstrates how to configure aliased providers:
# Default provider uses SYSADMIN for object creation (e.g., databases, warehouses)
provider "snowflake" {
alias = "sysadmin" organization_name = var.snowflake_org_name
account_name = var.snowflake_account_name
user = var.snowflake_user
private_key = var.snowflake_private_key
authenticator = "SNOWFLAKE_JWT" role = "SYSADMIN"}
# Aliased provider for security-related objects (e.g., roles, users, grants)
provider "snowflake" {
alias = "securityadmin" organization_name = var.snowflake_org_name
account_name = var.snowflake_account_name
user = var.snowflake_user
private_key = var.snowflake_private_key
authenticator = "SNOWFLAKE_JWT" role = "SECURITYADMIN"}
When using aliased providers, individual resource blocks must explicitly specify which provider to use via the provider meta-argument (e.g., provider = snowflake.securityadmin). This ensures that each resource is created with the minimum necessary privileges, enforcing a robust security posture directly within the code.
Once the secure connection is bootstrapped, Terraform can be used to define and manage the fundamental building blocks of the Snowflake environment. This section provides code examples for creating databases, virtual warehouses, and schemas - the foundational components for any data workload.
2.1 Laying the foundation: databases
The database is the top-level container for schemas and tables in Snowflake. The snowflake_database resource is used to provision and manage these containers.
The following HCL example creates a primary database for analytics workloads, demonstrating the use of the aliased sysadmin provider and an optional parameter for data retention.
resource "snowflake_database""analytics_db" {
provider = snowflake.sysadmin // Explicitly use the sysadmin provider for object creation name = "ANALYTICS" comment = "Primary database for analytics workloads managed by Terraform."// Optional: Configure Time Travel data retention period.// This setting can have cost implications. data_retention_time_in_days = 30}
A core strength of Terraform is its ability to manage dependencies implicitly through resource references. In this example, once the analytics_db resource is defined, other resources, such as schemas, can reference its attributes (e.g., snowflake_database.analytics_db.name).
2.2 Compute power: warehouses
Virtual warehouses are the compute engines in Snowflake, responsible for executing queries and data loading operations. FinOps makes a difference, especially once usage grows.The snowflake_warehouse resource provides comprehensive control over their configuration, enabling a balance between performance and cost.
This example defines a standard virtual warehouse for analytics and business intelligence tools, showcasing parameters for cost optimization and scalability.
resource "snowflake_warehouse""analytics_wh" {
provider = snowflake.sysadmin
name = "ANALYTICS_WH" comment = "Warehouse for the analytics team and BI tools."// Define the compute capacity of the warehouse. warehouse_size = "X-SMALL"// Cost-saving measures: suspend the warehouse when idle. auto_suspend = 60// Suspend after 60 seconds of inactivity. auto_resume = true// Optional: Configure for multi-cluster for higher concurrency. min_cluster_count = 1 max_cluster_count = 4 scaling_policy = "ECONOMY"// Prioritize conserving credits over starting clusters quickly.}
The parameters in this resource directly impact both performance and billing. warehouse_size determines the raw compute power and credit consumption per second. auto_suspend is a critical cost-control feature, ensuring that credits are not consumed when the warehouse is idle. For workloads with high concurrency needs, the min_cluster_count, max_cluster_count, and scaling_policy parameters allow the warehouse to dynamically scale out to handle query queues, and then scale back in to conserve resources. Managing these settings via Terraform ensures that cost and performance policies are consistently applied and version-controlled.
2.3 Organizing your data: schemas
Schemas are logical groupings of database objects like tables and views within a database. The snowflake_schema resource is used to create and manage these organizational units.
The following HCL creates a RAW schema within the ANALYTICS database defined earlier.
resource "snowflake_schema""raw_data" {
provider = snowflake.sysadmin
// Create an explicit dependency on the database resource. database = snowflake_database.analytics_db.name
name = "RAW" comment = "Schema for raw, unprocessed data ingested from source systems."}
It is important to note that when a new database is created in Snowflake, it automatically includes a default schema named PUBLIC. While this schema is created outside of Terraform's management, administrators should be aware of its existence. For environments that require strict access control, it is a common practice to immediately revoke all default privileges from the
PUBLIC schema to ensure it is not used inadvertently. Terraform can be used to manage this revocation if desired, but the schema itself will not be in the Terraform state unless explicitly imported.
Section 3: mastering access control with role hierarchies
Effective access control is a cornerstone of data governance and security. Snowflake's Role-Based Access Control (RBAC) model is exceptionally powerful, particularly its support for role hierarchies. Managing this model via Terraform provides an auditable, version-controlled, and scalable approach to permissions management. This section details how to construct a robust RBAC framework using a best-practice model of functional and access roles. At scale, keeping this clean is less about writing the first version and more about maintaining standards over time, which is why Platform Team as a Service often owns RBAC and grants as the platform grows.
3.1 The building blocks: creating account roles
The foundation of the RBAC model is the creation of roles. A recommended pattern is to create two distinct types of roles:
Functional roles: These roles represent a job function or a persona, such as DATA_ANALYST or DATA_ENGINEER. Users are granted these roles.
Access roles: These roles represent a specific set of privileges on a specific set of objects, such as SALES_DB_READ_ONLY or RAW_SCHEMA_WRITE. These roles are granted to functional roles, not directly to users.
This separation decouples users from direct permissions, making the system vastly more scalable and easier to manage. The snowflake_account_role resource is used to create both types of roles
// Define a functional role representing a user persona.resource "snowflake_account_role""data_analyst" {
provider = snowflake.securityadmin // Use the securityadmin provider for role management name = "DATA_ANALYST" comment = "Functional role for users performing data analysis and reporting."}
// Define an access role representing a specific set of privileges.resource "snowflake_account_role""analytics_db_read_only" {
provider = snowflake.securityadmin
name = "ANALYTICS_DB_READ_ONLY" comment = "Grants read-only access to all objects in the ANALYTICS database."}
3.2 Constructing the hierarchy: granting roles to roles
The true power of Snowflake's RBAC model is realized by creating hierarchies of roles. By granting access roles to functional roles, a logical and maintainable privilege structure is formed. If a data analyst needs access to a new data source, the corresponding access role is granted to the DATA_ANALYST functional role once, rather than granting privileges to every individual analyst. This pattern is essential for managing permissions at scale.
The snowflake_grant_account_role resource is used to create these parent-child relationships between roles. It is important to use this resource, as the older snowflake_role_grants resource is deprecated.
The following example demonstrates how to grant the ANALYTICS_DB_READ_ONLY access role to the DATA_ANALYST functional role, and then nest the functional role under the system SYSADMIN role to complete the hierarchy.
// Grant the access role to the functional role.// This gives all members of DATA_ANALYST the privileges of ANALYTICS_DB_READ_ONLY.resource "snowflake_grant_account_role""grant_read_access_to_analyst" {
provider = snowflake.securityadmin
role_name = snowflake_account_role.analytics_db_read_only.name
parent_role_name = snowflake_account_role.data_analyst.name
}
// Grant the functional role to SYSADMIN to create a clear role hierarchy.// This allows system administrators to manage and assume the functional role.resource "snowflake_grant_account_role""grant_analyst_to_sysadmin" {
provider = snowflake.securityadmin
role_name = snowflake_account_role.data_analyst.name
parent_role_name = "SYSADMIN"}
3.3 Assigning privileges to access roles
With the role structure in place, the final step is to grant specific object privileges to the access roles. The snowflake_grant_privileges_to_account_role resource is a consolidated and powerful tool for this purpose. This resource has evolved significantly in the Snowflake provider; older versions required separate grant resources for each object type (e.g., snowflake_database_grant), which resulted in verbose and repetitive code. The modern resource uses a more complex but flexible block structure (on_account_object, on_schema, etc.) to assign privileges. Users migrating from older provider versions may find this a significant but worthwhile refactoring effort.
This example grants the necessary USAGE and SELECT privileges to the ANALYTICS_DB_READ_ONLY access role.
// Grant USAGE privilege on the database to the access role.resource "snowflake_grant_privileges_to_account_role""grant_db_usage" {
provider = snowflake.securityadmin
account_role_name = snowflake_account_role.analytics_db_read_only.name
privileges = ["USAGE"]
on_account_object {
object_type = "DATABASE" object_name = snowflake_database.analytics_db.name
}
}
// Grant USAGE privilege on the schema to the access role.resource "snowflake_grant_privileges_to_account_role""grant_schema_usage" {
provider = snowflake.securityadmin
account_role_name = snowflake_account_role.analytics_db_read_only.name
privileges = ["USAGE"]
on_schema {
// Use the fully_qualified_name for schema-level objects. schema_name = snowflake_schema.raw_data.fully_qualified_name
}
}
// Grant SELECT on all existing tables in the schema.resource "snowflake_grant_privileges_to_account_role""grant_all_tables_select" {
provider = snowflake.securityadmin
account_role_name = snowflake_account_role.analytics_db_read_only.name
privileges = ["SELECT"]
on_schema_object {
all {
object_type_plural = "TABLES" in_schema = snowflake_schema.raw_data.fully_qualified_name
}
}
}
// Grant SELECT on all FUTURE tables created in the schema.resource "snowflake_grant_privileges_to_account_role""grant_future_tables_select" {
provider = snowflake.securityadmin
account_role_name = snowflake_account_role.analytics_db_read_only.name
privileges = ["SELECT"]
on_schema_object {
future {
object_type_plural = "TABLES" in_schema = snowflake_schema.raw_data.fully_qualified_name
}
}
}
A particularly powerful feature demonstrated here is the use of the future block. Granting privileges on future objects ensures that the access role will automatically have the specified permissions on any new tables created within that schema. This dramatically reduces operational overhead, as permissions do not need to be manually updated every time a new table is deployed. However, it is important to understand Snowflake's grant precedence: future grants defined at the schema level will always take precedence over those defined at the database level. This can lead to "insufficient privilege" errors if not managed carefully across different roles and grant levels.
3.4 An optional "Audit" role for bypassing data masks
In certain scenarios, such as internal security audits or compliance reviews, it may be necessary for specific, highly-trusted users to view data that is normally protected by masking policies. Creating a dedicated "audit" role for this purpose provides a controlled and auditable mechanism to bypass data masking when required.
This role should be considered a highly privileged functional role and granted to users with extreme care.
// Define a special functional role for auditing PII data.resource "snowflake_account_role""pii_auditor" {
provider = snowflake.securityadmin
name = "PII_AUDITOR" comment = "Functional role for users who need to view unmasked PII for audit purposes."}
Crucially, creating this role is not enough. For it to be effective, every relevant masking policy must be explicitly updated to include logic that unmasks data for members of the PII_AUDITOR role. This ensures that the ability to view sensitive data is granted on a policy-by-policy basis. An example of how to modify a masking policy to incorporate this audit role is shown in the following section.
Section 4: advanced data governance with dynamic data masking
Moving beyond infrastructure provisioning, Terraform can also codify and enforce sophisticated data governance policies. Snowflake's Dynamic Data Masking is a powerful feature for protecting sensitive data at query time. By managing these policies with Terraform, organizations can ensure that data protection rules are version-controlled, auditable, and consistently applied across all environments.
4.1 Defining the masking logic
A masking policy is a schema-level object containing SQL logic that determines whether a user sees the original data in a column or a masked version. The decision is made dynamically at query time based on the user's context, most commonly their active role.
The snowflake_masking_policy resource is used to define this logic. The policy's body contains a CASE statement that evaluates the user's session context and returns the appropriate value.
The following example creates a policy to mask email addresses for any user who is not in the DATA_ANALYST or PII_AUDITOR role.
resource "snowflake_masking_policy""email_mask" {
provider = snowflake.sysadmin // Policy creation often requires SYSADMIN or a dedicated governance role name = "EMAIL_MASK" database = snowflake_database.analytics_db.name
schema = snowflake_schema.raw_data.name
// Defines the signature of the column the policy can be applied to.// The first argument is always the column value to be masked. argument {
name = "email_val" type = "VARCHAR" }
// The return data type must match the input data type. return_type = "VARCHAR"// The core masking logic is a SQL expression. body = <<-EOF
CASE
WHEN IS_ROLE_IN_SESSION('DATA_ANALYST') OR IS_ROLE_IN_SESSION('PII_AUDITOR') THEN email_val
ELSE '*********' END
EOF
comment = "Masks email addresses for all roles except DATA_ANALYST and PII_AUDITOR."}
The SQL expression within the body argument offers immense flexibility. It can use various context functions (like CURRENT_ROLE() or IS_ROLE_IN_SESSION()) and even call User-Defined Functions (UDFs) to implement complex logic. However, this flexibility means the logic itself is not validated by Terraform's syntax checker; it is sent directly to Snowflake for validation during the
terraform apply step. It is also a strict requirement that the data type defined in the argument block and the return_type must match the data type of the column to which the policy will eventually be applied.
4.2 Applying the policy to a column
Creating a masking policy is only the first step; it does not protect any data on its own. The policy must be explicitly applied to one or more table columns. This crucial second step is often a point of confusion for new users, who may create a policy and wonder why data is still unmasked. The snowflake_table_column_masking_policy_application resource creates this essential link between the policy and the column.
The following example demonstrates how to apply the EMAIL_MASK policy to the EMAIL column of a CUSTOMERS table.
// For this example, we assume a 'CUSTOMERS' table with an 'EMAIL' column// already exists in the 'RAW' schema. In a real-world scenario, this table// might also be managed by Terraform or by a separate data loading process.// We use a data source to reference this existing table.data "snowflake_table""customers" {
database = snowflake_database.analytics_db.name
schema = snowflake_schema.raw_data.name
name = "CUSTOMERS"}
// Apply the masking policy to the specific column.resource "snowflake_table_column_masking_policy_application""apply_email_mask" {
provider = snowflake.sysadmin
table_name = "\"${data.snowflake_table.customers.database}\".\"${data.snowflake_table.customers.schema}\".\"${data.snowflake_table.customers.name}\"" column_name = "EMAIL"// The name of the column to be masked masking_policy_name = snowflake_masking_policy.email_mask.fully_qualified_name
// An explicit depends_on block ensures that Terraform creates the policy// before attempting to apply it, preventing race conditions. depends_on = [
snowflake_masking_policy.email_mask
]
}
This two-step process—defining the policy logic and then applying it - provides a clear and modular approach to data governance. The same policy can be defined once and applied to many different columns across multiple tables, ensuring that the masking logic is consistent and centrally managed.
Conclusion: the path to mature Snowflake IaC
This guide has charted a course from the initial, manual bootstrapping of a secure connection to the automated provisioning and governance of a production-grade Snowflake environment. To ensure the long-term success and scalability of managing Snowflake with Terraform, several key practices should be adopted as standard procedure:
Version control: All Terraform configuration files must be stored in a version control system like Git. This provides a complete, auditable history of all infrastructure changes and enables collaborative workflows such as pull requests for peer review before any changes are applied to production.
Remote state management: The default behaviour of Terraform is to store its state file locally. In any team or automated environment, this is untenable. A remote backend, such as an Amazon S3 bucket with a DynamoDB table for state locking, must be configured. This secures the state file, prevents concurrent modifications from corrupting the state, and allows CI/CD pipelines and team members to work from a consistent view of the infrastructure.
Modularity: As the number of managed resources grows, monolithic Terraform configurations become difficult to maintain. Code should be refactored into reusable modules. For instance, a module could be created to provision a new database along with a standard set of access roles and default schemas. This promotes code reuse, reduces duplication, and allows for more organized and scalable management of the environment.
Provider versioning: The Snowflake Terraform provider is actively evolving. To prevent unexpected breaking changes from new releases, it is crucial to pin the provider to a specific major version in the terraform block (e.g., version = "~> 2.8"). This allows for intentional, planned upgrades. When upgrading between major versions, it is essential to carefully review the official migration guides, as significant changes, particularly to grant resources, may require a concerted migration effort.
With this robust foundation in place, the path is clear for expanding automation to encompass even more of Snowflake's capabilities. The next logical steps include using Terraform to manage snowflake_network_policy for network security, snowflake_row_access_policy for fine-grained data filtering, and snowflake_task for orchestrating SQL workloads. Ultimately, the entire workflow should be integrated into a CI/CD pipeline, enabling a true GitOps model where every change to the Snowflake environment is proposed, reviewed, and deployed through a fully automated and audited process. By embracing this comprehensive approach, organizations can unlock the full potential of their data platform, confident in its security, scalability, and operational excellence.
Why Snowstack for Terraform and Snowflake
Automation without expertise can still fail. Terraform gives you the tools, but it takes experience and the right design patterns to turn Snowflake into a secure, cost-efficient, and scalable platform. The hard part is deciding what “good” looks like in your Snowflake account and making that repeatable across teams, environments, and change cycles.
That is where Snowstack comes in. As a Snowflake-first consulting partner, we help organizations move beyond trial-and-error scripts to fully automated, production-grade environments. Our engineers design secure architectures, embed Terraform best practices, and ensure governance and cost controls are built in from day one.
Start with the stable building blocks: warehouses, databases, schemas, roles, and grants. Codifying these gives
you a repeatable baseline, clearer reviews, and fewer "who changed what" moments. If you want help turning that
into a production standard, our
Snowflake consulting
is built for exactly that.
Yes. A dedicated service user keeps changes auditable, avoids personal-account dependencies, and makes it easier
to enforce least privilege. We usually set this up during a
AI-ready Snowflake Data Platform implementation
so the roles and permissions are clean from the start.
Key pair authentication is the go-to approach for automation because it avoids passwords and supports key
rotation. The real "gotchas" are how you store the private key, how you rotate it without downtime, and how you
lock down the service role. If you want a proven baseline, that's a common starting point in
Snowflake consulting.
Drift usually comes from "out-of-band" changes in Snowsight, differences in naming/identifiers, or having more
than one resource trying to own the same privilege set. The fix is to pick one source of truth, standardize
object identifiers, and keep grants/ownership patterns consistent. When teams need ongoing ownership, that's what
Platform Team as a Service
is for.
Make cost controls part of the code, not tribal knowledge. That means codifying warehouse sizing rules, auto
suspend, scaling settings, and environment defaults, then pairing it with a usage review cadence. If savings is
the goal, this is exactly the focus of our
FinOps
work.
Provider upgrades can include grant model changes and deprecations, which can break older configurations if
you're still using removed resources. Treat upgrades like a controlled migration: update in steps, migrate grant
resources intentionally, and avoid mixing old and new grant patterns in the same scope. If you want us to review
the upgrade path before you roll it out, start with
Snowflake consulting.
Yes, but do it in phases. First codify a baseline (core warehouses, core roles, and standard grants), then
gradually bring the rest under management through imports or controlled rebuilds. The goal is to reduce risk,
not "flip a switch" overnight. We typically approach this through
Snowflake consulting
or a scoped
AI-ready Snowflake Data Platform implementation.
Explore our latest blog posts for valuable insights.
We’ll keep you posted with fresh updates and resources.
Oops! Something went wrong while submitting the form.
Transform your data with Snowflake
You don't need to hire a data army or wait months to see results. Our Snowflake specialists will get you up and running fast, so you can make better decisions, cut costs, and beat competitors who are still stuck with spreadsheets and legacy systems