# Glaztech — Email Delivery Investigation & Transport Rules

## User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-5070
- **Role:** admin

---

## Session Summary

Steve Eastman (Glaztech) reported that emails from two vendor contacts — `hartsglass@centurytel.net` and `olemons@eastexglass.com` — were showing "Deliver" in MailProtector but not arriving at `SHVSALES@glaztech.com`. The emails were not appearing in M365 Quarantine either, which was the key diagnostic clue.

Investigation via Exchange Online message trace and connector inspection revealed the root cause: both sender domains (`centurytel.net` and `eastexglass.com`) publish DMARC policy `p=reject`. Glaztech's inbound connector ("Inbound Spam Filter") has Enhanced Filtering enabled with `EFSkipIPs` set to the three MailProtector IPs (162.248.93.233, 162.248.93.81, 65.113.52.82), but `SCLMinusOne` is `null` — meaning EOP does not trust MailProtector's verdict and re-evaluates all mail. With Enhanced Filtering, EOP looks past the MailProtector relay to the original sender's authentication. Both sender domains fail DMARC, producing hard `550 5.7.509` NDR rejections in Exchange Online before messages ever reach the mailbox or quarantine.

Additional rejected senders were identified from the email thread Steve forwarded: `SSales@arkglass.com`, `bossier@glassservices.com`, and an unnamed contact at `aaaglassinc.com` (Donald Arnold). DNS inspection of `glassservices.com` found a broken SPF record (`v=spf1 -all` — no authorized senders), meaning their emails will be rejected by all mail providers regardless of any Glaztech-side fix.

The fix applied was two SCL=-1 transport rules in the Glaztech Exchange Online tenant. Transport rules evaluate before the anti-spam pipeline (including DMARC enforcement) in EOP, so setting SCL=-1 bypasses the DMARC rejection for these specific senders. Rules were created via the Exchange Operator service principal using `POST /adminapi/beta/{tenant}/InvokeCommand` with `New-TransportRule` cmdlet (direct REST resource endpoints 404 in this tenant).

---

## Key Decisions

- **SCL=-1 transport rule over connector trust** — Setting `SCLMinusOne: true` on the connector would trust MailProtector's verdict for ALL inbound mail, which is too broad. Targeted transport rules scoped to specific senders are the more surgical fix.
- **Two rules instead of one** — The aaaglassinc.com sender was handled by domain (`SenderDomainIs`) rather than specific address since the contact email was not known. The other four were handled by exact address (`From`). Separate rules keep the logic clean.
- **glassservices.com SPF not fixable on our side** — Their `v=spf1 -all` will cause rejection everywhere. The appropriate action is notifying Steve so he can tell the glassservices.com vendor their IT needs to fix the SPF record. Our SCL=-1 rule still covers `bossier@glassservices.com` as a workaround.
- **No Syncro ticket created** — This was handled reactively via email thread; no ticket was opened during the session. Consider creating one retroactively if billing for this work.

---

## Problems Encountered

- **Direct REST resource endpoints 404**: `/adminapi/beta/{tenant}/TransportRule` returns 404. Fixed by using the `InvokeCommand` pattern: `POST /adminapi/beta/{tenant}/InvokeCommand` with `{"CmdletInput": {"CmdletName": "New-TransportRule", "Parameters": {...}}}`.
- **Transport rule name 66-char limit exceeded**: Initial rule name "SCL Bypass - hartsglass@centurytel.net and olemons@eastexglass.com" was 66 chars; EXO enforces a 64-char max. Fixed by shortening to "SCL Bypass - hartsglass + olemons (SHVSALES)" (44 chars).
- **Initial misdiagnosis**: First assumed emails were landing in Junk/spam folder (SCL scoring). Steve's follow-up with full message trace revealed actual cause was hard DMARC `550 5.7.509` NDR — the SCL=-1 fix still resolves this correctly since transport rules run before DMARC enforcement in the EXO pipeline.

---

## Configuration Changes

Two transport rules created in Glaztech M365 tenant (82931e3c-de7a-4f74-87f7-fe714be1f160):

