Least-privilege Snowflake grants

Provision the read-only role the dashboard connects with.

Back to admin
Provisioning script
Run as ACCOUNTADMIN once, then store the private key as a secret.
-- 1. Dedicated, least-privilege reporting role
use role accountadmin;
create role if not exists cost_dashboard_reader;

-- 2. Read-only access to account usage (no data access to user tables)
grant imported privileges on database snowflake to role cost_dashboard_reader;
grant database role snowflake.usage_viewer to role cost_dashboard_reader;

-- 3. Small dedicated warehouse with aggressive auto-suspend
create warehouse if not exists cost_dashboard_wh
  warehouse_size = xsmall
  auto_suspend = 60
  auto_resume = true
  initially_suspended = true;
grant usage, operate on warehouse cost_dashboard_wh to role cost_dashboard_reader;

-- 4. Service user with key-pair authentication (no password, no UI login)
create user if not exists svc_cost_dashboard
  default_role = cost_dashboard_reader
  default_warehouse = cost_dashboard_wh
  type = service
  rsa_public_key = '<PASTE_PUBLIC_KEY_BODY>';
grant role cost_dashboard_reader to user svc_cost_dashboard;

-- 5. Optional: pre-aggregated reporting table to cut ACCOUNT_USAGE scan cost
create schema if not exists analytics.finops;
create table if not exists analytics.finops.cost_analytics (
  usage_date date,
  warehouse_name string,
  database_name string,
  credits_used number(38,6),
  compute_credits number(38,6),
  cloud_services_credits number(38,6),
  storage_tb number(38,6)
);
grant usage on database analytics to role cost_dashboard_reader;
grant usage on schema analytics.finops to role cost_dashboard_reader;
grant select on table analytics.finops.cost_analytics to role cost_dashboard_reader;

-- 6. Never grant: ACCOUNTADMIN, SECURITYADMIN, SYSADMIN, or write privileges.
Operational notes
Security and cost-control behaviour of the data layer.
  • Key-pair (unencrypted PKCS#8) or OAuth only — password auth is rejected by the app.
  • The service user holds no write privileges anywhere in the account.
  • ACCOUNT_USAGE views carry up to 45 minutes of latency; reports are cached for 5 minutes.
  • Query ranges are clamped to 400 days and query listings are paginated server-side.
  • SQL text from QUERY_HISTORY is returned only for the current page and hidden in the UI by default.