Skip to content
PagesOutbound connectivity & firewall requirements

EIDGuard docs

On this page

Outbound connectivity & firewall requirements

What an EIDGuard deployment must be able to reach, and what breaks when it cannot. Written for customers who restrict egress with an Azure Firewall, an NVA, a UDR to an on-premises proxy, or NSG rules.

How to read this page. Every destination below is either

  • [code] — traced to a line in this repository, cited as file:line, or
  • [MS] — quoted from Microsoft's own documentation, with the link, or
  • [unverified] — believed to be needed but not provable from either. These are listed deliberately rather than omitted or asserted.

Nothing here is asserted from memory. If a destination you expect to see is missing, it is missing because it could not be verified — see What this page does not tell you.

Related: private-endpoints.md covers which services get private endpoints and why the Hybrid Worker needs internet at all. This page is the exhaustive destination list behind that summary.


1. Three different things need egress#

They are frequently confused, and a rule set written for one will not work for another.

Actor Where it runs What it is
A. The Function App (web tier) The App Service plan in the deployment's resource group Serves the SPA and the /api/* endpoints. Holds no Graph permissions of its own (web/api/requirements.psd1:2-5)
B. The Automation account / Hybrid Worker Azure Automation cloud sandbox, or the Linux worker VM in your VNet Runs every backup, restore, compare, cert rotation, export, deletion-protection raise and the networking deploy. This is where Microsoft Graph is actually called
C. An operator's machine Your workstation Two distinct cases: an admin's browser using the dashboard, and an engineer running the repo-root engine scripts against a test tenant. The engine scripts are not part of a customer deployment (see CLAUDE.md)

All destinations in this document are HTTPS on TCP 443 unless stated otherwise. Azure Automation's requirement of 443 is explicit:

Property Description
Port 443 for outbound internet access
Global URL *.azure-automation.net
Global URL of US Gov Virginia *.azure-automation.us

— Azure Automation network configuration details [MS]

The one probable exception is Ubuntu apt traffic during the worker bootstrap; see §4.3.


2. Quick reference#

A = Function App, B = Automation/worker, C = operator — and C is two different machines: an admin's browser and an engineer's workstation running the repo-root engine scripts. They have different requirements and are listed separately.

This table is derived, not written. Every row is transcribed from the per-actor section named in Detail, which is the source of truth. Three review rounds found the same defect — a summary row drifting to "Always" while the section it summarised stayed correct — so the table is now one row per destination per actor. There is deliberately no cell in which several actors share one condition, because that is the shape the errors took. If you change a body section, retranscribe its rows here; if the two disagree, the body section wins.