| Rule GUID | Name | Condition | Action |
|-----------|------|-----------|--------|
| 482c714a-8780-4c62-ae0a-0b6da9ca9d52 | SCL Bypass - hartsglass + olemons (SHVSALES) | From: hartsglass@centurytel.net, olemons@eastexglass.com, SSales@arkglass.com, bossier@glassservices.com | SetSCL: -1 |
| 7e0c01a8-ec22-43fe-b600-796c0f295aa5 | SCL Bypass - aaaglassinc.com (SHVSALES) | SenderDomainIs: aaaglassinc.com | SetSCL: -1 |

Both rules: State = Enabled.

---

## Credentials & Secrets

- **Exchange Operator Service Principal**
  - App ID: b43e7342-5b4b-492f-890f-bb5a4f7f40e9
  - Secret: Ct28Q~fKYUu.RvkMaGNAV1YeK6h-HBewCTPnwa.Y
  - Vault: `msp-tools/computerguru-exchange-operator.sops.yaml`
- **Glaztech Tenant ID**: 82931e3c-de7a-4f74-87f7-fe714be1f160

---

## Infrastructure & Servers

- **Glaztech M365 tenant**: 82931e3c-de7a-4f74-87f7-fe714be1f160
- **MailProtector IPs (EFSkipIPs on inbound connector)**: 162.248.93.233, 162.248.93.81, 65.113.52.82
- **EXO InvokeCommand endpoint**: `https://outlook.office365.com/adminapi/beta/{tenant}/InvokeCommand`
- **Token endpoint**: `https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token`

---

## Commands & Outputs

```bash
# Auth token
POST https://login.microsoftonline.com/82931e3c-.../oauth2/v2.0/token
scope=https://outlook.office365.com/.default

# Check connector Enhanced Filtering state
POST /adminapi/beta/{tenant}/InvokeCommand
{"CmdletInput":{"CmdletName":"Get-InboundConnector","Parameters":{"Identity":"Inbound Spam Filter"}}}
# → EFSkipIPs: [162.248.93.233, 162.248.93.81, 65.113.52.82], SCLMinusOne: null

# Create rule 1 (specific addresses)
{"CmdletInput":{"CmdletName":"New-TransportRule","Parameters":{
  "Name":"SCL Bypass - hartsglass + olemons (SHVSALES)",
  "From":["hartsglass@centurytel.net","olemons@eastexglass.com","SSales@arkglass.com","bossier@glassservices.com"],
  "SetSCL":-1,"Enabled":true}}}
# → GUID: 482c714a-8780-4c62-ae0a-0b6da9ca9d52

# Create rule 2 (domain)
{"CmdletInput":{"CmdletName":"New-TransportRule","Parameters":{
  "Name":"SCL Bypass - aaaglassinc.com (SHVSALES)",
  "SenderDomainIs":["aaaglassinc.com"],
  "SetSCL":-1,"Enabled":true}}}
# → GUID: 7e0c01a8-ec22-43fe-b600-796c0f295aa5
```

---

## Pending / Incomplete Tasks

- **Notify Steve about glassservices.com SPF**: `bossier@glassservices.com` will be rejected by all mail providers (SPF `v=spf1 -all`). Steve should tell the vendor their IT needs to fix the SPF record. Our SCL bypass is a workaround only.
- **Harts Glass resend**: The original rejected emails need to be resent by the sender. Our rule is now live but previously rejected messages are NDRed and will not auto-retry.
- **Syncro ticket**: No ticket opened for this work. Create retroactively if billing.

---

## Reference Information

- **DMARC root cause**: `centurytel.net` → `p=reject`; `eastexglass.com` → `p=reject`
- **NDR code**: `550 5.7.509` (DMARC failure under Enhanced Filtering)
- **Transport rule GUIDs**: 482c714a (addresses), 7e0c01a8 (aaaglassinc.com domain)
- **EXO InvokeCommand pattern**: Required because direct `/TransportRule` REST endpoint 404s in this tenant
