🔥 Now available: AI Dashboards. Learn More ➡️

From Snowflake Intelligence to the Spreadsheet: A Builder’s Guide to Last-Mile Data Activation

Published: July 13, 2026

down-chevron

Artem Chetverykov

Head of Product Marketing

Desktop Hero Image Mobile Hero Image

How to take governed answers from semantic views and Cortex all the way into the Excel and Google Sheets models where your business actually runs.

If you run a Snowflake environment, you have probably lived this sequence. You spend a quarter building a clean, governed reporting layer. You stand up a semantic view so Cortex Analyst can answer questions in natural language. Leadership loves the demo. And then, on the first day of month-end close, someone on the finance team exports the whole thing to CSV, pastes it into a spreadsheet, and builds the real forecast there.

Nobody did anything wrong. The warehouse is where data is governed; the spreadsheet is where decisions are made. The problem is the gap between them, and every CSV export that crosses that gap silently discards your row access policies, your column masking, and your single definition of revenue.

This post is a practical walkthrough for closing that gap. By the end, you will have:

  • A governed reporting layer with secure views and a semantic view that Cortex Analyst can reason over
  • A least-privilege Snowflake role scoped for spreadsheet consumption
  • Live, scheduled, query-pushdown delivery of that same governed data into Google Sheets and Excel using Coefficient, a Snowflake connected app
  • Query tagging so you can see exactly what the spreadsheet tier costs you
TLDR
Snowflake Intelligence solves trusted answers. It does not solve where business users work, which is still Excel and Google Sheets. Instead of fighting that, extend the governed layer into it: build secure views and a semantic view in Snowflake, create a read-only role scoped to the reporting schema, and connect spreadsheets through a connected app that pushes queries down to Snowflake instead of duplicating data. Same definitions, same governance, zero CSV exports.

The architecture in one picture

The pattern has four layers, and the important property is that nothing to the right of Snowflake ever becomes a second source of truth. Every layer reads through to the same governed objects.

Raw + modeled data
ANALYTICS.FINANCE tables (dbt, Dynamic Tables)
Governed layer
Secure views, masking, row access policies
Intelligence
Semantic view + Cortex Analyst
Last mile
Live Sheets / Excel via connected app (pushdown)

Figure 1: One governed path from modeled data to the spreadsheet. No exports, no copies.

Step 1: Build the governed reporting layer

Start with a schema that exists only to be consumed, separate from your modeling schemas. Everything downstream (BI, Cortex, spreadsheets) reads from here and nowhere else.

CREATE SCHEMA IF NOT EXISTS ANALYTICS.REPORTING;

 

— A secure view over your modeled revenue table.

 

— Secure views hide the definition and enforce policies for

 

— every consumer, including spreadsheet users.

 

CREATE OR REPLACE SECURE VIEW ANALYTICS.REPORTING.REVENUE_MONTHLY AS

 

SELECT

 

  close_month,

 

  business_unit,

 

  region,

 

  product_line,

 

  SUM(recognized_revenue) AS recognized_revenue,

 

  SUM(billed_amount) AS billed_amount,

 

  COUNT(DISTINCT account_id) AS active_accounts

 

FROM ANALYTICS.FINANCE.FCT_REVENUE

 

GROUP BY 1, 2, 3, 4;

 

If different teams should see different slices, attach a row access policy here, at the view level. This is the part a CSV export throws away, and the part this architecture preserves all the way to the spreadsheet.

 

CREATE OR REPLACE ROW ACCESS POLICY ANALYTICS.REPORTING.RAP_BUSINESS_UNIT

 

AS (business_unit STRING) RETURNS BOOLEAN ->

 

  EXISTS (

 

    SELECT 1 FROM ANALYTICS.SECURITY.UNIT_ENTITLEMENTS e

 

    WHERE e.role_name = CURRENT_ROLE()

 

      AND e.business_unit = business_unit

 

  );

 

ALTER VIEW ANALYTICS.REPORTING.REVENUE_MONTHLY

 

  ADD ROW ACCESS POLICY ANALYTICS.REPORTING.RAP_BUSINESS_UNIT

 

  ON (business_unit);

Step 2: Add a semantic view so Cortex Analyst speaks finance

Snowflake Intelligence is only as good as the semantics you give it. A semantic view declares your tables, relationships, dimensions, and metrics once, so Cortex Analyst answers “what was recognized revenue in EMEA last quarter” with your definition of recognized revenue, not a plausible guess.

CREATE OR REPLACE SEMANTIC VIEW ANALYTICS.REPORTING.FINANCE_SEMANTICS

TABLES (

revenue AS ANALYTICS.REPORTING.REVENUE_MONTHLY

PRIMARY KEY (close_month, business_unit, region, product_line)

COMMENT = ‘Monthly recognized revenue by unit, region, product line’

)

DIMENSIONS (

revenue.close_month COMMENT = ‘Accounting close month’,

revenue.business_unit COMMENT = ‘Internal business unit’,

revenue.region COMMENT = ‘Sales region (AMER, EMEA, APAC)’

)

METRICS (

revenue.total_recognized AS SUM(revenue.recognized_revenue)

COMMENT = ‘Recognized revenue per ASC 606 schedule’,

revenue.total_billed AS SUM(revenue.billed_amount)

COMMENT = ‘Gross billed amount before rev rec’

)

COMMENT = ‘Semantic model for finance reporting and Cortex Analyst’;

Now analysts, agents, and anyone using Snowflake Intelligence gets consistent answers. Which makes it all the more painful if the last mile still runs on a CSV from three weeks ago. Let’s fix that part.

Step 3: Create a least-privilege role for spreadsheet consumption

