Debugging an IIS-Hosted ASP.NET Core API on Azure VM: A Real-World Walkthrough

Overview

This article walks through a real-world debugging scenario involving an ASP.NET Core API deployed on an Azure VM behind IIS. The issue initially appeared to be a connectivity or deployment problem but ultimately turned out to be related to IIS hostname bindings and SNI (Server Name Indication).

The goal was to validate API availability directly on the VM and isolate issues between Azure routing, IIS configuration, and application behavior.


Step 1: Initial Problem

The API endpoint:

https://foo-vm.example.com/service/ProcessRequest

was returning:

404 Not Found

This raised several possible concerns:

  • Deployment failure
  • IIS misconfiguration
  • Routing issues
  • Network or SSL problems

Step 2: SSL / Certificate Validation

While testing direct HTTPS calls, the following error appeared:

Could not establish trust relationship for the SSL/TLS secure channel

Action Taken

  • Exported the server certificate (.cer)
  • Installed it on the local machine (Trusted Root / Intermediate store)
  • Alternatively, used curl with -k to bypass SSL validation:
curl -k https://foo-vm.example.com

Outcome

  • SSL issues were eliminated as a blocker
  • Able to reach the server over HTTPS

Step 3: Direct API Testing with curl

Multiple endpoints were tested:

curl -k https://foo-vm.example.com/
curl -k https://foo-vm.example.com/service/health
curl -k https://foo-vm.example.com/api/health

Result

All returned:

404 Not Found (Microsoft-IIS/10.0)

Insight

  • Requests were reaching IIS
  • But no matching route/application was found

Step 4: Validate Application Deployment

A simple health check endpoint was introduced:

/service/health

Expected response:

Healthy

However, even this endpoint returned 404 when accessed via the VM hostname.


Step 5: IIS Investigation

Upon inspecting IIS:

  • The API was not hosted under Default Web Site
  • Instead, it was hosted under a separate site:
Foo.ApiSvc

Key Finding

Requests to:

https://foo-vm.example.com

were hitting:

Default Web Site ❌

—not the actual API site.


Step 6: Binding and SNI Discovery (Root Cause)

IIS bindings for the API site showed:

Host Name: foo.example.com  
Port: 443
SNI: Enabled

Critical Insight

With SNI enabled, IIS routes requests based on the Host header.

So:

https://foo-vm.example.com  → Default Web Site → 404  
https://foo.example.com → Foo.ApiSvc → API

Step 7: Validate Using Host Header Override

Since DNS for foo.example.com was not directly usable from the VM, the Host header was manually injected:

curl -k https://foo-vm.example.com/service/health \
-H "Host: foo.example.com"

Result

Healthy

Conclusion

  • API was functioning correctly
  • IIS routing was working as designed
  • Issue was purely hostname-based routing

Step 8: Azure Layer Insight

The /service/... route seen earlier was part of the Azure routing layer, not IIS.

Architecture:

Azure Front Door / Gateway

foo.example.com

Azure VM (IIS with SNI)

ASP.NET Core API

Key Takeaway:

When bypassing Azure and hitting the VM directly, you must:

  • Use the correct hostname
    OR
  • Override the Host header

Application Pool Configuration Update

The IIS application pool was updated from .NET CLR v4.0 to No Managed Code to align with ASP.NET Core hosting best practices.

ASP.NET Core applications run on the CoreCLR in a separate process and do not depend on the IIS-managed CLR. While the previous setting did not prevent the application from running, updating it improves clarity and avoids confusion in future maintenance.


Step 9: Final Resolution

✅ Correct endpoint:

https://foo.example.com/service/health

✅ Or direct VM access with Host override:

curl -k https://foo-vm.example.com/service/health \
-H "Host: foo.example.com"

Key Learnings

1. IIS with SNI routes based on hostname, not IP

Incorrect hostname results in routing to the wrong site and returns 404.


2. Default Web Site is not always your application

Always verify IIS site bindings and application mapping.


3. Azure routing can mask backend behavior

The /service path was part of the Azure layer, not IIS configuration.


4. curl is a powerful debugging tool

  • -k bypasses SSL issues
  • -v shows detailed request/response
  • -H allows header injection