Destination Actor Required when Detail Evidence
login.microsoftonline.com A Always — JWKS fetch to validate every bearer token. Keys are cached in-process, which changes the symptom of a block but not the requirement (see §8). Plus the device-code and token endpoints during the setup wizard §3.2, §3.3 web/api/Modules/WebAuth/WebAuth.psm1:62-63; web/api/SetupDeviceCode/run.ps1:21; web/api/SetupComplete/run.ps1:38 [code]
login.microsoftonline.com B Only jobs that authenticate to a customer tenant — backup, restore, cert rotation, snapshot-vs-live compare. Not export, networking, a deletion-protection raise or a drift-mode compare: Connect-AzAccount -Identity is node-local §4.1 modules/ExternalIDBackup.psm1:1276 [code]
login.microsoftonline.com C browser Always — MSAL sign-in and silent renewal (hidden iframe) §5.1 web/app/src/api/auth.ts:77, :139, :157 [code]
login.microsoftonline.com C engine Always — token acquisition §5.2 modules/ExternalIDBackup.psm1:1276, :1310 [code]
graph.microsoft.com A Setup wizard only. The single server-side Graph helper is reachable only from SetupComplete — blocking it breaks initial setup at the web tier, not day-2 §3.3, §3.4 web/api/Modules/WebApi/WebApi.psm1:1823; call sites web/api/SetupComplete/run.ps1:66, :91, :107, :114, :118 [code]
graph.microsoft.com B Backup, restore, cert rotation and snapshot-vs-live compare. Not a snapshot-vs-snapshot (drift) compare, which reads both sides from blob storage §4.1 modules/ExternalIDBackup.psm1:1453; automation/runbook-compare.ps1:280-293 [code]
graph.microsoft.com C browser Tenant onboarding/offboarding and the Users page, browser-direct on the admin's delegated token. Not the setup wizard — that Graph traffic is actor A §5.1 web/app/src/api/graph.ts:10; web/app/src/pages/Users.tsx:57-67; Onboarding.tsx:34-35 [code]
graph.microsoft.com C engine Always — everything the scripts do §5.2 modules/ExternalIDBackup.psm1:1453 [code]
management.azure.com (ARM) A Always — starting/polling Automation jobs, schedules, alert rules, retention, worker status §3.2 web/api/Modules/WebApi/WebApi.psm1:1322; web/api/JobList/run.ps1:9 [code]
management.azure.com (ARM) B Three classes, per runbook. Required: the networking deploy (ARM-driven throughout) and an export whose destination is the configured source account (fails closed by design). Attempted but fails open: the overlapping-job guard on backup and estate-wide compare, and the ordinary export ownership probe. No explicit ARM call at all: restore, cert rotation, targeted snapshot-vs-live compare, drift-mode compare. See "What a blocked ARM endpoint does to the worker" below — including the unresolved question that applies to all of these equally §4.1, §4.2 required: automation/runbook-networking.ps1:199, :262; runbook-export.ps1:341, :347; modules/ExternalIDBackup.psm1:4391, :4550, :4588 (raise). fails open: modules/ExternalIDBackup.psm1:695-696, :790-793; runbook-export.ps1:180-184, :207, :224. no ARM: runbook-restore.ps1, runbook-rotate-cert.ps1 (only Connect-AzAccount, Key Vault and Storage data-plane cmdlets); runbook-compare.ps1:106-109, :338 gate both ARM paths behind $isScheduledVerification [code]
management.azure.com (ARM) C browser Only the ARM-backed features: one-click role grants, VNet discovery, NAT probing, encryption-at-host check/registration. Routine dashboard use goes through /api/* and the Function App calls ARM on the browser's behalf §5.1 web/app/src/api/auth.ts:119 (acquireArmToken), imported only by pages/Configuration.tsx:4 and pages/Networking.tsx:4 [code]
<account>.blob.core.windows.net A Always — config projection, snapshot manifests/reports, health cache, action-lock lease §3.2 modules/ExternalIDBackup.psm1:314; web/api/Modules/WebApi/WebApi.psm1:708, :743 [code]
<account>.blob.core.windows.net B Always — reading config, uploading snapshots and reports §4.1 modules/ExternalIDBackup.psm1:314, :498 [code]
<account>.blob.core.windows.net C engine Only when the config points at cloud storage. A private-networking lockdown removes this from a workstation outside the VNet — intended, not a bug §5.2 modules/ExternalIDBackup.psm1:314 [code]
<hostaccount>.{blob,queue,table}.core.windows.net A Always. Functions host state — three endpoints, not one §3.2 automation/runbook-networking.ps1:477-483; marketplace/bicep/modules/functionapp.bicep:110 [code]
<blobEndpoint>app/<packageBlobName> A Every cold start — WEBSITE_RUN_FROM_PACKAGE; the site downloads its own code §3.2 marketplace/bicep/modules/functionapp.bicep:110; endpoint from marketplace/bicep/modules/storage.bicep:166 [code]
<vault>.vault.azure.net A Tenant onboarding/offboarding, and every uncached health payload when the config carries keyVault.vaultName §3.3 web/api/TenantCerts/run.ps1:36, :40; modules/ExternalIDBackup.psm1:246 [code]
<vault>.vault.azure.net B Whenever Key Vault is in play — i.e. always in a real deployment. Fetching the backup/restore certificate §4.1 modules/ExternalIDBackup.psm1:167 [code]
<vault>.vault.azure.net C engine Only when the config points at Key Vault. Same lockdown caveat as blob storage above §5.2 modules/ExternalIDBackup.psm1:167 [code]
*.azure-automation.net B worker only Only when a Hybrid Worker is deployed. Explicitly not actor A — the web tier reaches Automation through ARM §4.4, §3.4 [MS]; web/api/ contains no Automation data-plane hostname [code, negative]
packages.microsoft.com B worker only Worker bootstrap, once per worker — the apt/pwsh half is guarded by if ! command -v pwsh, so a redeploy against an existing worker skips it §4.3 automation/runbook-networking.ps1:717, :721 [code]
PowerShell Gallery (cdn.powershellgallery.com, cdn.oneget.org) B worker only Every networking deploy, not just the first. Install-Module at :739 sits outside the pwsh guard and the Run Command at :741 is unconditional §4.3 automation/runbook-networking.ps1:739, :741 [code] + [MS] for the hostnames
PowerShell Gallery A Only if the deployed package was not vendored. The marketplace build patches managedDependency off, so a marketplace deployment makes no gallery call at runtime §3.3 modules/WebPackaging.psm1:100-106 [code]
PowerShell Gallery + cdn.oneget.org C engine First-run prerequisite install — Install-Module, Install-PSResource, Install-PackageProvider -Name NuGet. Omitting this fails setup before any engine operation starts §5.2 modules/Prerequisites.psm1:126, :188, :200, :203 [code]
<region>.monitoring.azure.com B Only on the scheduled estate-wide verification run — not an ad-hoc compare, not drift mode §4.2 automation/runbook-compare.ps1:203, :216, gated at :338 [code]
<customer account>.blob.core.windows.net B Only on an on-demand export. A storage account you name, in your subscription — an arbitrary hostname this page cannot enumerate §4.2 automation/runbook-export.ps1:362, :369, :636 [code]
Ubuntu apt repositories B worker only Worker bootstrap, first run only §4.3 [unverified]
activate.vambris.com (the licensingEndpoint template parameter, stamped into every marketplace package) A Once during setup, then every 6 hours. Every marketplace deployment has it: the package build stamps both licensingEndpoint and licensingAudience, so treat this row as always-on. Only a deployment built from the raw template source with both left empty makes no licensing call §7 web/api/Modules/WebApi/WebApi.psm1:818, :933; web/api/LicenceHeartbeat/function.json [code]

What a blocked ARM endpoint does to the worker. This needs stating in two halves, because one is provable from the code and the other is not, and an earlier draft of this page collapsed them into a single confident sentence.

Proven — the guards fail open. Three ARM-calling safety guards swallow their own failures by explicit design. If a job reaches them, a blocked ARM endpoint does not stop it; it silently disables the protection:

Guard ARM call If reached, with ARM blocked
Overlapping-job guard on backup and estate-wide compare Get-AzAutomationJob (modules/ExternalIDBackup.psm1:790-793) Returns $null — "carry on and run". The docstring is explicit: "EVERY failure path returns $null… A guard that cannot see is not a reason to stop taking backups" (:695-696). Two overlapping backups can then share a stamp prefix
Export destination-ownership probe Get-AzResource (automation/runbook-export.ps1:207), Get-AzResourceGroup (:224) Returns $null — the export proceeds. "Advisory throughout: every lookup failure returns $null… a failed probe must never block an export" (:180-184). The check that would refuse a destination doomed at teardown simply does not fire
Export own-resource-group probe Get-AzResource -ResourceGroupName (automation/runbook-export.ps1:235) Catch body is empty by design (:242-243) and the call carries -ErrorAction SilentlyContinue. Returns nothing — the export proceeds. This is the probe that refuses a destination inside the deployment's own resource group, i.e. the group whose deletion is the uninstall

What that third probe costs when it is blind, stated concretely, because it is the sharpest instance of "no symptom, which is the problem" on this page: the customer runs an export precisely to protect their data before deleting the deployment. With ARM blocked the probe cannot see that their chosen destination sits inside the resource group they are about to delete, so it does not refuse. The export reports success. The data lands in the group that gets deleted. Green job, total loss — and nothing anywhere reports an error.

The one export that does fail closed is the one where the destination is the configured source account: -RequireResourceGroupVerification is bound to exactly that condition at :347, and an unverifiable resource group is then a rejection (:228-230, :234-236), with a pre-check throwing earlier at :341.

Unresolved — whether a job gets that far. Every runbook opens with Connect-AzAccount -Identity (automation/runbook-main.ps1:28, runbook-export.ps1:123, and the equivalent first line in each of the others), which is before any guard above. Connect-AzAccount normally populates its context by enumerating subscriptions over ARM — the -SkipContextPopulation switch exists to suppress that, and no runbook passes it. Whether a blocked ARM endpoint therefore fails that call outright, or is tolerated, has not been tested here and is not provable from this repository. [unverified]

So the two possible behaviours are, and this page does not claim to know which:

  • If Connect-AzAccount -Identity tolerates it — jobs run to completion and report green while the guards above are silently off. The failure is invisible in job status.
  • If it does not — every runbook fails immediately at its first line, loudly and identically, and never reaches a guard at all. The fail-open table above is then irrelevant to diagnosis.

What to do is the same either way: allowlist ARM for the worker. The two branches differ only in what a block looks like, not in whether to permit it. Do not read the per-job scoping in the table above as a licence to block ARM — it is there so that, once you know which branch you are in, you can tell which jobs should have been affected.


3. Actor A — the Function App (web tier)#

3.1 Why the web tier's egress is special#

In a public deployment the Function App uses platform outbound and your firewall never sees it. In a private networking deployment the site is integrated with the app subnet and runs with vnetRouteAllEnabled = $true, which forces all of its outbound through your VNet. The reasoning is written into the deployment code:

# App (P-01): the site is integrated with the app subnet and runs with
# vnetRouteAllEnabled = $true below, so ALL of its outbound is forced through
# the VNet. Without an explicit egress path it depends on platform-provided
# SNAT, which is not a property to rest an authentication boundary on — the API
# validates bearer tokens against the tenant JWKS at login.microsoftonline.com,
# and it also reaches ARM and the (deliberately public) Automation control
# plane.

— automation/runbook-networking.ps1:384-391 [code]

So: if you deploy private networking, the app subnet needs a working egress path, or the dashboard stops being able to validate sign-ins. The deployment attaches its own NAT gateway to NAT-less subnets by default; existing customer NAT gateways are reused.

3.2 Required — always#

Destination Purpose Evidence
login.microsoftonline.com JWKS fetch for bearer-token validation: $uri = "https://login.microsoftonline.com/$TenantId/discovery/v2.0/keys". Cached per tenant in-process — Get-ApiSigningKeys returns $script:JwksCache without a network call (:58-59), so the fetch happens on a cold worker, or on -Force when an unknown kid indicates Entra rotated keys (:126-129). Still required; the cache only delays the symptom (§8) web/api/Modules/WebAuth/WebAuth.psm1:58-59, :62-63, :126-129 [code]
management.azure.com (ARM) Starting and polling Automation jobs, reading/writing schedules, alert rules, retention policies, hybrid worker status web/api/Modules/WebApi/WebApi.psm1:1322 (Start-AzAutomationRunbook), :1090, :1432, :1634; web/api/JobList/run.ps1:9, web/api/JobOutput/run.ps1:9,15,32, web/api/SettingsSchedule/run.ps1:22, web/api/SettingsAlerting/run.ps1:31 [code]
<WEBCONFIG_ACCOUNT>.blob.core.windows.net Reading the sanitized config projection, snapshot manifests/reports, the health cache, and the action-lock lease modules/ExternalIDBackup.psm1:314 (the single New-AzStorageContext factory); web/api/Modules/WebApi/WebApi.psm1:708, :743, :766, :857, :873, :1663 [code]
<host account>.blob/.queue/.table.core.windows.net The Azure Functions host's own state. Three endpoints, not one — the private-networking deploy creates all three DNS zones for exactly this reason (# The Functions host uses blob, queue AND table.) automation/runbook-networking.ps1:477-483 [code]
<blobEndpoint>app/<packageBlobName> WEBSITE_RUN_FROM_PACKAGE — the site downloads its own code from blob storage on every cold start marketplace/bicep/modules/functionapp.bicep:110, endpoint from marketplace/bicep/modules/storage.bicep:166 [code]

The ARM hostname is never a literal in the API code — it is resolved by the Az modules from the context established by Connect-AzAccount -Identity (web/api/profile.ps1:4-6). The same is true of the storage and Key Vault FQDNs, which Az composes from the account/vault name plus the cloud's DNS suffix.

3.3 Required — conditionally#

Destination Required when Evidence
<EIDB_KEYVAULT>.vault.azure.net Tenant onboarding/offboarding (issuing and disabling per-tenant certificates), and every uncached health payload if the config carries keyVault.vaultName web/api/TenantCerts/run.ps1:36,40,46,50; web/api/TenantOffboard/run.ps1:42,44; modules/ExternalIDBackup.psm1:246 via web/api/Modules/WebApi/WebApi.psm1:1608 [code]
login.microsoftonline.com device-code + token endpoints Setup wizard only. https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode and .../token, polled until the admin finishes signing in web/api/SetupDeviceCode/run.ps1:21; web/api/SetupComplete/run.ps1:38 [code]
graph.microsoft.com Setup wizard only. The one server-side Graph helper, Uri = "https://graph.microsoft.com/v1.0$Path", is reachable only from SetupComplete web/api/Modules/WebApi/WebApi.psm1:1823; call sites web/api/SetupComplete/run.ps1:66,91,107,114,118 [code]
PowerShell Gallery Only if the deployed package was not vendored. web/api/host.json:8-10 sets managedDependency.enabled = true, which makes the Functions PowerShell worker download web/api/requirements.psd1's modules at cold start — but the marketplace build patches that to false in the shipped zip (Disable-PackagedManagedDependency). A marketplace deployment therefore makes no gallery call at runtime modules/WebPackaging.psm1:100-106 [code]

3.4 Not needed by the web tier#

  • *.azure-automation.net — the web tier talks to Automation through ARM (Get-AzAutomationJob, Start-AzAutomationRunbook, Invoke-AzRestMethod). No Automation data-plane hostname appears anywhere in web/api/. [code]

  • Microsoft Graph, outside the setup wizard — by design:

    # Graph modules are deliberately absent:
    # the web API never talks to Microsoft Graph; anything that needs Graph
    # runs inside Automation runbook jobs.
    

    — web/api/requirements.psd1:2-5 [code]

  • Application Insights ingestion — web/api/host.json:12-18 configures a sampling policy, but no APPLICATIONINSIGHTS_CONNECTION_STRING is set anywhere in the customer template, so the sink has nowhere to send. [code, negative finding]


4. Actor B — the Automation account and the Hybrid Worker#

Two very different execution environments run the same runbooks:

  • Cloud sandbox (the default, and always for the start/stop and encryption-at-host jobs). Microsoft-managed; your firewall rules do not apply to it, and it cannot reach private-only resources.
  • Hybrid Runbook Worker VM (created by the private-networking feature). Runs inside your subnet, so every rule in this section applies to it.

The Automation account itself is deliberately left publicly reachable — publicNetworkAccess: true at marketplace/bicep/modules/automation.bicep:22, with the reason at automation/runbook-networking.ps1:822-823:

# 9. Lockdown, LAST. Automation stays public on purpose: the cloud start/stop
#    jobs need it, and the private endpoint carries worker traffic.

4.1 Required by most runbook runs#

Two destinations in this table are not needed by every runbook, and the distinction matters both for the allowlist and for diagnosing a failure. Every runbook opens with Connect-AzAccount -Identity, but that call takes its token from the node-local managed-identity endpoint — IMDS on the worker VM, IDENTITY_ENDPOINT on the Function App — not from a connection to login.microsoftonline.com. What actually reaches Entra over the network is the app-only certificate auth to the customer tenant: Connect-MgGraph -ClientId … -TenantId … -Certificate $cert (modules/ExternalIDBackup.psm1:1276). So:

  • Reach Entra and Graph: backup (runbook-main.ps1:112), restore (runbook-restore.ps1:57), cert rotation (runbook-rotate-cert.ps1:71), and snapshot-vs-live compare (runbook-compare.ps1:293).
  • Managed identity only — no Entra, no Graph: export (runbook-export.ps1:123 is its only auth call), the networking deploy (runbook-networking.ps1:90 likewise), raising deletion protection (runbook-raise-immutability.ps1:40 likewise), and a drift-mode compare, which takes the $driftMode branch at runbook-compare.ps1:280-284 and never reaches the Connect-BackupGraphFromConfig call in the else.

In practice almost every deployment runs backups, so both destinations belong in the allowlist regardless. The reason to keep the distinction is diagnostic: a blocked login.microsoftonline.com does not explain an export or networking job failing, and those two jobs succeeding does not prove it is reachable.

Destination Purpose Evidence
login.microsoftonline.com App-only certificate auth to the customer tenant. Not Connect-AzAccount -Identity, which is node-local modules/ExternalIDBackup.psm1:1276; reached from automation/runbook-main.ps1:112, runbook-compare.ps1:293, runbook-restore.ps1:57, runbook-rotate-cert.ps1:71 [code]
graph.microsoft.com The actual work. Connect-MgGraph -ClientId … -Certificate $cert then Invoke-MgGraphRequest, paging on @odata.nextLink. Same four runbooks as above modules/ExternalIDBackup.psm1:1276, :1453, :1501; every file in resources/ [code]
management.azure.com (ARM) Only some runbooks call ARM at all. The overlapping-job guard (backup, estate-wide compare) and the ordinary export ownership probe do, but both fail open, so neither makes ARM a hard requirement. The networking runbook is almost entirely ARM and cannot run without it; nor can an export whose destination is the configured source account, which fails closed; nor can raising deletion protection (runbook-raise-immutability.ps1), which reads the policy, its update history, the lifecycle policy and its own job record from ARM and refuses — changing nothing — when any of them cannot be read. Restore, cert rotation, a targeted snapshot-vs-live compare and a drift-mode compare make no explicit ARM call — their only Az calls are Connect-AzAccount, Key Vault and Storage data-plane cmdlets. All of this is subject to the unresolved Connect-AzAccount question in §2 modules/ExternalIDBackup.psm1:695-696, :790-793; automation/runbook-export.ps1:180-184, :207, :224, :341, :347; automation/runbook-networking.ps1:199, :262, :541; automation/runbook-compare.ps1:106-109, :338 [code]
<account>.blob.core.windows.net Reading the config projection, uploading snapshots and reports modules/ExternalIDBackup.psm1:314, :498; automation/runbook-networking.ps1:802 [code]
<vault>.vault.azure.net Fetching the backup/restore certificate — Get-AzKeyVaultSecret -VaultName $VaultName -Name $CertificateName -AsPlainText, with a retry ladder modules/ExternalIDBackup.psm1:167 [code]

Microsoft Graph has no private-link path: it does not appear anywhere in Azure Private Link availability, whose service tables list Automation, Key Vault, Storage, App Service and the rest but contain no Microsoft Graph or Microsoft Entra ID entry. [MS, negative] A worker in a subnet with no egress path therefore cannot back anything up, no matter how many private endpoints exist.

4.2 Required only in specific configurations#

Destination Required when Evidence
https://<region>.monitoring.azure.com/<resourceId>/metrics Only on the scheduled estate-wide verification (not on an ad-hoc compare, not on drift mode). Publishes the ExternalIDBackup/DriftDifferences custom metric. Region comes from the storage account's location, not the Automation account's automation/runbook-compare.ps1:201, :216-217, gated at :338 [code]
https://monitoring.azure.com/ as a token audience Same condition. Get-AzAccessToken -ResourceUrl 'https://monitoring.azure.com/' — this is a token request to Entra, not an HTTP call to that host automation/runbook-compare.ps1:203 [code]
<customer account>.blob.core.windows.net Only on an on-demand export. The destination is a storage account you name, in your subscription — an arbitrary hostname this document cannot enumerate automation/runbook-export.ps1:362, :369, :636 [code]
management.azure.com (ARM), fail closed Only when an Admin raises deletion protection. Raise-ExternalID-Immutability (automation/runbook-raise-immutability.ps1) reads the immutability policy and its update history (Get-AzRmStorageContainer), the lifecycle policy (Invoke-AzRestMethod), and Automation job records (Get-AzAutomationJob), then extends the policy. Unlike the guards in §2 it refuses on any read it cannot make — the job completes with "nothing was changed" — so a blocked ARM endpoint costs the raise, never correctness. It also reads the webconfig blob. On a private deployment it runs on the hybrid worker, and the dashboard refuses to start it while the worker is powered off (the same guard as export) modules/ExternalIDBackup.psm1:4391, :4550, :4588, :4719; automation/runbook-raise-immutability.ps1:40, :52, :74; web/api/ActionRaiseImmutability/run.ps1 (WorkerOffline) [code]

Microsoft's contract for the custom-metrics endpoint, verbatim:

curl -X POST 'https://<location>.monitoring.azure.com/<resourceId>/metrics' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <accessToken>' \
-d @custommetric.json

Metrics must be emitted to the same Azure Monitor regional endpoint as the region where the resource is deployed.

When you request a Microsoft Entra token to emit custom metrics, ensure that the audience or resource that the token is requested for is https://monitoring.azure.com/. Be sure to include the trailing slash.

— Send metrics to the Azure Monitor metric database by using a REST API [MS]

A Linux Hybrid Worker ships with neither PowerShell 7 nor the modules the runbooks need, so the networking runbook delivers a shell script by Invoke-AzVMRunCommand (automation/runbook-networking.ps1:741). Verbatim, the network-touching lines:

apt-get update -y
apt-get install -y wget apt-transport-https software-properties-common
. /etc/os-release
wget -q "https://packages.microsoft.com/config/ubuntu/${VERSION_ID}/packages-microsoft-prod.deb" -O /tmp/pmp.deb
dpkg -i /tmp/pmp.deb
apt-get update -y
apt-get install -y powershell

— automation/runbook-networking.ps1:718-724 [code]

Install-Module -Name $_.Key -RequiredVersion $_.Value -Scope AllUsers -Force -AllowClobber -Repository PSGallery

— automation/runbook-networking.ps1:739 [code], pinning Az.Accounts 5.5.1, Az.KeyVault 6.5.1, Az.Storage 9.4.0, Az.Automation 1.11.1, Microsoft.Graph.Authentication 2.25.0.

The image is pinned to Ubuntu 22.04 (Canonical / 0001-com-ubuntu-server-jammy / 22_04-lts-gen2, automation/runbook-networking.ps1:632), so ${VERSION_ID} resolves to 22.04.

So this phase needs:

Destination Status
packages.microsoft.com (HTTPS) [code] — the literal is at :721
packages.microsoft.com as an apt repository [unverified in this repo] — the .deb installs an apt source list, and apt-get install -y powershell then fetches from it. The behaviour is standard but the hostname is not written in our code
Ubuntu archive repositories [unverified] — apt-get update uses whatever /etc/apt/sources.list the marketplace image ships. Azure-hosted Ubuntu images conventionally point at azure.archive.ubuntu.com and security.ubuntu.com, over HTTP (port 80) by default, but neither hostname nor port is written anywhere in this repository. Verify against your own image before writing a rule
PowerShell Gallery Hostnames from Microsoft, below

Microsoft's current list, verbatim:

Required network endpoints#

The Install-* and Update-* cmdlets require internet access to connect to the network endpoints used by the PowerShell Gallery.

Ensure that your network access policies allow you to connect to TCP port 443 of the following endpoints.

Hosts required for package discovery and download:

  • cdn.oneget.org
  • cdn.powershellgallery.com

Hosts required when using the PowerShell Gallery website:

  • *.powershellgallery.com - website
  • go.microsoft.com and aka.ms - redirection services

Note

These endpoints have changed. The old endpoints that ended with azureedge.net are no longer supported.

The PowerShell Gallery requires Transport Layer Security (TLS) 1.2 or higher.

— Troubleshooting cmdlets [MS]

The bootstrap Run Command is unconditional on every Mode='deploy' run (automation/runbook-networking.ps1:741 sits outside the "VM does not exist" block). The apt/pwsh half is guarded by if ! command -v pwsh, so a re-run only re-contacts the gallery.

There is a second, latent gallery path: Initialize-AzModuleSet calls Install-Module when a module is missing (modules/ExternalIDBackup.psm1:97-99, :110), and it is reached on every Get-BackupStorageContext and every Key Vault read. On a correctly bootstrapped worker or in the sandbox the guard is false and nothing is fetched — but if a module ever goes missing, a runbook will try to reach the gallery mid-job. It uses the default repository and is not version-pinned.

4.4 Automation service (JRDS / agentsvc)#

The worker registers with the extension Microsoft.Azure.Automation.HybridWorker / HybridWorkerForLinux, configured with the account's own hybrid-service URL, read off ARM rather than composed:

$autoUrl = (Get-AzResource -ResourceId $autoId -ExpandProperties).Properties.automationHybridServiceUrl

— automation/runbook-networking.ps1:688, used at :700-703 [code]

Our code never parses or logs that URL, so its shape comes from Microsoft:

A JRDS endpoint is used by the hybrid worker to start/stop runbooks, download the runbooks to the worker, and to send the job log stream back to the Automation service. After enabling JRDS endpoint, the URL would look like this: https://<automationaccountID>.jrds.<region>.privatelink.azure-automation.net.

The agent service endpoint looks like: https://<automationAccountId>.agentsvc.<region>.azure-automation.net.

The URL for public & private endpoint would be the same, however, it would be mapped to a private IP address when Private link is enabled.

— Use Azure Private Link to securely connect networks to Azure Automation [MS]

If you need to allow-list by name rather than by wildcard, Microsoft publishes the per-region records:

| Region | DNS record | Location Code | | West Europe | https://<accountId>.webhook.we.azure-automation.net https://<accountId>.agentsvc.we.azure-automation.net https://<accountId>.jrds.we.azure-automation.net | we |

Replace <accountId> in the DNS record with GUID representing your Automation Account ID

— Azure Datacenter DNS records used by Azure Automation [MS] (the page lists every region; West Europe shown as the pattern)

Microsoft also names the service tags:

When you create network group security rules or configure Azure Firewall to allow traffic to the Automation service and the Log Analytics workspace, use the service tags GuestAndHybridManagement and AzureMonitor.

— Azure Automation network configuration details [MS]

In EIDGuard's private-networking topology this traffic normally does not leave the VNet. The deploy creates privatelink.azure-automation.net and two Automation private endpoints, DSCAndHybridWorker and Webhook (automation/runbook-networking.ps1:477, :530-531). Allow-list the public names anyway if your firewall filters by FQDN before DNS resolution, and expect registration to be slow the first time — the runbook retries at 0 / 300 / 600 / 900 seconds precisely because "JRDS can take a while to honor a newly approved private link" (automation/runbook-networking.ps1:674-676, :689).

The Azure VM Agent also downloads the extension package itself. That destination is a Microsoft platform detail with no literal in this repository — [unverified].

4.5 What private endpoints do and do not cover#

Created by the deploy (automation/runbook-networking.ps1:477-485, :519-533):

Private DNS zone Covers Condition
privatelink.vaultcore.azure.net Key Vault always
privatelink.blob.core.windows.net data storage (and host storage when supplied) always
privatelink.azure-automation.net Automation DSCAndHybridWorker + Webhook always
privatelink.queue.core.windows.net, privatelink.table.core.windows.net Functions host storage only when a host storage account is passed
privatelink.azurewebsites.net the dashboard itself only with IncludeWebAppPrivateEndpoint = yes

Everything else in this document still needs internet. In particular login.microsoftonline.com, graph.microsoft.com, management.azure.com, packages.microsoft.com and the PowerShell Gallery have no private path here.

Note also this Microsoft limitation, which is why the worker exists at all:

In the current implementation of Private Link, Automation account cloud jobs cannot access Azure resources that are secured using private endpoint. For example, Azure Key Vault, Azure SQL, Azure Storage account, etc. To workaround this, use a Hybrid Runbook Worker instead.

— Use Azure Private Link ... Azure Automation [MS]


5. Actor C — operator machines#

5.1 The admin's browser#

The SPA calls three external origins directly with the signed-in admin's own delegated token. This is not incidental — it is pinned by the shipped Content-Security-Policy, and a test asserts the list:

"connect-src": [
  "'self'",
  "https://login.microsoftonline.com",
  "https://graph.microsoft.com",
  "https://management.azure.com"
],
"frame-src": [
  "https://login.microsoftonline.com"
]

— web/api/csp.json:20-28, asserted by tests/SecurityHeaders.Tests.ps1:89-95 ("permits exactly the three origins the SPA calls") [code]

Origin Used for Evidence
login.microsoftonline.com MSAL sign-in, silent token renewal (which uses a hidden iframe, hence frame-src) web/app/src/api/auth.ts:77, :139, :157 [code]
graph.microsoft.com Tenant onboarding/offboarding and the Users page, browser-direct with the admin's delegated token web/app/src/api/graph.ts:10; scopes at web/app/src/pages/Onboarding.tsx:34-35, Users.tsx:15 [code]
management.azure.com The one-click role grants (Network Contributor, export destination), VNet discovery, the encryption-at-host feature check and registration web/app/src/lib/armGrant.ts:23, :149, :232; web/app/src/pages/Networking.tsx:88; web/app/src/lib/encryptionAtHost.ts:65, :69 [code]

Plus the dashboard's own hostname (https://<site>.azurewebsites.net, or your private address if the web private endpoint is enabled), and — during the setup wizard only — the device-login page Entra returns as verification_uri, opened in a new tab (web/app/src/pages/SetupWizard.tsx:126; the value is not hardcoded, it comes from web/api/SetupDeviceCode/run.ps1:33).

Fonts are bundled at build time, not loaded from a CDN (web/app/src/main.tsx:12-16). There is no analytics or telemetry origin.

5.2 An engineer running the repo-root engine scripts#

Backup-ExternalID.ps1, Restore-ExternalID.ps1 and Compare-ExternalIDBackup.ps1 are the engine kept for local testing against a test tenant. They are not shipped and never run in a customer deployment (CLAUDE.md), but they are the one place a human machine talks to these services directly.

Destination Purpose Evidence
login.microsoftonline.com Token acquisition modules/ExternalIDBackup.psm1:1276, :1310 [code]
graph.microsoft.com Everything the scripts do modules/ExternalIDBackup.psm1:1453 [code]
<vault>.vault.azure.net, <account>.blob.core.windows.net Only when the config points at Key Vault / cloud storage modules/ExternalIDBackup.psm1:167, :314 [code]
PowerShell Gallery + cdn.oneget.org First-run prerequisite install: Install-Module, Install-PSResource, Install-PackageProvider -Name NuGet modules/Prerequisites.psm1:126, :188, :200, :203 [code]

After a private-networking lockdown, a workstation outside the VNet loses Key Vault and Storage — that is the intended result, not a bug. See the ordering caveat in private-endpoints.md.


6. Sovereign and national clouds#

EIDGuard's endpoint handling is inconsistent across clouds, and this is worth knowing before you plan a Gov or China deployment.

Derived correctly (cloud-aware):

  • Storage, Key Vault and ARM FQDNs are composed by the Az modules from the connected environment's DNS suffixes — there is no blob.core.windows.net or vault.azure.net literal in the product code. The single storage-context factory is modules/ExternalIDBackup.psm1:314. [code]

  • The run-from-package URL is read off the account, with the reason stated in the template:

    // Read off the account rather than composed from the name: sovereign and
    // national clouds do not use blob.core.windows.net, and WEBSITE_RUN_FROM_PACKAGE
    // is built from this.
    

    — marketplace/bicep/modules/hoststorage.bicep:91-94 [code]. The primaryEndpoints.blob read that produces the value is in the sibling module, marketplace/bicep/modules/storage.bicep:166, consumed at marketplace/bicep/modules/functionapp.bicep:110.

  • The Easy Auth issuer: openIdIssuer: '${environment().authentication.loginEndpoint}${webAuthTenantId}/v2.0' — marketplace/bicep/modules/functionapp.bicep:169 [code]

Hardcoded to the global cloud (these would need changing for a sovereign deployment):

Literal File:line
https://login.microsoftonline.com/$TenantId/discovery/v2.0/keys web/api/Modules/WebAuth/WebAuth.psm1:62
https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode web/api/SetupDeviceCode/run.ps1:21
https://login.microsoftonline.com/organizations/oauth2/v2.0/token web/api/SetupComplete/run.ps1:38
https://graph.microsoft.com/v1.0$Path web/api/Modules/WebApi/WebApi.psm1:1823
the three CSP connect-src origins web/api/csp.json:22-24
https://www.powershellgallery.com/api/v2/package/Microsoft.Graph.Authentication/ marketplace/bicep/modules/automation.bicep:33

No Connect-MgGraph call in this repository passes -Environment, so Graph resolves to the module default. [code]

For reference, Microsoft's endpoints per cloud:

| National cloud | Azure portal endpoint | Microsoft Entra ID endpoint | | Azure global service | https://portal.azure.com | https://login.microsoftonline.com | | Azure US Government | https://portal.azure.us | https://login.microsoftonline.us | | Azure China operated by 21Vianet | https://portal.azure.cn | https://login.chinacloudapi.cn |

| National Cloud | Microsoft Graph | | Microsoft Graph global service | https://graph.microsoft.com | | Microsoft Graph for US Government L4 (GCC High) | https://graph.microsoft.us | | Microsoft Graph for US Government L5 (DOD) | https://dod-graph.microsoft.us | | Microsoft Graph China operated by 21Vianet | https://microsoftgraph.chinacloudapi.cn |

— Microsoft Graph national cloud deployments [MS]

And for Automation:

Private Link support with Azure Automation is available only in Azure Commercial and Azure US Government clouds.

— Use Azure Private Link ... Azure Automation [MS]

One product-level caveat is already recorded elsewhere and belongs here too: authenticationEventsFlows (user flows) is global-cloud only and requires the EnableMsGraphAuthenticationEventListener feature flag.

Bottom line: a sovereign-cloud deployment is not validated. Treat the hardcoded table above as a to-do list, not a configuration option.


7. The publisher-hosted licensing endpoint#

This section previously said the endpoint was not implemented. It is, and it is the only egress destination on this page whose failure mode is delayed and silent for a month before it costs the customer anything.

What calls it, and when#

Actor A — the Function App, always. Never the worker: under private networking the hybrid worker is deallocated ~22 hours a day, so a runbook heartbeat would be indistinguishable from a deleted deployment
Hostname activate.vambris.com (HTTPS, 443). Every marketplace package is stamped with it as the licensingEndpoint template default (marketplace/scripts/build-package.ps1, $LicensingDefaults) — a parameter rather than a literal in the template only because certification policy 300.4.5 forbids hard-coded URIs. Confirm the deployed value on the Function App: app setting EIDB_LICENSING_ENDPOINT (Settings → Environment variables), and allow exactly that host
Calls POST <endpoint>/api/register once during setup, and again on any six-hourly tick where no verified licence is stored (WebApi.psm1:818, web/api/LicenceHeartbeat/run.ps1:64) · POST <endpoint>/api/heartbeat every 6 hours once one is (WebApi.psm1:933, schedule 0 0 */6 * * *)
Auth A managed-identity token for licensingAudience. The token is minted at the worker-local identity endpoint, so this adds no Entra egress beyond login.microsoftonline.com, which actor A already needs
Payload Hostname and build stamp. The tenant and the deployment's principal are claims in the token, not fields the deployment asserts. No tenant ids, no object counts, no customer data

