Infrastructure Guide

Multi-Cloud Infrastructure-as-Code for Azure, AWS & GCP

Architecture Overview

Enterprise-grade cloud infrastructure with security-first design and cloud-native identity management.

Azure Resource Group
Virtual Network
App Service Subnet
App Service Linux / .NET 8
Managed Identity
Private Endpoint Subnet
PE-SQL
PE-Blob
PE-KV
Azure SQL / PostgreSQL Azure AD Auth
Blob Storage No SAS/Keys
Key Vault RBAC
Container Registry AcrPull RBAC

Security Features

Built with zero-trust principles and Azure security best practices.

No SAS/Shared Keys

Storage Account access exclusively via Azure AD authentication. No shared keys or SAS tokens.

Azure AD Only

SQL Server configured for Azure AD authentication only. No SQL authentication available.

Cloud-Native Identity Management

Azure Managed Identity, AWS IAM Roles, or GCP Service Accounts for secure resource access without credentials.

Private Network Access

Backend services accessible only through private networking (Azure Private Endpoints, AWS PrivateLink, GCP Private Service Connect).

Self-Hosted JWT Authentication

Cloud-agnostic JWT tokens for API authentication without dependency on Azure AD, AWS Cognito, or Google Identity Platform.

IP Whitelisting

Service access restricted to configured IP addresses only.

TLS 1.2 Minimum

All services enforce TLS 1.2 as the minimum protocol version.

API Authentication

Multi-provider authentication: OAuth2 (Azure AD, Self-Hosted JWT) or API Keys for Document API.

Authentication Architecture

