MongoDB — prerequisites and accounts

What to create on your MongoDB deployment so DBSheriff can monitor it. Covers both deployment shapes this engine supports — self-managed (Community or Enterprise, any host) and MongoDB Atlas — since the two diverge sharply on RBAC and on which controls are reachable from a plain database connection at all. Every fact below was verified live: on-prem against a real mongo:8 Community Edition container, and on Atlas against a real, Terraform-provisioned M10 replica set — not written from MongoDB's docs alone. Amazon DocumentDB is supported in part (§6: running operations, not completed ones), and Azure Cosmos DB for the MongoDB API is not supported (§6).

The three scripts

On an installed host there is no scripts/ folder, so read every scripts/<name>.py below as sudo /opt/dbsheriff/current/deploy/onprem/stack.sh tool <name> ..., for example sudo /opt/dbsheriff/current/deploy/onprem/stack.sh tool setup_dbsheriff mongodb mongo1.acme.internal. The scripts/ form is for a source checkout. The details are under "How to start the scripts" in Before you install: creating DBSheriff's database users.

Script What it does Example
setup_dbsheriff.py Recommended on self-managed. One command: checks/enables the prerequisite, then creates the accounts — but not the dbsheriff_view_users role that mongo_monitor holds on self-managed (§2): create that role first with create_reset_dbuser.py, selecting view_users on its own, or the mongo_monitor step fails because the role does not exist yet. On Atlas, the prerequisite half still works; the accounts half reports why it can't (see below) instead of creating anything. scripts/setup_dbsheriff.py mongodb mongo1.acme.internal
install_prereqs.py Prerequisite only — identical on both deployment shapes, and the only one of the three that's fully automated on Atlas. scripts/install_prereqs.py mongodb mongo1.acme.internal
create_reset_dbuser.py Accounts only, self-managed only — see below for why Atlas isn't automatable here. Its all choice creates view_users last, after mongo_monitor, so select view_users on its own first. scripts/create_reset_dbuser.py mongodb mongo1.acme.internal

Pass an Atlas SRV hostname (e.g. cluster0.xxxxx.mongodb.net) instead of a plain host to target Atlas — pick atlas at the platform prompt either way. install_prereqs.py and setup_dbsheriff.py ask which database you're registering for monitoring (profiling is per-database, identical question and identical command on both deployment shapes); create_reset_dbuser.py asks no such question and does not enable profiling — it grants read on every non-system database and leaves profiling as a manual follow-up. All three connect only as a database user — never a cloud credential, and never the Atlas Admin API Service Account below, which has no host/port/username/password shape at all. Add --dry-run to preview without changing anything, or --check for a read-only status report — see pre installation database users for the full flag reference shared by every engine.

On Atlas, account creation itself isn't automatable by any of these scripts — confirmed live, not a guess. createUser/updateUser return a clean Unauthorized over a direct database connection for every account type, even one holding Atlas's own atlasAdmin role — Atlas routes all user management through its own control plane, full stop. Running create_reset_dbuser.py/setup_dbsheriff.py against an Atlas cluster still does something useful: it reports this clearly per account instead of failing with a raw driver error, and it still handles the profiling prerequisite. Create the four accounts themselves via the Atlas UI (Database Access) or Terraform instead — §1 below has the exact roles each one needs either way.

Atlas now has functional parity with self-managed, except for the PS01, PS05, PS06, PS11 and PS13 posture checks (NOT_CHECKED on Atlas, §3), the A04 connection-quality check and role_table_grants (§5) — kill, lock, cluster-wide identity listing, and a real audit-logging posture check all work on both deployment shapes, live-verified end to end. Getting there needs one extra credential Atlas requires that self-managed doesn't: an Atlas Admin API Service Account (§1), since several MongoDB operations DBSheriff needs are structurally blocked from a database connection on Atlas, full stop, regardless of role — not a missing grant, a platform design choice. Read §5 for the handful of things that stay genuinely different between the two shapes even with that credential in place (mainly around encryption-at-rest and TLS posture checks, and the shape of the audit trail).

0. What must be enabled on the server

