Back to Case Studies
Enterprise Platform

Enterprise Document Pipeline Monitor

A secure, self-service document browser and ingestion observability dashboard spanning Azure AD, an on-premises .NET API, Amazon S3, and DynamoDB.

Role

Full-stack engineer and solution designer

Scope

Incremental enterprise delivery

Depth

11 min read

Problem

Business users could not confirm whether documents reached the search index, diagnose blocked files, or manage the document store without infrastructure support.

Solution

I combined direct S3 transfers through presigned URLs with layered authorization and a contract-first monitoring dashboard that could be built before the backend schema was final.

Impact

Authorized users gained permission-based document management and paginated pipeline visibility, with database audit events and S3 versioning preserving an attributable, recoverable change history.

Making an Enterprise Document Pipeline Observable and Self-Service

An internal pipeline moved engineering drawings, specifications, and part files from Amazon S3 into OpenSearch. It worked, but to most users it was a black box: they could not confirm whether a document had completed the journey, understand why it was blocked, or manage files without involving the infrastructure team.

I helped turn that invisible workflow into one focused internal product: a secure storage browser for document operations and a monitoring dashboard for ingestion health. The challenge was not simply building two screens. It was delivering useful software across Azure AD, an on-premises .NET API, and shared AWS infrastructure while the backend data contract was still evolving.

Enterprise documents moving through a monitored cloud ingestion pipeline
From document storage to searchable data: making every stage visible.

1. The operational gap

The existing workflow left both business users and support teams dependent on manual investigation.

1

Did my file complete processing?

Before
Ask an infrastructure engineer
Product response
See upload and search-sync status in one row
2

Why is a document stuck?

Before
Inspect AWS services manually
Product response
Read the pipeline error inline
3

Can I upload or organize files?

Before
Raise a support request
Product response
Use a familiar, permission-protected file browser
4

Can I review a larger result set?

Before
Ad hoc technical export
Product response
Filter, paginate, and export from the dashboard

The product goal was straightforward: reduce the distance between an operational question and a trustworthy answer, without weakening the security boundary around enterprise documents.

2. Constraints that shaped the system

  • Azure AD was the company identity standard; the AWS Cognito-based reference application could inform the UX, but not the architecture.
  • The API ran under IIS on premises, so it had no EC2, ECS, or Lambda instance role and could not rely on ambient AWS credentials.
  • The DynamoDB schema belonged to another team and was not finalized when frontend work began.
  • The S3 bucket also served a legacy tool, so policy and CORS changes had to remain additive, scoped, and reversible.
  • The team needed to ship incrementally, without waiting for every upstream decision to settle.

3. Architecture: control through the API, bytes around it

System boundaries

Angular application
Azure AD + app roles
.NET API on IIS — authorization and URL signing
Amazon S3 — document bytes
DynamoDB — pipeline status
OpenSearch — searchable documents

The central decision was to keep the API in the control path but out of the data path. The server authenticates the caller, authorizes the action, and creates a short-lived presigned URL. The browser then transfers the file directly to or from S3. Large file bytes never pass through IIS.

Authenticate
Authorize action
Issue presigned URL
Transfer directly with S3
Refresh status
Architecture connecting Angular, Azure AD, an on-premises .NET API, Amazon S3, DynamoDB, and OpenSearch
The API controls access while presigned URLs keep document bytes on the direct browser-to-S3 path.

4. A storage browser designed for the existing identity model

The storage experience uses familiar file-manager patterns—breadcrumbs, folders, upload progress, download, and recursive delete—while preserving server-side control over every privileged operation.

  • Browse uses S3 ListObjectsV2 with a delimiter to present object prefixes as folders.
  • Uploads and downloads use ten-minute presigned PUT and GET URLs.
  • Up to four uploads run concurrently, each with independent progress feedback.
  • Folders are represented by zero-byte objects with trailing-slash keys.
  • Recursive deletion paginates through a prefix and removes objects in batches.

This avoided turning the on-premises API into a file proxy and made upload throughput depend on the browser-to-S3 connection rather than server memory and bandwidth.

Enterprise document storage browser with folders, file actions, and upload progress
A self-service storage experience built around familiar file-management patterns.

5. Authorization with deliberately different failure modes

Authentication and authorization were split into two explicit layers. Azure AD login is mandatory, but signing in alone does not grant access. An administrator assigns approved employees to the application's Azure AD group, and an internal app-roles service verifies that membership before protected data is available.

1

Read

Example capabilities
Browse documents, download approved files, and view pipeline status
Assignment
Granted through the application group or role
2

Contribute

Example capabilities
Upload documents, create folders, and update permitted content
Assignment
Granted only to users whose responsibilities require changes
3

Manage

Example capabilities
Delete or restore content and administer operational actions
Assignment
Restricted to designated administrators

This supports least-privilege access: group membership determines whether someone can enter the application, while the assigned role determines whether that person can only view data or can also upload, modify, and manage documents. The API enforces these permissions for every operation; hiding a button in Angular is never treated as authorization.