A marketplace deployment always has it configured#

Both licensingEndpoint and licensingAudience must be present for any licensing call to happen; with either missing the deployment is completely inert: no registration, no heartbeat, no banner, no gate. Every marketplace package stamps both (marketplace/scripts/build-package.ps1, which refuses to build a package without them), so a deployment from the marketplace never runs in that state. It is reachable only by deploying the raw template source with both parameters left empty, which is a development path, not a customer one. A firewall that sees no traffic to activate.vambris.com from a marketplace deployment is therefore observing a problem (the host is blocked, or the principal is missing, below), not a deployment that was never configured.

The one failure a firewall cannot fix#

If the licensing application has no service principal in the tenant the deployment runs in, Entra refuses to mint the token at all (AADSTS500011): no request ever leaves, and the deployment walks the same 30/60-day ladder below. The Setup Wizard provisions that principal itself (reusing one that already exists). If that step failed, or the principal was deleted later, the dashboard banner says so and carries the approval link, the onboarding refusal names consent rather than networking, and the backup runbook's stop message does the same. Do not spend that outage on the firewall.

What a block costs, and when#

Nothing for a week, then progressively. Get-LicenceStandingState (modules/ExternalIDBackup.psm1:723-725) measures days since the last verified exchange:

Days blocked Consequence
0–7 Nothing. Silent retry every 6 hours
7–30 Dashboard banner, and an alert email if alerting is configured
30–60 Onboarding a new tenant is refused. Existing tenants keep backing up
60+ Scheduled backups stop.