Item Required? What it unlocks Without it How
The accounts of §1 with the grants of §2 required statements, kill, space, availability, classification DBSheriff cannot connect or cannot act §2
Profiling enabled on every monitored database (db.setProfilingLevel(1, {slowms: 0})) required the entire completed-statement/audit/perf-scan pipeline — MongoDB has no separate audit log to fall back to system.profile never gets written; no completed queries, no P13, no statement audit at all. The setting does not always survive: an Atlas rolling restart or a new primary resets it to level 0, and slowms has been seen to change from 0 on its own. DBSheriff reads the level, slowms, sampleRate and filter about every 30 seconds and raises a capture-gap alert (R96) when the level is 0, slowms is not 0, sampleRate is below 1 or a profile filter is set (the last two leave most statements out of system.profile), and sets them back through the enableProfiler action that the monitor account holds (the dbsheriff_profiler role of §1 and §2; an account created before that role existed needs the accounts step run again, see below); where the account lacks it, re-run install_prereqs.py run once per database (self-managed and Atlas both — this is a plain database command, not an Atlas control-plane setting) — automated by install_prereqs.py/setup_dbsheriff.py, which ask which database
system.profile as a capped collection of 64 to 256 MB (recommended; on Atlas as well, see the Atlas block below) recommended complete completed-statement capture: at slowms: 0 every operation is one profile document, and the default 1 MB ring holds about a thousand of them, so a few thousand trivial operations overwrite the one that mattered before any reader sees it the producer still reads the ring oldest-first in pages, and raises a capture-gap alert (R96) when it finds the ring wrapped, but what was overwritten is gone install_prereqs.py offers it (profile_size), and setup_dbsheriff.py does when run with --optional-prereqs=profile_size, or the commands below. On Atlas the same commands work for a database user holding atlasAdmin (a narrower user is refused, and the script then prints the commands to run as one that may)
dbsheriff_profiler custom role (self-managed and Atlas, §1 and §2; not on Amazon DocumentDB, §6) required for the profiler health check the monitor reads the profiling level and slowms (profile -1) and sets level 1 / slowms 0 again after a restart or a failover the check is skipped with one log line, and a switched-off profiler is seen only as an empty system.profile self-managed: made by scripts/create_reset_dbuser.py mongodb <host> and setup_dbsheriff.py as part of the mongo_monitor step; Atlas: a custom role you create and add to mongo_monitor, §1. An account created before this role existed needs the accounts step run again (choose reset), §2
dbsheriff_view_users custom role (self-managed only, §2; on Amazon DocumentDB it holds viewUser only, §6) required for PS01/PS05 and Account Inventory identities on self-managed cluster-wide usersInfo({forAllDBs: true}) PS01/PS05 report NOT_CHECKED; Account Inventory shows nothing for this server db.createRole(), §2 — create via scripts/create_reset_dbuser.py mongodb <host>, selecting view_users
Atlas Admin API Service Account (Atlas only, §1) required for lock, identities, and PS09 on Atlas real Atlas-side lock/unlock, real cluster-wide identity listing, a real audit-logging posture check those 3 features fall back to the self-managed mechanism, which is structurally blocked on Atlas — lock fails cleanly with LOCK_ERROR, identities/PS09 report nothing/an unreliable result Atlas UI → Organization → Access Manager → Applications → Service Accounts, §1
Atlas Database Auditing (Atlas UI/API only, not a database command) optional a real audit trail behind PS09's now-accurate check PS09 correctly reports it as off (this is now a real, trustworthy finding, not a guess) Atlas UI → Security → Advanced

The floor. A clusterMonitor+read monitor account gives you live + completed statement monitoring and space/availability metrics on both deployment shapes, once profiling is on. Kill, lock, identities, and PS09 all work on both shapes too — self-managed through database roles, Atlas through a combination of Atlas-specific roles and the Admin API Service Account. See §5 for what's still genuinely different in the details.

1. The accounts

Account What it does Self-managed role Atlas role
mongo_monitor live + completed statements (system.profile), space (dbStats), availability (serverStatus), profiler health (profile -1, and level 1 / slowms 0 set again) clusterMonitor@admin + read@\<db> + dbsheriff_view_users@admin (custom, §2) + dbsheriff_profiler@admin (custom, §2) clusterMonitor@admin + read@\<db> + dbsheriff_profiler (a custom role, below)
mongo_kill_user db.killOp(<opid>) hostManager@admin killOpSession@admin
mongo_lock_user revokes all roles / restores them userAdminAnyDatabase@admin read@admin (a formality — the real lock mechanism on Atlas goes through the Admin API Service Account below, not this account; see §2)
mongo_classify classification scan (collection/field sampling) read@\<db> per database to scan read@\<db> per database to scan
Atlas Admin API Service Account (Atlas only) real lock/unlock, real identity listing, real audit-logging check — everything a database connection structurally cannot do on Atlas n/a see below — not a database account at all
// self-managed — run against the admin database
db.createRole({
  role: "dbsheriff_view_users",
  privileges: [
    { resource: { cluster: true }, actions: ["viewUser"] },
    // viewRole (2026-09-20): needed for governance.role_table_grants'
    // rolesInfo call (custom-role discovery for DB Role auto-tagging) —
    // not yet granted on existing installs, see §2.
    { resource: { cluster: true }, actions: ["viewRole"] }
  ],
  roles: []
});

