10 KiB
Cascades of Tucson — Chris Knight accounting mailbox delegation
User
- User: Howard Enos (howard)
- Machine: Howard-Home
- Role: tech
Session Summary
Chris Knight needed access to the Cascades accounting email as an attached account in Outlook — able to read and to send. Routed the request through the remediation-tool skill against the Cascades M365 tenant (cascadestucson.com, 207fa277-e9d8-4eb7-ada1-1064d2221498).
The opening consent audit graded the tenant RED, reporting "app NOT consented (token mint failed)" for all four app tiers. That was a false negative. The wiki recorded that the Exchange Operator app had worked against this tenant as recently as 2026-06-12 (the grievances@/Surveys@ shared-mailbox delegations), which contradicted the audit. Testing the token mint directly surfaced the real fault: get-token.sh resolves the vault from ~/.claude/identity.json, but this machine's identity and vault_path (D:/vault) live in the repo at .claude/identity.json. With VAULT_ROOT_ENV=D:/vault the exchange-op token minted immediately. The tenant was fully consented the whole time.
Read-only recon via the EXO REST InvokeCommand endpoint identified accounting@cascadestucson.com as a SharedMailbox ("Accounting Dept.", account disabled — normal for shared) and confirmed accountingassistant@ is a distinct UserMailbox, not a delegate. The existing delegates — Lauren Hasselman, Ashley Jensen, Zachary Nelson — each held FullAccess + SendAs. The wiki independently records Chris Knight as "same access tier as Lauren Hasselman," so the grant pattern was unambiguous.
After explicit user confirmation, applied Add-MailboxPermission (FullAccess, InheritanceType All, AutoMapping $true) and Add-RecipientPermission (SendAs) via the exchange-op tier. Both returned OK. The first verification read-back showed FullAccess but not SendAs — Exchange replication lag, not a failed write. A re-read confirmed Chris on the SendAs list, and the trustee GUID returned by the write (a1f1bb9e-0cee-4a49-8397-189e6137d680) matched his ExternalDirectoryObjectId.
Closed out by updating wiki/clients/cascades-tucson.md with a new accounting@ delegation record (delegates, grant pattern, replication gotcha) and a note on Chris's staff line. Logged two errorlog entries for the path-drift defects found along the way.
Key Decisions
- Did not trust the RED consent audit. The wiki's 2026-06-12 record of a successful Exchange Operator write against this tenant contradicted it. Verifying the token mint directly was cheaper than acting on the audit — and acting on it would have meant emailing the client an admin-consent link they did not need.
- Granted SendAs, not SendOnBehalf. All three existing delegates hold SendAs; SendOnBehalf would render as "Chris Knight on behalf of Accounting Dept." to recipients and break parity with the rest of the accounting staff.
AutoMapping $truewas the specific mechanism the request required — "attached email account" means the mailbox self-attaches to the Outlook profile via AutoDiscover, rather than the user hand-adding it.- Re-read before declaring the SendAs write failed. The write returned a trustee GUID; resolving that GUID against
Get-Mailboxproved the grant landed and the read was stale. - Skipped a full
/wiki-lintpass. Two bullet edits to one existing article introduced no[[wikilinks]]. Ran the applicable checks (link integrity, article structure) instead of a whole-wiki scan.
Problems Encountered
consent-audit.shreported a false RED "app NOT consented" for all four tiers. Root cause was a local vault-path lookup failure, not tenant consent. The script conflates "could not read the vault" with "tenant has not consented." Resolved withVAULT_ROOT_ENV=D:/vault. Logged toerrorlog.md; the script should fail loud on a vault-path error rather than downgrading it to a consent finding. This is the dangerous one — the false negative points directly at sending a client an unnecessary consent link.get-token.shand peers read~/.claude/identity.json, but the authoritative identity (withvault_path) is the repo's.claude/identity.json. Same path-drift family..claude/commands/wiki-lint.mdhardcodescd D:/claudetools, butclaudetools_rooton this machine isC:/claudetools. Logged as--friction.- Post-write
Get-RecipientPermissiondid not show the new SendAs trustee. Exchange replication lag across replicas. Re-read confirmed. Recorded in the wiki so the next delegate-add does not misread this as a failure.
Configuration Changes
| File | Change |
|---|---|
wiki/clients/cascades-tucson.md |
Added accounting@ delegation record + grant pattern + replication gotcha (line ~291); appended accounting-mailbox note to Chris Knight's staff line (line ~214) |
errorlog.md |
2 entries: remediation-tool/consent-audit.sh (false RED), wiki-lint (hardcoded path, --friction) |
M365 tenant changes (Cascades): two permission grants on accounting@cascadestucson.com — see Commands below. No code changes.
Credentials & Secrets
None discovered, created, or rotated this session.
- Vault accessed read-only for app credentials:
msp-tools/computerguru-exchange-operator.sops.yaml,msp-tools/computerguru-security-investigator.sops.yaml. - Auth path was certificate-based (
[INFO] auth=cert), client_credentials flow. - Vault root on this machine:
D:/vault(from repo.claude/identity.json→vault_path).
Infrastructure & Servers
- M365 tenant:
cascadestucson.com| Tenant ID207fa277-e9d8-4eb7-ada1-1064d2221498 - EXO REST endpoint:
https://outlook.office365.com/adminapi/beta/207fa277-e9d8-4eb7-ada1-1064d2221498/InvokeCommand - Token scope:
https://outlook.office365.com/.default - Apps used: ComputerGuru Exchange Operator
b43e7342-5b4b-492f-890f-bb5a4f7f40e9(write); ComputerGuru Security Investigatorbfbc12a4-f0dd-4e12-b06d-997e7271e10c(read) - Target mailbox:
accounting@cascadestucson.com— SharedMailbox, "Accounting Dept.", cloud-only,AccountDisabled: true,GrantSendOnBehalfTo: [] - Grantee:
Chris.Knight@cascadestucson.com— UserMailbox |ExternalDirectoryObjectId: a1f1bb9e-0cee-4a49-8397-189e6137d680|Guid: 04610882-68ab-4f15-a1e6-11966bbdc44c| AD SIDS-1-5-21-3975168698-2031163149-733277609-62630518 - Adjacent:
accountingassistant@cascadestucson.comis a UserMailbox, not a delegate ofaccounting@
accounting@ delegates (verified post-change 2026-07-09)
| Delegate | FullAccess | SendAs |
|---|---|---|
| lauren.hasselman@ | yes | yes |
| ashley.jensen@ | yes | yes |
| zachary.nelson@ | yes | yes |
| Chris.Knight@ | yes (added) | yes (added) |
Commands & Outputs
Token mint — the fix for the false RED:
# FAILS: scripts look at ~/.claude/identity.json
bash scripts/get-token.sh cascadestucson.com exchange-op
# ERROR: vault_path not set in /c/Users/Howard/.claude/identity.json and VAULT_ROOT_ENV env var not set
# WORKS:
VAULT_ROOT_ENV=D:/vault bash scripts/get-token.sh cascadestucson.com exchange-op
# [INFO] auth=cert
# eyJ0eXAiOiJKV1QiLCJub25jZSI6...
The two grants (exchange-op token, POST to InvokeCommand):
{"CmdletInput":{"CmdletName":"Add-MailboxPermission","Parameters":{
"Identity":"accounting@cascadestucson.com",
"User":"Chris.Knight@cascadestucson.com",
"AccessRights":"FullAccess","InheritanceType":"All","AutoMapping":true}}}
{"CmdletInput":{"CmdletName":"Add-RecipientPermission","Parameters":{
"Identity":"accounting@cascadestucson.com",
"Trustee":"Chris.Knight@cascadestucson.com",
"AccessRights":"SendAs","Confirm":false}}}
Outputs:
[OK] S-1-5-21-3975168698-2031163149-733277609-62630518 -> FullAccess (Deny=False)
[OK] a1f1bb9e-0cee-4a49-8397-189e6137d680 -> SendAs (Allow)
Verification (note the lag — first SendAs read omitted Chris, second included him):
{"CmdletInput":{"CmdletName":"Get-MailboxPermission","Parameters":{"Identity":"accounting@cascadestucson.com"}}}
{"CmdletInput":{"CmdletName":"Get-RecipientPermission","Parameters":{"Identity":"accounting@cascadestucson.com"}}}
Errorlog:
bash .claude/scripts/log-skill-error.sh "remediation-tool/consent-audit.sh" "<false RED>" \
--context "tenant=cascadestucson.com machine=HOWARD-HOME"
bash .claude/scripts/log-skill-error.sh "wiki-lint" "<hardcoded D:/claudetools>" \
--friction --context "ref=identity.json/claudetools_root machine=HOWARD-HOME"
Pending / Incomplete Tasks
- Chris's Outlook: AutoMapping attaches via AutoDiscover refresh — typically a few hours, reliably after an Outlook restart. SendAs can take ~60 min to replicate before sending from
accounting@stops bouncing. Not yet confirmed on his endpoint (DESKTOP-N5G1ROO, GuruRMM agent205025ee-2676-4498-8a27-e88562a6f69a). Worth a spot-check if he reports the mailbox missing. consent-audit.shfix (real bug, unfixed): must distinguish "vault unreadable" from "tenant not consented." Currently a local config fault renders as a client-facing consent gap. Highest-value of the three.get-token.sh+ sibling scripts: resolve identity from repo.claude/identity.json(honoringclaudetools_root), not~/.claude/identity.json. Until fixed, every remediation-tool invocation on this machine needsVAULT_ROOT_ENV=D:/vault..claude/commands/wiki-lint.md: replace hardcodedcd D:/claudetools.- No Syncro ticket was opened or referenced for this change. If it should be billed or tracked, it needs one.
- No
/wiki-compilerun — wiki was hand-edited directly.
Reference Information
- Wiki article:
wiki/clients/cascades-tucson.md - Skill:
C:\Users\Howard\.claude\skills\remediation-tool—scripts/get-token.sh,scripts/consent-audit.sh,references/graph-endpoints.md - Prior art (same grant pattern, 2026-06-12):
grievances@+Surveys@shared-mailbox delegations, Syncro ticket #32417 - Chris Knight workstation setup (2026-06-08): DESKTOP-N5G1ROO, Win 11 Pro for Workstations, AD account
chris.knight(OU=Administrative) - EXO app-auth note: when the MSP app cert is absent from the Windows cert store, use client_credentials → EXO-scoped token →
Connect-ExchangeOnline -AccessToken - Tenant consent link (only if genuinely needed — it was not this session):
https://login.microsoftonline.com/207fa277-e9d8-4eb7-ada1-1064d2221498/adminconsent?client_id=b43e7342-5b4b-492f-890f-bb5a4f7f40e9