Restore, compare and export are never gated, at any point on this ladder — a customer breached on day 70 must not meet a licensing error.

Recovery is immediate and needs no re-onboarding: the next successful call resets the clock with no penalty period.

Why this one is easy to get wrong#

Three properties combine badly for a firewall reviewer:

  • CreateNatGateway='no' plus a default-deny egress policy blocks it entirely, and nothing fails at deployment time.
  • The failure is swallowed by design — a background tick must not surface as a failed invocation, so the only immediate evidence is a warning in the function's logs.
  • The customer-visible symptom arrives weeks later and reads as a licensing problem, not a networking one.

A deployment given a licensing endpoint it cannot reach therefore stays silent for a week, warns from day 7, refuses new onboarding from day 30, and stops scheduled backups at day 60. If you are locking down egress, this destination is not optional.


8. Troubleshooting — what each blocked destination looks like#

Blocked egress rarely produces a message naming the firewall. These are the observable symptoms.

Blocked Symptom
login.microsoftonline.com from the Function App, during setup Immediate and total. The setup wizard is not token-validated — it is gated by the one-time setup key (web/api/SetupDeviceCode/run.ps1:8) and posts straight to Entra: the device-code endpoint at :21, then the token endpoint at web/api/SetupComplete/run.ps1:38. No cache is involved, so on an unconfigured deployment the wizard fails at the first step and setup cannot start at all. This is the one Entra symptom with no delay
login.microsoftonline.com from the Function App, during day-2 Delayed and intermittent, not immediate — signing keys are cached per tenant in-process, and Get-ApiSigningKeys returns that cache without touching the network (web/api/Modules/WebAuth/WebAuth.psm1:58-59). A worker that has already validated one token keeps serving /api/* fine. Failures start on a cold worker (empty cache) or when Entra rotates keys and the unknown kid forces the refresh at :126-129. Then the JWKS fetch at :62-63 hits its 15-second timeout and the call 401s. Consequence for testing: a firewall change can look clean for hours and break at the next cold start or key rotation — restart the app to test it honestly. Sign-in itself still appears to succeed; the browser reaches Entra fine, it is the server that cannot validate
login.microsoftonline.com from the browser Sign-in never completes. Silent renewal fails first and looks like a random logout mid-session — CSP frame-src exists specifically for that iframe (web/api/csp.json:41)
login.microsoftonline.com from the worker Not a failure at Connect-AzAccount -Identity — that token is node-local and still succeeds. Jobs get past the Azure connection, past Key Vault, and fail at the Graph connection instead, with an authentication error rather than a network one. Backup, restore, cert rotation and snapshot-vs-live compare fail this way; export, the networking deploy and a drift-mode compare are unaffected and still pass, which is the tell that distinguishes this from a broader egress block
graph.microsoft.com Backups, restores, cert rotation and snapshot-vs-live compares fail after connecting to Azure successfully — the job gets far enough to log tenant setup, then dies on the first resource. A drift-mode compare (CompareToStamp) still succeeds: it never calls Graph. The web tier is unaffected (it never calls Graph outside setup), so the dashboard looks healthy while the tenant-facing jobs fail. Private endpoints will not fix this: Graph has no private link
management.azure.com from the Function App The Jobs page is empty or errors; Start buttons fail; the Health card degrades. Nothing can be started, and already-running jobs become invisible
management.azure.com from the browser Setup and day-2 mostly work, but the one-click role grants fail (Network Contributor, export destination), VNet discovery on the Networking page returns nothing, and the encryption-at-host check reports "state could not be determined"
management.azure.com from the worker Two possible shapes, and which one you see is not settled — check §2 before diagnosing. Either every runbook fails immediately and identically at its opening Connect-AzAccount -Identity, in which case the symptom is loud and obvious; or that call is unaffected and jobs run to completion reporting green, in which case the symptom is nothing at all — the overlapping-job guard and the export ownership probe fail open, so two backups can overlap and share a stamp prefix, and an export into storage that dies at teardown is no longer refused. The networking deploy fails outright in both shapes (it is ARM-driven), as does an export whose destination is the configured source account (refused by design at automation/runbook-export.ps1:341). If jobs are green, that is consistent with the second shape and is not evidence ARM is reachable — do not test this one with job status
<account>.blob.core.windows.net from the Function App Nearly everything 500s: the config projection, snapshot lists, health cache and the action-lock lease all live in blob storage. This is the highest-blast-radius block on the web tier
Functions host storage (blob/queue/table) The site does not start at all — no error page from the app, because the app never runs. Missing the queue and table endpoints (allowing only blob) produces the same outcome and is a classic mistake; the deploy creates all three DNS zones for this reason (automation/runbook-networking.ps1:478-483)
WEBSITE_RUN_FROM_PACKAGE blob The site serves the previous package, or fails to start after a restart or upgrade. Looks like "the upgrade didn't apply"
<vault>.vault.azure.net from the worker Jobs fail while fetching the certificate. modules/ExternalIDBackup.psm1:165-178 retries 4 times, so expect a slow failure with repeated Key Vault messages before the job gives up
<vault>.vault.azure.net from the Function App Tenant onboarding cannot issue a certificate and stalls at the cert step; the health card's certificate-expiry field goes unknown. Backups keep working — the runbooks fetch certificates themselves
*.azure-automation.net (worker) The worker registration fails during the networking deploy, after the VM, subnets, NAT and private endpoints already exist. The runbook retries at 0/5/10/15 minutes and then throws Hybrid worker registration failed after 4 attempts (automation/runbook-networking.ps1:709). If it breaks later, jobs sit in Queued forever and never reach Running
packages.microsoft.com The networking deploy completes and reports success, but the worker has no PowerShell 7. The next scheduled backup is the thing that fails — a delayed symptom that looks unrelated to the deploy
PowerShell Gallery (worker bootstrap) Same shape: deploy succeeds, pwsh exists, but a runbook fails with a module-not-found error on Az.Accounts, Az.KeyVault, Az.Storage, Az.Automation or Microsoft.Graph.Authentication. This can bite on a redeploy, not only on first provisioning — the gallery install is unguarded, so a rule that opened the gallery once for the initial build and closed it again fails here while packages.microsoft.com stays quiet (its half is guarded). A networking redeploy that succeeds on a worker with an intact module set is not evidence the gallery is reachable
PowerShell Gallery (deployment time) marketplace/bicep/modules/automation.bicep:33 imports Microsoft.Graph.Authentication into the Automation account from the gallery. This is fetched by the Automation service, not from your network — a customer-side block does not affect it
<region>.monitoring.azure.com Backups and compares still succeed. The scheduled estate-wide verification job fails at the very end only when it found differences — the publish error is caught at automation/runbook-compare.ps1:344-345 and raised at :426 only if ($problems.Count -gt 0). A clean verification with this endpoint blocked completes successfully, logging just a Could not publish the drift metric warning. That is the dangerous case: the 0 that auto-resolves the stateful drift alert is never published, so a previously firing drift alert stays firing after the drift is fixed, and job status shows nothing wrong. Do not rely on job outcome to detect this block — grep job output for the warning, or alert on the DriftDifferences metric going stale
Ubuntu apt repositories The bootstrap fails at apt-get update, before PowerShell is installed. Same delayed symptom as packages.microsoft.com
Customer export destination account Only the on-demand export job fails. It is a storage account in your own subscription, so it is easy to forget it is in scope
Publisher licensing endpoint Nothing, for a week. The six-hourly call warns into the Function App logs (licence heartbeat failed, or Heartbeat could not reach the licensing service) and is otherwise swallowed — a background tick must not surface as a failed invocation. Then the ladder starts: a dashboard banner at 7 days, new tenant onboarding refused at 30, scheduled backups stopped at 60. Restore, compare and export keep working throughout, which is the tell: a deployment where only onboarding and the schedule are affected, with a licensing banner, is an egress problem and not a licensing one. A deployment with no licensingEndpoint parameter makes no call here at all and none of this applies

Two general rules.

  1. A healthy dashboard proves almost nothing about the worker. They are different execution environments with different egress paths. The dashboard never calls Graph; the worker calls it constantly.
  2. Bootstrap failures surface a day later. packages.microsoft.com and the PowerShell Gallery are contacted during the networking deploy, but the failure shows up at the next scheduled backup. If the first scheduled backup after a networking deploy fails, suspect the bootstrap. Note the two are not on the same schedule: packages.microsoft.com is contacted once per worker (guarded by if ! command -v pwsh), while the gallery is contacted on every deploy — so a redeploy can reintroduce this failure on a worker that was previously fine.

Useful first checks, from the worker or any host in/peered to the VNet:

# Both should resolve to a private address INSIDE your private-endpoint subnet
# — not to a public one. Do not expect 10.x specifically: that is only the
# dedicated-VNet default (10.60.1.0/24, automation/runbook-networking.ps1:323).
# In existing-VNet mode the range is whatever you passed as PeSubnetPrefix
# (:33), which is legitimately 172.16/12, 192.168/16 or any other RFC1918 space.
nslookup <your-vault>.vault.azure.net
nslookup <your-storage>.blob.core.windows.net
curl -sS -o /dev/null -w '%{http_code}\n' https://graph.microsoft.com/v1.0/$metadata
curl -sS -o /dev/null -w '%{http_code}\n' https://login.microsoftonline.com/common/discovery/v2.0/keys

What this page does not tell you#

Listed honestly rather than guessed at. Each needs verification before it goes into a firewall rule.

  • The Azure VM Agent's extension-download endpoint. The Hybrid Worker extension package is fetched by the platform agent; no literal exists in this repository, and the destination is not documented here.
  • The Ubuntu apt repository hostnames and ports used by the worker bootstrap. They come from the marketplace image's sources.list, not from our code. Ubuntu's defaults are HTTP (port 80), which matters if you allow 443 only.
  • The managed-identity token audiences. Connect-AzAccount -Identity (web/api/profile.ps1:6) requests resources chosen by Az.Accounts, not by this repository. The local identity endpoint itself is node-local and not a firewall concern.
  • The Application Insights ingestion endpoint. web/api/host.json:12-18 enables the sink but no connection string is set in the customer template, so no destination is derivable — and, as far as the code shows, nothing is sent.
  • IP ranges. This page lists FQDNs only. If you must filter by IP, use Microsoft's service tags (GuestAndHybridManagement, AzureMonitor, AzureActiveDirectory, Storage, AzureKeyVault, AzureResourceManager) or the published Azure IP Ranges and Service Tags JSON. This repository asserts no IP ranges.
  • Whether an FQDN-filtering firewall (Azure Firewall application rules, an explicit proxy) is sufficient for all of the above. Not tested here.
  • Sovereign-cloud operation — see §6.
  • The publisher licensing HOSTNAME. The call is implemented and documented in §7, but the hostname arrives as a deployment parameter and this page still cannot name it.