// LIVE-281: the one action the profiler health check needs, on the monitored database only (repeat the privilege per database you
// register; never the cluster, never admin, config or local)
db.createRole({
  role: "dbsheriff_profiler",
  privileges: [
    { resource: { db: "<your database>", collection: "" }, actions: ["enableProfiler"] }
  ],
  roles: []
});

db.createUser({
  user: "mongo_monitor", pwd: "<pick a strong password>",
  roles: [
    { role: "clusterMonitor", db: "admin" },
    { role: "read", db: "<your database>" },
    { role: "dbsheriff_view_users", db: "admin" },
    { role: "dbsheriff_profiler", db: "admin" }
  ]
});
db.createUser({
  user: "mongo_kill_user", pwd: "<pick a strong password>",
  roles: [{ role: "hostManager", db: "admin" }]
});
db.createUser({
  user: "mongo_lock_user", pwd: "<pick a strong password>",
  roles: [{ role: "userAdminAnyDatabase", db: "admin" }]
});
db.createUser({
  user: "mongo_classify", pwd: "<pick a strong password>",
  roles: [{ role: "read", db: "<your database>" }]
});

// enable profiling on every database you register (repeat per database)
use <your database>
db.setProfilingLevel(1, { slowms: 0 });

// recommended: make the profiler's ring large enough to survive a burst (64 to 256 MB; the default is 1 MB).
// It drops the existing profile documents, so do it once, before the first monitoring poll.
db.setProfilingLevel(0);
db.system.profile.drop();
db.createCollection("system.profile", { capped: true, size: 134217728 });   // 128 MB
db.setProfilingLevel(1, { slowms: 0 });
// Atlas database accounts — via the Atlas UI (Database Access) or
// Terraform (mongodbatlas_database_user, provider >= 2.0.0).
// monitor:  clusterMonitor@admin + read@<db> + the custom role dbsheriff_profiler (below)
// kill:     killOpSession@admin   (NOT hostManager — Atlas rejects it, §2)
// lock:     read@admin — a formality only, see the Service Account below
// classify: read@<db>

// LIVE-281: a custom role (Atlas UI: Database Access, Custom Roles; Terraform: mongodbatlas_custom_db_role; Admin API:
// POST /groups/{groupId}/customDBRoles/roles) with the one action the profiler health check needs, on the monitored database only,
// then give it to mongo_monitor next to its two built-in roles (Terraform: roles { role_name = "dbsheriff_profiler", database_name = "admin" }):
//   { "roleName": "dbsheriff_profiler",
//     "actions": [ { "action": "ENABLE_PROFILER", "resources": [ { "db": "<your database>", "collection": "" } ] } ] }

// profiling is still a plain database command on Atlas, not an Atlas UI setting:
use <your database>
db.setProfilingLevel(1, { slowms: 0 });

// recommended on Atlas too, and more so than on a self-managed server: the Atlas profiler ring is 1 MB, and DBSheriff's own
// polling writes into it, so the default ring holds about four minutes of operations (measured on an M10). A database user with the
// atlasAdmin role may recreate it (live-verified: profiling off, drop, create, profiling on all accepted); do it once, before the
// first monitoring poll, because it drops the existing profile documents. install_prereqs.py does this when run as such a user.
db.setProfilingLevel(0);
db.system.profile.drop();
db.createCollection("system.profile", { capped: true, size: 134217728 });   // 128 MB
db.setProfilingLevel(1, { slowms: 0 });

The Atlas Admin API Service Account is not a database user at all — it's an OAuth2 client-credentials principal, created in the Atlas UI under Organization → Access Manager → Applications → Service Accounts → Create. Grant it access to this specific project with these roles:

