Research Brief · Prepared by Chief · March 12, 2026
Topics: Azure CLI · Microsoft Graph CLI · Container Apps · M365 Automation · Enterprise Assistant Architecture
There are actually two separate CLI tools that matter here, and they cover completely different layers of our stack. Understanding both is what unlocks the full picture.
Layer: Infrastructure
Manages the Azure platform itself — Container Apps, Key Vault, App Registrations, ACR, networking, AI services. This is how we deploy, update, monitor, and scale the enterprise assistant.
brew install azure-cli
Layer: M365 Data
Manages Microsoft 365 services — mail, calendar, OneDrive, Teams, users, contacts. This is the assistant's actual capabilities layer — how it reads email, checks calendars, and manages files.
dotnet tool install --global Microsoft.Graph.Cli
az controls what the assistant runs on. mgc controls what the assistant can do. Wire them together and you have a self-managing, fully agentic enterprise bot.
az) — Infrastructure ControlOur enterprise assistant runs as an Azure Container App (one per user). The az containerapp command group is how we manage it programmatically.
| What You Want to Do | Command |
|---|---|
| Deploy a new user's assistant from scratch | az containerapp up --name assistant-dave --resource-group rg-enterprise --image myacr.azurecr.io/assistant:latest |
| Push a new version (zero-downtime) | az containerapp update --name assistant-dave --image myacr.azurecr.io/assistant:v2.1 |
| Set/update environment variables | az containerapp update --name assistant-dave --set-env-vars "ANTHROPIC_KEY=secretref:anthropic-key" |
| Tail live logs | az containerapp logs show --name assistant-dave --follow |
| Check revision/deployment status | az containerapp revision list --name assistant-dave --output table |
| Scale replicas up/down | az containerapp update --name assistant-dave --min-replicas 1 --max-replicas 3 |
| Restart (force new revision) | az containerapp revision restart --name assistant-dave --revision <rev-name> |
| List all user assistants | az containerapp list --resource-group rg-enterprise --output table |
The right pattern for our multi-tenant deployment: store all secrets in Azure Key Vault (one per client org, or one shared vault), reference them from Container Apps. The assistant never sees raw credentials — it gets secret refs only.
# Store a secret in Key Vault az keyvault secret set \ --vault-name kv-enterprise-prod \ --name "dave-graph-token" \ --value "eyJ0eXAiOiJKV1Q..." # Reference it in the Container App (no raw value in config) az containerapp secret set \ --name assistant-dave \ --secrets "graph-token=keyvaultref:https://kv-enterprise-prod.vault.azure.net/secrets/dave-graph-token" # Surface it as an env var inside the container az containerapp update \ --name assistant-dave \ --set-env-vars "GRAPH_TOKEN=secretref:graph-token"
Each user's assistant needs an App Registration in their Azure tenant (or ours, delegated) to call Graph API. The CLI manages the whole lifecycle:
# Create the app registration for a new client deployment
az ad app create \
--display-name "Enterprise Assistant - Dave Devries" \
--sign-in-audience AzureADMyOrg
# Grant Graph API permissions (mail, calendar, files)
az ad app permission add \
--id <app-id> \
--api 00000003-0000-0000-c000-000000000000 \
--api-permissions \
e1fe6dd8-ba31-4d61-89e7-88639da4683d=Scope \ # Mail.Read
Calendars.ReadWrite=Scope \
Files.ReadWrite=Scope
# Create a client secret (rotate periodically)
az ad app credential reset --id <app-id> --years 1
# Build and push new assistant image az acr build \ --registry myenterpriseacr \ --image assistant:v2.1 \ --file Dockerfile . # List available images/tags az acr repository show-tags \ --name myenterpriseacr \ --repository assistant # All users auto-pull new image on next restart # (or use revision-mode=multiple for gradual rollout)
mgc) — The Assistant's CapabilitiesThis is what makes the assistant actually useful. Graph CLI wraps every Microsoft Graph API endpoint in a scriptable CLI — mail, calendar, OneDrive, Teams, users, tasks, contacts. The assistant calls these as tools.
# List recent inbox messages
mgc users messages list --user-id dave@ash.com \
--filter "isRead eq false" \
--select "subject,from,receivedDateTime,bodyPreview" \
--top 20
# Read a specific message
mgc users messages get --user-id dave@ash.com --message-id <id>
# Send a reply
mgc users messages reply --user-id dave@ash.com --message-id <id> \
--body '{"comment": "Thanks for sending this over..."}'
# Move to folder / mark read
mgc users messages update --user-id dave@ash.com --message-id <id> \
--body '{"isRead": true}'
# Check upcoming events
mgc users calendar-view list --user-id dave@ash.com \
--start-date-time "2026-03-13T00:00:00Z" \
--end-date-time "2026-03-14T23:59:59Z"
# Create a meeting
mgc users events create --user-id dave@ash.com \
--body '{
"subject": "Provider Network Review",
"start": {"dateTime": "2026-03-15T10:00:00", "timeZone": "America/Los_Angeles"},
"end": {"dateTime": "2026-03-15T11:00:00", "timeZone": "America/Los_Angeles"},
"attendees": [{"emailAddress": {"address": "colleague@ash.com"}}]
}'
# Find free/busy slots
mgc users get-mail-tips --user-ids '["dave@ash.com", "colleague@ash.com"]' \
--mail-tips-options "automaticReplies"
# List files in a folder mgc users drive root children list --user-id dave@ash.com # Get a specific file's content mgc users drive items content get \ --user-id dave@ash.com --drive-item-id <id> # Upload / update a file mgc users drive items content put \ --user-id dave@ash.com --drive-item-id <id> \ --body @report.pdf
# Send a Teams message (bot-style)
mgc users chats messages create \
--user-id dave@ash.com --chat-id <chat-id> \
--body '{"body": {"content": "Here is your morning briefing..."}}'
# List Teams the user is in
mgc users joined-teams list --user-id dave@ash.com
The enterprise assistant is a FastAPI container. Each skill is a Python function that shells out to az or mgc CLI commands (or calls the SDK equivalents directly). The LLM (Claude) decides which tools to call. The CLI is the execution layer.
# How a "skill" looks inside the assistant container
# skills/email_skill.py
import subprocess, json
def get_unread_emails(user_id: str, top: int = 10) -> list:
"""Fetch unread emails for a user via Graph CLI."""
result = subprocess.run([
"mgc", "users", "messages", "list",
"--user-id", user_id,
"--filter", "isRead eq false",
"--select", "subject,from,receivedDateTime,bodyPreview",
"--top", str(top),
"--output", "json"
], capture_output=True, text=True)
return json.loads(result.stdout)["value"]
def deploy_new_revision(app_name: str, image_tag: str, resource_group: str):
"""Update the assistant's Container App to a new image (self-update)."""
subprocess.run([
"az", "containerapp", "update",
"--name", app_name,
"--resource-group", resource_group,
"--image", f"myacr.azurecr.io/assistant:{image_tag}"
])
Here's the angle that makes this genuinely powerful for the demo: the assistant can manage itself via az CLI.
az containerapp logs show to inspect its own logs, diagnoses the issue, and reports backaz containerapp update on itselfaz containerapp update --max-replicasmogcliFound a newer open-source tool worth knowing about: mogcli (github.com/jaredpalmer/mogcli) — described as "agent-friendly CLI for M365." Built by Jared Palmer (creator of Formik, Turborepo). Covers Mail, Calendar, Contacts, Groups, Tasks, OneDrive — same as mgc but lighter, designed specifically for agent use cases (piping JSON output into LLMs).
Worth watching. If the Microsoft Graph CLI (mgc) feels heavy for our container deployments, mogcli might be a cleaner fit.
| Action | Why | Effort |
|---|---|---|
Install az CLI + authenticate to our Azure subscription |
Prerequisite for everything else. az login → pick subscription |
5 min |
Run az containerapp list -g rg-enterprise --output table |
See our live Container Apps in one shot; confirm they're healthy | 5 min |
Install mgc and connect to Dave's M365 (delegated auth) |
Test a real mail fetch against his inbox — proves the stack works end to end | 1-2 hrs |
Build a thin Python skill wrapper around mgc mail + calendar commands |
This becomes the Email Claw and Calendar Claw in the assistant | Half day |
Add a az containerapp logs show --follow tool to the assistant |
Self-monitoring capability — assistant can report on its own health | 2 hrs |
| Key Vault setup for Dave's Graph token | Production-ready secrets handling; healthcare compliance story | Half day |
1. Get az managing our existing Container Apps → admin confidence
2. Get mgc reading Dave's email + calendar → assistant's first real capability
3. Wrap both in Python skills → plug into the FastAPI assistant
4. Add self-monitoring (logs, health, restart) → demo wow moment
5. Key Vault for secrets → healthcare-compliant production deployment
Sources: learn.microsoft.com/en-us/cli/azure (Azure CLI official docs, March 2026) · github.com/microsoftgraph/msgraph-cli (Microsoft Graph CLI) · github.com/jaredpalmer/mogcli (agent-friendly M365 CLI) · learn.microsoft.com/en-us/azure/container-apps (Container Apps documentation) · devblogs.microsoft.com/microsoft365dev (Graph CLI v1.0 GA announcement)
Report prepared by Chief · March 12, 2026 · Internal use only