5. ASP.NET Core hosting configuration

  • App pool should be set to No Managed Code
  • Runtime and hosting bundle were already functioning correctly

Final Summary

What initially appeared to be a deployment or API issue was ultimately traced to a hostname binding mismatch caused by IIS SNI configuration.

Once the correct hostname was used—or injected via the Host header—the API routed correctly and responded as expected.

Root cause: Incorrect Host header when bypassing Azure routing
Resolution: Use the correct hostname or override the Host header

Document Set vs. Corporate Record in SharePoint

When to use each, and why it matters

As I matured my SharePoint architecture for Tanolis, I experimented with two different approaches:

  1. Document Sets (container-based model)
  2. Corporate Record (content type–based model)

Both are powerful.
But they solve different problems.

Understanding the difference is what turns SharePoint from “file storage” into a structured operating system.


1️⃣ Document Set Approach

(Container-Based Model)

What is a Document Set?

A Document Set is like a smart folder.

It allows you to group multiple related documents together under one container and apply shared metadata to the entire group.

Think of it as:

A project folder with intelligence.


When Should You Use Document Sets?

Use Document Sets when you need:

  • A project container
  • A client container
  • A case file
  • A contract package
  • A proposal bundle
  • A structured execution space

In plain English:

Use Document Sets when you are managing work that produces many related documents.


Example

Project: “City of New Orleans – Data Modernization”

Inside the Document Set:

  • Contract
  • SOW
  • Architecture diagrams
  • Invoices
  • Meeting notes
  • Change requests

All grouped under one structured container.


Benefits of Document Sets

✅ 1. Everything stays together

You don’t lose related documents across folders.

✅ 2. Shared metadata

You set metadata once (Client, Project Code, Status) and it applies to everything inside.

✅ 3. Automatic folder provisioning

You can auto-create subfolders like:

  • 01 – Contract
  • 02 – Design
  • 03 – Execution

✅ 4. Great for operational work

Perfect for delivery environments.


What Document Sets Are NOT Good For

They are not ideal for:

  • Corporate governance
  • Certifications
  • Tax filings
  • Policies
  • Compliance tracking

Because those are individual records, not grouped work artifacts.


2️⃣ Corporate Record Approach

(Content Type–Based Model)

What is a Corporate Record?

A Corporate Record is a structured document type.

Instead of grouping documents into containers, each document is treated as a governed record with required metadata.

Think of it as:

A database entry that happens to be a document.


When Should You Use Corporate Record Content Types?

Use this model for:

  • Articles of Organization
  • Operating Agreement
  • SWaM Certification
  • SAM Registration
  • Insurance Certificates
  • Tax filings
  • Policies
  • Resolutions

In plain English:

Use Corporate Record when the document itself is the official record.


Example

SWaM Certification

Metadata:

  • Record Status = Active
  • Expiration Date = Dec 2, 2030
  • Authority = DSBSD
  • Responsible Owner = Shahzad

This is a single governed record.

It doesn’t need a folder.
It needs structure and tracking.


Benefits of Corporate Record Model

✅ 1. Clean governance

Every document has required metadata like:

  • Status
  • Expiration date
  • Owner

✅ 2. Easy compliance dashboard

You can filter:

  • Expiring in 90 days
  • Active certifications
  • Archived records

✅ 3. Audit-ready

When SBA, SWaM, or a prime contractor asks for documents:
You can filter and export instantly.

✅ 4. Cleaner architecture

No unnecessary containers.
Just structured records.


Side-by-Side Comparison

Use CaseDocument SetCorporate Record
Project delivery✅ Yes❌ No
Client engagement container✅ Yes❌ No
Governance documents❌ No✅ Yes
Compliance tracking❌ No✅ Yes
Tax filings❌ No✅ Yes
Proposal bundles✅ YesSometimes
Expiration monitoringNot idealExcellent

Simple Mental Model

If you are managing work, use a Document Set.
If you are managing official records, use Corporate Record.

Work = container
Record = structured document


Why Separation Matters