Admin Endpoints (/api/admin/*)

OAuth2 ONLY - Requires admin_client_id configuration (Azure AD or Self-Hosted JWT). API keys cannot access admin endpoints.

Document API (/api/documents/*)

API Key OR OAuth2 - For systems that don't support OAuth2 (e.g., Salesforce Outbound Messages).

API Key Management

Via Admin REST API - Keys are created, rotated, and stored securely in Azure Key Vault.

API Key Authentication

For Document API (non-OAuth2 systems)

How It Works

API Keys are managed via the Admin REST API, not in configuration files:

  • Header Name X-API-Key
  • Key Storage Azure Key Vault (secure)
  • Management Admin REST API
  • Access Scope Document API only
GET /api/documents
curl -X GET "https://app-docservice-dev.azurewebsites.net/api/documents" \
  -H "X-API-Key: your-api-key-here" \
  -H "Content-Type: application/json"
Important

API Keys cannot access Admin endpoints. Use OAuth2 (Azure AD or Self-Hosted JWT) with admin_client_id for administrative operations.

Microsoft Entra ID

OAuth 2.0 / Bearer Token authentication

Configuration

Azure AD is always enabled when TenantId is configured. Only AdminClientId needs to be set:

  • TenantId From azure.tenant_id (auto)
  • AdminClientId REQUIRED for Admin

Additional OAuth2 clients are registered via the Admin API, not configuration files.

Bearer /api/documents
curl -X GET "https://app-docservice-dev.azurewebsites.net/api/documents" \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1Q..." \
  -H "Content-Type: application/json"
Token Acquisition

Clients obtain tokens via OAuth 2.0 Client Credentials flow or user authentication, depending on the client type.

Self-Hosted JWT

Cloud-agnostic OAuth 2.0 authentication

Configuration

Perfect for multi-cloud scenarios or when you don't want dependency on cloud-specific identity providers (Azure AD, AWS Cognito, Google Identity Platform).

  • Issuer URL Your API base URL
  • Token Lifetime Configurable (default: 1h)
  • Signing Key Securely stored in Key Vault / Secrets Manager
  • Compatible With Azure, AWS, GCP, On-Premises
POST /api/oauth2/token
curl -X POST "https://your-api.com/api/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=your-client-id" \
  -d "client_secret=your-client-secret"
Use Cases

Multi-cloud deployments, on-premises installations, or when you need full control over the authentication flow without external dependencies.

Setting up Microsoft Entra ID App Registration

Follow these steps to configure Azure AD authentication for the Document Service API.

1

Create App Registration (API)

  1. Go to Azure Portal → Microsoft Entra ID → App registrations
  2. Click New registration
  3. Enter name: DocumentService-API-{env} (e.g., DocumentService-API-Dev)
  4. Select Accounts in this organizational directory only
  5. Click Register
  6. Copy the Application (client) ID - this is your ClientId
  7. Copy the Directory (tenant) ID - this is your TenantId
2

Expose an API (Define Scopes)

  1. In your App Registration, go to Expose an API
  2. Click Set next to Application ID URI, use default: api://{client-id}
  3. Click Add a scope:
    • Scope name: access_as_application
    • Who can consent: Admins and users
    • Admin consent display name: Access Document Service API
    • Admin consent description: Allows the app to access Document Service API
  4. Click Add scope
3

Register Client Applications

For each service that needs to call the API (e.g., Salesforce), create a separate App Registration:

  1. Create a new App Registration: DocumentService-Client-Salesforce
  2. Go to Certificates & secrets → New client secret
  3. Copy the secret value immediately (it won't be shown again!)
  4. Go to API permissions → Add a permission
  5. Select My APIs → DocumentService-API
  6. Select the access_as_application scope
  7. Click Grant admin consent (requires admin role)
Token Request Client Credentials Flow
POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token

client_id={client-app-id}
&client_secret={client-secret}
&scope=api://{api-client-id}/.default
&grant_type=client_credentials
4

Configure via Environment Variables

Set authentication environment variables in your container runtime (Docker -e, Azure App Settings, AWS ECS Task Definition, GCP Cloud Run Variables):

ENV Environment Variables
# Authentication Mode (Both = Azure AD + API Key)
Authentication__Mode=Both

# Azure Entra ID (REQUIRED for Admin endpoints)
Authentication__AzureAd__Instance=https://login.microsoftonline.com/
Authentication__AzureAd__TenantId=YOUR_TENANT_ID
Authentication__AzureAd__AdminClientId=YOUR_ADMIN_CLIENT_ID
Authentication__AzureAd__IsEnabled=true

# API Key (for Document API, managed via Admin REST API)
Authentication__ApiKey__Enabled=true
Authentication__ApiKey__HeaderName=X-API-Key
# NOTE: API Keys are created via POST /api/admin/clients
Configuration Syntax

Use double-underscore (__) as section separator for environment variables. Example: Authentication__AzureAd__TenantId=...

API Key Management via Admin REST API

API keys are created and managed through the Admin REST API using OAuth2 authentication.

1

Create API Client with Key

Authenticate with the Admin Client ID and create a new API client:

POST /api/admin/clients
curl -X POST "https://your-api.azurewebsites.net/api/admin/clients" \
  -H "Authorization: Bearer {admin-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Salesforce-Integration",
    "description": "API client for Salesforce",
    "generateApiKey": true,
    "keyVaultStorage": {
      "storeInKeyVault": true,
      "secretName": "apikey-salesforce"
    },
    "permissions": ["Documents.Read", "Documents.Write"]
  }'
Response

The API key is returned once in the response. Store it securely! If storeInKeyVault is true, you can retrieve it later from Key Vault.

2

Retrieve Key from Key Vault

If the key was stored in Key Vault, retrieve it via the Admin API:

GET /api/admin/clients/{guid}/key
curl -X GET "https://your-api.azurewebsites.net/api/admin/clients/{client-guid}/key" \
  -H "Authorization: Bearer {admin-token}"
3

Rotate API Key

Generate a new key (old key becomes invalid immediately):

POST /api/admin/clients/{guid}/rotate-key
curl -X POST "https://your-api.azurewebsites.net/api/admin/clients/{client-guid}/rotate-key" \
  -H "Authorization: Bearer {admin-token}"

Supported Client Types

Service-to-Service

Backend services like Salesforce, Azure Functions, or other APIs that call the Document Service without user interaction.

Client Credentials Flow

User Applications

Web apps or SPAs where users log in with their Azure AD credentials to access documents on their behalf.

Authorization Code Flow

Non-OAuth2 Systems

For systems that cannot use OAuth2 (e.g., Salesforce Outbound Messages). API keys created via Admin API and stored in Key Vault.

API Key (X-API-Key Header)

Network Access Modes

Configure network isolation per resource type based on environment requirements.

public

Access from all IP addresses allowed. Azure AD authentication still required.

Use Case: Development, Testing
Not recommended for production

restricted

Access only from IP whitelist, VNet, and Azure Services.

Use Case: Staging, QAS
  • IP Whitelist active
  • VNet rules enabled
  • Azure Services bypass

private

No public access. Only accessible via Private Endpoints.

Use Case: Production
  • Private Endpoint only
  • DNS via Private Zone
  • Zero public exposure

Network Access Per Resource

Each cloud resource can be configured with its own network access mode.

Resource Available Modes Description
App Service / Container public restricted Controls inbound access to the web application (Landing Page, APIs, API Docs).
public: Allow connections from ANY IP address
restricted: Only allow connections from configured IP whitelist
SQL Database public restricted private Controls access to the database server.
public: Allow connections from ANY IP address
restricted: Only whitelisted IPs + VNet subnets + cloud services
private: Only via Private Endpoint / PrivateLink (no public access)
Storage Account public restricted private Controls access to Blob / Object Storage.
public: Allow connections from ANY IP address
restricted: Only whitelisted IPs + VNet subnets + cloud services
private: Only via Private Endpoint / PrivateLink (no public access)

Technical Implementation Details

App Service / Container (public)

When set to public:

  • No IP restrictions configured
  • All incoming HTTP/HTTPS requests accepted
  • Authentication still enforced on protected endpoints

App Service / Container (restricted)

When set to restricted:

  • Only whitelisted IPs allowed
  • Cloud service traffic allowed (for deployment pipelines)
  • All other requests blocked at network level

Database / Storage (restricted)

When set to restricted:

  • Only whitelisted IPs allowed
  • VNet subnets (App Service / Container subnet) allowed
  • Cloud services bypass enabled
  • All other public access denied

Database / Storage (private)

When set to private:

  • Public network access completely disabled
  • Private Endpoint / PrivateLink created in VNet
  • Private DNS zone configured for name resolution
  • Only VNet-integrated resources can connect

Container Deployment

Deploy the Document Service container in your cloud environment.

1

Pull the Container Image

Pull the latest Document Service container from your configured container registry.

DOCKER Container Registry
# Azure Container Registry
docker pull yourregistry.azurecr.io/documentservice:latest

# AWS ECR
docker pull 123456789012.dkr.ecr.eu-central-1.amazonaws.com/documentservice:latest

# GCP Artifact Registry
docker pull europe-docker.pkg.dev/project-id/repo/documentservice:latest
2

Configure Environment Variables

Set all required environment variables for your cloud provider. See the Configuration Guide for all available settings.

ENV Required Variables
# Database (required)
Database__Provider=PostgreSQL
ConnectionStrings__DefaultConnection=Host=...;Database=documentservice;...

# Storage (required)
BlobStorage__AccountName=yourstorageaccount
BlobStorage__ContainerName=documents

# Authentication (required)
Authentication__Mode=Both
Authentication__AzureAd__TenantId=YOUR_TENANT_ID
Authentication__AzureAd__AdminClientId=YOUR_ADMIN_CLIENT_ID

# Branding (optional)
Branding__LandingPageRedirectUrl=https://portal.yourcompany.com
Branding__ShowApiDocs=false
3

Run the Container

Start the container with your environment variables. The service runs on port 8080 by default.

DOCKER docker run
docker run -d \
  -p 8080:8080 \
  -e "Database__Provider=PostgreSQL" \
  -e "ConnectionStrings__DefaultConnection=Host=..." \
  -e "BlobStorage__AccountName=yourstorageaccount" \
  -e "Authentication__Mode=Both" \
  -e "Authentication__AzureAd__TenantId=YOUR_TENANT_ID" \
  -e "Authentication__AzureAd__AdminClientId=YOUR_ADMIN_CLIENT_ID" \
  yourregistry.azurecr.io/documentservice:latest
4

Verify Deployment

Check the health endpoint and validate the cloud configuration.

CURL Health & Validation
# Health check
curl https://your-service-url/health

# Validate cloud configuration (requires Admin token)
curl -X POST https://your-service-url/api/admin/system/validate-config \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN"

Estimated Costs

Monthly cost estimation for the basic development configuration.

Service SKU ~Cost/Month
App Service Plan B1 (Basic) ~13 EUR
Azure SQL Database Basic (5 DTU) ~5 EUR
Storage Account Standard LRS ~1-5 EUR
Key Vault Standard ~0.03 EUR/10k ops
Private Endpoints 3x Endpoints ~22 EUR
Total (Dev) ~45-50 EUR

*Prices vary by region and actual usage. Private Endpoints optional in dev.

Environment Configurations

Recommended settings for different deployment stages.

DEV

Development

  • App Service Access: public
  • SQL Access: public
  • Storage Access: public
  • Private Endpoints: Optional
  • App Service SKU: B1
  • SQL SKU: Basic
QAS

Quality Assurance

  • App Service Access: public
  • SQL Access: restricted
  • Storage Access: restricted
  • Private Endpoints: Recommended
  • App Service SKU: S1
  • SQL SKU: S0
PROD

Production

  • App Service Access: public
  • SQL Access: private
  • Storage Access: private
  • Private Endpoints: Required
  • App Service SKU: P1v3
  • SQL SKU: S2+

Troubleshooting

Common issues and their solutions.

Login failed for user '<token-identified principal>'

Cause: SQL database user for Managed Identity not created.

Solution: Run the SQL script from Post-Deployment Step 5 to create the database user.

Access denied. Caller was not found on any access policy.

Cause: Key Vault RBAC assignment missing for App Service.

Solution: Verify the "Key Vault Secrets User" role is assigned to the App Service Managed Identity.

Private Endpoint connectivity issues

Cause: VNet integration, DNS, or NSG misconfiguration.

Solution:

  • Verify VNet Integration is active on App Service
  • Check Private DNS Zones are correctly linked to VNet
  • Review NSG rules if configured

Storage container creation fails

Cause: Deployer doesn't have Storage Blob Data Contributor role.

Solution: Wait for RBAC propagation (~2 minutes) or manually assign the role.