🧭 How toLeadershipIntermediate✨ AI-assisted

How to Build a GPT-Powered Executive Digest on Azure OpenAI

WittyTech Daily··4 min read
#azure-openai#gpt-5#microsoft-foundry#leadership-reporting

Leadership doesn't want your Jira board. They want three bullet points: what's on track, what's at risk, and what needs a decision from them today. Building that GPT executive digest on Azure OpenAI by hand every week is the kind of task that eats a Friday afternoon and still lands two days late.

By the end of this guide you'll have a scheduled job that gathers project signals, sends them to a GPT-5 deployment with a strict output schema, and delivers a formatted leadership briefing through Outlook, no manual copy-pasting required.

Deploy GPT-5 in Microsoft Foundry

Azure OpenAI Service now lives inside Microsoft Foundry (formerly Azure AI Foundry). Create the resource and deploy a model with the Azure CLI:

az cognitiveservices account create \
  --name <your-resource-name> \
  --resource-group <your-resource-group> \
  --kind AIServices \
  --sku S0 \
  --location eastus2 \
  --custom-domain <your-resource-name>

az cognitiveservices account deployment create \
  --name <your-resource-name> \
  --resource-group <your-resource-group> \
  --deployment-name exec-digest-gpt5 \
  --model-name gpt-5-mini \
  --model-version "2025-08-07" \
  --model-format OpenAI \
  --sku-name GlobalStandard \
  --sku-capacity 10

gpt-5-mini is cheap enough to run daily and still handles structured summarization well. Swap in gpt-5 if you want deeper reasoning over long status reports. Pick the Global or Data Zone deployment type based on where your leadership data is allowed to sit.

Define the digest as a strict schema

Structured outputs force the model to return exactly the shape you ask for, no stray prose to parse around. Define it with Pydantic:

from pydantic import BaseModel

class DigestItem(BaseModel):
    title: str
    detail: str
    owner: str

class ExecutiveDigest(BaseModel):
    one_line_summary: str
    kpis: list[DigestItem]
    risks: list[DigestItem]
    blockers: list[DigestItem]

Every field in a structured output schema has to be marked required, and objects need additionalProperties: false under the hood. The openai Python SDK handles that translation for you when you pass a Pydantic model.

Call GPT-5 with structured outputs

Authenticate with Microsoft Entra ID instead of an API key, so nothing sensitive sits in an environment variable:

from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from openai import OpenAI

token_provider = get_bearer_token_provider(
    DefaultAzureCredential(), "https://ai.azure.com/.default"
)
client = OpenAI(
    base_url="https://<your-resource-name>.openai.azure.com/openai/v1/",
    api_key=token_provider,
)

def build_digest(raw_status_notes: str) -> ExecutiveDigest:
    completion = client.beta.chat.completions.parse(
        model="exec-digest-gpt5",
        messages=[
            {
                "role": "system",
                "content": (
                    "You write a terse executive AI summary for engineering "
                    "leadership. Flag only real risks and blockers, not "
                    "routine status."
                ),
            },
            {"role": "user", "content": raw_status_notes},
        ],
        response_format=ExecutiveDigest,
    )
    return completion.choices[0].message.parsed

raw_status_notes is whatever you already have: exported Jira tickets, a Slack channel dump, a project tracker export. Feed it in as plain text and let GPT-5 do the structuring.

Schedule the GPT executive digest with Azure Functions

Wrap build_digest in a timer-triggered function so it runs every weekday morning:

import azure.functions as func

app = func.FunctionApp()

@app.timer_trigger(schedule="0 0 7 * * 1-5", arg_name="mytimer", run_on_startup=False)
def exec_digest_timer(mytimer: func.TimerRequest) -> None:
    notes = fetch_status_notes()  # your own aggregation logic
    digest = build_digest(notes)
    send_digest(digest)

The cron expression 0 0 7 * * 1-5 fires at 7:00 AM UTC, Monday through Friday. Adjust it to your leadership team's timezone and cadence.

Deliver the digest through Outlook

Azure Functions can't reliably send external email on its own; outbound SMTP from shared compute IPs gets blocked or throttled. Route delivery through a Logic App instead. Create one with an HTTP request trigger and an Office 365 Outlook "Send an email (V2)" action, then call it from send_digest:

import requests

def send_digest(digest: ExecutiveDigest) -> None:
    body_html = "<br>".join(
        f"<b>{item.title}</b>: {item.detail} (owner: {item.owner})"
        for item in digest.risks + digest.blockers
    )
    requests.post(
        "https://<your-logic-app>.azurewebsites.net/api/exec-digest-trigger",
        json={
            "subject": f"Exec digest: {digest.one_line_summary}",
            "body": body_html,
            "to": "leadership-team@yourcompany.com",
        },
        timeout=30,
    )

Map those JSON fields to the Outlook action's Subject, Body and To fields inside the Logic App designer.

Gotchas

  • Structured outputs need every schema field marked required. Use a nullable union type instead of leaving a field truly optional.
  • Structured outputs don't support parallel tool calls, and Azure doesn't yet support them with Assistants or Foundry Agent Service, so keep this to a plain chat completion.
  • GPT-5 deployment availability and quota vary by region and subscription. Check capacity in the Foundry portal before you commit to a go-live date.
  • Don't paste raw Jira exports with customer PII straight into the prompt. Strip or mask identifiers in fetch_status_notes() first.

A digest that leadership actually reads beats a dashboard nobody opens. Once GPT-5 is structuring the summary and a timer trigger is doing the sending, the only manual work left is deciding what counts as worth flagging.

← More in Leadership