Mixing both models inside the same library causes:

  • Extra columns
  • Schema confusion
  • Broken views
  • Hard-to-maintain governance

Separating them gives you:

  • A Corporate Core layer
  • A Projects / Execution layer
  • A clean architecture

This mirrors real enterprise design.


My Final Architecture Pattern

Corporate Side (Governance Layer)

Uses:
Corporate Record base content type
Derived types:

  • Governance Record
  • Policy Document
  • Compliance Record
  • Tax Filing
  • Proposal Record

No Document Sets here.


Projects Side (Execution Layer)

Uses:
Document Set as Project Container

With:

  • Client
  • Project Status
  • Contract Value
  • Structured subfolders

Why This Matters Long-Term

This separation:

  • Prepares Tanolis for audits
  • Makes renewals impossible to miss
  • Keeps compliance structured
  • Creates reusable architecture for future clients
  • Mirrors SaaS-style domain separation

You’re essentially applying:

Clean architecture principles inside SharePoint.


Final Plain English Summary

Use Document Set when you need a smart project folder.

Use Corporate Record when you need a governed, trackable official document.

They are both powerful.

But they solve different problems.

Understanding that difference is what turns SharePoint into a platform instead of storage.

How Metadata-Driven SharePoint Libraries Enable Future SaaS Automation

Most teams use SharePoint as a file storage system. Folders get created, documents get uploaded, and over time structure becomes messy. Search becomes harder, reporting becomes manual, and automation becomes nearly impossible.

The turning point comes when you stop thinking in folders and start thinking in metadata.

A metadata-driven SharePoint library doesn’t just store files — it stores structured information about your business operations. That structure is what enables automation and future SaaS capabilities.

Here’s how.


Folders Organize Storage. Metadata Organizes Meaning.

Folders answer:

Where is the file stored?

Metadata answers:

What is this file, who owns it, and how is it used?

For example, instead of:

Projects
 └── ClientA
      └── Contract.pdf

you get:

DocumentProject IDClientTypeStatus
Contract.pdf2026-0001ClientAContractSigned

Now SharePoint understands the document, not just its location.


Why Metadata Matters for Automation

Automation tools don’t understand folder names. They understand data.

Example automations enabled by metadata:

Automatic Document Routing

If:

Document Type = Invoice

Then:

  • Move to Finance workflow
  • Trigger billing automation
  • Notify accounting

No folder scanning required.


Contract Expiration Alerts

If:

Expiration Date = 2026-03-31

Then:

  • Notify team 30 days earlier
  • Start renewal workflow automatically

Folders alone cannot do this.


Cross-Project Reporting

With metadata:

Show all Active projects with High risk
Show all invoices pending payment
Show all contracts expiring this quarter

Without metadata, reporting requires manual effort.


Metadata Enables SaaS Product Thinking

This is where SharePoint work starts looking like SaaS architecture.

Your SaaS product later will need:

  • Projects
  • Documents
  • Contracts
  • Billing
  • Compliance tracking
  • Deliverables
  • Work logs

Each of these is metadata-driven.

In other words:

SharePoint metadata model = future product data model

Your document structure becomes a prototype for your SaaS logic.


Document Sets: Project Containers

Using Document Sets adds structure:

Project
 ├── Contracts
 ├── Finance
 ├── Delivery
 └── Admin

Project metadata lives at the container level, while documents inherit project context but keep their own lifecycle metadata.

This creates a natural separation:

LevelOwns
ProjectClient, status, risk, dates
DocumentType, owner, version, expiration

This mirrors SaaS project systems.


Automation Comes Later — Structure Comes First

A common mistake is trying to automate before structure exists.

Correct sequence:

  1. Standardize folder structure
  2. Define metadata
  3. Separate project vs document data
  4. Organize views
  5. Start automation
  6. Build dashboards
  7. Integrate systems
  8. Productize workflows

Automation works only when data is structured.


Long-Term Benefits

A metadata-driven library enables:

  • Faster search
  • Clean reporting
  • Automated workflows
  • Compliance tracking
  • Financial oversight
  • Project dashboards
  • SaaS-ready data models

And most importantly:

Less manual effort as operations scale.


Final Takeaway