Role (Atlas UI name) Needed for Notes
Project Database Access Admin lock/unlock, real identity listing The one role that lets DBSheriff actually modify database users through the Admin API — confirmed live this is sufficient for both, no broader role needed.
Project Cluster Log Viewer — Its name suggests it covers audit-log access; confirmed live it does not — kept here only because it's harmless and was part of this credential's original grant. Safe to omit.
Project Read Only baseline Included automatically by every other project role — listed for clarity, not strictly needed as its own grant.
Project Owner PS09 (audit-logging check) specifically The one operation genuinely needing broader access — confirmed live via a real 401 that Cluster Log Viewer does not cover the audit-log configuration endpoint despite its name. If you don't need PS09's Atlas-side accuracy, you can skip granting Owner and keep the credential scoped to just Database Access Admin.

Also required: an API Access List entry on the Service Account itself (separate from the cluster's own network access list, and — unlike the cluster's list — Atlas rejects 0.0.0.0/0 here) for whichever IP address DBSheriff's own containers/scripts actually call the Admin API from.

Store the resulting Client ID + Client Secret, together with the project's group ID, as a JSON blob {"client_id", "client_secret", "group_id"} in the service_account_key field of that server's registration, sent through the Monitored Servers API (POST /api/monitored-servers). The Admin Console form has no field for it on a MongoDB server, and the value is kept encrypted in governance.monitored_servers.service_account_key_encrypted (the registry's monitor-tier credential slot — unused by MongoDB's own database connection, same "generic credential blob, engine decides the shape" convention BigQuery/Databricks/Snowflake already use for their own API credentials), so do not write the JSON into that column with SQL: DBSheriff cannot decrypt it, logs bad service_account_key and treats the key as absent.

2. Grant reference (why each one is needed — and what Atlas rejects)

3. What DBSheriff reads

Producer (producer-mongo-pool) — no setup beyond §1-2 for mongo_monitor