Spreadsheet users should reach exactly one schema: the reporting layer. Create a dedicated role and a small warehouse so the workload is isolated and measurable.

USE ROLE SECURITYADMIN;

CREATE ROLE IF NOT EXISTS SHEETS_READONLY;

USE ROLE SYSADMIN;

CREATE WAREHOUSE IF NOT EXISTS SHEETS_WH

WAREHOUSE_SIZE = ‘XSMALL’

AUTO_SUSPEND = 60   — suspend fast; sheet refreshes are bursty

AUTO_RESUME = TRUE

INITIALLY_SUSPENDED = TRUE;

GRANT USAGE ON WAREHOUSE SHEETS_WH TO ROLE SHEETS_READONLY;

GRANT USAGE ON DATABASE ANALYTICS TO ROLE SHEETS_READONLY;

GRANT USAGE ON SCHEMA ANALYTICS.REPORTING TO ROLE SHEETS_READONLY;

GRANT SELECT ON ALL VIEWS IN SCHEMA ANALYTICS.REPORTING TO ROLE SHEETS_READONLY;

GRANT SELECT ON FUTURE VIEWS IN SCHEMA ANALYTICS.REPORTING TO ROLE SHEETS_READONLY;

GRANT ROLE SHEETS_READONLY TO USER FINANCE_SVC_USER;

— The connector runs under the user’s default role and warehouse,

— so pin both to the scoped objects.

ALTER USER FINANCE_SVC_USER

SET DEFAULT_ROLE = SHEETS_READONLY,

DEFAULT_WAREHOUSE = SHEETS_WH;

Two things to notice. The role has no access to ANALYTICS.FINANCE or any raw schema, so even a hand-written query from a spreadsheet can only touch governed views. And because row access policies evaluate against CURRENT_ROLE(), the entitlements you defined in Step 1 follow the data into the sheet automatically.

Step 4: Connect the spreadsheet with query pushdown

This is where a connected app like Coefficient comes in. It runs as a sidebar inside Google Sheets & Excel and executes queries directly against Snowflake under the role you just created. The data lands in the grid, but it is never copied into a second system of record: every refresh is a fresh SELECT pushed down to Snowflake.

The setup is UI work rather than code:

  1. Install the add-in from the Google Workspace Marketplace or Microsoft AppSource and open the sidebar.
  2. Add a Snowflake connection: your Snowflake account name, database (ANALYTICS), and warehouse (SHEETS_WH), authenticating as FINANCE_SVC_USER. Prefer OAuth or key-pair authentication over passwords; Snowflake is retiring password-only sign-in in 2026. The connection runs under the user’s default role, which you pinned to SHEETS_READONLY above.
  3. Import data either through the visual query builder (pick the REVENUE_MONTHLY view, choose columns and filters) or with custom SQL when the sheet needs a specific shape:

— Import backing a board-pack pivot. {{business_unit}} is a SQL

— parameter linked to a spreadsheet cell, so editing that cell

— re-runs the query in Snowflake with the new value.

SELECT close_month, region, product_line,

       recognized_revenue, billed_amount

FROM ANALYTICS.REPORTING.REVENUE_MONTHLY

WHERE close_month >= DATEADD(month, -12, CURRENT_DATE())

  AND business_unit = {{business_unit}}

ORDER BY close_month;

  1. Set a refresh schedule on the import: hourly (down to every hour), daily, or weekly, with a manual refresh button in the sheet header for ad hoc pulls. Monthly Snapshots can archive each month-end state to its own tab for audit.

From the finance team’s perspective nothing changed: they are still in their spreadsheet, with their formulas and their pivot tables. From your perspective everything changed: the numbers in that model are now the same numbers Cortex Analyst gives, refreshed on a schedule, filtered by the same row access policy, with zero CSVs in flight.

Step 5: Make the spreadsheet tier observable

Because all spreadsheet traffic flows through one role and one warehouse, measuring it is a single query against ACCOUNT_USAGE:

SELECT

DATE_TRUNC(‘day’, start_time) AS day,

COUNT(*) AS queries,

SUM(total_elapsed_time) / 1000 / 60 AS total_minutes,

SUM(credits_used_cloud_services) AS cloud_services_credits

FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY

WHERE role_name = ‘SHEETS_READONLY’

AND start_time >= DATEADD(day, -30, CURRENT_TIMESTAMP())

GROUP BY 1

ORDER BY 1 DESC;

In practice this tier is cheap. Reporting views are aggregated, the warehouse is an XSMALL that suspends after 60 seconds, and scheduled refreshes batch naturally. What you get back is significant: every refresh in QUERY_HISTORY is a spreadsheet that did not become a stale, ungoverned copy of your data.

Why this complements Snowflake Intelligence rather than competing with it

It is tempting to treat natural-language analytics and spreadsheets as rivals for the same users. They are not. Cortex Analyst and Snowflake Intelligence are how people ask questions of governed data. Spreadsheets are where people operate on it: building driver-based forecasts, reconciling billing, running headcount scenarios. The semantic view you built in Step 2 serves the first need. The live connection you built in Step 4 serves the second. Both read the same secure views, so for the first time the answer in the AI chat and the number in cell C14 of the board pack agree by construction.

Wrapping up

What you built, in order: a secure reporting schema with row access policies, a semantic view for Cortex Analyst, a least-privilege role and isolated warehouse for spreadsheet workloads, a live pushdown connection into Sheets and Excel with scheduled refresh, and an observability query to keep it honest.

The takeaway for builders: the last mile is not a tooling problem to ignore or a user habit to break. It is a surface to govern. Snowflake already gives you the primitives; the only decision is whether business users reach the governed layer directly, or through CSVs that quietly undo all of it.