The moment your document system understands business context, not just file paths, automation becomes possible.

Metadata turns SharePoint from file storage into an operational platform.

And once operations are structured, productization becomes achievable.

Building a Scalable SharePoint Project Workspace — Lessons from Today’s Setup

Today I finalized a major restructuring of my SharePoint project workspace, moving from an improvised document layout to a scalable, metadata-driven structure suitable for consulting, subcontracting, and future SaaS delivery work.

The goal was simple: build a project system that will still work five years from now without constant redesign.

Here’s what happened and what I learned.


Starting Point: Folder Chaos vs Structure

Like many teams, documents were growing organically:

  • Contracts in one place
  • HR documents somewhere else
  • Weekly reports in another folder
  • Financial and timesheet data mixed with operations

This works for small teams, but quickly breaks once projects multiply.

So I standardized the structure.


Standardized Project Folder Model

Each project now follows the same lifecycle structure:

01 — Contract & Governance

Everything that legally establishes and governs the project.

Examples:

  • Prime contracts
  • Subcontracts
  • Amendments
  • NDAs
  • Compliance documents

02 — Planning & Design

Pre-execution project preparation.

Examples:

  • Proposals
  • Staffing plans
  • Architecture/design documents
  • Project plans

03 — Execution & Delivery

Core delivery and operational work.

Examples:

  • Technical work
  • Weekly reports
  • Deliverables
  • Work logs

04 — Financials

Billing and financial tracking.

Examples:

  • Invoices
  • Timesheets
  • Banking records
  • Expenses
  • Tax documentation

05 — Admin & Closeout

Administrative and HR matters.

Examples:

  • Training certificates
  • Onboarding docs
  • Compliance forms
  • Remote work agreements
  • Closeout documentation

The Big Lesson: Metadata Beats Folders

The real breakthrough today wasn’t just folder structure.

It was realizing:

Folders organize storage. Metadata organizes understanding.

By using SharePoint metadata:

  • Project-level data lives on the Document Set
  • Document-level data stays on each document
  • Views show combined data cleanly
  • Documents remain individually searchable
  • Automation becomes possible later

So now:

  • Project metadata appears at project level
  • Document metadata remains editable per document
  • Views can filter, group, and report without moving files

Folders give structure; metadata gives intelligence.


Key Fix That Unblocked Everything

At one point, Document Set configuration kept failing.

The solution:

  • Delete and recreate the document library cleanly.
  • Re-add content types and metadata correctly.
  • Configure Document Sets before heavy customization.

Sometimes resetting is faster than debugging corruption.


Templates and Proposals Standardization

I also organized:

Templates Library

Contains reusable assets:

  • Capability statement
  • Invoice templates
  • NDA/MSA templates
  • Proposal templates
  • Standard project structure guide

Proposals Library

Organized by lifecycle stage:

  • Active
  • Submitted
  • Won
  • Lost

Metadata will later allow reporting without relying on folders alone.


Why This Matters Long-Term

This structure now supports:

  • Consulting projects
  • Government subcontracting
  • Multi-client work
  • Future SaaS delivery operations
  • Automation workflows
  • Reporting dashboards

Most importantly, it removes daily friction.


Final Takeaway

The biggest realization:

Good document structure isn’t about today’s convenience — it’s about future scalability.

A clean SharePoint structure saves time, reduces confusion, and supports automation later.

And today, the foundation is finally in place.

Designing a Metadata-Driven SharePoint Project Library with Automated Document Set Structure

Designing a Metadata-Driven SharePoint Project Library with Automated Document Set Structure.

1. Why Start with Metadata (Not Folders)

Most SharePoint project libraries fail for one reason:

They start with folders instead of metadata.

Folders solve navigation.
Metadata solves governance.

For a scalable project portfolio library, the structure must be driven by:

  • Project ID
  • Project Year
  • Portfolio Category
  • Project Type
  • Project Status
  • Client
  • Risk Level
  • Stage Gate

This allows:

  • View filtering (Active, Closed, Government, By Year)
  • Reporting
  • Automation
  • Lifecycle management
  • Future Power BI integration

Folders alone cannot do that.


