Australian contact centre (first tenant) · built as a reusable service · 2026

Multi-tenant Teams call reporting & analytics

An Azure-hosted pipeline that pulls each customer's Teams call records from Microsoft Graph daily, classifies every call leg, and delivers reports — with onboarding fully automated in GitHub Actions.

  • Microsoft 365 & Teams
  • Code & DevOps
  • Azure
call legs matched the carrier report
149/150
runtime bugs found and fixed on first live run
4
daily run — inside the free tier
~4 min
Architecture

How it fits together

  1. 01

    Source

    • Microsoft Graph callRecords
  2. 02

    Process

    • Azure Automation runbook
    • PowerShell 7
  3. 03

    Store

    • Azure Key Vault
    • Azure Blob Storage
  4. 04

    Report

    • Graph sendMail
    • Call Journey Explorer (HTML)
  5. 05

    Deploy

    • GitHub Actions (OIDC)
    • Bicep
Outcome

Daily classified call reporting for a live customer tenant, matching the carrier's own usage report on 149 of 150 call legs.

The problem

The customer ran a certified third-party contact-centre app on top of native Teams call queues and auto attendants, and couldn’t trace a call end-to-end across the two. They wanted daily volume and abandonment reporting — and the solution had to deploy to any customer tenant with no tenant-specific assumptions.

What I did

  • Proved the data model with real test calls first: a single Graph call record holds the whole journey — PSTN caller → resource account → queue → answering agent — as nested sessions.
  • Wrote a PowerShell classifier that is fully structural, never name-based: queues by user-agent role, people by directory lookup, vendor services by app ID from a shared registry. Adding a new vendor is one JSON entry.
  • Turned it into an Azure Automation runbook using managed identity, per-customer Key Vault secrets, Blob output and a locked-down Graph sendMail.
  • Built GitHub Actions workflows that onboard and offboard customers with per-customer Bicep and automatically registered OIDC credentials. Validating the shared Bicep with what-if caught three drift bugs before they were applied.
  • Built a single-file Call Journey Explorer dashboard with filters, KPI cards and a per-call timeline drill-down.
  • Used the same data for a 21-day abandoned-call investigation that separated genuine queue abandons from calls that only ever reached the IVR — explaining why two reporting systems disagreed.

Result

The first live runs surfaced four runtime bugs (including time zones failing inside the Automation sandbox and PowerShell 7.2’s separate module list) — all fixed. Cross-checked against the carrier’s Direct Routing report, 149 of 150 call legs matched within ±15 seconds, and the compute sits comfortably inside the Automation free tier.