currentOp({active: true}) for live (currently-running) operations; system.profile (via the same monitor connection, read@\<db>) for completed ones, polled and watermarked the same way every other event-log-shaped engine (BigQuery/Snowflake/Databricks) works — not a cumulative-counter diff the way pg_stat_statements is. Completed queries of the same shape (queryHash) within one poll cycle are grouped into a single message with a real calls_delta count — this does feed R38 BURST_CALL_VOLUME (live-verified: a genuine burst triggered a real, server-side account lock via this exact path — see docs/engines-feature-matrix.md's MongoDB row).

Admin/DDL commands (create, drop, collMod, createIndexes, dropIndexes, renameCollection) are mapped to SQL-DDL-shaped strings (the producer's DDL command table) so the shared, keyword-based is_ddl classifier recognizes them without a MongoDB-specific branch — without this, PRIVILEGED-strict-DDL scoring never fires for MongoDB DDL at all.

Accounts that are never scored. The accounts DBSheriff itself logs in as are kept out of scoring, the statement audit and the coverage figures: they are read from the user of each connection URI you registered (monitor, kill, lock, posture, classification), plus the default names of this guide (mongo_monitor, mongo_kill_user, mongo_lock_user, mongo_classify), which are also exempt from the honeytoken rule (R64). On an Atlas cluster (a *.mongodb.net host in the monitor URI) the principals Atlas runs itself are kept out too: __system (replication, about 25 profile entries a minute), mms-monitoring-agent ($collStats on every collection every ten minutes) and mms-automation. Your own exclusion list (Admin Console) is added on top of both.

Collections named inside a command. A statement names every collection it touches, not only the one it ran against: the from of a $lookup or $graphLookup, the collection of a $unionWith, the target of $merge and $out, the output of mapReduce, and the collection of distinct, count and findAndModify. A read of a sensitive collection (or of the honeytoken) through one of them is scored as a read of that collection.

Governance collector (mongodb-governance-collector)

Every 30s: system.profile → governance.mongodb_statement_audit (this is MongoDB's statement-audit trail — self-managed Community has no native audit log at all, confirmed structurally absent, not just unconfigured: buildInfo().modules is an empty array and every audit-related getParameter call fails outright).

Every hour: identities, config snapshot, protection, access log, coverage, posture (the posture scan, §4 below). Identities use usersInfo on self-managed (needs dbsheriff_view_users) and the Atlas Admin API's GET /databaseUsers on Atlas (needs the Service Account, §1) — both confirmed live returning real, correctly-normalized account data. Each hourly step runs independently; one step failing no longer blocks the others, including the posture scan itself.

Posture scan — 8 codes (PS01, PS05, PS06, PS07, PS08, PS09, PS11, PS13)

Code Check Self-managed Atlas
PS01 Default/demo account enabled Real NOT_CHECKED — the check lists users with usersInfo, which Atlas does not allow over a database connection (§2); the Service Account is not used for it
PS05 Over-broad admin role membership Real NOT_CHECKED (same)
PS06 Unencrypted connection observed Real NOT_CHECKED, with the reason in the note. Atlas refuses any connection that is not TLS (live-proved: a real non-TLS client connection attempt was flatly rejected), and the cumulative handshake counts of its mongod include Atlas's own internal infrastructure connections, so the check cannot say anything about your account's sessions.
PS07 TLS not enforced server-wide Real Real — Atlas genuinely sets net.tls.mode=requireTLS at the mongod level
PS08 Authentication not enforced Real Real — Atlas genuinely sets security.authorization=enabled
PS09 Audit logging not enabled Real (Community has none, by design) Real, via the Admin API's GET /auditLog (needs the Service Account with Project Owner, §1) — confirmed live this reports the genuine, current Database Auditing toggle for the project, not a guess. Without Project Owner granted, PS09 and PS13 report NOT_CHECKED; the self-managed check, which is unreliable on Atlas (reads a flag Atlas's real audit config never sets), is used only when no Service Account is configured at all.
PS11 Encryption at rest not enabled Real NOT_CHECKED, with the reason in the note. Atlas encrypts every cluster at rest by default (AES-256, cloud-provider-managed, cannot be disabled) and never sets the self-managed WiredTiger flag this check reads, so the check cannot say anything about the cluster.
PS13 Audit log not adequately protected NOT_CHECKED whenever PS09 fails NOT_CHECKED regardless of PS09's result — Atlas has no local-vs-syslog destination concept for this check to evaluate either way

On Atlas, PS06 and PS11 are NOT_CHECKED — both are platform-structural (the platform refuses non-TLS connections and encrypts every cluster at rest; no Admin API endpoint changes that), not a missing credential — and PS01 and PS05 are NOT_CHECKED too. PS07, PS08 and PS09 (with Project Owner) are as trustworthy on Atlas as on self-managed; PS13 is NOT_CHECKED as above.

Classification scan (mongo_classify, §1)

Samples up to MONGO_SCAN_SAMPLE_SIZE (default 100) documents per collection per database, extracting field names one level deep into nested documents (e.g. address.street) — never field values unless value-sampling is separately enabled. system.* collections (in practice, just system.profile once profiling is on) are excluded from scanning.

4. Verification

// run as mongo_monitor
db.adminCommand({ currentOp: true, active: true });   // should succeed, empty or real ops
db.<yourCollection>.find({}).limit(1);                // read access confirmed

Expected producer log line on first successful poll: [<server>][poll] nothing new (or a captured statement if one was running) — an Unauthorized error here means the monitor account's grants from §1 aren't in place yet, or profiling was never enabled (db.getProfilingStatus() should show {"was": 1, "slowms": 0}).

// run as mongo_monitor, on the monitored database: the profiler health check's own read (needs the enableProfiler action)
db.getSiblingDB("<your database>").runCommand({ profile: -1 });   // { was: 1, slowms: 0, ok: 1 }
// "not authorized ... profile" means the dbsheriff_profiler role (§1) is not on this account yet: run the accounts step again
// self-managed only — confirms dbsheriff_view_users actually works
// (run as mongo_monitor)
db.adminCommand({ usersInfo: { forAllDBs: true } });
# Atlas Admin API Service Account — confirms the credential and roles
# actually work before wiring it into the registry. Replace {client_id}/
# {client_secret}/{group_id} with the real values.
b64=$(echo -n "{client_id}:{client_secret}" | base64 -w0)
token=$(curl -s --request POST --url https://cloud.mongodb.com/api/oauth/token \
  --header "authorization: Basic $b64" \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'grant_type=client_credentials' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
curl -s --request GET \
  --url "https://cloud.mongodb.com/api/atlas/v2/groups/{group_id}/databaseUsers" \
  --header "Authorization: Bearer $token" \
  --header 'Accept: application/vnd.atlas.2023-01-01+json'

A successful response lists every real database user on the project — if this fails with 403, check the Service Account's API Access List entry (§1) first; if it fails with 401, check its project roles.

# classification catalog access (run inside the admin container; `<env>` is the environment name you gave the installer, `prod` by default)
sudo docker exec -it dbsheriff-<env>-admin python scripts/classify_scan.py --engines mongodb --server <name>

Expect PENDING candidates for real field names matching DBSheriff's PII name rules, and — once profiling is on and some traffic has run — real statement rows in governance.mongodb_statement_audit within 30 seconds.

5. Known limitations

6. Amazon DocumentDB (supported in part) and Azure Cosmos DB for the MongoDB API (not supported)

Both speak the MongoDB protocol and are not MongoDB. Everything below was measured on a live Amazon DocumentDB 5.0 cluster (TLS on, one db.t3.medium instance) in October 2026.

Amazon DocumentDB: what DBSheriff does there

Capability On DocumentDB Why
Running operations ($currentOp, every second) Works. A 90-second aggregate was published as a running operation 2 seconds after it started and scored 0.9. $currentOp is supported.
Completed statements, the statement audit, the Query Health scan Not captured. Only an operation that is still running when a poll reads it is seen; a statement that finishes within a second, and DDL such as dropCollection, leave no trace. The console marks the server completed capture: off. DocumentDB has no system.profile: setProfilingLevel answers code 303 (the profiler writes to Amazon CloudWatch Logs through log exports, which DBSheriff does not read) and a capped collection cannot be created.
Kill (killOp) The kill account needs hostManager@admin; killOp itself works (an operation was interrupted 5 seconds after the call).
Lock and unlock Works with userAdminAnyDatabase@admin: a new login and an already open session were both refused at once; unlock restored the roles.
Classification scan Works (read@\<db>).
Account Inventory, PS01 and PS05 Work with the role of the next block (usersInfo across all databases).
Posture checks PS06 to PS09, PS11 and PS13: PS06 (unencrypted connections observed), PS07 (TLS), PS08 (authentication), PS09 and PS13 (audit log), PS11 (encryption at rest) Not checked, with the reason beside each: DocumentDB does not support getCmdLineOpts or getParameter, and its serverStatus carries no TLS-handshake counts to compare for PS06. They are never reported as findings; check TLS, authentication, auditing and encryption in the AWS console (cluster parameter group, KMS key, log exports). The same holds for a Cosmos DB server, should one be registered by other means (it is not supported).
role_table_grants (which custom roles reach a classified collection) Not collected. The rolesInfo command needs the viewRole action, which DocumentDB cannot grant.
Configuration snapshot (compliance report, MG7) Edition reads Amazon DocumentDB; authentication, TLS and bind address are not evaluated. No start-up options (getCmdLineOpts).

The accounts on DocumentDB. Create them as the cluster's master user, in the admin database. They are the self-managed accounts of §1 with one difference, the dbsheriff_view_users role: DocumentDB refuses the viewRole action (Cannot grant privileges for the viewRole action on the cluster resource.), so the guide's role cannot be created there, and without it mongo_monitor cannot be created either (No role named dbsheriff_view_users@admin). Give the role viewUser alone:

// Amazon DocumentDB — run against the admin database as the master user
db.createRole({
  role: "dbsheriff_view_users",
  privileges: [ { resource: { cluster: true }, actions: ["viewUser"] } ],   // no viewRole: DocumentDB refuses it
  roles: []
});
db.createUser({ user: "mongo_monitor", pwd: "<pick a strong password>",
  roles: [ { role: "clusterMonitor", db: "admin" }, { role: "read", db: "<your database>" },
           { role: "dbsheriff_view_users", db: "admin" } ] });   // no dbsheriff_profiler: DocumentDB has no profiler, nothing for enableProfiler to act on
db.createUser({ user: "mongo_kill_user", pwd: "<pick a strong password>", roles: [ { role: "hostManager", db: "admin" } ] });
db.createUser({ user: "mongo_lock_user", pwd: "<pick a strong password>", roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] });
db.createUser({ user: "mongo_classify",  pwd: "<pick a strong password>", roles: [ { role: "read", db: "<your database>" } ] });
// do NOT run the profiling statements of §1 (setProfilingLevel, the capped system.profile): DocumentDB refuses them with code 303.
// install_prereqs.py / setup_dbsheriff.py report that error for the profiling prerequisite on a DocumentDB host: skip it.

scripts/create_reset_dbuser.py mongodb <cluster endpoint> with view_users selected creates the role with viewUser only when the host is a *.docdb.amazonaws.com cluster, and also when DocumentDB answers the guide's role with the viewRole refusal above.

The running-operations grant. $currentOp on its own is allowed to every account, but DocumentDB authorises a pipeline with a second stage after it (DBSheriff's running-time $match) against the admin database, so the monitor above gets Authorization failure (code 13) on that pipeline. DBSheriff handles it: on that answer it runs $currentOp alone, filters by running time itself, remembers the choice for the server (so the pool does not fail once a second) and writes one WARNING to the producer log naming the grant that makes the server do the filtering again. Nothing has to be granted for monitoring to work; if you prefer the server to filter, give mongo_monitor the read role on admin (or readAnyDatabase) in addition. With the fallback each poll reads every active operation of the node it is connected to.

The CA bundle. DocumentDB's certificate is signed by the Amazon RDS CA. Download the global bundle, https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem, and register the connection string with the bundle's path inside the DBSheriff containers:

mongodb://mongo_monitor:<url-encoded password>@<cluster endpoint>.docdb.amazonaws.com:27017/?authSource=admin&tls=true&tlsCAFile=/certs/mongodb-ca.pem&replicaSet=rs0&retryWrites=false

Every DBSheriff service that holds target credentials (the producer pool, the governance collector, the kill and lock paths, the classification and posture scans) opens the connection, so the file must be mounted into all of them. The compose files mount one bundle per engine at /certs/<engine>-ca.pem from the host file named by <ENGINE>_SSL_CA_HOST_PATH. For MongoDB set MONGODB_SSL_CA_HOST_PATH in the stack's .env to the bundle on the host (for Amazon DocumentDB the Amazon RDS global bundle, global-bundle.pem, or a self-managed server's own CA) and write tlsCAFile=/certs/mongodb-ca.pem in the connection string, as above; then docker compose up -d. MongoDB has the mount and no MONGODB_SSL_CA variable: the CA travels in the connection string, so there is nothing for a variable to name, and a server set to verify is not refused by DBSheriff for lack of one: the driver reports the certificate failure. (A deployment made before this release can name the path of another engine's mount that holds the same bundle, for example tlsCAFile=/certs/mysql-ca.pem.)

The mark in the Monitored Servers list. A server on DocumentDB shows completed capture: off beside its host, with the sentence above as the tooltip. DBSheriff knows the platform from the host name (a name ending .docdb.amazonaws.com): at once when the server was registered through the API with that host, and for a server registered by its connection string alone (the console form has no Host field for MongoDB) within about an hour, when the governance collector next reads the string and records the platform.

Azure Cosmos DB for the MongoDB API is not supported. Cosmos DB rejects the $currentOp aggregation DBSheriff reads running operations with (Expected type string but found int), and has no killOp, no profiler and no user commands, so a registered server could only fail every second. The Admin Console refuses a registration whose Host or connection string is a *.mongo.cosmos.azure.com (or its sovereign-cloud forms .us, .cn, .de) or *.documents.azure.com host (HTTP 422, with a one-sentence explanation); setup_dbsheriff.py, create_reset_dbuser.py and install_prereqs.py refuse such a host with the same sentence before they connect; and a producer pool leaves such a server out if it was registered before. The vCore service (*.mongocluster.cosmos.azure.com) was not part of the probe that found these commands refused, so it is not refused, and it is not a tested platform either. Use a self-managed MongoDB, MongoDB Atlas or Amazon DocumentDB server.

Connecting to the target securely

setup_dbsheriff.py, create_reset_dbuser.py and install_prereqs.py connect as your administrator (onboard_target.py does not handle MongoDB: it refuses the engine before it connects), and the administrator password and every new account's password cross that connection, so they verify the server's certificate by default (--ssl-mode verify-full; --ca-file <bundle> names the CA, for self-managed MongoDB tlsCAFile; Atlas is always mongodb+srv over verified TLS). A refused certificate stops the run and the message names the flag; --ssl-mode require encrypts without verifying (an explicit opt-down that prints a warning) and --ssl-mode disable --allow-plaintext sends everything in clear text (a trusted private link only). A generated password is never printed: it is stored in the registry (encrypted), or written to a new mode-600 file (--credentials-file) whose path is shown. Account names must match [A-Za-z][A-Za-z0-9_]*, and every database, schema or warehouse name the scripts discover is quoted as an identifier before it goes into a statement.