Repository navigation
Azure Hosting Cutover
Reference page for the Azure Front Door edge, repo digitalspace/eagle-edge. Both estates serve the
eagle-public Angular bundle from Front Door and hold a separate origin group per app: Front Door
talks to eagle-api and DEMI directly. Both estates also serve the eagle-admin console from
storage. rproxy is not in the path for app traffic on either estate. See Front Door Per-App Split for the origin groups,
routes and patterns, which are the same on both estates.
Test also hosts DEMI's frontend and admin endpoints on the same profile.
Not the same thing as the search backend move; see Eagle Search for that.
Prod:
browser → Front Door (eagle-edge-prod) → route eagle-api → eagle-api-6cdc9e-prod.apps.silver.devops.gov.bc.ca
→ route admin → storage static website (eagle-admin console, `$web/admin/`)
→ route demi/demi-cached, usage, analytics → demi-apim-prod.azure-api.net
→ route default → storage static website (eagle-public bundle)
www.projects.eao.gov.bc.ca resolves the same way.
Test:
browser → Front Door (eagle-edge-test) → route eagle-api → eagle-api-6cdc9e-test.apps.silver.devops.gov.bc.ca
→ route admin → storage static website (eagle-admin console, `$web/admin/`)
→ route demi/demi-cached, usage, analytics → demi-apim-test.azure-api.net
→ route default → storage static website (eagle-public bundle)
Neither eagle-test.apps.silver.devops.gov.bc.ca nor eagle-prod.apps.silver.devops.gov.bc.ca —
the OpenShift routes that fronted app traffic through rproxy — are in this path. See
Front Door per-app split.
One Bicep template, azure/main.bicep, deploys a differently-shaped profile per estate.
| Test | Prod | |
|---|---|---|
| Resource group | c4b0a8-test-rg |
rg-eagle-public-prod |
| Subscription | 7897ceb1-… |
be5924ac-1083-4a1b-be92-7b444882cfd9 |
| Front Door profile | eagle-edge-test |
eagle-edge-prod |
| eagle-public endpoint | eagle-public-test-dbg8ghh8gjd0bscx.a02.azurefd.net |
eagle-public-prod-aafug4ahavgzbvh9.a01.azurefd.net |
Storage ($web) |
eaglepubtestvymaysch2agd |
eaglepubprodrhj3zycszo6j (.z9.web.core.windows.net) |
eagle-admin storage ($web/admin/) |
eagleadmtestvymaysch2agd |
eagleadmprodrhj3zycszo6j |
| Also on this profile | DEMI frontend + DEMI admin endpoints (own storage, own CSP) | nothing else |
| Backend origin | per-app: eagle-api-6cdc9e-test..., the eagle-admin storage static website, demi-apim-test.azure-api.net — see Front Door Per-App Split
|
per-app: eagle-api-6cdc9e-prod..., the eagle-admin storage static website, demi-apim-prod.azure-api.net — see Front Door Per-App Split
|
| Custom domains |
test.projects.eao.gov.bc.ca, www.test.projects.eao.gov.bc.ca
|
projects.eao.gov.bc.ca, www.projects.eao.gov.bc.ca
|
| WAF / security policy |
eagleedgetestwaf / sp-test
|
eagleedgeprodwaf / sp-prod
|
| Shared identity, Log Analytics, App Insights |
eagle-search-identity-test, eagle-search-logs-test, eagle-search-insights-test (misnamed, shared with demi-api and eagle-search-api — never rename) |
none (another subscription); prod's own Log Analytics/App Insights pair belongs to the availability test |
Endpoint hostnames are <name>-<hash>.<zone>.azurefd.net with both the hash and the zone assigned
by Azure at deploy time — this profile came back a02/a01, not a z01 you might expect from
older docs. Read them from the edgeEndpointHostNames deployment output or epic_azure_status,
never compose one. A wrong zone code still resolves through that zone's wildcard and returns 404 for
every page even though the deployment reports success.
Both estates declare their public hostnames as Front Door custom domains with a managed TLS
certificate (TLS 1.2 minimum). Each stays state Pending, and serves no traffic, until its
_dnsauth TXT record exists in Province DNS and the hostname's own DNS record points at the
endpoint — declaring a domain moves nothing on its own. Public DNS for both hostnames resolves to
the Front Door endpoint directly.
A WAF policy (Standard_AzureFrontDoor SKU, custom rules only, no managed rule set) attaches to the
custom domains, not to the *.azurefd.net endpoint, so rate limiting sees the browser's own address
rather than a single upstream proxy. Three rate-limit rules, keyed on socket address, 1-minute
window, Prevention mode:
| Rule | Path prefix | Threshold / min |
|---|---|---|
search |
/demi-search/ |
600 |
analytics |
/analytics |
1200 |
general |
/api/ |
1800 |
A rule set change reaches Front Door's edge PoPs after the ARM deployment reports Succeeded, up to
~3 minutes later (measured 2026-08-15 at 75s and 2m36s on two separate deploys). A single check
right after a deploy proves nothing; poll. Until the rules land, a deep link falls through to
storage's 404 document — same bytes, 404 status:
curl -o /dev/null -w '%{http_code}' https://<endpoint>/projects/deadbeef # want 200Purge fixes neither this nor the App Service CORS window described in the eagle-edge README — both are config lag, not cached content.
Workflow Deploy eagle-public to Azure production (LIVE SITE) (deploy-azure-prod.yaml on
develop), workflow_dispatch with a version tag verified on test:
gh workflow run "Deploy eagle-public to Azure production (LIVE SITE)" --repo bcgov/eagle-public -f version=v2.7.28It builds from the tag, uploads to $web, purges the edge, then smoke-tests. Environment
azure-prod gates the run on a reviewer. Concurrency group deploy-azure-prod,
cancel-in-progress: false.
Staging is Deploy eagle-public to Azure staging (deploy-azure-staging.yaml), same input, target
c4b0a8-test-rg endpoint eagle-public-test-dbg8ghh8gjd0bscx.a02.azurefd.net.
Headers as served:
| File | Cache-Control |
|---|---|
index.html, env.js
|
no-cache, no-store, must-revalidate |
*-[hash].js, *.css
|
public, max-age=31536000, immutable |
assets/* |
no-cache (not content-hashed; angular.json sets outputHashing: 'bundles') |
The workflow asserts Cache-Control by reading the blob back. A manual
az storage blob upload-batch must pass --content-cache-control or it silently strips the
header; the command exits 0 on precondition failures.
A stuck run: with cancel-in-progress: false, a run left in waiting (environment approval never
given) blocks every later run in the group. Later runs sit in pending with
pending_deployments empty. Cancel the stale waiting run. pending = queued behind concurrency;
waiting = approval gate.
gh api "repos/bcgov/eagle-public/actions/workflows/335990413/runs?per_page=20" --jq '.workflow_runs[] | "\(.id) \(.status)"'Security headers come from the Front Door securityheaders rule set, not from the pod nginx. The
AFD CSP adds base-uri 'self'; frame-ancestors 'none' and X-Frame-Options: DENY match the pod
policy.
A merge to main deploys test. Prod deploys only from a v* tag:
gh workflow run deploy.yaml --ref vX.Y.Z -R digitalspace/eagle-edge. Read
./scripts/deploy-infra.sh prod (what-if) locally first. See Deployment Pipeline.
An incremental deploy never deletes a route or rule set that a param change stops declaring — it
keeps holding the patterns the new routes claim. Delete it by hand first with
az afd route delete / az afd rule delete, then deploy.
- An ABAC condition on the
c4b0a8management group Owner assignment deniesroleAssignments/writefor Owner, Contributor, User Access Administrator, RBAC Administrator and two custom roles. Nobody in the subscription can grant Contributor to the CI identity.roleDefinitions/writeis not denied; CI identityeagle-edge-cicd-prodholds custom roleeagle-edge-deployer-prod, created byscripts/bootstrap-cicd.sh prodineagle-edge. - What-if always reports one
ModifyonoriginGroups/og-eagle-public/origins/storage:hostNameandoriginHostHeader"change" to an unevaluatedreference()expression. What-if cannot resolve runtimereference(). The real value does not change.
SPA fallback is the AFD spafallback rule, keyed on UrlFileExtension LessThanOrEqual 0. An
Angular route containing a dot returns 404 with a page that still renders. Status-only checks and
crawlers see the 404.
eagle-public-availability-prod GETs https://projects.eao.gov.bc.ca/ every 300 s from five North
American locations and fails when the body lacks <app-root>. It targets the public hostname, not
the AFD endpoint, and checks content because a 200 status does not prove the app rendered. It runs
no JavaScript, so it proves the shell is served, not that the app boots.
Alerting is off: availabilityAlertEmail is empty (main.prod.bicepparam reads
AVAILABILITY_ALERT_EMAIL from the deploying shell), so no action group and no metric alert exist.
Results are recorded and nobody is notified. To turn it on, export AVAILABILITY_ALERT_EMAIL and
redeploy the edge. Restore any hand-edited test setting by redeploying the template, not by PATCH.
publicNetworkAccessForIngestion on the Insights component must stay Enabled. The probe fleet
writes over the public internet and there is no AMPLS; Disabled fails silently.
-
curl -sI https://projects.eao.gov.bc.ca/→ 200 withx-azure-ref, not a 301. -
index.html→no-store; a hashed asset →immutable; AFDx-cache: TCP_HITon hashed assets. -
/api/config→ 200 from eagle-api. - A deep link → 200; a dotted route → 404 (expected).
-
www.projects.eao.gov.bc.ca→ same as apex.
Both estates: see Front Door Per-App Split § rproxy retirement for scaling rproxy back up and reverting the edge routes.
Prod: see also Rollback Procedures § eao-nginx. For eagle-admin on test and prod, see Rollback Procedures § eagle-admin on test and prod.
Test: oc scale deploy rproxy -n 6cdc9e-test --replicas=2 restores rproxy; see
Front Door Per-App Split for reverting the edge routes.
- Front Door Per-App Split
- Azure Migration Strategy
- Reverse Proxy
- Deployment Pipeline
-
Configuration Management —
/api/config, including the dev/test access gate this edge does not enforce
Eagle Documentation
Operations
- Deployment Pipeline
- Helm Charts
- Reverse Proxy
- Rollback Procedures
- Eagle Public Release Lines
- Eagle Search Archive
- Azure Migration Strategy
- Azure Hosting
- Front Door Per-App Split
- Front Door Ban Ladder
- Error Logging
- Secrets Management
Architecture
- Architecture Overview
- API Architecture
- Analytics Architecture
- Angular Patterns
- Eagle Public Page Layout
- Eagle Public Adding A Page
- Extended Project Pages
- ENGAGE–EAGLE Integration
- BCGW Project Export
- Eagle Search
- Document Content Search
- Eagle DEMI
- Eagle DEMI Push Sweep
- Eagle DEMI Re-Push and Backfill
- Eagle Notify
- Project Data Models
- Async Jobs API
- Bulk Document Download
Configuration
Help
Repositories
Environments