Azure Security & Monitoring

Security configuration, network security group rules, monitoring setup, and audit findings for PSI Azure infrastructure.


Security Audit Summary

Last audited: 2026-06-24 (full-scope automated compliance scan — all resource types)

App Service Security

App ServiceHTTPS OnlyPublic AccessAuth MethodPrivate Endpoint
bom-explorer-webYesDisabledApp-layer authbom-explorer-web-pe
psi-portalYesDisabledApp-layer authpsi-portal-pe
erp-migration-api (DMT)YesDisabledApp-layer auth (EasyAuth disabled intentionally)ERPMigrationAPIEndpoint
ps-project-explorerYesDisabledApp-layer authps-project-explorer-pe
ps-progressive-viewYesDisabledEasyAuth + app-layerps-progressive-view-pe
redbook-webYesDisabledApp-layer authredbook-web-pe
ps-redbook-dashboardYesDisabledEasyAuth + app-layerps-redbook-dashboard-pe
prgjsmes-prodYesDisabledApp-layer authprgjsmes-prod-pe
psredbookphotosYesEnabledNone (intentional)None
ps-shipphotosYesEnabledNone (intentional)None
psi-zebra-trackingYesEnabled ⚠️App-layer authNone — app has no PE (only the SQL has psi-zebra-tracking-sql-pe)
psi-datasyncYesEnabled ⚠️App-layer authpsi-datasync-pe (10.160.0.19) — PE exists but public access was never disabled
csm-boardYesDisabledApp-layer auth (MSAL PKCE) + tenant CA “Require MFA”None — public-exception (ADR-0028)
ps-winget-sourceYesDisabledApp-layer authps-winget-source-pe
ps-argo-analyticsYesDisabledApp-layer authps-argo-analytics-pe
procisely-redirectYesDisabledApp-layer auth (redirect only)None
ps-dispatchYesDisabledApp-layer authps-dispatch-pe (SCM: 10.160.140.22)
psi-serviceYesDisabledApp-layer auth (MSAL redirect)psi-service-pe (10.160.140.24)
psi-notify-listenerYesDisabledApp-layer auth (function app)TBD
ps-buildvsbuy 🚧No 🚧Enabled 🚧None yet — in active developmentps-buildvsbuy-pe (10.160.140.5)

🚧 ps-buildvsbuy is a brand-new app under active development (created June 2026, after the 2026-06-24 audit). It is not a compliance violation — it just hasn’t been hardened yet. Pre-launch checklist before it goes live: enable httpsOnly, attach an approved auth profile, and disable public network access (its PE already exists at 10.160.140.5 — add DC + Azure privatelink DNS records first, see dns-standards). Listed here so the hardening isn’t forgotten at launch.

Note: psredbookphotos and ps-shipphotos are intentionally public — they serve photo assets that need broad accessibility.

SQL Server Security

PropertyValue
Serverprocserv-proddata
Public AccessEnabled (firewall rules for PSI office IPs)
Private EndpointPS-ProdData-SQL-Private (10.160.140.4)
TLS1.2 minimum
Entra AdminADevereaux@progressivesurface.com

Note: Public access is enabled with IP-restricted firewall rules for management access from PSI office IPs. Internal app traffic routes through the private endpoint.

EasyAuth Configuration

Apps currently using Azure App Service EasyAuth:

  • ps-progressive-view
  • ps-redbook-dashboard

Baseline:

  • Provider: Azure Active Directory only
  • Unused social providers: Disabled (Apple, Facebook, GitHub, Google, Twitter — removed 2026-02-23)
  • Session: 8-hour fixed cookie expiration
  • Nonce validation: Enabled (5-minute expiration)
  • HTTPS required: Yes

