Export System
The export system is a config-driven pipeline that turns SQL queries against the Gold Warehouse into formatted, optionally encrypted Excel files — delivered via email (Microsoft Graph API) or downloaded through a web UI. Everything is defined in a single YAML file; adding a new export requires zero code changes.
Architecture
sequenceDiagram participant C as exports.yml participant H as Handler participant Q as Query Runner participant E as Excel Writer participant M as Email (Graph API) C->>H: Export definition H->>Q: Execute SQL vs Gold Q-->>H: DataFrame H->>E: Generate .xlsx E-->>H: File bytes H->>M: Send with attachment
Flow in Detail
-
Config loading (
config.py):exports.ymlis parsed and validated. Every export must definequery_file,recipients,filename, andsubject. The config loader validates that referenced SQL files exist and that at least one recipient is specified. -
Query execution (
query_runner.py): Connects to the Gold Warehouse via pyodbc + ODBC Driver 18 with AAD token auth. The token is acquired throughDefaultAzureCredential(Managed Identity in Azure,az loginlocally) scoped tohttps://database.windows.net/.default. The SQL file is read from disk and executed, returning a pandas DataFrame. -
Excel generation (
excel_writer.py): The DataFrame is written to an in-memory.xlsxfile using openpyxl. Column widths are auto-adjusted based on content length (capped at 50 characters). The\{date\}placeholder in the filename is resolved to the current UTC date. -
Optional encryption: If the export defines a
passwordfield, the Excel file is encrypted usingmsoffcrypto-toolwith Office standard encryption. The password is included in the email body so recipients can open the file. -
Email delivery (
shared/notifications.py): The encrypted (or plain) Excel file is sent as an email attachment via the Microsoft Graph API, using the sender address fromexports.ymlor theEXPORT_SENDER_EMAILenvironment variable. -
Tracking (
tracker.py): Successful runs update a last-run timestamp, enabling the timer blueprint to skip exports that have already run within their schedule window.
Config-Driven Architecture
All export definitions live in functions/exports/exports.yml. The top-level sender field defines the default email sender. Each export under exports: is a self-contained definition:
sender: daan.aerts@geris.nl
exports:
finance_report:
query_file: queries/finance_report.sql # SQL file relative to exports/
schedule: "0 0 8 1 * *" # Cron: 8am on 1st of month
recipients:
- cfo@geris.nl
filename: "Finance_Report_{date}" # {date} -> YYYY-MM-DD
subject: "Monthly Finance Report"
body: "See attached monthly finance report."
allowed_groups: # Per-export authorization
- "15c18657-4701-4f29-9fe4-59a71424b71a"
password: auto # auto-generate memorable password
Adding a New Export
- Write a SQL query file in
functions/exports/queries/ - Add an entry to
exports.ymlwith the required fields - Deploy the Function App — no code changes needed
Schedule Syntax
The schedule field uses 6-field cron syntax (second, minute, hour, day, month, day-of-week). Set schedule: null for manual-only exports that can only be triggered via the API or UI.
Trigger Modes
Scheduled (Timer)
The export_timer_bp blueprint fires on a 15-minute interval and checks each export's schedule field against the current time using croniter. Exports whose cron expression matches are executed automatically.
API (Programmatic)
The export_api_bp blueprint exposes an HTTP endpoint for triggering exports programmatically:
POST /api/exports/{export_name}/run
Supports an optional recipient query parameter to override the default recipients (must be a @geris.nl address for security).
Web UI (On-Demand)
The export_ui_bp blueprint serves a lightweight HTML interface where authorized users can:
- Browse available exports
- Trigger an export and download the resulting Excel file directly
- View export history and last-run timestamps
The UI is served from functions/exports/static/ and communicates with the API endpoints.
Password Encryption
Exports support three password modes:
password value | Behavior |
|---|---|
Not set / false | No encryption — plain Excel file |
auto or true | Auto-generate a memorable password (format: adjective-noun-NN, e.g., calm-eagle-47) |
"custom-string" | Use the provided string as the password |
When encryption is enabled:
- The Excel file is encrypted using
msoffcrypto-tool(Office standard encryption) - The password is appended to the email body so recipients can open the file
- A new random password is generated per export run (for
automode)
Per-Export Group Authorization
Each export can restrict who is allowed to trigger it via the allowed_groups field. This is a list of Entra ID security group object IDs. When set, the handler checks the caller's group membership claims against this list before executing the export.
This is particularly important for exports containing sensitive financial or HR data — only members of the specified groups can trigger the export through the API or UI.
Environment Variables
| Variable | Purpose |
|---|---|
EXPORT_WAREHOUSE_SERVER | Gold Warehouse hostname |
EXPORT_WAREHOUSE_DATABASE | Gold Warehouse database name (e.g., Gold_Warehouse) |
EXPORT_SENDER_EMAIL | Override sender email (falls back to exports.yml sender) |
ALLOWED_GROUP_IDS | Comma-separated group IDs for UI/API auth (when Easy Auth is enabled) |
GO-LIVE BLOCKERS
Two issues must be resolved before the export system goes to production. See the ADR Log for full details.
Export UI Auth Not Enabled
Auth checks on the Export Manager endpoints (ui.py, api.py) are commented out with TODO: Re-enable markers. The export UI is currently open to anyone with the Function App URL.
Fix: Create App Registration, enable Easy Auth on the Function App, uncomment auth checks, set ALLOWED_GROUP_IDS.
Email Sender Not Configured
The Function App's Managed Identity lacks Mail.Send permission in Microsoft Graph. Email sending does not work yet — the Download button on the UI works as a workaround for testing.
Fix: Create a dedicated shared mailbox (e.g., exports@geris.nl), grant scoped Mail.Send permission to the MI, update EXPORT_SENDER_EMAIL.
Related Pages
- Azure Functions Overview — module architecture and shared utilities
- Function Endpoints — auto-generated endpoint reference
- ADR Decision Log — go-live blocker decisions