1

Angular route guard

Failure behavior
Fails open if the role service is unavailable
Responsibility
Navigation guidance only; avoids a misleading lockout caused by a UX dependency
2

.NET API

Failure behavior
Fails closed and returns 403
Responsibility
Actual security boundary protecting S3 and DynamoDB actions

The asymmetry is intentional. Bypassing or breaking a client-side guard can never grant access to backend data because every controller action independently revalidates the user. The required group remains environment configuration, so enabling the final stakeholder-approved group does not require an application rewrite.

Comparison of a fail-open client navigation guard and fail-closed server authorization
The client check improves navigation UX; the server remains the authoritative security boundary.

6. Auditable changes and recoverable documents

Document access is only one part of the control model. Every mutating action—upload, replacement, folder creation, metadata update, and deletion—is recorded as an audit event with the authenticated user, action, object key, timestamp, and relevant version reference. This creates a searchable history of who changed what and when.

User performs action
API validates permission
Change is applied
Audit event is stored
Version remains recoverable

S3 versioning retains earlier object versions when a document is replaced or deleted. The database audit trail explains the business action, while the S3 version ID points to the corresponding object state. Together, they support later investigation, comparison, and recovery without presenting version history as a substitute for authorization.

7. Building the dashboard before the data contract was final

Waiting for the final DynamoDB schema would have blocked the entire dashboard. Instead, I defined the service contract around the questions the UI needed to answer, then built a realistic mock implementation behind it. That allowed the filters, KPI cards, table states, pagination, and errors to be developed against stable behavior.

Define UI contract
Seed realistic mock records
Build and test dashboard
Map confirmed DynamoDB schema
Swap DI registration

When the real table shape was confirmed, the production implementation replaced the mock through dependency injection. The frontend contract did not change, and the mock stayed useful for development and failure-state testing.

Development mock service and production DynamoDB service sharing the same interface
The stable interface allowed the real data service to replace the mock without changing the dashboard contract.

8. Turning pipeline data into operational answers

The dashboard reduces two backend fields—upload state and search-index sync state—into three user-facing outcomes: Synced, Pending, and Blocked. A blocked state can represent failure in either stage, with the original error visible for diagnosis.

LoadedSyncedPendingBlocked

Data is loaded in paginated batches rather than sending the full table to the browser. The table had no suitable secondary index, so the first production implementation uses a DynamoDB Scan with date, filename, deletion, and status filters. Each response includes the current rows and an opaque page token wrapping LastEvaluatedKey. The client returns that token to load the next batch without needing to understand the DynamoDB key structure.

This is a pragmatic first version, not a claim that Scan is the ideal access pattern. DynamoDB applies the page limit before the filter expression, so a page may contain fewer visible rows even when more matches exist. The behavior is documented and the UI can continue loading until the token is empty. If data volume or traffic grows, a status-and-date access pattern backed by a secondary index is the natural next optimization.

Document pipeline monitoring dashboard with KPIs, filters, statuses, and blocked-file errors
Operational status, filtering, and failure context are presented in one focused dashboard.

9. Production-minded decisions beyond the happy path

  • Runtime configuration loads before Angular bootstraps, allowing one build artifact to serve multiple environments.
  • AWS credentials support local developer secrets while remaining compatible with the SDK default provider chain for a future managed-identity approach.
  • Shared-bucket IAM and CORS changes were documented with their prior state and rollback steps before deployment.
  • Database audit events and S3 version IDs connect user actions to recoverable document states.
  • MSAL-triggered HTTP updates explicitly re-enter Angular's zone so authenticated requests reliably update the view.
  • The dashboard favors searchable rows and visible errors over decorative charts because the primary job is finding what is stuck and why.

10. Outcome and engineering value

The resulting tool works end to end: authorized group members can access capabilities appropriate to their assigned permissions, document changes remain attributable and recoverable, and the monitoring dashboard reads paginated ingestion state from DynamoDB without requiring AWS console access.

More importantly, the delivery strategy removed avoidable blockers. Presigned URLs respected the on-premises hosting constraint. Interface-first development decoupled UI progress from an evolving schema. Layered authorization kept convenience checks separate from real enforcement. Reversible infrastructure changes protected a shared production dependency.

The case study demonstrates the kind of work I value most: translating ambiguous operational pain into a secure, understandable product—and making explicit tradeoffs so the next engineer can evolve it safely.

11. What I would evolve next

  • Replace long-lived service credentials with the organization’s approved workload-identity mechanism.
  • Introduce a DynamoDB index or purpose-built read model when usage justifies moving beyond Scan.
  • Add trend metrics for processing latency and failure rate once enough history exists to make them meaningful.
  • Complete the app-group configuration after the directory owner confirms the production group.

Company, environment, tenant, account, and document identifiers have been generalized to preserve confidentiality.