Processing a PDF with artificial intelligence sounds simple: receive the document, extract its contents, enrich them and store the result. Complexity appears when every step introduces another service, deployment and failure point.
This reference architecture shows how to simplify that system on Azure before defining it with Terraform.
Before turning boxes and arrows into infrastructure, it is worth reviewing the diagram critically: which component solves a real need, which merely passes work along and which failure will be difficult to explain during an incident.
This is a hypothetical technical scenario created for educational purposes. It does not describe a client, a real project or achieved results. Every environment must validate security, performance, cost and regulatory requirements with its own data.
The scenario
The system must receive PDFs, react when a file arrives, analyse it with Azure AI Document Intelligence, enrich the contents with a language model, store a structured result and provide logs and traceability.
A first design variant solves the functional flow but adds several operational layers:
- Azure Logic Apps as the orchestrator;
- three chained Azure Functions;
- a scheduled trigger;
- monolithic Terraform infrastructure.
No individual decision was inherently wrong. The issue was the combined cost of operating all of them.
Where complexity appeared
Logic Apps acting only as an intermediary
Logic Apps is useful when orchestration contains multiple connectors and states. Here, its main job was detecting a change and invoking a Function.
Azure Event Grid can already react to Microsoft.Storage.BlobCreated. Removing the intermediary reduces configuration, cost and diagnostic paths.
Three Functions in sequence
Separating components improves a design when responsibilities and lifecycles differ. In this case, fragmentation meant deploying, observing and maintaining three pieces for one operation.
An intermediate failure stopped the pipeline and made document context harder to reconstruct.
Polling an event-based process
The scheduled trigger periodically asked whether documents were waiting. That creates delay and executions with no work.
Blob arrival is already the business event. Using it as the trigger makes the system react only when work exists.
Terraform without clear boundaries
One infrastructure block mixed compute, ingress, data and observability. Changes had a larger impact radius and parts could not be reused safely.
Proposed architecture
The simplified flow is:
PDF → Azure Blob Storage
→ Event Grid
→ Azure Function
→ Document Intelligence
→ Azure OpenAI
→ Cosmos DB
The Function receives the event, retrieves the document, coordinates analysis and stores the result. Application Insights centralises logs, errors and timing.
One Function is not always better. It fits while processing shares a lifecycle and remains within execution limits. Long-running jobs, independent retries or high volume may justify queues, Durable Functions or another orchestration pattern.
Isolate storage by responsibility
Storage should distinguish incoming documents, processed documents, internal Function state and structured results in Cosmos DB. Those boundaries improve permissions, retention, recovery and diagnosis.
Terraform modules
Infrastructure can follow operational boundaries:
modules/
ingestion/ Storage and Event Grid
processing/ Function App and identity
intelligence/ Document Intelligence and OpenAI
data/ Cosmos DB
observability/ Application Insights and alerts
The goal is not modularisation for appearance. Every module should represent a responsibility with understandable changes and permissions.
Security and operations
Before production, decide:
- managed identities versus secrets;
- private access to Storage, OpenAI and Cosmos DB;
- document encryption and retention;
- failed PDF handling;
- idempotency for duplicate events;
- file size and type limits;
- alerts for errors, latency and backlog;
- cost allocation by environment or client.
Event Grid may deliver an event more than once. Processing must identify completed documents and avoid duplicate results.
What to measure
An operable architecture needs a baseline:
- upload-to-result time;
- processed and failed documents;
- retries by component;
- cost per document;
- token consumption;
- analysis and enrichment duration;
- incidents requiring intervention.
These measures show when the solution needs queues, parallelism or additional controls.
The important decision happens before Terraform
Infrastructure as Code makes an architecture reproducible, but it can also reproduce unnecessary complexity.
Simplify the flow first. Then define ownership, failure and observability. Only then should the design become Terraform modules.
If your agency repeats this solution across clients, the next step is not copying the repository. It is creating a multi-client delivery foundation that reuses the process while keeping data and access isolated.