2. Core Architecture

Site

Tanolis Projects

Library

Projects

Content Type

Document Set (for each project container)


3. Core Metadata Design

Create these site columns:

ColumnType
Project IDSingle line of text
Project YearChoice
Portfolio CategoryChoice
Project TypeChoice
Project StatusChoice
ClientSingle line
Risk LevelChoice
Stage GateChoice
Start DateDate
Target End DateDate

Attach these columns to the Document Set content type, not individual files.

This ensures:

  • Each project container carries structured metadata
  • Views operate on project-level attributes
  • Documents inherit metadata if configured

4. Why Document Set (Not Just Folder)

A Document Set is:

  • A special content type
  • A container with metadata
  • A logical project object

It behaves like a folder but supports:

  • Custom metadata
  • Welcome page
  • Shared columns
  • Governance workflows

A normal folder cannot do that.


5. Required SharePoint Configuration

Enable Document Sets

Site Settings → Site Collection Features
Activate Document Sets

Then:

Library Settings → Advanced Settings
✔ Allow management of content types

Add Document Set to the library.


6. The Problem: Auto-Creating Subfolders Inside Each New Project

Goal:

When I create:

2026-003_ClientZ_NewApp

Power Automate should automatically create:

01-Contract Governance
02-Planning Design
03-Execution Delivery
04-Financials
05-Admin Closeout

Inside that Document Set.

No duplicate containers.
No root-level folder creation.
No accidental “Shared Documents” folder nesting.


7. The Correct Trigger (Important)

Use:

When a file is created (properties only)

Why?

Because:

  • A Document Set inside a document library is treated as a file/folder object.
  • “When an item is created” is for SharePoint lists.
  • Using the wrong trigger causes null content type errors and template failures.

Lock this in.


8. The Final Working Flow Design

Step 1 – Trigger

When a file is created (properties only)
Library: Projects


Step 2 – Condition

Check:

Content Type
is equal to
Document Set

No expressions.
No startsWith.
No ContentTypeId hacks.

Keep it clean.


Step 3 – Initialize Folder Array

Initialize variable:

Type: Array

[
"01-Contract Governance",
"02-Planning Design",
"03-Execution Delivery",
"04-Financials",
"05-Admin Closeout"
]

Step 4 – Apply to Each

Loop through the folder array.

Inside the loop:

Create new folder.


9. The Critical Folder Path Expression

This is where most implementations fail.

Correct expression:

concat(triggerOutputs()?['body/{Path}'], triggerOutputs()?['body/{FilenameWithExtension}'], '/', item())

Why this works:

From trigger output:

{Path} = Shared Documents/
{FilenameWithExtension} = foo set

Final path becomes:

Shared Documents/foo set/01-Contract Governance

Which means:

Folders are created inside the Document Set container — not in the root.


10. Common Mistakes (And Why They Fail)

❌ Using only Filename

concat(triggerOutputs()?['body/{FilenameWithExtension}'],'/',item())

Result:
Creates duplicate root folder or wrong nesting.


❌ Using ContentTypeId startsWith

Leads to:

startsWith expects string but got null

Because wrong trigger context.


❌ Using “When an item is created”

Causes:

  • Null content type
  • Condition failures
  • Inconsistent behavior

11. Handling Race Conditions

Sometimes folder creation hangs because:

The Document Set is not fully provisioned when the flow runs.

Solution:

Add a small Delay (5 seconds minimum on consumption plan).

Or use retry policy.


12. Optional Enhancements

You can extend this design to:

  • Auto-assign permissions based on Portfolio Category
  • Notify PM when project is created
  • Trigger approval workflow at Stage Gate change
  • Auto-create Teams channel per project
  • Sync metadata to Dataverse

13. Architectural Pattern Summary

What you built is:

✔ Metadata-first design
✔ Document Set container model
✔ Automated structural provisioning
✔ Governance-ready foundation

This scales to:

  • 10 projects
  • 100 projects
  • 1,000 projects

Without structural drift.


14. Final Design Philosophy

Folders are operational.
Metadata is strategic.

Document Sets give you both.

Power Automate enforces consistency.