DBSheriff connects to every database it monitors with its own accounts, one per job, never your application's login. This page lists, for each of the twelve engines, which accounts are required and which are optional, what each one is allowed to do, and the statements that create it.
Two ways to create them:
create_reset_dbuser.py (or setup_dbsheriff.py, which also sets the server prerequisites) connects as your administrator and runs exactly the statements below. --dry-run prints the statements without changing anything: with create_reset_dbuser.py it makes no connection at all, while setup_dbsheriff.py --dry-run still connects to check the prerequisites but writes nothing. On an installed host: sudo /opt/dbsheriff/current/deploy/onprem/stack.sh tool create_reset_dbuser <engine> <hostname>.<password> with a strong password of your own, and the <...> names with yours (the tool discovers these; by hand you list them). The statements are the same on a managed service except where this page says otherwise.| Tier | Accounts | What it may change in your database |
|---|---|---|
| Monitor | monitor, and its read extensions posture, classify, plan (and MongoDB's view-users role) | Nothing. Reads the database's own views and statistics; classify also reads application rows. |
| Respond | kill, lock | Ends a session or locks an account. Reversible, time-boxed changes only. |
| Control | control | Creates masks, roles and limits that stay in place if DBSheriff stops. Off by default; created only on request. |
Only the monitor account is required to register a server. Everything else is added when you use the feature it serves. In the Admin Console the kill, lock and posture credentials are optional fields in the collapsed Advanced sections:
CAN_MANAGE on the monitored warehouses and can cancel statements; only the lock, revoke and tag write-back fail without the response principal. Create the accounts before you turn on automatic response.| Engine | monitor | kill | lock | posture | classify | plan | control |
|---|---|---|---|---|---|---|---|
| PostgreSQL, Aurora PostgreSQL | Required | To respond | To respond | Optional | For classification | Recommended | Opt-in |
| MySQL, Aurora MySQL | Required | To respond | To respond | Optional | For classification | Recommended | Opt-in |
| SQL Server (all platforms) | Required | To respond | To respond | Optional | For classification | — | Opt-in |
| Oracle | Required | To respond | To respond | Optional | For classification | — | — |
| SAP HANA | Required | To respond | To respond | Optional | For classification | — | — |
| Amazon Redshift | Required | To respond | To respond | Optional | For classification | Recommended | — |
| Snowflake | Required | To respond | To respond | Optional | For classification | — | Opt-in |
| MongoDB, DocumentDB, Atlas | Required (+ view-users role on self-managed) | To respond | To respond | — | For classification | — | — |
| Azure Synapse | Required | Workspace admin login | Workspace admin login | Azure AD app (4 checks) | For classification | — | — |
| Google BigQuery | Required | One response account (kill and lock) | (same account) | Optional | For classification | — | — |
| Databricks | Required | One response principal (kill and lock) | (same principal) | Optional | For classification | — | — |
| Google Cloud Spanner | Required | To respond | To respond | Optional | For classification | — | — |
To respond means: required if you want DBSheriff to end sessions (kill) or lock accounts (lock), optional if you only monitor and alert. For classification means: required only to scan application data for sensitive columns. Opt-in accounts are created only when you ask for them.
Plain roles with a password; one set per server, CONNECT and read grants per database. Default port 5432. Full guide: setup guide.
| Account | Default name | Needed? | Tier | What it is for | If it is missing |
|---|---|---|---|---|---|
| monitor | dbsheriff_monitor |
Required | Monitor (read) | Live statements, stats, lock waits, table read counters | The server cannot be registered: every poll, collector and scan starts from this account. |
| kill | dbsheriff_kill |
Required to end sessions | Respond | Terminate/cancel statements and sessions | A kill is tried with the monitor account, which holds no right to end a session, so it fails and is recorded as an error. |
| lock | dbsheriff_lock |
Required to lock accounts | Respond | Lock ladder, graduated response, classification write-back | A lock is tried with the kill account, then the monitor account; neither can lock, so it fails and is recorded as an error. |
| posture | dbsheriff_posture |
Optional | Monitor (read extension) | Posture checks + hourly governance collection | The monitor account does the posture checks and the hourly governance read. |
| classify | dbsheriff_classify |
Required for classification | Monitor (read extension) | Column metadata, value sampling for classification | No value sampling for this server. There is no fallback to the monitor account, on purpose: it would try to read application rows with an account that should not have them. |
| plan | dbsheriff_plan |
Recommended | Monitor (read extension) | EXPLAIN of captured statements under SELECT-only rights (recommended, not used yet; no signal, file-read, write or DDL rights) | Nothing else changes. It is the login an EXPLAIN of a captured statement can run under, with SELECT-only rights; nothing in DBSheriff uses it yet, and the EXPLAIN runs under the monitor account until that is switched. The Admin Console form has no field for its password: create_reset_dbuser.py and setup_dbsheriff.py record it in the registry, and onboard_target.py writes it to its mode-600 credentials file instead. |
| control | dbsheriff_control |
Optional, opt-in | Control | Creates and changes roles, role settings and memberships for native controls; CREATEROLE only, nothing else | A server cannot be set to managed mode without it. Native controls stay off (the default) unless an approver chooses script mode, in which DBSheriff writes the SQL and your DBA runs it; script mode needs no account. This account never falls back to another one, and is only created when you ask for it (--with-control). |
dbsheriff_monitorSelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / AlloyDB / VM), Azure (Flexible Server / VM)
CREATE ROLE dbsheriff_monitor LOGIN PASSWORD '<password>';
GRANT pg_monitor, pg_read_all_stats TO dbsheriff_monitor;
\c <each non-system database>
GRANT CONNECT ON DATABASE <each non-system database> TO dbsheriff_monitor;
-- repeat per non-system schema in <each non-system database>:
GRANT USAGE ON SCHEMA <schema> TO dbsheriff_monitor;
GRANT REFERENCES ON ALL TABLES IN SCHEMA <schema> TO dbsheriff_monitor;
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema> GRANT REFERENCES ON TABLES TO dbsheriff_monitor;
-- optional SQL-log-read grants skipped here — see the setup guide for pg_read_server_files etc.
-- RDS/Aurora: no filesystem access — read the server log via the log_fdw extension instead (see the guide's AWS section), not a plain GRANT.
dbsheriff_killSelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / AlloyDB / VM), Azure (Flexible Server / VM)
CREATE ROLE dbsheriff_kill LOGIN PASSWORD '<password>';
GRANT pg_monitor TO dbsheriff_kill; -- required for the pre-kill verification read, not optional
GRANT pg_signal_backend TO dbsheriff_kill;
\c <each non-system database>
GRANT CONNECT ON DATABASE <each non-system database> TO dbsheriff_kill;
dbsheriff_lockSelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / AlloyDB / VM), Azure (Flexible Server / VM)
CREATE ROLE dbsheriff_lock LOGIN PASSWORD '<password>' CREATEROLE;
GRANT pg_signal_backend TO dbsheriff_lock;
-- PostgreSQL 16+ only (checked live, not here — no connection in --dry-run): also grants dbsheriff_lock ADMIN OPTION on every existing non-superuser role, so the lock ladder can actually alter/drop them — see the setup guide, "Creating the roles".
\c <each non-system database>
GRANT CONNECT ON DATABASE <each non-system database> TO dbsheriff_lock;
GRANT CREATE ON DATABASE <each non-system database> TO dbsheriff_lock; -- write-back, when dbsheriff_lock doesn't own the tables
dbsheriff_postureSelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / AlloyDB / VM), Azure (Flexible Server / VM)
CREATE ROLE dbsheriff_posture LOGIN PASSWORD '<password>';
GRANT pg_monitor, pg_read_all_stats TO dbsheriff_posture;
\c <each non-system database>
GRANT CONNECT ON DATABASE <each non-system database> TO dbsheriff_posture;
-- repeat per non-system schema in <each non-system database>:
GRANT USAGE ON SCHEMA <schema> TO dbsheriff_posture;
GRANT REFERENCES ON ALL TABLES IN SCHEMA <schema> TO dbsheriff_posture;
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema> GRANT REFERENCES ON TABLES TO dbsheriff_posture;
dbsheriff_classifySelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / AlloyDB / VM), Azure (Flexible Server / VM)
CREATE ROLE dbsheriff_classify LOGIN PASSWORD '<password>';
\c <each non-system database>
GRANT CONNECT ON DATABASE <each non-system database> TO dbsheriff_classify;
-- repeat per schema to scan in <each non-system database> (narrow this list if you prefer):
GRANT USAGE ON SCHEMA <schema> TO dbsheriff_classify;
GRANT SELECT ON ALL TABLES IN SCHEMA <schema> TO dbsheriff_classify;
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema> GRANT SELECT ON TABLES TO dbsheriff_classify;
dbsheriff_planSelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / AlloyDB / VM), Azure (Flexible Server / VM)
CREATE ROLE dbsheriff_plan LOGIN PASSWORD '<password>';
\c <each non-system database>
GRANT CONNECT ON DATABASE <each non-system database> TO dbsheriff_plan;
-- repeat per schema to scan in <each non-system database> (narrow this list if you prefer):
GRANT USAGE ON SCHEMA <schema> TO dbsheriff_plan;
GRANT SELECT ON ALL TABLES IN SCHEMA <schema> TO dbsheriff_plan;
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema> GRANT SELECT ON TABLES TO dbsheriff_plan;
dbsheriff_controlSelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / AlloyDB / VM), Azure (Flexible Server / VM)
CREATE ROLE dbsheriff_control LOGIN PASSWORD '<password>' CREATEROLE;
-- control: CREATEROLE only. It lets the account create roles and set per-role limits (statement_timeout, CONNECTION LIMIT); on PostgreSQL 16+ it can change only the roles it created or holds ADMIN OPTION on, so give it ADMIN OPTION on the specific roles a control will manage (GRANT <role> TO dbsheriff_control WITH ADMIN OPTION), never on all of them. See the setup guide.
\c <each non-system database>
GRANT CONNECT ON DATABASE <each non-system database> TO dbsheriff_control;
Plain users with a password, tied to the address DBSheriff connects from. Default port 3306. Full guide: setup guide.
| Account | Default name | Needed? | Tier | What it is for | If it is missing |
|---|---|---|---|---|---|
| monitor | dbsheriff_monitor |
Required | Monitor (read) | Statement history, process list, session/table counters, accounts, error log | The server cannot be registered: every poll, collector and scan starts from this account. |
| kill | dbsheriff_kill |
Required to end sessions | Respond | Terminate/cancel statements and sessions | A kill is tried with the monitor account, which holds no right to end a session, so it fails and is recorded as an error. |
| lock | dbsheriff_lock |
Required to lock accounts | Respond | Lock ladder, graduated response, classification write-back | A lock is tried with the kill account, then the monitor account; neither can lock, so it fails and is recorded as an error. |
| posture | dbsheriff_posture |
Optional | Monitor (read extension) | Posture checks + hourly governance collection | The monitor account does the posture checks and the hourly governance read. |
| classify | dbsheriff_classify |
Required for classification | Monitor (read extension) | Column metadata, value sampling for classification | No value sampling for this server. There is no fallback to the monitor account, on purpose: it would try to read application rows with an account that should not have them. |
| plan | dbsheriff_plan |
Recommended | Monitor (read extension) | EXPLAIN of captured statements under SELECT-only rights (recommended, not used yet; no PROCESS, FILE, write or DDL rights) | Nothing else changes. It is the login an EXPLAIN of a captured statement can run under, with SELECT-only rights; nothing in DBSheriff uses it yet, and the EXPLAIN runs under the monitor account until that is switched. The Admin Console form has no field for its password: create_reset_dbuser.py and setup_dbsheriff.py record it in the registry, and onboard_target.py writes it to its mode-600 credentials file instead. |
| control | dbsheriff_control |
Optional, opt-in | Control | Creates and drops roles and sets per-account limits for native controls; CREATE USER and ROLE_ADMIN, plus FIREWALL_ADMIN on Enterprise | A server cannot be set to managed mode without it. Native controls stay off (the default) unless an approver chooses script mode, in which DBSheriff writes the SQL and your DBA runs it; script mode needs no account. This account never falls back to another one, and is only created when you ask for it (--with-control). |
dbsheriff_monitorSelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / VM), Azure (Flexible Server / VM)
CREATE USER 'dbsheriff_monitor'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT PROCESS ON *.* TO 'dbsheriff_monitor'@'<address DBSheriff connects from>';
GRANT SELECT ON performance_schema.* TO 'dbsheriff_monitor'@'<address DBSheriff connects from>';
GRANT SELECT (User, Host, plugin, account_locked, password_expired, ssl_type, max_questions, max_user_connections, password_last_changed, password_lifetime, User_attributes, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Reload_priv, Shutdown_priv, Process_priv, File_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Show_db_priv, Super_priv, Create_tmp_table_priv, Lock_tables_priv, Execute_priv, Repl_slave_priv, Repl_client_priv, Create_view_priv, Show_view_priv, Create_routine_priv, Alter_routine_priv, Create_user_priv, Event_priv, Trigger_priv, Create_tablespace_priv, Create_role_priv, Drop_role_priv) ON mysql.user TO 'dbsheriff_monitor'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.global_grants TO 'dbsheriff_monitor'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.role_edges TO 'dbsheriff_monitor'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.func TO 'dbsheriff_monitor'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.component TO 'dbsheriff_monitor'@'<address DBSheriff connects from>';
GRANT USAGE ON `<each schema to monitor>`.* TO 'dbsheriff_monitor'@'<address DBSheriff connects from>';
-- optional: SELECT ON mysql.tables_priv and SELECT ON mysql.db, as two separate GRANTs (role_table_grants auto-discovery of DB Roles on classified tables) — skipped, see the setup guide.
-- optional: SELECT ON mysql.general_log — only needed when mysql_audit_source is the general_log table; skipped, see the setup guide.
dbsheriff_killSelf-managed (your own server or VM), GCP (Cloud SQL / VM)
CREATE USER 'dbsheriff_kill'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT CONNECTION_ADMIN, PROCESS ON *.* TO 'dbsheriff_kill'@'<address DBSheriff connects from>';
GRANT SELECT ON performance_schema.* TO 'dbsheriff_kill'@'<address DBSheriff connects from>';
AWS (RDS / Aurora)
CREATE USER 'dbsheriff_kill'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT PROCESS ON *.* TO 'dbsheriff_kill'@'<address DBSheriff connects from>';
GRANT SELECT ON performance_schema.* TO 'dbsheriff_kill'@'<address DBSheriff connects from>';
GRANT EXECUTE ON PROCEDURE mysql.rds_kill TO 'dbsheriff_kill'@'<address DBSheriff connects from>';
GRANT EXECUTE ON PROCEDURE mysql.rds_kill_query TO 'dbsheriff_kill'@'<address DBSheriff connects from>';
Azure (Flexible Server / VM)
CREATE USER 'dbsheriff_kill'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT PROCESS ON *.* TO 'dbsheriff_kill'@'<address DBSheriff connects from>';
GRANT SELECT ON performance_schema.* TO 'dbsheriff_kill'@'<address DBSheriff connects from>';
GRANT EXECUTE ON PROCEDURE mysql.az_kill TO 'dbsheriff_kill'@'<address DBSheriff connects from>';
GRANT EXECUTE ON PROCEDURE mysql.az_kill_query TO 'dbsheriff_kill'@'<address DBSheriff connects from>';
dbsheriff_lockSelf-managed (your own server or VM), GCP (Cloud SQL / VM)
CREATE USER 'dbsheriff_lock'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT CREATE USER ON *.* TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT CONNECTION_ADMIN ON *.* TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.user TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.global_grants TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT SELECT ON performance_schema.* TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
-- optional write-back target (mysql_tag_writeback): CREATE DATABASE IF NOT EXISTS dbsheriff; GRANT SELECT, INSERT, UPDATE, CREATE ON dbsheriff.* TO the lock account — skipped, see the setup guide.
AWS (RDS / Aurora)
CREATE USER 'dbsheriff_lock'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT CREATE USER ON *.* TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT EXECUTE ON PROCEDURE mysql.rds_kill TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT EXECUTE ON PROCEDURE mysql.rds_kill_query TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.user TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.global_grants TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT SELECT ON performance_schema.* TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
-- optional write-back target (mysql_tag_writeback): CREATE DATABASE IF NOT EXISTS dbsheriff; GRANT SELECT, INSERT, UPDATE, CREATE ON dbsheriff.* TO the lock account — skipped, see the setup guide.
Azure (Flexible Server / VM)
CREATE USER 'dbsheriff_lock'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT CREATE USER ON *.* TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT EXECUTE ON PROCEDURE mysql.az_kill TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT EXECUTE ON PROCEDURE mysql.az_kill_query TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.user TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.global_grants TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
GRANT SELECT ON performance_schema.* TO 'dbsheriff_lock'@'<address DBSheriff connects from>';
-- optional write-back target (mysql_tag_writeback): CREATE DATABASE IF NOT EXISTS dbsheriff; GRANT SELECT, INSERT, UPDATE, CREATE ON dbsheriff.* TO the lock account — skipped, see the setup guide.
dbsheriff_postureSelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / VM), Azure (Flexible Server / VM)
CREATE USER 'dbsheriff_posture'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT PROCESS ON *.* TO 'dbsheriff_posture'@'<address DBSheriff connects from>';
GRANT SELECT ON performance_schema.* TO 'dbsheriff_posture'@'<address DBSheriff connects from>';
GRANT SELECT (User, Host, plugin, account_locked, password_expired, ssl_type, max_questions, max_user_connections, password_last_changed, password_lifetime, User_attributes, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Reload_priv, Shutdown_priv, Process_priv, File_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Show_db_priv, Super_priv, Create_tmp_table_priv, Lock_tables_priv, Execute_priv, Repl_slave_priv, Repl_client_priv, Create_view_priv, Show_view_priv, Create_routine_priv, Alter_routine_priv, Create_user_priv, Event_priv, Trigger_priv, Create_tablespace_priv, Create_role_priv, Drop_role_priv) ON mysql.user TO 'dbsheriff_posture'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.global_grants TO 'dbsheriff_posture'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.role_edges TO 'dbsheriff_posture'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.func TO 'dbsheriff_posture'@'<address DBSheriff connects from>';
GRANT SELECT ON mysql.component TO 'dbsheriff_posture'@'<address DBSheriff connects from>';
GRANT USAGE ON `<each schema to monitor>`.* TO 'dbsheriff_posture'@'<address DBSheriff connects from>';
-- optional: SELECT ON mysql.tables_priv and SELECT ON mysql.db, as two separate GRANTs (role_table_grants auto-discovery of DB Roles on classified tables) — skipped, see the setup guide.
-- optional: SELECT ON mysql.general_log — only needed when mysql_audit_source is the general_log table; skipped, see the setup guide.
dbsheriff_classifySelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / VM), Azure (Flexible Server / VM)
CREATE USER 'dbsheriff_classify'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT SELECT ON performance_schema.threads TO 'dbsheriff_classify'@'<address DBSheriff connects from>';
GRANT SELECT ON `<each schema to monitor>`.* TO 'dbsheriff_classify'@'<address DBSheriff connects from>';
-- classify: SELECT on performance_schema.threads (and no other performance_schema table) lets DBSheriff's classification sessions name their own thread, so that the producer does not score them as a customer's `unknown`. It also shows this login the server's live process list, including the statement each session is running. A server without that table (MariaDB) refuses it: it is skipped with a note and the scan runs as before (MariaDB is left as it was). A server onboarded before this change gets it by running this command again (reset).
dbsheriff_planSelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / VM), Azure (Flexible Server / VM)
CREATE USER 'dbsheriff_plan'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT SELECT ON `<each schema to monitor>`.* TO 'dbsheriff_plan'@'<address DBSheriff connects from>';
dbsheriff_controlSelf-managed (your own server or VM), AWS (RDS / Aurora), GCP (Cloud SQL / VM), Azure (Flexible Server / VM)
CREATE USER 'dbsheriff_control'@'<address DBSheriff connects from>' IDENTIFIED BY '<password>';
GRANT CREATE USER ON *.* TO 'dbsheriff_control'@'<address DBSheriff connects from>';
GRANT ROLE_ADMIN ON *.* TO 'dbsheriff_control'@'<address DBSheriff connects from>';
GRANT FIREWALL_ADMIN ON *.* TO 'dbsheriff_control'@'<address DBSheriff connects from>';
GRANT EXECUTE ON PROCEDURE mysql.sp_set_firewall_mode TO 'dbsheriff_control'@'<address DBSheriff connects from>';
-- control: CREATE USER lets the account create and drop roles and set per-account limits; ROLE_ADMIN lets it grant roles; FIREWALL_ADMIN and EXECUTE on mysql.sp_set_firewall_mode are MySQL Enterprise Edition only (the allow-list feature of the server itself, which needs both). A privilege the server refuses is skipped with a note, never widened. See the setup guide.
Server logins plus a user in each database; the grants differ most between platforms. Default port 1433. Full guide: setup guide.
| Account | Default name | Needed? | Tier | What it is for | If it is missing |
|---|---|---|---|---|---|
| monitor | dbsheriff_monitor |
Required | Monitor (read) | Live requests/sessions, counters, logins, error log, audit reads | The server cannot be registered: every poll, collector and scan starts from this account. |
| kill | dbsheriff_kill |
Required to end sessions | Respond | Ends sessions (ALTER ANY CONNECTION / KILL DATABASE CONNECTION) | A kill is tried with the monitor account, which holds no right to end a session, so it fails and is recorded as an error. |
| lock | dbsheriff_lock |
Required to lock accounts | Respond | Lock ladder (ALTER LOGIN DISABLE + kill / DENY CONNECT), graduated response (DENY SELECT) | A lock is tried with the kill account, then the monitor account; neither can lock, so it fails and is recorded as an error. |
| posture | dbsheriff_posture |
Optional | Monitor (read extension) | Hourly governance collection + posture scan | The monitor account does the posture checks and the hourly governance read. |
| classify | dbsheriff_classify |
Required for classification | Monitor (read extension) | Column metadata, statistics, existing classifications, value sampling | No value sampling for this server. There is no fallback to the monitor account, on purpose: it would try to read application rows with an account that should not have them. |
| control | dbsheriff_control |
Optional, opt-in | Control | Creates masks and roles for native controls; per database ALTER ANY MASK and ALTER ANY ROLE, nothing at server level | A server cannot be set to managed mode without it. Native controls stay off (the default) unless an approver chooses script mode, in which DBSheriff writes the SQL and your DBA runs it; script mode needs no account. This account never falls back to another one, and is only created when you ask for it (--with-control). |
dbsheriff_monitorSelf-managed SQL Server and Azure SQL Managed Instance
CREATE LOGIN dbsheriff_monitor WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
GRANT VIEW SERVER STATE, VIEW ANY DEFINITION, VIEW ANY DATABASE TO dbsheriff_monitor;
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_monitor') CREATE USER dbsheriff_monitor FOR LOGIN dbsheriff_monitor; -- in master: catalog + (on-prem) the audit reader
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_monitor') CREATE USER dbsheriff_monitor FOR LOGIN dbsheriff_monitor; -- no data access: catalog only
-- optional, not run here: Extended Events statement capture (dbsheriff_stmt_file) and SQL Server Audit — named-login statement attribution and the audit trail's login/DDL/DCL records; without either, completed statements score as 'unknown'. Required outright on RDS Express/Web, which has no Extended Events at all.
-- optional, not run here: GRANT EXECUTE ON sys.xp_readerrorlog — on-prem failed-login fallback when the audit's login groups are off.
Amazon RDS for SQL Server
CREATE LOGIN dbsheriff_monitor WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
GRANT VIEW SERVER STATE, VIEW ANY DEFINITION, VIEW ANY DATABASE TO dbsheriff_monitor;
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_monitor') CREATE USER dbsheriff_monitor FOR LOGIN dbsheriff_monitor; -- no data access: catalog only
-- optional, not run here: Extended Events statement capture (dbsheriff_stmt_file) and SQL Server Audit — named-login statement attribution and the audit trail's login/DDL/DCL records; without either, completed statements score as 'unknown'. Required outright on RDS Express/Web, which has no Extended Events at all.
Cloud SQL for SQL Server
CREATE LOGIN dbsheriff_monitor WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_monitor') CREATE USER dbsheriff_monitor FOR LOGIN dbsheriff_monitor; -- no data access: catalog only
-- by hand, once, as the built-in sqlserver admin; NOT run by this command because it makes dbsheriff_monitor as privileged as the admin account: ALTER SERVER ROLE CustomerDbRootRole ADD MEMBER dbsheriff_monitor;
-- optional, not run here: Extended Events statement capture (dbsheriff_stmt_file) and SQL Server Audit — named-login statement attribution and the audit trail's login/DDL/DCL records; without either, completed statements score as 'unknown'. Required outright on RDS Express/Web, which has no Extended Events at all.
Azure SQL Database
CREATE LOGIN dbsheriff_monitor WITH PASSWORD = '<password>';
ALTER SERVER ROLE ##MS_DatabaseConnector## ADD MEMBER dbsheriff_monitor;
ALTER SERVER ROLE ##MS_ServerStateReader## ADD MEMBER dbsheriff_monitor;
ALTER SERVER ROLE ##MS_DefinitionReader## ADD MEMBER dbsheriff_monitor;
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_monitor') CREATE USER dbsheriff_monitor FOR LOGIN dbsheriff_monitor;
GRANT VIEW DATABASE STATE TO dbsheriff_monitor;
-- optional, not run here: Extended Events statement capture (dbsheriff_stmt_file) and SQL Server Audit — named-login statement attribution and the audit trail's login/DDL/DCL records; without either, completed statements score as 'unknown'. Required outright on RDS Express/Web, which has no Extended Events at all.
-- optional, not run here: GRANT VIEW DATABASE SECURITY AUDIT — needed on dbsheriff_posture specifically (not just dbsheriff_monitor) for the governance collector's Azure Blob audit read (see collect_auth()/azure_audit_conn).
dbsheriff_killSelf-managed SQL Server and Azure SQL Managed Instance, Amazon RDS for SQL Server
CREATE LOGIN dbsheriff_kill WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
GRANT ALTER ANY CONNECTION, VIEW SERVER STATE TO dbsheriff_kill;
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_kill') CREATE USER dbsheriff_kill FOR LOGIN dbsheriff_kill; -- no permission in the database: lets the kill session open it
Cloud SQL for SQL Server
CREATE LOGIN dbsheriff_kill WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_kill') CREATE USER dbsheriff_kill FOR LOGIN dbsheriff_kill; -- no permission in the database: lets the kill session open it
-- by hand, once, as the built-in sqlserver admin; NOT run by this command because it makes dbsheriff_kill as privileged as the admin account: ALTER SERVER ROLE CustomerDbRootRole ADD MEMBER dbsheriff_kill;
Azure SQL Database
CREATE LOGIN dbsheriff_kill WITH PASSWORD = '<password>';
ALTER SERVER ROLE ##MS_DatabaseConnector## ADD MEMBER dbsheriff_kill;
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_kill') CREATE USER dbsheriff_kill FOR LOGIN dbsheriff_kill;
GRANT VIEW DATABASE STATE TO dbsheriff_kill;
GRANT KILL DATABASE CONNECTION TO dbsheriff_kill;
dbsheriff_lockSelf-managed SQL Server and Azure SQL Managed Instance, Amazon RDS for SQL Server
CREATE LOGIN dbsheriff_lock WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
GRANT ALTER ANY LOGIN, ALTER ANY CONNECTION, VIEW SERVER STATE TO dbsheriff_lock;
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_lock') CREATE USER dbsheriff_lock FOR LOGIN dbsheriff_lock;
ALTER ROLE db_securityadmin ADD MEMBER dbsheriff_lock; -- DENY SELECT on a table, graduated response
-- optional, not run here: GRANT ALTER ANY SENSITIVITY CLASSIFICATION + the mssql_tag_writeback switch — write-back of a sensitivity label after classification approval.
Cloud SQL for SQL Server
CREATE LOGIN dbsheriff_lock WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_lock') CREATE USER dbsheriff_lock FOR LOGIN dbsheriff_lock;
ALTER ROLE db_securityadmin ADD MEMBER dbsheriff_lock; -- DENY SELECT on a table, graduated response
-- by hand, once, as the built-in sqlserver admin; NOT run by this command because it makes dbsheriff_lock as privileged as the admin account: ALTER SERVER ROLE CustomerDbRootRole ADD MEMBER dbsheriff_lock;
-- optional, not run here: GRANT ALTER ANY SENSITIVITY CLASSIFICATION + the mssql_tag_writeback switch — write-back of a sensitivity label after classification approval.
Azure SQL Database
CREATE LOGIN dbsheriff_lock WITH PASSWORD = '<password>';
ALTER SERVER ROLE ##MS_DatabaseConnector## ADD MEMBER dbsheriff_lock;
ALTER SERVER ROLE ##MS_LoginManager## ADD MEMBER dbsheriff_lock; -- lets dbsheriff_lock attempt ALTER LOGIN ... DISABLE for audit-trail consistency; NOT trusted as the lock itself (live-verified it doesn't block new connections on Azure SQL Database) — the real lock is the per-database DENY CONNECT below
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_lock') CREATE USER dbsheriff_lock FOR LOGIN dbsheriff_lock;
GRANT CONTROL ON DATABASE::[<each Azure SQL Database on this logical server>] TO dbsheriff_lock; -- lets dbsheriff_lock DENY/GRANT another principal's CONNECT permission — the real lock mechanism on Azure SQL Database (ALTER LOGIN ... DISABLE does not reliably block connections there, live-verified)
-- optional, not run here: GRANT ALTER ANY SENSITIVITY CLASSIFICATION + the mssql_tag_writeback switch — write-back of a sensitivity label after classification approval.
dbsheriff_postureSelf-managed SQL Server and Azure SQL Managed Instance
CREATE LOGIN dbsheriff_posture WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
GRANT VIEW SERVER STATE, VIEW ANY DEFINITION, VIEW ANY DATABASE TO dbsheriff_posture;
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_posture') CREATE USER dbsheriff_posture FOR LOGIN dbsheriff_posture; -- in master: catalog + (on-prem) the audit reader
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_posture') CREATE USER dbsheriff_posture FOR LOGIN dbsheriff_posture; -- no data access: catalog only
-- optional, not run here: Extended Events statement capture (dbsheriff_stmt_file) and SQL Server Audit — named-login statement attribution and the audit trail's login/DDL/DCL records; without either, completed statements score as 'unknown'. Required outright on RDS Express/Web, which has no Extended Events at all.
Amazon RDS for SQL Server
CREATE LOGIN dbsheriff_posture WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
GRANT VIEW SERVER STATE, VIEW ANY DEFINITION, VIEW ANY DATABASE TO dbsheriff_posture;
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_posture') CREATE USER dbsheriff_posture FOR LOGIN dbsheriff_posture; -- no data access: catalog only
-- optional, not run here: Extended Events statement capture (dbsheriff_stmt_file) and SQL Server Audit — named-login statement attribution and the audit trail's login/DDL/DCL records; without either, completed statements score as 'unknown'. Required outright on RDS Express/Web, which has no Extended Events at all.
Cloud SQL for SQL Server
CREATE LOGIN dbsheriff_posture WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_posture') CREATE USER dbsheriff_posture FOR LOGIN dbsheriff_posture; -- no data access: catalog only
-- by hand, once, as the built-in sqlserver admin; NOT run by this command because it makes dbsheriff_posture as privileged as the admin account: ALTER SERVER ROLE CustomerDbRootRole ADD MEMBER dbsheriff_posture;
-- optional, not run here: Extended Events statement capture (dbsheriff_stmt_file) and SQL Server Audit — named-login statement attribution and the audit trail's login/DDL/DCL records; without either, completed statements score as 'unknown'. Required outright on RDS Express/Web, which has no Extended Events at all.
Azure SQL Database
CREATE LOGIN dbsheriff_posture WITH PASSWORD = '<password>';
ALTER SERVER ROLE ##MS_DatabaseConnector## ADD MEMBER dbsheriff_posture;
ALTER SERVER ROLE ##MS_ServerStateReader## ADD MEMBER dbsheriff_posture;
ALTER SERVER ROLE ##MS_DefinitionReader## ADD MEMBER dbsheriff_posture;
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_posture') CREATE USER dbsheriff_posture FOR LOGIN dbsheriff_posture;
GRANT VIEW DATABASE STATE TO dbsheriff_posture;
-- optional, not run here: Extended Events statement capture (dbsheriff_stmt_file) and SQL Server Audit — named-login statement attribution and the audit trail's login/DDL/DCL records; without either, completed statements score as 'unknown'. Required outright on RDS Express/Web, which has no Extended Events at all.
-- optional, not run here: GRANT VIEW DATABASE SECURITY AUDIT — needed on dbsheriff_posture specifically (not just dbsheriff_monitor) for the governance collector's Azure Blob audit read (see collect_auth()/azure_audit_conn).
dbsheriff_classifySelf-managed SQL Server and Azure SQL Managed Instance, Amazon RDS for SQL Server, Cloud SQL for SQL Server
CREATE LOGIN dbsheriff_classify WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_classify') CREATE USER dbsheriff_classify FOR LOGIN dbsheriff_classify;
GRANT SELECT ON SCHEMA::[<each schema to scan>] TO dbsheriff_classify;
-- optional, not run here: GRANT VIEW ANY SENSITIVITY CLASSIFICATION — existing labels (SSMS Data Discovery & Classification, Purview) as classification evidence; without it sys.sensitivity_classifications returns no rows, silently.
Azure SQL Database
CREATE LOGIN dbsheriff_classify WITH PASSWORD = '<password>';
ALTER SERVER ROLE ##MS_DatabaseConnector## ADD MEMBER dbsheriff_classify;
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_classify') CREATE USER dbsheriff_classify FOR LOGIN dbsheriff_classify;
GRANT SELECT ON SCHEMA::[<each schema to scan>] TO dbsheriff_classify;
-- optional, not run here: GRANT VIEW ANY SENSITIVITY CLASSIFICATION — existing labels (SSMS Data Discovery & Classification, Purview) as classification evidence; without it sys.sensitivity_classifications returns no rows, silently.
dbsheriff_controlSelf-managed SQL Server and Azure SQL Managed Instance, Amazon RDS for SQL Server, Cloud SQL for SQL Server
CREATE LOGIN dbsheriff_control WITH PASSWORD = '<password>', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
USE [<each non-system database>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_control') CREATE USER dbsheriff_control FOR LOGIN dbsheriff_control;
GRANT ALTER ANY MASK TO dbsheriff_control; -- native controls: add and drop a dynamic data mask
GRANT ALTER ANY ROLE TO dbsheriff_control; -- native controls: create and drop roles, change a role's members
-- not run here, by design: ALTER on each TABLE whose column a mask will change (GRANT ALTER ON OBJECT::<schema>.<table> TO the control account), given by your DBA when the table is approved — ALTER ANY MASK alone does not let an account add a mask to a table it has no right to alter. Members of sysadmin and db_owner, and holders of UNMASK, see clear data whatever a mask says. See the setup guide.
Azure SQL Database
CREATE LOGIN dbsheriff_control WITH PASSWORD = '<password>';
ALTER SERVER ROLE ##MS_DatabaseConnector## ADD MEMBER dbsheriff_control;
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'dbsheriff_control') CREATE USER dbsheriff_control FOR LOGIN dbsheriff_control;
GRANT ALTER ANY MASK TO dbsheriff_control;
GRANT ALTER ANY ROLE TO dbsheriff_control;
-- not run here, by design: ALTER on each TABLE whose column a mask will change (GRANT ALTER ON OBJECT::<schema>.<table> TO the control account), given by your DBA when the table is approved — ALTER ANY MASK alone does not let an account add a mask to a table it has no right to alter. Members of sysadmin and db_owner, and holders of UNMASK, see clear data whatever a mask says. See the setup guide.
Users created inside the pluggable database, not in the root container. Default port 1521. Full guide: setup guide.
| Account | Default name | Needed? | Tier | What it is for | If it is missing |
|---|---|---|---|---|---|
| monitor | oracle_monitor |
Required | Monitor (read) | Sessions, shared pool, segment stats, unified audit trail, users/privileges | The server cannot be registered: every poll, collector and scan starts from this account. |
| kill | oracle_kill_user |
Required to end sessions | Respond | Cancel statements / end sessions | A kill is tried with the monitor account, which holds no right to end a session, so it fails and is recorded as an error. |
| lock | oracle_lock_user |
Required to lock accounts | Respond | Lock ladder (ACCOUNT LOCK + session kill), graduated response, write-back | A lock is tried with the kill account, then the monitor account; neither can lock, so it fails and is recorded as an error. |
| posture | oracle_posture |
Optional | Monitor (read extension) | Hourly governance collection + posture scan | The monitor account does the posture checks and the hourly governance read. |
| classify | dbsheriff_classify |
Required for classification | Monitor (read extension) | Column metadata, statistics, comments, TSDP/redaction/annotations; value sampling | No value sampling for this server. There is no fallback to the monitor account, on purpose: it would try to read application rows with an account that should not have them. |
oracle_monitorSelf-managed (your own server or VM, Oracle Database Free), Oracle on a Google Cloud VM, Oracle on an Azure VM
CREATE USER oracle_monitor IDENTIFIED BY "<password>";
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO oracle_monitor;
GRANT AUDIT_VIEWER TO oracle_monitor;
-- optional (23ai+, when oracle_firewall is on): GRANT SQL_FIREWALL_VIEWER TO oracle_monitor;
-- prerequisite, NOT run here (needs an instance restart to take effect) — confirm unified auditing is actually writing records: 23ai+ always does; mixed mode (12.2-21c, RDS) needs audit_trail = 'DB,EXTENDED' — see install_prereqs.py / the setup guide's "Prerequisites" section (no connection needed here to check: this is dry-run text).
Amazon RDS for Oracle
CREATE USER oracle_monitor IDENTIFIED BY "<password>";
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO oracle_monitor;
GRANT READ ON audsys.unified_audit_trail TO oracle_monitor;
-- optional (23ai+, when oracle_firewall is on): GRANT SQL_FIREWALL_VIEWER TO oracle_monitor;
-- prerequisite, NOT run here (needs an instance restart to take effect) — confirm unified auditing is actually writing records: 23ai+ always does; mixed mode (12.2-21c, RDS) needs audit_trail = 'DB,EXTENDED' — see install_prereqs.py / the setup guide's "Prerequisites" section (no connection needed here to check: this is dry-run text).
oracle_kill_userSelf-managed (your own server or VM, Oracle Database Free), Oracle on a Google Cloud VM, Oracle on an Azure VM
CREATE USER oracle_kill_user IDENTIFIED BY "<password>";
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO oracle_kill_user;
GRANT ALTER SYSTEM TO oracle_kill_user;
Amazon RDS for Oracle
CREATE USER oracle_kill_user IDENTIFIED BY "<password>";
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO oracle_kill_user;
GRANT EXECUTE ON rdsadmin.rdsadmin_util TO oracle_kill_user;
oracle_lock_userSelf-managed (your own server or VM, Oracle Database Free), Oracle on a Google Cloud VM, Oracle on an Azure VM
CREATE USER oracle_lock_user IDENTIFIED BY "<password>";
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO oracle_lock_user;
GRANT ALTER USER, ALTER SYSTEM, GRANT ANY OBJECT PRIVILEGE TO oracle_lock_user;
-- optional write-back extras, NOT run here — must be granted by SYS itself (RDS: rdsadmin.rdsadmin_util.grant_sys_object), not by a DBA-privileged admin account: GRANT CREATE PROCEDURE, COMMENT ANY TABLE TO oracle_lock_user; GRANT EXECUTE ON sys.dbms_rls TO oracle_lock_user; GRANT ADMINISTER ROW LEVEL SECURITY POLICY TO oracle_lock_user; -- 23ai+ only GRANT EXECUTE ON sys.dbms_tsdp_manage TO oracle_lock_user; -- Enterprise Edition/Free only
Amazon RDS for Oracle
CREATE USER oracle_lock_user IDENTIFIED BY "<password>";
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO oracle_lock_user;
GRANT ALTER USER, GRANT ANY OBJECT PRIVILEGE TO oracle_lock_user;
GRANT EXECUTE ON rdsadmin.rdsadmin_util TO oracle_lock_user;
-- optional write-back extras, NOT run here — must be granted by SYS itself (RDS: rdsadmin.rdsadmin_util.grant_sys_object), not by a DBA-privileged admin account: GRANT CREATE PROCEDURE, COMMENT ANY TABLE TO oracle_lock_user; GRANT EXECUTE ON sys.dbms_rls TO oracle_lock_user; GRANT ADMINISTER ROW LEVEL SECURITY POLICY TO oracle_lock_user; -- 23ai+ only GRANT EXECUTE ON sys.dbms_tsdp_manage TO oracle_lock_user; -- Enterprise Edition/Free only
oracle_postureSelf-managed (your own server or VM, Oracle Database Free), Oracle on a Google Cloud VM, Oracle on an Azure VM
CREATE USER oracle_posture IDENTIFIED BY "<password>";
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO oracle_posture;
GRANT AUDIT_VIEWER TO oracle_posture;
-- optional (23ai+, when oracle_firewall is on): GRANT SQL_FIREWALL_VIEWER TO oracle_posture;
-- prerequisite, NOT run here (needs an instance restart to take effect) — confirm unified auditing is actually writing records: 23ai+ always does; mixed mode (12.2-21c, RDS) needs audit_trail = 'DB,EXTENDED' — see install_prereqs.py / the setup guide's "Prerequisites" section (no connection needed here to check: this is dry-run text).
Amazon RDS for Oracle
CREATE USER oracle_posture IDENTIFIED BY "<password>";
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO oracle_posture;
GRANT READ ON audsys.unified_audit_trail TO oracle_posture;
-- optional (23ai+, when oracle_firewall is on): GRANT SQL_FIREWALL_VIEWER TO oracle_posture;
-- prerequisite, NOT run here (needs an instance restart to take effect) — confirm unified auditing is actually writing records: 23ai+ always does; mixed mode (12.2-21c, RDS) needs audit_trail = 'DB,EXTENDED' — see install_prereqs.py / the setup guide's "Prerequisites" section (no connection needed here to check: this is dry-run text).
dbsheriff_classifySelf-managed (your own server or VM, Oracle Database Free), Amazon RDS for Oracle, Oracle on a Google Cloud VM, Oracle on an Azure VM
CREATE USER dbsheriff_classify IDENTIFIED BY "<password>";
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO dbsheriff_classify;
-- optional ("recommended" in the setup guide), NOT run here — value sampling needs explicit per-table grants the operator chooses: GRANT READ ON <schema>.<table> TO dbsheriff_classify; (23ai+: GRANT READ ANY TABLE ON SCHEMA <schema> TO dbsheriff_classify;). Without it, classification still runs from names, statistics, comments and native registrations alone.
Users created inside the tenant database, never in SYSTEMDB. Default port 39015. Full guide: setup guide.
| Account | Default name | Needed? | Tier | What it is for | If it is missing |
|---|---|---|---|---|---|
| monitor | hana_monitor |
Required | Monitor (read) | Live statements/connections, lock waits, SQL plan cache, audit log | The server cannot be registered: every poll, collector and scan starts from this account. |
| kill | hana_kill_user |
Required to end sessions | Respond | Disconnect/cancel sessions (ALTER SYSTEM DISCONNECT/CANCEL) | A kill is tried with the monitor account, which holds no right to end a session, so it fails and is recorded as an error. |
| lock | hana_lock_user |
Required to lock accounts | Respond | Deactivate/reactivate users, terminate sessions, throttle + revoke response rungs | A lock is tried with the kill account, then the monitor account; neither can lock, so it fails and is recorded as an error. |
| posture | hana_posture |
Optional | Monitor (read extension) | Posture checks + hourly governance collection | The monitor account does the posture checks and the hourly governance read. |
| classify | dbsheriff_classify |
Required for classification | Monitor (read extension) | Column metadata, value sampling for classification | No value sampling for this server. There is no fallback to the monitor account, on purpose: it would try to read application rows with an account that should not have them. |
hana_monitorCREATE USER hana_monitor PASSWORD "<password>" NO FORCE_FIRST_PASSWORD_CHANGE;
GRANT CATALOG READ TO hana_monitor;
GRANT AUDIT READ TO hana_monitor; -- separate from CATALOG READ: without it SYS.AUDIT_LOG resolves but returns 0 rows
-- not handled here: global_auditing_state=true and the DBSHERIFF_AUTH policy are automated by install_prereqs.py; DBSHERIFF_DML/DBSHERIFF_DDL need an explicit schema/table target list and stay manual-only (the setup guide, section 3) -- AUDIT READ above lets this account read SYS.AUDIT_LOG, but it stays empty until those are enabled.
hana_kill_userCREATE USER hana_kill_user PASSWORD "<password>" NO FORCE_FIRST_PASSWORD_CHANGE;
GRANT SESSION ADMIN TO hana_kill_user;
GRANT CATALOG READ TO hana_kill_user;
hana_lock_userCREATE USER hana_lock_user PASSWORD "<password>" NO FORCE_FIRST_PASSWORD_CHANGE;
GRANT USER ADMIN TO hana_lock_user; -- ALTER USER ... DEACTIVATE/ACTIVATE USER NOW
GRANT SESSION ADMIN TO hana_lock_user; -- terminate a deactivated user's already-open sessions
GRANT CATALOG READ TO hana_lock_user;
GRANT WORKLOAD ADMIN TO hana_lock_user; -- throttle response rung: CREATE/DROP WORKLOAD CLASS/MAPPING
-- repeat per schema in <tenant database> (narrow this list if you prefer -- the guide's section 1 only requires this on schemas holding a CRITICAL/SENSITIVE table; auto-discovery here is deliberately broader, see the module docstring):
GRANT SELECT ON SCHEMA "<each non-system schema in the tenant database>" TO hana_lock_user WITH GRANT OPTION;
hana_postureCREATE USER hana_posture PASSWORD "<password>" NO FORCE_FIRST_PASSWORD_CHANGE;
GRANT CATALOG READ TO hana_posture;
GRANT AUDIT READ TO hana_posture; -- separate from CATALOG READ: without it SYS.AUDIT_LOG resolves but returns 0 rows
-- not handled here: global_auditing_state=true and the DBSHERIFF_AUTH policy are automated by install_prereqs.py; DBSHERIFF_DML/DBSHERIFF_DDL need an explicit schema/table target list and stay manual-only (the setup guide, section 3) -- AUDIT READ above lets this account read SYS.AUDIT_LOG, but it stays empty until those are enabled.
dbsheriff_classifyCREATE USER dbsheriff_classify PASSWORD "<password>" NO FORCE_FIRST_PASSWORD_CHANGE;
-- repeat per schema in <tenant database> (narrow this list if you prefer -- the guide's section 1 only requires this on schemas holding a CRITICAL/SENSITIVE table; auto-discovery here is deliberately broader, see the module docstring):
GRANT SELECT ON SCHEMA "<each non-system schema in the tenant database>" TO dbsheriff_classify;
Plain users plus RBAC roles; SYSLOG ACCESS UNRESTRICTED where the statements below say so. Default port 5439. Full guide: setup guide.
| Account | Default name | Needed? | Tier | What it is for | If it is missing |
|---|---|---|---|---|---|
| monitor | redshift_monitor |
Required | Monitor (read) | Live + completed statements (SYS_QUERY_HISTORY), lock waits (PG_LOCKS), EXPLAIN-based full-table-read estimates | The server cannot be registered: every poll, collector and scan starts from this account. |
| kill | redshift_kill_user |
Required to end sessions | Respond | Pre-check + terminate/cancel sessions (PG_TERMINATE_BACKEND / PG_CANCEL_BACKEND) | A kill is tried with the monitor account, which holds no right to end a session, so it fails and is recorded as an error. |
| lock | redshift_lock_user |
Required to lock accounts | Respond | Blocks new logins (CONNECTION LIMIT 0), terminates open sessions | A lock is tried with the kill account, then the monitor account; neither can lock, so it fails and is recorded as an error. |
| posture | redshift_posture |
Optional | Monitor (read extension) | Posture checks (PS03/PS04/PS05) + hourly governance collection; monitor is used when this account is absent | The monitor account does the posture checks and the hourly governance read. |
| classify | redshift_classify |
Required for classification | Monitor (read extension) | Column metadata + value sampling for classification | No value sampling for this server. There is no fallback to the monitor account, on purpose: it would try to read application rows with an account that should not have them. |
| plan | redshift_plan |
Recommended | Monitor (read extension) | EXPLAIN of captured statements under SELECT-only rights (recommended, not used yet; no SYSLOG, RBAC role, write or DDL rights) | Nothing else changes. It is the login an EXPLAIN of a captured statement can run under, with SELECT-only rights; nothing in DBSheriff uses it yet, and the EXPLAIN runs under the monitor account until that is switched. The Admin Console form has no field for its password: create_reset_dbuser.py and setup_dbsheriff.py record it in the registry, and onboard_target.py writes it to its mode-600 credentials file instead. |
redshift_monitorCREATE USER "redshift_monitor" PASSWORD '<password>';
ALTER USER "redshift_monitor" SYSLOG ACCESS UNRESTRICTED;
GRANT ROLE "sys:monitor" TO "redshift_monitor";
-- connected to <each non-system database on the cluster>; repeat per schema holding a monitored table (EXPLAIN needs the same privilege as running the statement, for R65):
GRANT USAGE ON SCHEMA <schema> TO "redshift_monitor";
GRANT SELECT ON ALL TABLES IN SCHEMA <schema> TO "redshift_monitor";
-- optional, not handled here: enable_user_activity_logging on the cluster's parameter group (a parameter-group setting, not SQL, and doesn't gate SYS_QUERY_HISTORY anyway - see install_prereqs.py, which do automate it), and the read-only AWS IAM permission that lets posture codes PS09/PS11/PS65/PS66 report PASS/FAIL instead of NOT_CHECKED (a process-level AWS credential, not a database grant, not automated anywhere). See the setup guide.
redshift_kill_userCREATE USER "redshift_kill_user" PASSWORD '<password>';
ALTER USER "redshift_kill_user" SYSLOG ACCESS UNRESTRICTED;
GRANT ROLE "sys:monitor" TO "redshift_kill_user";
GRANT ROLE "sys:operator" TO "redshift_kill_user";
redshift_lock_userCREATE USER "redshift_lock_user" PASSWORD '<password>';
ALTER USER "redshift_lock_user" SYSLOG ACCESS UNRESTRICTED;
GRANT ROLE "sys:monitor" TO "redshift_lock_user";
GRANT ROLE "sys:secadmin" TO "redshift_lock_user";
GRANT ROLE "sys:operator" TO "redshift_lock_user";
redshift_postureCREATE USER "redshift_posture" PASSWORD '<password>';
ALTER USER "redshift_posture" SYSLOG ACCESS UNRESTRICTED;
GRANT ROLE "sys:monitor" TO "redshift_posture";
redshift_classifyCREATE USER "redshift_classify" PASSWORD '<password>';
-- connected to <each non-system database on the cluster>; repeat per schema to scan (narrow this list if you prefer):
GRANT USAGE ON SCHEMA <schema> TO "redshift_classify";
GRANT SELECT ON ALL TABLES IN SCHEMA <schema> TO "redshift_classify";
redshift_planCREATE USER "redshift_plan" PASSWORD '<password>';
-- connected to <each non-system database on the cluster>; repeat per schema to scan (narrow this list if you prefer):
GRANT USAGE ON SCHEMA <schema> TO "redshift_plan";
GRANT SELECT ON ALL TABLES IN SCHEMA <schema> TO "redshift_plan";
Service users with key-pair authentication, no passwords. Every statement runs as ACCOUNTADMIN once; DBSheriff never holds that role. Default port n/a. Full guide: setup guide.
| Account | Default name | Needed? | Tier | What it is for | If it is missing |
|---|---|---|---|---|---|
| monitor | dbsheriff_monitor |
Required | Monitor (read) | Query & login history, warehouse/user/account MONITOR (read-only) | The server cannot be registered: every poll, collector and scan starts from this account. |
| kill | dbsheriff_kill |
Required to end sessions | Respond | Cancel statements, suspend warehouses (OPERATE) | A kill is tried with the monitor account, which holds no right to end a session, so it fails and is recorded as an error. |
| lock | dbsheriff_lock |
Required to lock accounts | Respond | Disable users, abort sessions, tag write-back (SECURITYADMIN) | A lock is tried with the kill account, then the monitor account; neither can lock, so it fails and is recorded as an error. |
| posture | dbsheriff_posture |
Optional | Monitor (read extension) | ACCOUNT_USAGE + Trust Center reads for posture/compliance evidence | The monitor account does the posture checks and the hourly governance read. |
| classify | dbsheriff_classify |
Required for classification | Monitor (read extension) | Column value sampling for classification (SELECT on monitored tables) | No value sampling for this server. There is no fallback to the monitor account, on purpose: it would try to read application rows with an account that should not have them. |
| control | dbsheriff_control |
Optional, opt-in | Control | Creates roles and applies masking policies for native controls; CREATE ROLE and APPLY MASKING POLICY on the account | A server cannot be set to managed mode without it. Native controls stay off (the default) unless an approver chooses script mode, in which DBSheriff writes the SQL and your DBA runs it; script mode needs no account. This account never falls back to another one, and is only created when you ask for it (--with-control). |
dbsheriff_monitorUSE ROLE ACCOUNTADMIN;
CREATE WAREHOUSE IF NOT EXISTS DBSHERIFF_WH WITH WAREHOUSE_SIZE = 'XSMALL' AUTO_SUSPEND = 60 AUTO_RESUME = TRUE INITIALLY_SUSPENDED = TRUE MIN_CLUSTER_COUNT = 1 MAX_CLUSTER_COUNT = 1;
CREATE RESOURCE MONITOR IF NOT EXISTS DBSHERIFF_RM WITH CREDIT_QUOTA = 60 FREQUENCY = MONTHLY START_TIMESTAMP = IMMEDIATELY TRIGGERS ON 80 PERCENT DO NOTIFY ON 100 PERCENT DO SUSPEND_IMMEDIATE;
ALTER WAREHOUSE DBSHERIFF_WH SET RESOURCE_MONITOR = DBSHERIFF_RM;
CREATE ROLE IF NOT EXISTS dbsheriff_monitor_role;
GRANT ROLE dbsheriff_monitor_role TO ROLE SYSADMIN;
CREATE USER IF NOT EXISTS dbsheriff_monitor TYPE = SERVICE DEFAULT_ROLE = dbsheriff_monitor_role DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- when dbsheriff_monitor does not exist yet
ALTER USER dbsheriff_monitor SET RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- instead, when dbsheriff_monitor already exists (CREATE USER IF NOT EXISTS would leave its old key in place)
GRANT ROLE dbsheriff_monitor_role TO USER dbsheriff_monitor;
GRANT USAGE, MONITOR ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_monitor_role;
GRANT MONITOR USAGE ON ACCOUNT TO ROLE dbsheriff_monitor_role; -- statements that run on no warehouse
GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE TO ROLE dbsheriff_monitor_role;
-- repeat per database you choose (--databases, or asked for); without a choice: every customer database of the account, except Snowflake's own, DBSHERIFF_DROP, the personal USER$ databases and SNOWFLAKE_LEARNING_DB:
GRANT USAGE ON DATABASE "<each chosen database>" TO ROLE dbsheriff_monitor_role;
-- repeat per existing warehouse (SHOW WAREHOUSES) — re-run when a warehouse is added:
GRANT MONITOR ON WAREHOUSE "<each warehouse>" TO ROLE dbsheriff_monitor_role;
-- repeat per existing user (SHOW USERS) — re-run when a user is added:
GRANT MONITOR ON USER "<each user>" TO ROLE dbsheriff_monitor_role;
ALTER USER dbsheriff_monitor SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY; -- only if DBSHERIFF_HOST_POLICY already exists (install_prereqs.py creates it — guide section 3); skipped with a note otherwise, never fails account creation
dbsheriff_killUSE ROLE ACCOUNTADMIN;
CREATE WAREHOUSE IF NOT EXISTS DBSHERIFF_WH WITH WAREHOUSE_SIZE = 'XSMALL' AUTO_SUSPEND = 60 AUTO_RESUME = TRUE INITIALLY_SUSPENDED = TRUE MIN_CLUSTER_COUNT = 1 MAX_CLUSTER_COUNT = 1;
CREATE RESOURCE MONITOR IF NOT EXISTS DBSHERIFF_RM WITH CREDIT_QUOTA = 60 FREQUENCY = MONTHLY START_TIMESTAMP = IMMEDIATELY TRIGGERS ON 80 PERCENT DO NOTIFY ON 100 PERCENT DO SUSPEND_IMMEDIATE;
ALTER WAREHOUSE DBSHERIFF_WH SET RESOURCE_MONITOR = DBSHERIFF_RM;
CREATE ROLE IF NOT EXISTS dbsheriff_kill_role;
GRANT ROLE dbsheriff_kill_role TO ROLE SYSADMIN;
CREATE USER IF NOT EXISTS dbsheriff_kill TYPE = SERVICE DEFAULT_ROLE = dbsheriff_kill_role DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- when dbsheriff_kill does not exist yet
ALTER USER dbsheriff_kill SET RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- instead, when dbsheriff_kill already exists (CREATE USER IF NOT EXISTS would leave its old key in place)
GRANT ROLE dbsheriff_kill_role TO USER dbsheriff_kill;
GRANT USAGE, OPERATE ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_kill_role;
-- repeat per database you choose (--databases, or asked for); without a choice: every customer database of the account, except Snowflake's own, DBSHERIFF_DROP, the personal USER$ databases and SNOWFLAKE_LEARNING_DB:
GRANT USAGE ON DATABASE "<each chosen database>" TO ROLE dbsheriff_kill_role;
-- repeat per existing warehouse (SHOW WAREHOUSES) — re-run when a warehouse is added:
GRANT OPERATE ON WAREHOUSE "<each warehouse>" TO ROLE dbsheriff_kill_role;
ALTER USER dbsheriff_kill SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY; -- only if DBSHERIFF_HOST_POLICY already exists (install_prereqs.py creates it — guide section 3); skipped with a note otherwise, never fails account creation
dbsheriff_lockUSE ROLE ACCOUNTADMIN;
CREATE WAREHOUSE IF NOT EXISTS DBSHERIFF_WH WITH WAREHOUSE_SIZE = 'XSMALL' AUTO_SUSPEND = 60 AUTO_RESUME = TRUE INITIALLY_SUSPENDED = TRUE MIN_CLUSTER_COUNT = 1 MAX_CLUSTER_COUNT = 1;
CREATE RESOURCE MONITOR IF NOT EXISTS DBSHERIFF_RM WITH CREDIT_QUOTA = 60 FREQUENCY = MONTHLY START_TIMESTAMP = IMMEDIATELY TRIGGERS ON 80 PERCENT DO NOTIFY ON 100 PERCENT DO SUSPEND_IMMEDIATE;
ALTER WAREHOUSE DBSHERIFF_WH SET RESOURCE_MONITOR = DBSHERIFF_RM;
CREATE ROLE IF NOT EXISTS dbsheriff_lock_role;
GRANT ROLE dbsheriff_lock_role TO ROLE SYSADMIN;
CREATE USER IF NOT EXISTS dbsheriff_lock TYPE = SERVICE DEFAULT_ROLE = dbsheriff_lock_role DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- when dbsheriff_lock does not exist yet
ALTER USER dbsheriff_lock SET RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- instead, when dbsheriff_lock already exists (CREATE USER IF NOT EXISTS would leave its old key in place)
GRANT ROLE dbsheriff_lock_role TO USER dbsheriff_lock;
GRANT USAGE ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_lock_role;
GRANT ROLE SECURITYADMIN TO ROLE dbsheriff_lock_role;
ALTER USER dbsheriff_lock SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY; -- only if DBSHERIFF_HOST_POLICY already exists (install_prereqs.py creates it — guide section 3); skipped with a note otherwise, never fails account creation
-- optional, NOT executed — sensitivity tag write-back (guide section 7: DBSHERIFF_DROP database/tag, APPLY grants)
dbsheriff_postureUSE ROLE ACCOUNTADMIN;
CREATE WAREHOUSE IF NOT EXISTS DBSHERIFF_WH WITH WAREHOUSE_SIZE = 'XSMALL' AUTO_SUSPEND = 60 AUTO_RESUME = TRUE INITIALLY_SUSPENDED = TRUE MIN_CLUSTER_COUNT = 1 MAX_CLUSTER_COUNT = 1;
CREATE RESOURCE MONITOR IF NOT EXISTS DBSHERIFF_RM WITH CREDIT_QUOTA = 60 FREQUENCY = MONTHLY START_TIMESTAMP = IMMEDIATELY TRIGGERS ON 80 PERCENT DO NOTIFY ON 100 PERCENT DO SUSPEND_IMMEDIATE;
ALTER WAREHOUSE DBSHERIFF_WH SET RESOURCE_MONITOR = DBSHERIFF_RM;
CREATE ROLE IF NOT EXISTS dbsheriff_posture_role;
GRANT ROLE dbsheriff_posture_role TO ROLE SYSADMIN;
CREATE USER IF NOT EXISTS dbsheriff_posture TYPE = SERVICE DEFAULT_ROLE = dbsheriff_posture_role DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- when dbsheriff_posture does not exist yet
ALTER USER dbsheriff_posture SET RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- instead, when dbsheriff_posture already exists (CREATE USER IF NOT EXISTS would leave its old key in place)
GRANT ROLE dbsheriff_posture_role TO USER dbsheriff_posture;
GRANT USAGE ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_posture_role;
GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE TO ROLE dbsheriff_posture_role;
GRANT APPLICATION ROLE SNOWFLAKE.TRUST_CENTER_VIEWER TO ROLE dbsheriff_posture_role;
ALTER USER dbsheriff_posture SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY; -- only if DBSHERIFF_HOST_POLICY already exists (install_prereqs.py creates it — guide section 3); skipped with a note otherwise, never fails account creation
-- optional, NOT executed — paid Trust Center scanner packages (CIS Benchmarks, Threat Intelligence); enabled in Snowsight under Monitoring -> Trust Center -> Scanner Packages, not via SQL (guide section 0/section 9)
dbsheriff_classifyUSE ROLE ACCOUNTADMIN;
CREATE WAREHOUSE IF NOT EXISTS DBSHERIFF_WH WITH WAREHOUSE_SIZE = 'XSMALL' AUTO_SUSPEND = 60 AUTO_RESUME = TRUE INITIALLY_SUSPENDED = TRUE MIN_CLUSTER_COUNT = 1 MAX_CLUSTER_COUNT = 1;
CREATE RESOURCE MONITOR IF NOT EXISTS DBSHERIFF_RM WITH CREDIT_QUOTA = 60 FREQUENCY = MONTHLY START_TIMESTAMP = IMMEDIATELY TRIGGERS ON 80 PERCENT DO NOTIFY ON 100 PERCENT DO SUSPEND_IMMEDIATE;
ALTER WAREHOUSE DBSHERIFF_WH SET RESOURCE_MONITOR = DBSHERIFF_RM;
CREATE ROLE IF NOT EXISTS dbsheriff_classify_role;
GRANT ROLE dbsheriff_classify_role TO ROLE SYSADMIN;
CREATE USER IF NOT EXISTS dbsheriff_classify TYPE = SERVICE DEFAULT_ROLE = dbsheriff_classify_role DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- when dbsheriff_classify does not exist yet
ALTER USER dbsheriff_classify SET RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- instead, when dbsheriff_classify already exists (CREATE USER IF NOT EXISTS would leave its old key in place)
GRANT ROLE dbsheriff_classify_role TO USER dbsheriff_classify;
GRANT USAGE ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_classify_role;
-- repeat per database you choose (--databases, or asked for); without a choice: every customer database of the account, except Snowflake's own, DBSHERIFF_DROP, the personal USER$ databases and SNOWFLAKE_LEARNING_DB:
GRANT USAGE ON DATABASE "<each chosen database>" TO ROLE dbsheriff_classify_role;
GRANT USAGE ON ALL SCHEMAS IN DATABASE "<each chosen database>" TO ROLE dbsheriff_classify_role;
GRANT SELECT ON ALL TABLES IN DATABASE "<each chosen database>" TO ROLE dbsheriff_classify_role;
GRANT USAGE ON FUTURE SCHEMAS IN DATABASE "<each chosen database>" TO ROLE dbsheriff_classify_role;
GRANT SELECT ON FUTURE TABLES IN DATABASE "<each chosen database>" TO ROLE dbsheriff_classify_role;
ALTER USER dbsheriff_classify SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY; -- only if DBSHERIFF_HOST_POLICY already exists (install_prereqs.py creates it — guide section 3); skipped with a note otherwise, never fails account creation
-- optional, NOT executed — SYSTEM$CLASSIFY native classification + tag write-back, Enterprise edition only (guide section 7)
dbsheriff_controlUSE ROLE ACCOUNTADMIN;
CREATE WAREHOUSE IF NOT EXISTS DBSHERIFF_WH WITH WAREHOUSE_SIZE = 'XSMALL' AUTO_SUSPEND = 60 AUTO_RESUME = TRUE INITIALLY_SUSPENDED = TRUE MIN_CLUSTER_COUNT = 1 MAX_CLUSTER_COUNT = 1;
CREATE RESOURCE MONITOR IF NOT EXISTS DBSHERIFF_RM WITH CREDIT_QUOTA = 60 FREQUENCY = MONTHLY START_TIMESTAMP = IMMEDIATELY TRIGGERS ON 80 PERCENT DO NOTIFY ON 100 PERCENT DO SUSPEND_IMMEDIATE;
ALTER WAREHOUSE DBSHERIFF_WH SET RESOURCE_MONITOR = DBSHERIFF_RM;
CREATE ROLE IF NOT EXISTS dbsheriff_control_role;
GRANT ROLE dbsheriff_control_role TO ROLE SYSADMIN;
CREATE USER IF NOT EXISTS dbsheriff_control TYPE = SERVICE DEFAULT_ROLE = dbsheriff_control_role DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- when dbsheriff_control does not exist yet
ALTER USER dbsheriff_control SET RSA_PUBLIC_KEY = '<public key of the key pair you generate>'; -- instead, when dbsheriff_control already exists (CREATE USER IF NOT EXISTS would leave its old key in place)
GRANT ROLE dbsheriff_control_role TO USER dbsheriff_control;
GRANT USAGE ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_control_role;
GRANT CREATE ROLE ON ACCOUNT TO ROLE dbsheriff_control_role;
GRANT APPLY MASKING POLICY ON ACCOUNT TO ROLE dbsheriff_control_role; -- Enterprise edition feature; the grant itself is accepted on every edition
ALTER USER dbsheriff_control SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY; -- only if DBSHERIFF_HOST_POLICY already exists (install_prereqs.py creates it — guide section 3); skipped with a note otherwise, never fails account creation
-- NOT executed — GRANT CREATE MASKING POLICY ON SCHEMA DBSHERIFF_DROP.GOVERNANCE once that schema exists (guide section 7), Enterprise edition; see the setup guide
SQL logins in master plus a user in the pool database. Only monitor and classify are SQL accounts; kill, lock and posture are described below the statements. Default port 1433. Full guide: setup guide.
| Account | Default name | Needed? | Tier | What it is for | If it is missing |
|---|---|---|---|---|---|
| monitor | synapse_monitor |
Required | Monitor (read) | Live + completed statements (dm_pdw_exec_requests/sessions), lock waits, account inventory | The server cannot be registered: every poll, collector and scan starts from this account. |
| kill | synapse_kill |
Required to end sessions | Respond | Terminate sessions (KILL) | Kills fail. Use the workspace SQL administrator login (see below the statements). |
| lock | synapse_lock |
Required to lock accounts | Respond | Block new connections (DENY CONNECT) | Locks fail. Use the workspace SQL administrator login (see below the statements). |
| posture | synapse_posture |
Optional | Monitor (read extension) | ARM reads for PS09/PS11/PS67/PS68 -- an Azure AD app registration, not a SQL login at all | Posture checks PS09, PS11, PS67 and PS68 report NOT_CHECKED. This is an Azure AD app registration, not a SQL account. |
| classify | synapse_classify |
Required for classification | Monitor (read extension) | Column metadata + value sampling for classification | No value sampling for this server. There is no fallback to the monitor account, on purpose: it would try to read application rows with an account that should not have them. |
synapse_monitor-- connect to master:
CREATE LOGIN synapse_monitor WITH PASSWORD = '<password>';
-- then connect to the dedicated SQL pool database itself:
CREATE USER synapse_monitor FOR LOGIN synapse_monitor;
GRANT VIEW DATABASE STATE TO synapse_monitor;
GRANT VIEW DEFINITION ON DATABASE::<pool database> TO synapse_monitor; -- Account Inventory, see the setup guide, section 2
-- repeat per non-system schema in the pool database (dbo by default):
GRANT SELECT ON SCHEMA::<schema> TO synapse_monitor;
synapse_killNot a SQL account you create: see Synapse: kill, lock and posture below.
synapse_lockNot a SQL account you create: see Synapse: kill, lock and posture below.
synapse_postureNot a SQL account you create: see Synapse: kill, lock and posture below.
synapse_classify-- connect to master:
CREATE LOGIN synapse_classify WITH PASSWORD = '<password>';
-- then connect to the dedicated SQL pool database itself:
CREATE USER synapse_classify FOR LOGIN synapse_classify;
-- repeat per non-system schema in the pool database (dbo by default):
GRANT SELECT ON SCHEMA::<schema> TO synapse_classify;
GRANT VIEW SERVER STATE and GRANT CONTROL SERVER both fail with error 40521). Enter that login as the kill and the lock credential.az ad sp create-for-rbac --name "dbsheriff-posture-<workspace>" \
--role "Reader" \
--scopes "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Synapse/workspaces/<workspace>"
The posture credential is a JSON string with exactly these seven keys: tenant_id, client_id, client_secret, subscription_id, resource_group, workspace_name, sql_pool_name. The names az prints (tenant, appId, password) are not accepted. The Admin Console's server form has no Synapse choice yet, so a Synapse pool is registered through the Admin Console API (setup guide, "Register the pool").
Database users with built-in and custom roles. On Atlas the accounts are created in the Atlas UI or Terraform, see below. Default port 27017. Full guide: setup guide.
| Account | Default name | Needed? | Tier | What it is for | If it is missing |
|---|---|---|---|---|---|
| view_users | dbsheriff_view_users |
Required on self-managed | Monitor (read extension) | Self-managed-only custom role granting cluster-wide usersInfo/rolesInfo (viewUser + viewRole; viewUser only on Amazon DocumentDB) — no Atlas equivalent, see NotAutomatable below | The identity checks PS01 and PS05 report NOT_CHECKED and the Account Inventory shows nothing for the server. |
| monitor | mongo_monitor |
Required | Monitor (read) | Live + completed statements (system.profile), space (dbStats), availability (serverStatus) | The server cannot be registered: every poll, collector and scan starts from this account. |
| kill | mongo_kill_user |
Required to end sessions | Respond | Db.killOp( |
A kill is tried with the monitor account, which holds no right to end a session, so it fails and is recorded as an error. |
| lock | mongo_lock_user |
Required to lock accounts | Respond | Self-managed: revokes/restores all roles; Atlas: a formality account only — the real mechanism is the Atlas Admin API Service Account, see NotAutomatable below | Locks fail. On self-managed MongoDB this account does the lock; on Atlas it is a formality and the Atlas Admin API service account (below) does it. |
| classify | mongo_classify |
Required for classification | Monitor (read extension) | Classification scan (collection/field sampling) | No value sampling for this server. There is no fallback to the monitor account, on purpose: it would try to read application rows with an account that should not have them. |
Create dbsheriff_view_users before mongo_monitor (the monitor holds it). Both custom roles are part of the statements below.
dbsheriff_view_usersSelf-managed MongoDB, Amazon DocumentDB, MongoDB on a Google Cloud VM, MongoDB on an Azure VM
db.createRole({ role: "dbsheriff_view_users", privileges: [{ resource: { cluster: true }, actions: ["viewUser"] }, { resource: { cluster: true }, actions: ["viewRole"] }], roles: [] });
# Amazon DocumentDB (a *.docdb.amazonaws.com host) refuses to grant the viewRole action: on such a host the role is created with viewUser only, which is what the Account Inventory and the account checks read. Only role_table_grants (rolesInfo) is lost.
MongoDB Atlas
Not created with a statement on this platform: create it in the Atlas UI or with Terraform, see MongoDB Atlas below.
mongo_monitorSelf-managed MongoDB, Amazon DocumentDB, MongoDB on a Google Cloud VM, MongoDB on an Azure VM
db.createRole({ role: "dbsheriff_profiler", privileges: [{ resource: { db: "<each non-system database>", collection: "" }, actions: ["enableProfiler"] }], roles: [] });
# dbsheriff_profiler: the one privilege the monitor was missing for the profiler health check, enableProfiler on each monitored database (never the cluster, never admin/config/local). It lets DBSheriff read the profiling level (`profile -1`) and set level 1 / slowms 0 again after a restart or a failover. It is a narrow grant, but it also lets this account change the profiling level of those databases, which is how the statement record DBSheriff reads could be switched off: protect the monitor's password like the other DBSheriff accounts. An account created before this change needs the monitor step run again (create_reset_dbuser.py, choose reset).
db.createUser({ user: "mongo_monitor", pwd: "<password>", roles: [{ role: "clusterMonitor", db: "admin" }, { role: "read", db: "<each non-system database>" }, { role: "dbsheriff_view_users", db: "admin" }, { role: "dbsheriff_profiler", db: "admin" }] });
# Amazon DocumentDB (a *.docdb.amazonaws.com host) has no profiler (setProfilingLevel answers code 303): on such a host the monitor is made without the dbsheriff_profiler role and its enableProfiler action, which would have nothing to act on.
MongoDB Atlas
Not created with a statement on this platform: create it in the Atlas UI or with Terraform, see MongoDB Atlas below.
mongo_kill_userSelf-managed MongoDB, Amazon DocumentDB, MongoDB on a Google Cloud VM, MongoDB on an Azure VM
db.createUser({ user: "mongo_kill_user", pwd: "<password>", roles: [{ role: "hostManager", db: "admin" }] });
MongoDB Atlas
Not created with a statement on this platform: create it in the Atlas UI or with Terraform, see MongoDB Atlas below.
mongo_lock_userSelf-managed MongoDB, Amazon DocumentDB, MongoDB on a Google Cloud VM, MongoDB on an Azure VM
db.createUser({ user: "mongo_lock_user", pwd: "<password>", roles: [{ role: "userAdminAnyDatabase", db: "admin" }] });
MongoDB Atlas
Not created with a statement on this platform: create it in the Atlas UI or with Terraform, see MongoDB Atlas below.
mongo_classifySelf-managed MongoDB, Amazon DocumentDB, MongoDB on a Google Cloud VM, MongoDB on an Azure VM
db.createUser({ user: "mongo_classify", pwd: "<password>", roles: [{ role: "read", db: "<each non-system database>" }] });
MongoDB Atlas
Not created with a statement on this platform: create it in the Atlas UI or with Terraform, see MongoDB Atlas below.
Atlas does not allow user management over a database connection, whatever the role, so none of the statements above can create an Atlas account. Create these in the Atlas UI (Database Access) or with Terraform (mongodbatlas_database_user), then run the profiling command from the setup guide on each monitored database:
| Account | Roles |
|---|---|
mongo_monitor |
clusterMonitor@admin, read@\<db>, and the custom role dbsheriff_profiler |
mongo_kill_user |
killOpSession@admin (Atlas rejects hostManager) |
mongo_lock_user |
read@admin. A formality only: the real lock on Atlas goes through the service account below |
mongo_classify |
read@\<db> per database to scan |
dbsheriff_view_users |
Not available on Atlas (custom roles cannot carry viewUser); cluster-wide identity listing uses the service account below |
The custom role (Atlas UI: Database Access, Custom Roles; Terraform: mongodbatlas_custom_db_role; Admin API: POST /groups/{groupId}/customDBRoles/roles):
{ "roleName": "dbsheriff_profiler",
"actions": [ { "action": "ENABLE_PROFILER", "resources": [ { "db": "<your database>", "collection": "" } ] } ] }
The Atlas Admin API service account is not a database user. Create it under Organization, Access Manager, Applications, Service Accounts, Create. Grant it on this project Project Database Access Admin (lock, unlock and identity listing), and Project Owner only if you want the Atlas-side audit-log check (PS09) to be accurate. Give it an API Access List entry for the address DBSheriff calls the Admin API from (Atlas rejects 0.0.0.0/0 there). Store {"client_id", "client_secret", "group_id"} as the server's service_account_key through the Admin Console API (POST /api/monitored-servers); never write it into the database column by hand, because DBSheriff could not decrypt it. Full detail: MongoDB setup guide, section 1.
Google Cloud service accounts with IAM roles on the project; there is no CREATE USER. Four accounts: monitor, one response account that does both kill and lock, posture, classify. Full guide: BigQuery setup guide.
| Account | Needed? | Tier | Roles on the project | If it is missing |
|---|---|---|---|---|
dbsheriff-bq-monitor |
Required | Monitor (read) | roles/bigquery.resourceViewer, roles/logging.viewer, roles/logging.privateLogViewer, roles/bigquery.jobUser, roles/bigquery.dataViewer |
The project cannot be registered. |
dbsheriff-bq-response |
Required to cancel jobs and lock identities | Respond | roles/bigquery.admin, roles/resourcemanager.projectIamAdmin, roles/iam.serviceAccountKeyAdmin, roles/iam.serviceAccountAdmin, roles/datacatalog.categoryAdmin; optionally roles/iam.denyAdmin at the folder or organisation (Google does not allow it on a project) |
Cancel, lock and the policy-tag write-back run with the monitor key and fail. |
dbsheriff-bq-posture |
Optional | Monitor (read extension) | roles/bigquery.metadataViewer, roles/bigquery.jobUser, roles/bigquery.resourceViewer, roles/iam.securityReviewer, roles/logging.viewer, roles/logging.privateLogViewer |
The monitor key does the posture reads. |
dbsheriff-classify |
Required for classification | Monitor (read extension) | roles/bigquery.jobUser, roles/datacatalog.viewer, and roles/bigquery.dataViewer on the datasets to scan |
No value sampling for this project. |
P=<project-id>
gcloud services enable bigquery.googleapis.com cloudresourcemanager.googleapis.com \
iam.googleapis.com logging.googleapis.com datacatalog.googleapis.com --project=$P
for sa in monitor response posture; do
gcloud iam service-accounts create dbsheriff-bq-$sa --project=$P --display-name="DBSheriff $sa"
done
gcloud iam service-accounts create dbsheriff-classify --project=$P --display-name="DBSheriff classification"
M=serviceAccount:dbsheriff-bq-monitor@$P.iam.gserviceaccount.com
for r in roles/bigquery.resourceViewer roles/logging.viewer roles/logging.privateLogViewer \
roles/bigquery.jobUser roles/bigquery.dataViewer; do
gcloud projects add-iam-policy-binding $P --member=$M --role=$r; done
R=serviceAccount:dbsheriff-bq-response@$P.iam.gserviceaccount.com
for r in roles/bigquery.admin roles/resourcemanager.projectIamAdmin roles/iam.serviceAccountKeyAdmin \
roles/iam.serviceAccountAdmin roles/datacatalog.categoryAdmin; do
gcloud projects add-iam-policy-binding $P --member=$R --role=$r; done
# optional, folder or organisation level (the deny-policy rung of the lock ladder):
# gcloud resource-manager folders add-iam-policy-binding <folder-id> --member=$R --role=roles/iam.denyAdmin
S=serviceAccount:dbsheriff-bq-posture@$P.iam.gserviceaccount.com
for r in roles/bigquery.metadataViewer roles/bigquery.jobUser roles/bigquery.resourceViewer \
roles/iam.securityReviewer roles/logging.viewer roles/logging.privateLogViewer; do
gcloud projects add-iam-policy-binding $P --member=$S --role=$r; done
C=serviceAccount:dbsheriff-classify@$P.iam.gserviceaccount.com
for r in roles/bigquery.jobUser roles/datacatalog.viewer; do
gcloud projects add-iam-policy-binding $P --member=$C --role=$r; done
bq add-iam-policy-binding --member=$C --role=roles/bigquery.dataViewer $P:<dataset> # one per dataset to scan
# one key per account; paste the JSON content (not the path) into the Admin Console
for sa in dbsheriff-bq-monitor dbsheriff-bq-response dbsheriff-bq-posture dbsheriff-classify; do
gcloud iam service-accounts keys create ./$sa-key.json --iam-account=$sa@$P.iam.gserviceaccount.com
done
Running DBSheriff on Google Cloud with Workload Identity lets you leave the key fields blank; on any other host set ALLOW_ADC_SERVERS=true first.
Five Google Cloud service accounts, each with a database-scoped IAM role (and a few project-scoped ones). Spanner has no login or password. Full guide: Spanner setup guide.
| Account | Needed? | Tier | IAM roles | If it is missing |
|---|---|---|---|---|
spanner-monitor |
Required | Monitor (read) | roles/spanner.databaseReader (database), roles/spanner.viewer (instance) |
The database cannot be registered. |
spanner-kill |
Required to end sessions | Respond | roles/spanner.databaseUser (database) |
Kills fail. |
spanner-lock |
Required to lock accounts | Respond | roles/spanner.databaseAdmin (database), roles/logging.privateLogViewer (project) |
Locks fail; without the log role, sessions are not attributed to a principal. |
spanner-posture |
Optional | Monitor (read extension) | roles/spanner.databaseReader (database), roles/iam.securityReviewer (project) |
The monitor account does the posture reads; PS09 cannot be evaluated. |
spanner-classify |
Required for classification | Monitor (read extension) | roles/spanner.databaseReader (database) |
No value sampling for this database. |
Data Access audit logging for spanner.googleapis.com is off by default and is also required for principal attribution (setup guide, section 0 and the last block below).
PROJECT=<your-project-id>
INSTANCE=<your-instance-id>
DATABASE=<your-database-id>
gcloud services enable spanner.googleapis.com cloudresourcemanager.googleapis.com \
logging.googleapis.com iam.googleapis.com --project="${PROJECT}"
for role in monitor kill lock posture classify; do
gcloud iam service-accounts create "spanner-${role}" \
--project="${PROJECT}" --display-name="DBSheriff Spanner ${role}"
done
# database-scoped roles
bind() { gcloud spanner databases add-iam-policy-binding "${DATABASE}" --instance="${INSTANCE}" --project="${PROJECT}" \
--member="serviceAccount:spanner-$1@${PROJECT}.iam.gserviceaccount.com" --role="$2"; }
bind monitor roles/spanner.databaseReader
bind kill roles/spanner.databaseUser
bind lock roles/spanner.databaseAdmin
bind posture roles/spanner.databaseReader
bind classify roles/spanner.databaseReader
# instance-scoped (monitor only)
gcloud spanner instances add-iam-policy-binding "${INSTANCE}" --project="${PROJECT}" \
--member="serviceAccount:spanner-monitor@${PROJECT}.iam.gserviceaccount.com" --role="roles/spanner.viewer"
# project-scoped (lock and posture only)
gcloud projects add-iam-policy-binding "${PROJECT}" \
--member="serviceAccount:spanner-lock@${PROJECT}.iam.gserviceaccount.com" --role="roles/logging.privateLogViewer"
gcloud projects add-iam-policy-binding "${PROJECT}" \
--member="serviceAccount:spanner-posture@${PROJECT}.iam.gserviceaccount.com" --role="roles/iam.securityReviewer"
# one JSON key per account
for role in monitor kill lock posture classify; do
gcloud iam service-accounts keys create "spanner-${role}-key.json" \
--iam-account="spanner-${role}@${PROJECT}.iam.gserviceaccount.com"
done
# Data Access logging: read the policy, add the audit config below at its top level, write it back
gcloud projects get-iam-policy "${PROJECT}" --format=json > policy.json
# "auditConfigs": [{"service": "spanner.googleapis.com",
# "auditLogConfigs": [{"logType": "DATA_READ"}, {"logType": "DATA_WRITE"}]}]
gcloud projects set-iam-policy "${PROJECT}" policy.json
OAuth service principals, not database users. Four principals: monitor, one response principal that does both kill and lock, posture, classify. Requires Unity Catalog on the workspace. Full guide: Databricks setup guide.
| Principal | Needed? | Tier | Privileges | If it is missing |
|---|---|---|---|---|
dbsheriff_monitor |
Required | Monitor (read) | CAN_MANAGE on every monitored SQL warehouse; USE CATALOG, BROWSE on your catalogs and USE SCHEMA on their schemas (metadata only, no SELECT) |
The workspace cannot be registered. |
dbsheriff_response |
Required to cancel statements and lock identities | Respond | Member of the workspace admins group; CAN_USE on a warehouse; USE CATALOG, USE SCHEMA and APPLY TAG where tag write-back is wanted |
Lock, revoke and tag write-back run with the monitor principal and fail. A cancel is also tried with the monitor principal; it can succeed, because that principal holds CAN_MANAGE on the monitored warehouses, which is the privilege a cancel needs. |
dbsheriff_posture |
Optional | Monitor (read extension) | USE SCHEMA and SELECT on system.access, system.query, system.billing (granted by an account admin); CAN_USE on one warehouse |
The monitor principal does the posture reads; audit events, query back-fill and cost are missing without the system tables. |
dbsheriff_classify |
Required for classification | Monitor (read extension) | CAN_USE on a warehouse; USE CATALOG, USE SCHEMA and SELECT on the schemas to sample |
No value sampling for this workspace. |
for sp in dbsheriff_monitor dbsheriff_response dbsheriff_posture dbsheriff_classify; do
databricks service-principals create --display-name $sp --json '{"active": true}'
done
databricks service-principals list --output json | jq -r '.[] | select(.displayName|startswith("dbsheriff")) | "\(.displayName) id=\(.id) client_id=\(.applicationId)"'
# one OAuth secret per principal (two years); it is printed once
databricks service-principal-secrets-proxy create <numeric id> --json '{"lifetime":"63072000s"}'
# warehouse permissions
databricks api patch /api/2.0/permissions/sql/warehouses/<warehouse id> --json \
'{"access_control_list":[{"service_principal_name":"<monitor client id>","permission_level":"CAN_MANAGE"}]}'
databricks api patch /api/2.0/permissions/sql/warehouses/<warehouse id> --json \
'{"access_control_list":[{"service_principal_name":"<posture client id>","permission_level":"CAN_USE"}]}'
databricks api patch /api/2.0/permissions/sql/warehouses/<warehouse id> --json \
'{"access_control_list":[{"service_principal_name":"<classify client id>","permission_level":"CAN_USE"}]}'
# the response principal joins the admins group
databricks groups list --output json | jq -r '.[] | select(.displayName=="admins") | .id'
databricks api patch /api/2.0/preview/scim/v2/Groups/<admins group id> --json \
'{"schemas":["urn:ietf:params:scim:api:messages:2.0:PatchOp"],"Operations":[{"op":"add","value":{"members":[{"value":"<response numeric id>"}]}}]}'
Unity Catalog grants, run in a SQL editor as a metastore admin:
GRANT USE CATALOG, BROWSE ON CATALOG <catalog> TO `<monitor client id>`;
GRANT USE SCHEMA ON CATALOG <catalog> TO `<monitor client id>`;
GRANT USE SCHEMA, SELECT ON SCHEMA system.access TO `<posture client id>`;
GRANT USE SCHEMA, SELECT ON SCHEMA system.query TO `<posture client id>`;
GRANT USE SCHEMA, SELECT ON SCHEMA system.billing TO `<posture client id>`;
GRANT USE CATALOG ON CATALOG <catalog> TO `<classify client id>`;
GRANT USE SCHEMA, SELECT ON SCHEMA <catalog>.<schema> TO `<classify client id>`;
-- tag write-back on approval (optional)
GRANT USE CATALOG ON CATALOG <catalog> TO `<response client id>`;
GRANT USE SCHEMA ON CATALOG <catalog> TO `<response client id>`;
GRANT APPLY TAG ON CATALOG <catalog> TO `<response client id>`;
The client id goes in the console's User field and the secret in the Password field of the matching tier.
Enter each account's user name and password (or key) in the Admin Console, Monitored Servers, when you add the server: monitor in the main fields, kill, lock, posture and classification in the collapsed Advanced sections. They are stored encrypted in DBSheriff's registry and nowhere else; see the section "How secrets are handled after the install" of the installation guide (it is also on the published install page). The PostgreSQL, MySQL, SQL Server, Oracle and Snowflake setup guides each end with a table of what each account could do if its password (or key) leaked.