Admin CLI for Microsoft 365 OneDrive + SharePoint + Mail via Microsoft Graph.
Project description
m365ctl
An admin CLI for Microsoft 365 — OneDrive, SharePoint, and Exchange Mail — built on Microsoft Graph.
Safe by default: dry-run, plan-file workflow, scope allow-lists, audit log, and undo on every mutation.
Table of contents
- Highlights
- Installation
- Quickstart
- Features
- Safety model
- Configuration
- Documentation
- Contributing
- License
- Disclaimer
Highlights
- Read-only by default. Mutating verbs require
--confirmand are logged. - Local catalog. DuckDB mirrors of files and mail enable offline queries.
- Plan / replay. Bulk operations produce reviewable plan files before execution.
- Undoable. Every mutation records before/after state for
m365ctl undo. - Scope guards.
allow_drives/allow_mailboxes/deny_pathsenforced on every call. - App-only auth. Certificate-based auth against your Entra app registration.
Installation
Requires Python 3.11+. Install with uv:
# Standalone CLI (recommended for end users):
uv tool install m365ctl
# Or as a project dependency:
uv add m365ctl
# Or with pipx / pip:
pipx install m365ctl
This installs the m365ctl console script. Verify with m365ctl --help.
For local development from source:
git clone https://github.com/aren13/m365ctl
cd m365ctl
uv sync --all-extras
# The repo's bin/*.sh shims (./bin/od-auth, ./bin/mail-list, ...) are
# convenience aliases for `uv run m365ctl <domain> <verb>`.
Quickstart
-
Register an Entra app and grant Graph permissions — see docs/setup/azure-app-registration.md.
-
Generate a client certificate — see docs/setup/certificate-auth.md.
-
Configure the CLI:
# Grab the template from the repo or copy from `m365ctl --help` output. curl -O https://raw.githubusercontent.com/aren13/m365ctl/main/config.toml.example mv config.toml.example config.toml # Edit config.toml: tenant id, app id, cert path, allow-lists.
-
Authenticate and verify:
m365ctl od auth login m365ctl od auth whoami m365ctl mail whoami
Full walkthrough: docs/setup/first-run.md (≤ 20 minutes from install to whoami).
Features
OneDrive and SharePoint
| Capability | Commands |
|---|---|
| Catalog mirror | od-catalog-refresh, od-catalog-status |
| Search | od-search (server and local) |
| Inventory | od-inventory (top-by-size, top-by-age, dups) |
| File operations | od-move, od-rename, od-copy |
| Sensitivity | od-label |
| Sharing audit | od-audit-sharing |
| Recycle bin | od-clean, od-delete |
| Download | od-download (bulk + resumable) |
| Capability | Commands |
|---|---|
| Read | mail-list, mail-get, mail-read, mail-search |
| Folders / labels | mail-folders, mail-categories, mail-categorize |
| Compose | mail-draft, mail-send, mail-reply, mail-forward |
| Move / flag | mail-move, mail-copy, mail-flag, mail-focus |
| Inbox rules | mail-rules (create, update, delete, import, export) |
| Mailbox settings | mail-settings, mail-ooo, mail-signature |
| Triage DSL | mail-triage (YAML rules over local catalog) |
| Catalog | mail-catalog-refresh, mail-catalog-status |
| Export | mail-export (EML, MBOX, attachments, full mailbox) |
| Hard delete | mail-clean, mail-empty (triple-gated) |
| Delegation | mail-delegate, --mailbox shared:<addr> routing |
| Send-as | mail-sendas (app-only, fully audited) |
| Scheduled send | mail-send --schedule-at <iso> |
| Convenience verbs | mail-digest, mail-archive, mail-snooze, mail-top-senders, mail-size-report, mail-unsubscribe |
Each verb supports --help. See docs/mail/convenience-commands.md for the convenience-verb reference.
Safety model
| Layer | Behavior |
|---|---|
| Dry-run default | Mutating verbs print a plan and exit unless --confirm is passed. |
| Plan files | Bulk operations write a plan file that can be reviewed before --replay. |
| Scope allow-lists | allow_drives / allow_mailboxes gate every API call; deny_paths / deny_folders are absolute. |
| Audit log | Each mutation records a before/after block to a tamper-resistant log. |
| Undo | m365ctl undo <op-id> (or od-undo / mail-undo) replays the inverse using the audit record. |
| Hard-delete gate | Irreversible deletes require --confirm, a TTY-typed phrase, and an extra escalation for large ops. |
Configuration
config.toml controls auth, scopes, paths, and feature flags. Start from config.toml.example:
[auth]
tenant_id = "..."
client_id = "..."
cert_path = "~/.m365ctl/cert.pem"
[scope]
allow_drives = ["..."]
allow_mailboxes = ["user@example.com"]
deny_paths = ["/Confidential"]
[mail]
schedule_send_enabled = false
Catalog files, plan files, and the audit log default to cache/, plans/, and logs/ under the project root — override per config.toml.
Documentation
| Topic | Link |
|---|---|
| Azure app registration | docs/setup/azure-app-registration.md |
| Certificate-based auth | docs/setup/certificate-auth.md |
| First-run walkthrough | docs/setup/first-run.md |
| PnP PowerShell prerequisites | docs/ops/pnp-powershell-setup.md |
| Mail convenience verbs | docs/mail/convenience-commands.md |
| Roadmap | docs/roadmap.md |
| Changelog | CHANGELOG.md |
| Contributing | CONTRIBUTING.md |
Contributing
Issues and pull requests welcome. Before opening a PR, please read CONTRIBUTING.md and run the test suite:
uv run pytest
uv run ruff check .
uv run mypy src
License
Apache-2.0. See LICENSE.
Disclaimer
This is an independent open-source project. Not affiliated with, endorsed by, or supported by Microsoft. "Microsoft 365", "OneDrive", "SharePoint", and "Exchange" are trademarks of Microsoft Corporation.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file m365ctl-1.15.0.tar.gz.
File metadata
- Download URL: m365ctl-1.15.0.tar.gz
- Upload date:
- Size: 409.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e46f783ed3f17417c4d8aca5dadd41997f900f431321f6b94a336ca95aba8f50
|
|
| MD5 |
5f4f4daf900e44af7ca58ee9900211d1
|
|
| BLAKE2b-256 |
33eca6ada001f90fadeb2c54dd5e1bf1c3bcc96efdc43a6dbd81622e14974a54
|
Provenance
The following attestation bundles were made for m365ctl-1.15.0.tar.gz:
Publisher:
release.yml on aren13/m365ctl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
m365ctl-1.15.0.tar.gz -
Subject digest:
e46f783ed3f17417c4d8aca5dadd41997f900f431321f6b94a336ca95aba8f50 - Sigstore transparency entry: 1421943020
- Sigstore integration time:
-
Permalink:
aren13/m365ctl@284b09a7adbca260b329c338e8880f4baa052282 -
Branch / Tag:
refs/tags/v1.15.0 - Owner: https://github.com/aren13
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@284b09a7adbca260b329c338e8880f4baa052282 -
Trigger Event:
push
-
Statement type:
File details
Details for the file m365ctl-1.15.0-py3-none-any.whl.
File metadata
- Download URL: m365ctl-1.15.0-py3-none-any.whl
- Upload date:
- Size: 267.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0fa30c85e068c994802b7699688e3c3301938477dd014f4ebc4c0cff592e0d99
|
|
| MD5 |
f0172a7ac30931ca35de15cdd5ecc894
|
|
| BLAKE2b-256 |
75b19339a02aa24b96034e31fefdffed6cac07d8c3e2740be3201097d8a7e014
|
Provenance
The following attestation bundles were made for m365ctl-1.15.0-py3-none-any.whl:
Publisher:
release.yml on aren13/m365ctl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
m365ctl-1.15.0-py3-none-any.whl -
Subject digest:
0fa30c85e068c994802b7699688e3c3301938477dd014f4ebc4c0cff592e0d99 - Sigstore transparency entry: 1421943085
- Sigstore integration time:
-
Permalink:
aren13/m365ctl@284b09a7adbca260b329c338e8880f4baa052282 -
Branch / Tag:
refs/tags/v1.15.0 - Owner: https://github.com/aren13
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@284b09a7adbca260b329c338e8880f4baa052282 -
Trigger Event:
push
-
Statement type: