How to Build a GPT-Powered Executive Digest on Azure OpenAI
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.