Azure Resource Map
Complete mapping of Azure resources to GitHub repositories, including app registrations, deployment identity patterns, runners, and resource groups.
Repository → Azure Resource Map
The master reference for which Azure resources belong to each GitHub repository.
| Repository | Azure Resource | Resource Type | Resource Group | Custom Domain | App Registration |
|---|---|---|---|---|---|
| PSI.UniData.API | — | On-prem (PS-PROXY) | — | api.progressivesurface.com | PSI.UniData.API |
| psi-explorer-web | bom-explorer-web | App Service (Linux) | PS-WEBAPPS | explorer.progressivesurface.com | PSI PSI Explorer |
| redbook-web | redbook-web | App Service (Linux) | PS-WEBAPPS | redbook.progressivesurface.com | PSI-Redbook-Web |
| psi-portal | psi-portal | App Service (Linux) | PS-WEBAPPS | portal.progressivesurface.com | PSI Portal |
| project-explorer | ps-project-explorer | App Service (Linux) | PS-WEBAPPS | projects.progressivesurface.com | PSI Project Explorer |
| erp-migration-tool | erp-migration-api | App Service (Linux) | PS-WEBAPPS | dmt.progressivesurface.com | PSI ERP Migration Tool |
| PRGJSMES | prgjsmes-prod | App Service (Windows) | PS-WEBAPPS | psmes.progressivesurface.com | PRGJSMES |
| progressive-data-view | ps-progressive-view | App Service (Linux) | PS-WEBAPPS | — | — |
| redbook-dashboard | ps-redbook-dashboard | App Service (Linux) | PS-WEBAPPS | quality.progressivesurface.com | PSI Redbook Dashboard |
| PSI-Wiki-Site | psi-wiki | Static Web App | PS-WEBAPPS | wiki.progressivesurface.com | PSI Wiki |
| redbook-photos | psredbookphotos | App Service (Node 20) | PsRedbookPhotos | — | — |
| shipping-photos | ps-shipphotos | App Service (Node 24) | PS-RG-01 | — | — |
| psi-nfc-launcher | psi-tap-bridge | Function App (Linux) | PS-WEBAPPS | — | PSI TAP Bridge |
| argo-analytics | ps-argo-analytics | App Service (Linux) | PS-WEBAPPS | argo.progressivesurface.com | — |
| psi-winget-source | ps-winget-source | App Service (Linux/Docker) | PS-WEBAPPS | packages.progressivesurface.com | PSI WinGet Source |
| psi-winget-source | psiwingetpkgs | Storage Account | PS-WEBAPPS | — | — |
| psi-winget-source | psicontainers | Container Registry (Basic) | PS-WEBAPPS | psicontainers.azurecr.io | — |
| csm-board | csm-board | App Service (Linux) | PS-WEBAPPS | board.progressivesurface.com | CSM Board |
| ZebraTracking | psi-zebra-tracking | App Service (Linux, .NET 8) | PS-WEBAPPS | — | — |
| psi-data-pipeline | psi-datasync | App Service (Linux) | PS-WEBAPPS | — | — |
| psi-nfc-launcher | psi-notify-listener | Function App (Linux) | PS-WEBAPPS | — | — |
| procisely-redirect | procisely-redirect | App Service (Linux) | PS-WEBAPPS | procisely.com | — |
| (TBD) | ps-dispatch | App Service (Linux, .NET 8) | PS-WEBAPPS | — | — |
| build-vs-buy 🚧 | ps-buildvsbuy | App Service (Linux) | PS-WEBAPPS | — (default host only) | — |
🚧
ps-buildvsbuyis new (June 2026) and under active development — not yet hardened. Its PEps-buildvsbuy-peexists (10.160.140.5) but public access is still enabled andhttpsOnlyis off. Pre-launch hardening checklist is in azure-security.
Entra ID App Registrations
Each web app has an App Registration in Microsoft Entra ID for authentication. Some repos also have a separate service principal or managed identity deploy path for GitHub Actions.
Application Registrations
| App Registration | Application (Client) ID | Maps to Repository | Purpose |
|---|---|---|---|
| PSI.UniData.API | b3db69d9-5d15-457d-b660-88b336fc00fa | PSI.UniData.API | API auth |
| PSI PSI Explorer | 13d6930a-97d9-4a8d-b355-a31074fbd53d | psi-explorer-web | Client/app-layer auth |
| PSI-Redbook-Web | 85a9ca9b-2bdf-4c68-b115-24d14ad55f43 | redbook-web | Client/app-layer auth |
| PSI Redbook Dashboard | 294f8c40-a7bd-4a26-9888-ab49987582b4 | redbook-dashboard | EasyAuth + app-layer |
| PSI Project Explorer | 971a34d6-004c-40c0-86d9-f610d83a26ca | project-explorer | Client/app-layer auth |
| PSI Portal | 7f929c7f-2483-4206-93b6-11225e07ca85 | psi-portal | Client/app-layer auth |
| PSI ERP Migration Tool | 55dae93e-2df7-46fd-b677-01c384d22394 | erp-migration-tool | Client/app-layer auth (platform auth disabled) |
| PSI Wiki | 9f4f895d-d74d-46df-a04a-febfc52fbf34 | PSI-Wiki-Site | SWA auth |
| PRGJSMES | e52f5171-93ba-4e68-b4a7-3ba1409cda09 | PRGJSMES | Client/app-layer auth |
| PSI TAP Bridge | 11d892e3-e20b-4fb2-aef7-6f9b37d02cb6 | psi-nfc-launcher | Graph API (client credentials) |
| PSI WinGet Source | b0b01ac6-8f1f-4b55-af50-c582da3dfd77 | psi-winget-source | WinGet REST API auth (Entra ID + pre-authorized WinGet client) |
| CSM Board | 9eeff376-82ba-40cf-a4b9-d2ed4970d82d | csm-board | SPA auth (MSAL.js PKCE) for the hosted Claude Work Board; isFallbackPublicClient: true so the per-developer csm agent can use device-code flow against the same app reg |
| PSI Dispatch BC (client) | 4d1fc1cc-4589-40e2-b6fb-adeefd1237d9 | psi-dispatch | psi-dispatch Phase-2 BC reads (client credentials; BC API.ReadWrite.All + UniData.ServiceRead). Owner: Colin Orlando. Admin-consented 2026-08-11; registered inside BC DEV only — per-environment BC registration is required on top of the Entra grant |
Deployment Service Principals
These are used by GitHub Actions for az webapp deploy via federated credentials or client secrets.
| Service Principal | Application ID | Deploys to Repository |
|---|---|---|
| github-deploy-progressive-view | 7b2e8877-75a6-46a8-b3b4-6f29d9678993 | progressive-data-view |
| psi-portal-deploy | 15237620-9676-4701-b912-fef07a31a162 | psi-portal |
| prgjsmes-github-deploy | 8a999858-747c-406e-a69f-1e2c4efb1d24 | PRGJSMES |
Tenant ID: a83ae943-0a50-49cc-83c3-479b7a44b7fb
GitHub Actions Deployment Secrets
Deployment auth posture was standardized in April 2026:
- App Service production deploys: identity-based (
az login --identityon self-hosted runner, or Entra federated credentials) - Static Web App deploys: SWA API token
- Publish profile deploys: deprecated for production App Services
| Pattern | Typical Secret/Config | Notes |
|---|---|---|
| App Service (federated SP) | AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_SUBSCRIPTION_ID | Use azure/login OIDC or equivalent Entra federated auth |
| App Service (psi-internal runner) | No deploy secret required | Uses runner managed identity (az login --identity) |
| Static Web App | AZURE_STATIC_WEB_APPS_API_TOKEN | Required for SWA deploy action |
| Wiki automation | WIKI_ACCESS_TOKEN | GHE API access token for wiki sync tasks |
Deployment Credential Operations
# Verify basic publishing credentials are disabled (production baseline)
az rest --method get \
--uri "https://management.azure.com/subscriptions/<SUB_ID>/resourceGroups/PS-WEBAPPS/providers/Microsoft.Web/sites/<APP_NAME>/basicPublishingCredentialsPolicies/scm?api-version=2022-03-01"
az rest --method get \
--uri "https://management.azure.com/subscriptions/<SUB_ID>/resourceGroups/PS-WEBAPPS/providers/Microsoft.Web/sites/<APP_NAME>/basicPublishingCredentialsPolicies/ftp?api-version=2022-03-01"
# Static Web App deployment token (still required)
az staticwebapp secrets list \
--name psi-wiki --resource-group PS-WEBAPPS \
--query "properties.apiKey" -o tsv
# Set/update a GitHub secret on GHE.com
GH_HOST=progressivesurface.ghe.com gh secret set SECRET_NAME \
-R ProgressiveSurface/repo-name --body "value"Resource Groups
| Resource Group | Purpose | Key Resources |
|---|---|---|
| PS-WEBAPPS | All production web applications | 16 App Services, 3 Function Apps, 1 Static Web App, 2 SQL Servers, 1 KV, 1 ACR, App Service Plans |
| PS-RG-01 | Core infrastructure | VNet (PS-VNMAIN), DNS zone, VMs, Key Vaults, ps-shipphotos, Logic Apps, Service Bus, Automation |
| ProcServices-Prod-Data | Database resources | procserv-proddata SQL server (10 databases) |
| PsRedbookPhotos | Redbook photo storage | psredbookphotos App Service, SyncBlogtoSdrive Logic App |
| PSLogicApps | Integration workflows | PaylocityInbound, redbook-feedback-notifier, entra-bc-employee-sync Logic Apps |
| PS-SPEECH | AI / Speech services | PS-SPEECH1, PS-SPEECH2 Cognitive Services (SpeechServices F0) |
| rg-GPT5-9353 | AI Foundry (GPT-5) | gpt5-9353-resource, adeve-midqp8v8-eastus2 AIServices |
| SERVER-NCUS-CSP | CSP-managed server infra | akv-19 Key Vault, nmm-app-runbooks Automation Account |
| AVD-NCUS-CSP | Azure Virtual Desktop (CSP) | PS-AVD-DEV-0, PS-AVD-POOL-0 session hosts (Worksighted managed) |
| AzureIOT_Test | IoT dev/test | PSTestHub IoT Hub |
Virtual Machines & Runners
Azure VMs
| VM Name | OS | Resource Group | Size | Subnet / IP | Purpose |
|---|---|---|---|---|---|
| ps-cicd-runner | Ubuntu 24.04 | PS-RG-01 | Standard_B2as_v2 | PS-SERVERS / 10.160.0.9 | GitHub Actions runner (primary) |
| ps-cicd-runner-2 | Ubuntu 24.04 | PS-RG-01 | Standard_B2s | PS-SERVERS / 10.160.0.18 | GitHub Actions runner (secondary) |
| PS-AZ-DC01 | Windows Server | PS-RG-01 | — | PS-SERVERS / 10.160.0.5 | Domain controller, DNS server |
| PS-AZ-SFTP1 | Windows Server | PS-RG-01 | — | PS-SERVERS / — | SFTP file transfer server |
| PS-AZ-APPS-0 | Windows Server | PS-RG-01 | — | PS-SERVERS / — | Application server (purpose TBD) |
| PS-AZ-LS3 | Windows Server | PS-RG-01 | — | PS-SERVERS / — | Line Scanning / LS3 server |
| PS-AZ-OPTIX | Windows Server | PS-RG-01 | — | PS-SERVERS / — | Cognex OPTIX vision server |
| PS-AZ-SRVC-0 | Windows Server | PS-RG-01 | — | PS-SERVERS / — | Service VM (purpose TBD) |
| ps-argo-etl | Linux | PS-RG-01 | — | PS-SERVERS / — | ETL backend for ps-argo-analytics |
Note: ps-cicd-runner-2 has an NSG (ps-cicd-runner-2NSG) with only inbound SSH allow; outbound is default-allow. ps-cicd-runner has no NSG. Both runners have identical software: Node.js 20, Python 3.12, .NET 8 SDK, GitHub Actions runner v2.334.0.
GitHub Actions Runners
Registered to progressivesurface.ghe.com/ProgressiveSurface.
| Runner Name | OS | Location | Labels | Serves |
|---|---|---|---|---|
| ps-cicd-runner | Linux | Azure VM (PS-RG-01) | self-hosted, Linux, X64, psi-internal | redbook-web, psi-explorer-web, project-explorer, erp-migration-tool, and other Linux deploys |
| ps-cicd-runner-2 | Linux | Azure VM (PS-RG-01) | self-hosted, Linux, X64, psi-internal | Same as ps-cicd-runner (parallel capacity) |
| PS-GR-RUNNER | Windows | On-premises | self-hosted, Windows, X64, dotnet-framework | PSI.All CI (CredentialsManager, parity harness) |
| PS-PROXY | Windows | On-premises server | self-hosted, Windows, X64, ps-proxy, dotnet-8, node, unidata-access | PSI.UniData.API deploy |
| PS-PLCRunner | Windows | On-premises | self-hosted, Windows, X64, ps-plcrunner | PLC-related workflows |
ps-cicd-runner / ps-cicd-runner-2 deploy to App Services with private endpoints. Runner agent at /home/runner/actions-runner/, runs as runner user, managed by systemd (actions.runner.ProgressiveSurface.<name>.service).
To register a new Linux runner:
# Generate token (valid 1 hour)
GH_HOST=progressivesurface.ghe.com gh api --method POST orgs/ProgressiveSurface/actions/runners/registration-token
# On the VM (as root):
mkdir -p /home/runner/actions-runner && cd /home/runner/actions-runner
curl -o runner.tar.gz -L https://github.com/actions/runner/releases/download/v2.334.0/actions-runner-linux-x64-2.334.0.tar.gz
tar xzf runner.tar.gz && rm runner.tar.gz
chown -R runner:runner /home/runner/actions-runner
./bin/installdependencies.sh
sudo -u runner ./config.sh --url https://progressivesurface.ghe.com/ProgressiveSurface --token <TOKEN> --name <NAME> --labels psi-internal --unattended --replace
./svc.sh install runner && systemctl start actions.runner.ProgressiveSurface.<NAME>.servicePS-PROXY hosts the UniData API directly. Runner at C:\actions-runner.
Database Resources
| Resource | Type | FQDN | Region | Resource Group |
|---|---|---|---|---|
| procserv-proddata | Azure SQL Server | procserv-proddata.database.windows.net | North Central US | ProcServices-Prod-Data |
| psi-zebra-tracking-sql | Azure SQL Server | psi-zebra-tracking-sql.database.windows.net | North Central US | PS-WEBAPPS |
Used by: PRGJSMES, project-explorer, erp-migration-tool (→ procserv-proddata); ZebraTracking (→ psi-zebra-tracking-sql).
Region constraint: North Central US has no availability zones. Zone-redundant databases (ZR-DB) and GZRS/ZRS backup redundancy are therefore not available on this server. Use GRS (geo-redundant) for backup storage and rely on PITR + LTR + (optional) failover groups to South Central US for DR. Any app that requires ZR-DB must provision a new server in an AZ-capable region (e.g. South Central US, East US 2).
Backup Policies
Per-database backup policies (current state, as of 2026-06-01):
| Database | PITR retention | LTR (weekly/monthly/yearly) | Backup storage | Used Size | Notes |
|---|---|---|---|---|---|
| PRGJSMES | 35 days | 4W / 12M / 5Y (week 1) | GRS (Geo) | ~407 MB | Production MES — compliance + DR baseline; ⚠️ autopause disabled (~$760/mo) |
| PSI_Analytics | 7 days | none | GRS | ~8.4 GB | ⚠️ Needs LTR; large dataset |
| CapExNext | 7 days | none | GRS | ~28 MB | ⚠️ Needs LTR |
| winget | 14 days | none | GRS | ~26 MB | ⚠️ Needs LTR |
| CalibrationPortal | 7 days | none | GRS | ~31 MB | ⚠️ Needs LTR |
| DataSync | 7 days | none | LRS | ~12.2 GB | ⚠️ Needs LTR + Geo storage; 10 DTU severely underpowered for 12 GB |
| Procisely | 7 days | none | LRS | ~26 MB | ⚠️ Needs LTR + Geo storage |
| PS-Line6 | 7 days | none | LRS | Paused | Paused — ⚠️ Needs LTR + Geo storage; confirm decommission |
| PS-AZ-PSDB01 | 7 days | none | LRS | Paused | Paused — ⚠️ Needs LTR + Geo storage; confirm decommission |
| alerts | 7 days | none | LRS | ~TBD | GP_S Gen5 1 vCore, Online — ⚠️ Needs LTR + Geo storage; new DB (first seen 2026-06-24) |
psi-zebra-tracking-sql databases:
| Database | PITR retention | LTR | Backup storage | Notes |
|---|---|---|---|---|
| psi-zebra-tracking-db | 7 days | none | (default) | ~30 MB |
Maintenance commands:
# View current PITR policy
az sql db str-policy show -g ProcServices-Prod-Data -s procserv-proddata -n PRGJSMES
# View current LTR policy
az sql db ltr-policy show -g ProcServices-Prod-Data -s procserv-proddata -n PRGJSMES
# Update PITR retention (max 35 days on GP tier)
az sql db str-policy set -g ProcServices-Prod-Data -s procserv-proddata -n <db> \
--retention-days 35 --diffbackup-hours 12
# Set LTR policy (ISO 8601 durations)
az sql db ltr-policy set -g ProcServices-Prod-Data -s procserv-proddata -n <db> \
--weekly-retention P4W --monthly-retention P12M --yearly-retention P5Y --week-of-year 1
# Change backup storage redundancy (Local | Zone | Geo | GeoZone — Zone/GeoZone unsupported in NCUS)
az sql db update -g ProcServices-Prod-Data -s procserv-proddata -n <db> \
--backup-storage-redundancy GeoRestore from backup:
# Point-in-time restore (within PITR window — up to 35 days)
az sql db restore -g ProcServices-Prod-Data -s procserv-proddata -n PRGJSMES-restored \
--dest-name PRGJSMES-restored --source-database PRGJSMES --time "2026-04-20T12:00:00Z"
# Restore from a long-term backup (list first, then restore by resource ID)
az sql db ltr-backup list --location northcentralus \
--resource-group ProcServices-Prod-Data --server procserv-proddata --database PRGJSMESConnecting to Azure SQL
The SQL server uses a private endpoint (PS-ProdData-SQL-Private at 10.160.140.4). On the PSI network, DNS resolves to the private IP via the privatelink.database.windows.net zone on PS-AZ-DC01.
Required tool: sqlcmd (go-sqlcmd) — the modern cross-platform SQL CLI with Entra MFA support.
# Install
winget install sqlcmd
# Connect (uses your az login session for auth)
sqlcmd -S procserv-proddata.database.windows.net -d PRGJSMES --authentication-method=ActiveDirectoryDefaultNote: The legacy
SQLCMD.EXE(inC:\Program Files\Microsoft SQL Server\Client SDK\) does NOT support Entra MFA. It fails withAADSTS50076. Always use the new go-basedsqlcmdinstalled via winget (installs toC:\Program Files\sqlcmd\).
Alternatives: SSMS or Azure Data Studio also work — use “Microsoft Entra MFA” authentication.
SQL Server Entra Admin
| Property | Value |
|---|---|
| Admin | ADevereaux@progressivesurface.com |
| Public Access | Enabled (firewall rules for PSI office IPs) |
| Private Endpoint | PS-ProdData-SQL-Private (10.160.140.4) |
| TLS | 1.2 minimum |
Database Users
| Database | User | Role | Purpose |
|---|---|---|---|
| PRGJSMES | prgjsmes-prod | db_ddladmin, db_datareader, db_datawriter | App Service managed identity |
| PRGJSMES | dcooper@progressivesurface.com | db_owner | DBA / schema management |
Managing Database Users
# Create user from Entra ID
sqlcmd -S procserv-proddata.database.windows.net -d PRGJSMES \
--authentication-method=ActiveDirectoryDefault \
-Q "CREATE USER [user@progressivesurface.com] FROM EXTERNAL PROVIDER"
# Grant db_owner
sqlcmd -S procserv-proddata.database.windows.net -d PRGJSMES \
--authentication-method=ActiveDirectoryDefault \
-Q "ALTER ROLE db_owner ADD MEMBER [user@progressivesurface.com]"
# Verify
sqlcmd -S procserv-proddata.database.windows.net -d PRGJSMES \
--authentication-method=ActiveDirectoryDefault \
-Q "SELECT dp.name, r.name AS role_name FROM sys.database_principals dp JOIN sys.database_role_members rm ON dp.principal_id = rm.member_principal_id JOIN sys.database_principals r ON rm.role_principal_id = r.principal_id WHERE dp.type IN ('E','X')"Key Vault
| Property | Value |
|---|---|
| Name | ps-certificates-kv |
| Resource Group | PS-RG-01 |
| Vault URI | https://ps-certificates-kv.vault.azure.net/ |
Contents
| Secret/Certificate | Purpose |
|---|---|
| wildcard-progressivesurface | Wildcard SSL cert (*.progressivesurface.com) — thumbprint: 8ECD7C39FA4BD44E10D3D89A80EF33F3922A291A, expires 2027-02-03 |
| App-specific secrets | Referenced via @Microsoft.KeyVault(...) in App Service settings |
Static Web App
| Property | Value |
|---|---|
| Name | psi-wiki |
| Default Hostname | happy-flower-0b5174910.6.azurestaticapps.net |
| Custom Domain | wiki.progressivesurface.com |
| SKU | Free ⚠️ (no auth restriction, no private endpoint) |
| Repository | PSI-Wiki-Site (branch: v4) — ⚠️ repoUrl not connected |
| Resource Group | PS-WEBAPPS |
⚠️ The Static Web App is on Free tier. Consider upgrading to Standard ($9/mo) to enable AAD authentication restrictions (internal-only access). Deployment uses an API token — rotate if the token is committed to any repository.
Networking Quick Reference
| Resource | Value |
|---|---|
| VNet | PS-VNMAIN |
| DNS Zone | progressivesurface.com (in PS-RG-01) |
| Private DNS Zone | privatelink.azurewebsites.net (in PS-RG-01) |
| Primary DNS Server | 10.160.0.5 (PS-AZ-DC01) |
| NAT Gateway (egress) | PS-NAT-GW-SERVERS → 52.162.239.139 — the public egress for everything in PS-SERVERS, including both CI runners. Any App Service IP allowlist must include this or deploys break. |
Azure Front Door (added 2026-07-28)
PSI’s first Front Door deployment. Billing is per profile, not per app — one profile
hosts many endpoints, custom domains, origin groups and routes, so putting additional apps
behind psi-frontdoor adds no base cost. Use it rather than creating a second profile.
| Resource | Value |
|---|---|
| Profile | psi-frontdoor, RG PS-WEBAPPS, SKU Standard_AzureFrontDoor (~$35/mo base) |
frontDoorId | 9ad6dca2-bac8-4679-900b-6a0d6c5052d5 — the X-Azure-FDID value origin locks match on |
| WAF policy | psiWafPolicy (Standard_AzureFrontDoor), Prevention mode, custom RateLimitRule rateLimitPerIp = 1000 req/min per client IP → Block |
| Security policy | psi-waf-assoc — binds the WAF to the endpoint + custom domain |
| Endpoint | Hostname | Origin | Serves |
|---|---|---|---|
board | board-efejceb3d6acf7ay.a03.azurefd.net | csm-board.azurewebsites.net (host header preserved as board.progressivesurface.com) | board.progressivesurface.com |
Tier limits — Standard vs Premium
Standard supports custom WAF rules only. Microsoft-managed rulesets, bot protection, and Private Link to origin are Premium-only (~$330/mo). Rate limiting is available on Standard. Don’t plan a “managed ruleset” on Standard — it isn’t a policy edit, it’s a tier upgrade.
Origin-lock pattern (how csm-board stops answering the internet directly):
# Main site: only our Front Door, matched by BOTH source service tag and FDID header
az webapp config access-restriction add -g PS-WEBAPPS -n <app> \
--rule-name AllowFrontDoorOnly --action Allow --priority 100 \
--service-tag AzureFrontDoor.Backend \
--http-header x-azure-fdid=9ad6dca2-bac8-4679-900b-6a0d6c5052d5
# SCM: the CI runner NAT must be allowed or deploys 403
az webapp config access-restriction add -g PS-WEBAPPS -n <app> --scm-site true \
--rule-name AllowCIRunnerNAT --action Allow --priority 100 --ip-address 52.162.239.139/32Adding the first Allow rule creates an implicit Deny all at priority 2147483647 — that is
what removes open-internet reachability. Verified 2026-07-28: forging the x-azure-fdid
header from a non-Front-Door source still returns 403, because the service-tag source
check is evaluated as well.
az afdCLI potholes (cost real time)
- Git Bash mangles leading-slash arguments into Windows paths —
--probe-path /api/healthbecameC:/Program Files/Git/api/health, and resource IDs becameC:/Program Files/Git/subscriptions/.... ExportMSYS_NO_PATHCONV=1or use PowerShell.--enforce-certificate-name-checktakestrue/false, notEnabled/Disabledlike every adjacent flag.az network front-door waf-policy rule createhas no--deferin the shipped CLI; set custom rules viawaf-policy update --custom-rules "{rules:[...]}".- First-time edge propagation on a new profile took ~17.5 minutes. Until it completes the endpoint returns 404 with
X-Cache: CONFIG_NOCACHEeven though every resource readsprovisioningState: Succeeded. That is not a misconfiguration — wait it out.
Subnets
| Subnet | CIDR | Purpose |
|---|---|---|
| PS-SERVERS | 10.160.0.0/23 | VMs, private endpoints (legacy) |
| PS-WebApps | 10.160.150.0/24 | App Service VNet integration (delegated) |
| PS-ProdData | 10.160.140.0/24 | Private endpoints (preferred for new) |
| PS-VMXSBNT | 10.160.50.0/24 | VMs |
Private Endpoints
| Service | Private Endpoint | IP Address | Subnet |
|---|---|---|---|
| psi-portal | psi-portal-pe | 10.160.0.6 | PS-SERVERS |
| shippingappfunctions | — | 10.160.0.16 | PS-SERVERS |
| psi-explorer-web | bom-explorer-web-pe | 10.160.0.17 | PS-SERVERS |
| psi-tap-bridge | psi-tap-bridge-pe | 10.160.140.6 | PS-ProdData |
| erp-migration-api | ERPMigrationAPIEndpoint | 10.160.140.9 | PS-ProdData |
| ps-project-explorer | ps-project-explorer-pe | 10.160.140.10 | PS-ProdData |
| prgjsmes-prod | prgjsmes-prod-pe | 10.160.140.11 | PS-ProdData |
| prgjsmes-prod (staging slot) | prgjsmes-prod-staging-pe | 10.160.140.18 | PS-ProdData |
| prgjsmes-prod (⚠️ duplicate) | prgjsmes-prod-pe (PS-WEBAPPS) | 10.160.140.13 | PS-ProdData |
| redbook-web | redbook-web-pe | 10.160.140.12 | PS-ProdData |
| ps-redbook-dashboard | ps-redbook-dashboard-pe | 10.160.140.14 | PS-ProdData |
| ps-progressive-view | ps-progressive-view-pe | 10.160.140.15 | PS-ProdData |
| ps-argo-analytics | ps-argo-analytics-pe | 10.160.140.16 | PS-ProdData |
| ps-winget-source | ps-winget-source-pe | 10.160.140.17 | PS-ProdData |
| ps-buildvsbuy 🚧 | ps-buildvsbuy-pe | 10.160.140.5 | PS-ProdData |
| psi-datasync | psi-datasync-pe | 10.160.0.19 | PS-SERVERS |
| ps-dispatch | ps-dispatch-pe | 10.160.140.22 | PS-ProdData |
| psargostorage | PSARGOSHAREPRIVATE | 10.160.0.11 | PS-SERVERS |
| procserv-proddata | PS-ProdData-SQL-Private | 10.160.140.4 | PS-ProdData |
| psi-zebra-tracking-sql | psi-zebra-tracking-sql-pe | 10.160.140.21 | PS-ProdData |
| PSTestHub | AzureIOT_PE | 10.160.140.7, .8, .19, .20 | PS-ProdData |
| ps-certificates-kv | ps-certificates-kv-pe | 10.160.140.23 | PS-ProdData |
| psi-service | psi-service-pe | 10.160.140.24 | PS-ProdData |
| ps-docgen | ps-docgen-pe | 10.160.140.25 | PS-ProdData |
| psql-psi-iot | psql-psi-iot-pe | 10.160.140.26 | PS-ProdData |
| ps-iot-bridge | ps-iot-bridge-pe | 10.160.140.27, .28 | PS-ProdData |
| ps-iot-grafana | ps-iot-grafana-pe | 10.160.140.29 | PS-ProdData |
| ps-iot-console | ps-iot-console-pe | 10.160.140.30 | PS-ProdData |
| ps-sop | ps-sop-pe | 10.160.140.31 | PS-ProdData |
| psi-finance-kv | psi-finance-kv-pe | 10.160.140.32 | PS-ProdData |
| ps-bom-coverage | ps-bom-coverage-pe | 10.160.140.33 | PS-ProdData |
| ps-ach-web | ps-ach-web-pe | 10.160.140.34 | PS-ProdData |
Next free PS-ProdData PE IP: 10.160.140.35.
.4–.34are all in use — verified against live NIC ipConfigurations 2026-08-26 (az network nic list, filtered to10.160.140.*), not against the DNS zone. ⚠ A live NIC scan alone is not sufficient:.28is a second ipconfig onps-iot-bridgeand does not appear if you only readipConfigurations[0]. Check this table as well.
.34allocated 2026-08-26 tops-ach-web, pinned explicitly with--ip-config … private-ip-address=rather than assigned dynamically, because the DNS request had to name the address before any record existed. One more way to be misled, and it is the mirror image of the.33note below: a clone of this repo that is behind will show an older next-free address. Confirm this page againstoriginbefore trusting it — a stale local copy said.32was free on the day.34was taken.
.33allocated 2026-08-10 tops-bom-coverage. Worth recording why the NIC scan alone would have misled here too: from subscription1f3a4b35…a live scan returned nothing above.31, so.32looked free — but.32ispsi-finance-kv, whose PE sits in theprivatelink.vaultcore.azure.netzone and in a subscription that scan cannot see. This table, not the scan, is what prevented a collision. Same failure mode psi-service hit on 2026-06-29, one row further down.⚠ Two traps, both of which have already cost time:
- A private endpoint can hold more than one ipconfig, so count addresses rather than endpoints. The single
AzureIOT_PEfrontingPSTestHubholds four (.7,.8,.19,.20), andps-iot-bridgeholds two (.27,.28)..8,.19and.20were live but entirely undocumented here until 2026-08-05 — a table that lists one row per service silently under-counts.- The allocation is not webapp-only.
.23is theps-certificates-kvPE, in theprivatelink.vaultcore.azure.netzone and therefore invisible from the azurewebsites records — which is exactly how psi-service was bitten on 2026-06-29. Check live NIC allocations across the whole /24, never just the webapp DNS zone.The
prgjsmes-prod-peat 10.160.140.13 (PS-WEBAPPS) is a duplicate of the production web PE at 10.160.140.11 (PS-RG-01) — flagged for cleanup in psi-azure-admin#1.⚠️ No private endpoint:
psi-zebra-tracking(app),psi-notify-listener,csm-board,ps-shipphotos,psredbookphotos,procisely-redirect. These are either exempt (see webapp-compliance-standard) or pending remediation.psi-zebra-trackingandpsi-datasyncapps additionally still have public access enabled despite policy — see psi-azure-admin#1.
Logic Apps / Integrations
| Name | Resource Group | Purpose | Auth Method |
|---|---|---|---|
| PaylocityInbound | PSLogicApps | HR/payroll inbound data | API connection (unknown) |
| redbook-feedback-notifier | PSLogicApps | Redbook notification emails | API connection (unknown) |
| entra-bc-employee-sync | PSLogicApps | Entra ↔ Business Central employee sync | API connection (unknown) |
| SyncBlogtoSdrive | PsRedbookPhotos | Sync blog content to ShareDrive | API connection (unknown) |
| egnyte-stp-sync | PS-RG-01 | Egnyte file storage sync | API connection (unknown) |
⚠️ Logic App connector credentials are stored in
Microsoft.Web/connectionsresources, NOT in Key Vault. Each workflow’s authentication should be reviewed and migrated to managed identity where supported. As of May 2026 this has not been done.
AI / Cognitive Services
| Name | Resource Group | Kind | SKU | Local Auth | Notes |
|---|---|---|---|---|---|
| PS-SPEECH1 | PS-SPEECH | SpeechServices | F0 | ⚠️ Enabled | Dev/test free tier |
| PS-SPEECH2 | PS-SPEECH | SpeechServices | F0 | ⚠️ Enabled | Dev/test free tier |
| gpt5-9353-resource | rg-GPT5-9353 | AIServices | S0 | ⚠️ Enabled | GPT-5 evaluation |
| adeve-midqp8v8-eastus2 | rg-gpt5-9353 | AIServices | S0 | ⚠️ Enabled | AI Foundry project |
| psi-foundry-dcooper | PS-WEBAPPS | AIServices | S0 | ⚠️ Enabled | Personal AI Foundry (dcooper) — ⚠️ in PS-WEBAPPS RG |
Recommendation: Set disableLocalAuth=true on any production Cognitive Services account to enforce managed identity authentication and disable API key access.
RBAC — Subscription-Level Assignments
| Principal | Type | Role | Notes |
|---|---|---|---|
| ADevereaux@progressivesurface.com | User | Owner | IT lead |
| progadmin@progressivesurface.com | User | Owner | Shared admin |
| wsadmin@progressivesurfaceinc.onmicrosoft.com | User | Owner | Break-glass account (.onmicrosoft.com) |
| worksighted@progressivesurface.com | User | Owner | Worksighted MSP — approved for AVD/CSP mgmt |
| account-web-admin-portal-4v72iquhmxtiu (NMM #1) | Service Principal | Owner | Nerdio Manager — manages PS-RG-01 infra |
| account-web-admin-portal-y7jefihe7cin6 (NMM #2) | Service Principal | Owner | Nerdio Manager — manages SERVER-NCUS-CSP/AVD |
| poperative@progressivesurface.com | User | Contributor | Operations account |
Best practice: ≤3 subscription Owners. PSI currently has 5 Users/SPs with Owner. Recommendation: move
wsadminandworksightedto PIM Just-in-Time access; convert NMM SPs to a custom role scoped to AVD resource groups.
Source Control
| Property | Value |
|---|---|
| Platform | GitHub Enterprise Cloud (EMU) |
| URL | https://progressivesurface.ghe.com/ProgressiveSurface |
| Repositories | 13 |
| Identity Provider | Microsoft Entra ID (SCIM) |
Related Pages
- PSI Web App Compliance Standard — Canonical implementation and compliance baseline
- deploy-to-azure — Deployment architecture, private endpoints, auth, CI/CD
- dns-standards — Canonical DNS rules (public zone, privatelink two-zone rule, slots, Umbrella)
- azure-remediation-2026-04 — April 2026 hardening rollout and validation evidence
- data-brain — Data sources and systems (AFTEC, PDM, Redbook)
- index — Application inventory and technology patterns
- dev-setup — Developer tools setup
Last updated: 2026-06-25 (doc-currency audit — resolved TBD private-endpoint IPs, cataloged ps-buildvsbuy, flagged duplicate prgjsmes-prod-pe)