Exception noted:

  • erp-migration-api (DMT) runs with platform auth disabled and app-layer auth, because anonymous root/health access is required for current operation and remote MCP health checks.
  • csm-board — public-exposure exception (ADR-0028, csm-board#28).
    • Rule excepted: “Internal web apps must use private endpoints and have public access disabled.”
    • App / env: csm-board (board.progressivesurface.com), prod, PS-WEBAPPS, shared asp-erp-migration-tool B3 Basic plan.
    • Business reason: the board must be reachable externally by remote developers and their federated csm agent, which authenticates headlessly via MSAL device-code over an outbound WebSocket — a path that cannot satisfy edge pre-auth (App Proxy / Easy Auth) and is broken by a private-only posture. Private Endpoints are also unavailable on the B3 Basic shared plan. Identity is gated at the app layer and by the tenant-wide “Require MFA” Conditional Access policy (verified 2026-06-24 to cover csm-board, agent device-code intact). Edge exposure is reduced by Front Door + WAF + origin-lock (ADR-0028 Phase 2, provisioning operator-gated).
    • Owner: adevereaux@progressivesurface.com.
    • Next review: when the board is reclassified internal-only or the plan moves to Standard+ (then revisit Private Endpoint), else review with the next compliance sweep.

Network Security Groups

All NSGs are in the PS-RG-01 resource group. Most use only Azure default rules — custom rules are called out below.

Default Rules (All NSGs)

Every NSG includes these 6 Azure default rules:

NamePriorityDirectionAccessSourceDestinationPort
AllowVnetInBound65000InboundAllowVirtualNetworkVirtualNetwork*
AllowAzureLoadBalancerInBound65001InboundAllowAzureLoadBalancer**
DenyAllInBound65500InboundDeny***
AllowVnetOutBound65000OutboundAllowVirtualNetworkVirtualNetwork*
AllowInternetOutBound65001OutboundAllow*Internet*
DenyAllOutBound65500OutboundDeny***

NSGs with Default Rules Only (No Custom Rules)

These NSGs have no additional custom rules beyond the Azure defaults:

NSGAssociated VM
basicNsgPS-AZ-APPS-0-nic2PS-AZ-APPS-0
basicNsgps-az-optix204PS-AZ-OPTIX (NIC)
PS-AZ-DC01-nsgPS-AZ-DC01 (Domain Controller)
PS-AZ-LS3-nsgPS-AZ-LS3 (App Proxy Connector)
PS-AZ-OPTIX-nsgPS-AZ-OPTIX

NSGs with Custom Rules

PS-AZ-MESHCENTRAL-nsg (Deleted 2026-02-23)

This NSG was orphaned (not attached to any NIC or subnet) and had SSH/HTTP/HTTPS open to any source IP. Deleted during security remediation.

PS-AZ-OPTIX2-nsg (Deleted 2026-02-23)

This NSG was associated with PS-AZ-OPTIX2 which was decommissioned. Had RDP open to any source IP. VM, disk, NIC, and NSG all deleted during security remediation.

PS-AZ-SFTP1-nsg

RulePriorityDirectionAccessProtocolSourceDest Port
RDP300InboundAllowTCP65.23.94.210/323389
AllowAnyCustom1822Inbound301InboundAllowTCP*1822

SFTP1 follows better practice — RDP is locked to a specific IP. Port 1822 is the custom SFTP port.


Monitoring & Alerting

Application Insights

App Insights ResourceResource GroupConnected To
ps-intunedatacollection-fa-aiPS-RG-01Intune data collection function
psredbookphotosPS-RG-01(not connected to webapp)
psredbookphotos202411071353PsRedbookPhotospsredbookphotos webapp
ps-shippingphotosPS-WEBAPPS(not connected to ps-shipphotos webapp)
mlworkspace19587593495PS-RG-01ML workspace

Webapp Monitoring Status

WebappApplication InsightsStatus
psi-explorer-webpsi-webapps-insightsTelemetry active
psi-portalpsi-webapps-insightsTelemetry active
erp-migration-apipsi-webapps-insightsTelemetry active
ps-project-explorerpsi-webapps-insightsTelemetry active
ps-progressive-viewpsi-webapps-insightsTelemetry active
redbook-webpsi-webapps-insightsTelemetry active
ps-redbook-dashboardpsi-webapps-insightsTelemetry active
prgjsmes-prodpsi-webapps-insightsTelemetry active
psredbookphotospsredbookphotos202411071353Telemetry active
ps-shipphotosps-shippingphotosTelemetry active

All 10 webapps now have Application Insights configured. The shared resource psi-webapps-insights (PS-WEBAPPS) covers 8 apps; the two photo services use their own dedicated resources.

Alert Rules

AlertScopeSeverityFrequency
Available Memory BytesPS-AZ-SFTP135 min
Percentage CPUPS-AZ-SFTP135 min
OS Disk IOPS ConsumedPS-AZ-SFTP135 min
Network In TotalPS-AZ-SFTP135 min
Data Disk IOPS ConsumedPS-AZ-SFTP135 min
Network Out TotalPS-AZ-SFTP135 min

Gap: Only PS-AZ-SFTP1 has metric alerts. No alerts exist for any webapps, the SQL server, or other VMs. No activity log alerts are configured.


Key Vault Inventory

Key VaultResource GroupLocationPurposeStatus
ps-certificates-kvPS-RG-01North Central USPrimary — wildcard SSL cert + app secretsActive
migratekv1295344273PS-RG-01North Central USAzure MigrateDeleted 2026-02-23
GRtoAzure6174kvPS-RG-01West US 2Azure Migrate (GR migration)Deleted 2026-02-23
ps-intunekvPS-RG-01North Central USIntune integration (RBAC-locked)Needs review
akv-34-4v72iquhmxtiu3PS-RG-01North Central USAuto-generated (access policy locked)Needs review
akv-19-y7jefihe7cin65SERVER-NCUS-CSPNorth Central USCSP-managed (server infrastructure)Needs review
mlworkspace10747543858PS-RG-01North Central USML workspace (access policy locked)Needs review

ps-certificates-kv is the primary vault for app deployments. Two Azure Migrate vaults were deleted (migration complete). The remaining 4 require elevated access to investigate.


Remediation History

DateActionDetails
2026-06-25Wiki doc-currency audit — live-vs-doc reconciliationReconciled deploy-to-azure / azure-security / azure-resources against live Azure. Found: psi-datasync + psi-zebra-tracking apps still publicNetworkAccess=Enabled (docs said Disabled); psi-datasync PE exists (10.160.0.19) but public access never flipped; duplicate prgjsmes-prod-pe (10.160.140.13 PS-WEBAPPS vs 10.160.140.11 PS-RG-01); new in-development app ps-buildvsbuy (+PE .5). Resolved all TBD PE IPs in azure-resources; created dns-standards canonical page. No live Azure changes made — remediation tracked in psi-azure-admin#1.
2026-06-24Full compliance audit + new resource cataloging + TLS AutoFixAudit: 0 Critical, 17 High, 53 Medium, 15 Low across 17 web apps, 4 function apps, 2 SQL servers (11 DBs), 6 KVs, 1 ACR, 1 Service Bus, 5 Cognitive Services, 20 Storage. New resources found: ps-dispatch (.NET 8 App Service, PS-WEBAPPS) and alerts SQL DB (GP_S Gen5, procserv-proddata) — both absent from June 1 audit. AutoFix: TLS 1.2 enforced on 4 remaining TLS 1.0 storage accounts (stgpsavddev06c387541, stgpsavdpool095e48998, stgpsazapps0a4b13855, stgpsazsrvc0b9175975). Manual validation confirmed 67/67 resources match raw az CLI pull — zero drift.
2026-06-02ps-progressive-view MI + stray artifact cleanup; ps-winget-source stale connection string fixps-winget-source: Removed stale App Service connection string (WinGetDb/SQLAzure, psdbadmin/empty password) that was overriding the KV ref app setting — root cause of Login failed for user 'psdbadmin' crash. HTTP 200 restored. ps-progressive-view: Enabled system-assigned MI (93657eb7), granted get Access Policy on ps-certificates-kv, deleted stray value app setting (artifact from prior wrong JSON array format). MICROSOFT_PROVIDER_AUTHENTICATION_SECRET KV ref now resolving. HTTP 200 confirmed via PE. ps-certificates-kv: Removed stray IP rule 209.124.58.162/32 (admin machine, no longer needed).
2026-06-02KV DC DNS zone + full privatelink DNS sync auditCreated privatelink.vaultcore.azure.net primary zone on PS-AZ-DC01 (Forest-replicated), A record ps-certificates-kv → 10.160.140.23. Removed WEBSITE_DNS_SERVER=168.63.129.16 workaround — KV refs resolve via DC. Full DC/Azure sync audit: added ps-progressive-view/.scm (10.160.140.15) and psi-zebra-tracking-sql (10.160.140.21) to DC; added ps-dispatch/.scm (10.160.140.22) and shippingappfunctions/.scm (10.160.0.16) to Azure privatelink.azurewebsites.net zone. All 5 privatelink zones now in sync.
2026-06-01ps-winget-source KV PE + Access Policy; full incident remediationCreated privatelink.vaultcore.azure.net Private DNS zone (PS-RG-01), linked to PS-VNMAIN, PE ps-certificates-kv-pe at 10.160.140.23 (PS-ProdData). Added Access Policy (get secrets) for ps-winget-source MI (ebe7de0c). Set WEBSITE_DNS_SERVER=168.63.129.16 to bypass PS-AZ-DC01 for private DNS zone resolution. Both KV refs now Resolved. See audits/2026-06-01/incident-kv-winget-remediation.md.
2026-06-01Secrets migration, SQL PE, and full SQL size/cost assessmentMigrated 5 plain-text secrets to ps-certificates-kv: ps-shipphotos (AZURE_STORAGE_ACCOUNT_KEY), ps-progressive-view (MICROSOFT_PROVIDER_AUTHENTICATION_SECRET), ps-winget-source (BlobStorage__ConnectionString, ConnectionStrings__WinGetDb), psi-notify-listener (DEPLOYMENT_STORAGE_CONNECTION_STRING). Created PE psi-zebra-tracking-sql-pe (10.160.140.21), disabled public access on psi-zebra-tracking-sql. Full SQL assessment: DataSync has 12.2 GB data on 10 DTU (underpowered), PRGJSMES autopause disabled (~$760/mo), 9/10 DBs missing LTR. Full report at audits/2026-06-01/sql-assessment.md.
2026-06-01Full subscription extended audit + 10 auto-remediationsExtended audit script to cover SQL, KV, ACR, Service Bus, Cognitive Services, Storage. Auto-fixed: purge protection + network Deny enabled on 5 KVs (ps-intunekv, akv-34, akv-19, mlworkspace10747543858, ps-certificates-kv); TLS 1.2 enforced on 5 storage accounts (psiwingetpkgs, stgpsavddev06c387541, stgpsavdpool095e48998, stgpsazapps0a4b13855, stgpsazsrvc0b9175975). All apps smoke-tested; HTTP 200 on all custom domains confirmed post-change.
2026-05-27Extended resource enumeration — 57 additional resources catalogedFull subscription sweep beyond App Services: discovered 2 SQL servers, 10 databases, 5 Logic Apps, 5 Cognitive Services, Service Bus, 6 KVs, ACR, Static Web App, 12 VMs, 2 NMM Automation Accounts. Auto-fixed: Service Bus TLS 1.0→1.2; Container Registry admin user disabled; psizebratrackingkv purge protection + network Deny. All new resources documented in wiki.
2026-05-27Disabled SCM/FTP basic publishing auth on 4 newly discovered appspsi-zebra-tracking, psi-tap-bridge, psi-notify-listener, ps-intunedatacollection — all had basic auth enabled; disabled via automated compliance scan.
2026-05-27Fixed psi-notify-listener HTTPS-only = falseFunction app was missing httpsOnly=true; corrected via az functionapp update --set httpsOnly=true.
2026-05-27Re-migrated ps-project-explorer secrets to Key VaultDB_PASSWORD and GRAPH_CLIENT_SECRET had regressed to plain text since April 2026 audit; re-migrated to ps-certificates-kv references. Root cause TBD (likely a workflow deploy overwriting app settings).
2026-05-27Full subscription compliance scan — new apps onboardedFirst automated scan covering all 16 web apps + 4 function apps. New apps discovered: psi-zebra-tracking, psi-datasync, psi-notify-listener, procisely-redirect. Persistent psi-azure-admin agent project created at C:\GIT\psi-azure-admin for weekly scheduled audits.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2026-04-02Migrated sensitive app settings to Key Vault referencesApplied across target production apps; standardized on ps-certificates-kv naming pattern app--setting.
2026-04-02Disabled SCM/FTP basic publishing auth on production app targetsSet basicPublishingCredentialsPolicies/scm=false and /ftp=false for hardened apps to reduce credential attack surface.
2026-04-02Real deploy validation runs executedRan one-by-one workflow_dispatch deploy validations and endpoint probes for production app set; all target runs successful after workflow fixes.
2026-04-02Fixed ERP deploy health gate for EasyAuth-aware checksUpdated ERP deploy workflow to treat 200/401/403 as healthy/reachable instead of requiring only 200.
2026-04-02Resolved DMT HTTP 401 incidentCorrected erp-migration-api authsettingsV2 (requireAuthentication=false, AllowAnonymous, identity providers disabled) restoring dmt.progressivesurface.com to HTTP 200 on / and /api/health.
2026-04-02Cross-site auth regression scanVerified no DMT-like regression on other custom domains; quality.progressivesurface.com remains intentionally auth-enforced (401) under EasyAuth.
2026-02-23Enabled HTTPS-only on ps-progressive-viewWas the only webapp allowing HTTP
2026-02-23Disabled unused social providersRemoved Apple, Facebook, GitHub, Google, Twitter, legacyMicrosoftAccount from ps-progressive-view and ps-redbook-dashboard EasyAuth
2026-02-23Decommissioned PS-AZ-OPTIX2Deleted VM, OS disk, NIC, and NSG (had RDP open to internet)
2026-02-23Deleted orphaned MeshCentral NSGNSG had SSH/HTTP/HTTPS open to internet, was not attached to anything
2026-02-23Deleted Azure Migrate Key VaultsRemoved migratekv1295344273 and GRtoAzure6174kv (migration complete)
2026-02-23Enabled Application Insights on all webappsCreated psi-webapps-insights (shared), wired ps-shipphotos to existing resource. All 10 apps now have telemetry. Note: removed ApplicationInsightsAgent__EXTENSION_VERSION=~3 from Linux/Node.js apps (causes startup issues — agent is .NET only). Kept on prgjsmes-prod (Windows/.NET).
2026-02-23Fixed api.progressivesurface.com DNSReplaced CNAME (→ ps-proxy.ad.ptihome.com) with A record (→ 192.9.201.217). CNAME was leaking to hostile wildcard on external ptihome.com domain (PSI no longer owns ptihome.com).
2026-02-23Initial security auditFull tenant audit of all apps, NSGs, monitoring, auth

Recommendations

Completed

  • Restrict RDP/SSH access on MeshCentral and OPTIX2 NSGs — Deleted (orphaned NSG + decommissioned VM)
  • Enable Application Insights on production webapps — All 10 webapps now have telemetry
  • Wire ps-shipphotos to its existing Application Insights resource — Done
  • Delete Azure Migrate Key Vaults — Done (migratekv1295344273, GRtoAzure6174kv)
  • Review 4 remaining Key Vaults — Done 2026-06-01: all 5 remaining KVs (ps-intunekv, akv-34, akv-19, mlworkspace, ps-certificates-kv) now have purge protection enabled and network default=Deny+AzureServices bypass
  • Container Registry admin user enabled — Disabled 2026-05-27
  • Service Bus TLS 1.0 — Updated to 1.2 on 2026-05-27

Remaining — High Priority

NEW (2026-06-25) — public-access drift, tracked in psi-azure-admin#1. psi-datasync and psi-zebra-tracking web apps have publicNetworkAccess=Enabled despite the private-endpoint policy — psi-datasync’s PE already exists (10.160.0.19) but public access was never flipped; psi-zebra-tracking has no app PE at all. Disable public access once DC + Azure privatelink DNS records are confirmed (see dns-standards). Also remove the duplicate prgjsmes-prod-pe (10.160.140.13 in PS-WEBAPPS, redundant with 10.160.140.11 in PS-RG-01). ps-buildvsbuy is new and still in development — harden before launch, not a violation yet.

  1. Add alert rules for webapp HTTP 5xx errors and response time thresholds
  2. Convert auth exceptions (app-layer-only apps) to approved profile matrix with owner + expiry
  3. Migrate plain-text secrets on remaining apps to Key Vault — Done 2026-06-01: ps-shipphotos (AZURE_STORAGE_ACCOUNT_KEY), ps-progressive-view (MICROSOFT_PROVIDER_AUTHENTICATION_SECRET), ps-winget-source (BlobStorage__ConnectionString, ConnectionStrings__WinGetDb) all migrated to ps-certificates-kv. Exception: psi-notify-listener DEPLOYMENT_STORAGE_CONNECTION_STRING is a Flex Consumption platform setting resolved before KV auth — kept plain text (see CLAUDE.md exceptions)
  4. Enable managed identity on apps missing it: ps-argo-analytics, ps-progressive-view, ps-shipphotos. ps-winget-source MI enabled 2026-06-01. ps-progressive-view MI enabled 2026-06-02 (KV ref now resolving). ps-argo-analytics and ps-shipphotos have no KV refs — MI not required.
  5. Enable App Insights on: ps-argo-analytics, csm-board, ps-winget-source, psi-tap-bridge, psi-datasync
  6. psi-zebra-tracking-sql: Disable public access; provision private endpoint — Done 2026-06-01: PE at 10.160.140.21, public access disabled
  7. SQL database LTR policies: 9/10 databases have no LTR — PSI_Analytics (8.4 GB) and DataSync (12.2 GB) are highest priority
  8. DataSync on 10 DTU with 12.2 GB data — severe underprovisioning risk; scale to Standard S2 (50 DTU) or S3 (100 DTU)
  9. PRGJSMES autopause disabled — billing ~$760/month continuously; re-enable autopause if workload allows (currently set to -1/never)
  10. Investigate ps-project-explorer secret regressionDB_PASSWORD and GRAPH_CLIENT_SECRET reverted to plain text between April and May audits; re-migrated May 2026. Root cause: deploy workflow likely overwrites app settings. Add KV reference preservation check to deploy workflow.

Remaining — Medium Priority

  • Enable HTTPS-only on 4 AVD storage accounts (stgpsavddev06c387541, stgpsavdpool095e48998, stgpsazapps0a4b13855, stgpsazsrvc0b9175975) — TLS 1.2 enforced; HTTPS-only still off (AVD compatibility testing needed)
  • Disable local auth on production Cognitive Services (gpt5-9353-resource, psi-foundry-dcooper) — enforce managed identity auth
  • Logic App connector audit — review authentication on 5 Logic App connectors (PaylocityInbound, entra-bc-employee-sync, egnyte-stp-sync, etc.)
  • Add activity log alerts for security-sensitive operations (role assignments, NSG changes, KV access)
  • Document Conditional Access policies in Entra ID
  • Complete upstream secret rotation for integration credentials now that KV references are in place
  • RBAC cleanup — reduce subscription Owner count; move Worksighted to PIM JIT access

Remaining — Low Priority

  • Create disaster recovery runbook for SQL server and critical apps
  • Review all public IPs (9 total) to ensure none are unnecessarily exposed
  • Upgrade Container Registry from Basic to Standard (geo-replication, vulnerability scanning)


Last updated: 2026-06-25 (doc-currency audit — live-vs-doc reconciliation; see Remediation History)