Skip to content

Azure IPAM v4.0.0 Release #377

Open
DCMattyG wants to merge 200 commits into
mainfrom
ipam-3.7.0
Open

Azure IPAM v4.0.0 Release #377
DCMattyG wants to merge 200 commits into
mainfrom
ipam-3.7.0

Conversation

@DCMattyG

@DCMattyG DCMattyG commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Azure IPAM v4.0.0

This is a major release that delivers significant framework upgrades, a data grid migration, authentication modernization, comprehensive documentation overhaul, revamped examples, and numerous bug fixes.


Major Framework & Dependency Upgrades

  • React 18 → 19: Full migration including removal of forwardRef wrappers (ref-as-prop), removal of PropTypes, explicit null for useRef() calls, and migration of LoadingButton to Button
  • MSAL v4 → v5 (@azure/msal-browser 4.x → 5.x, @azure/msal-react 3.x → 5.x): Removed obsolete config, consolidated event types, fixed silent token timeout recovery (timed_out error code), and prevented iframe fallback timeout loops
  • Inovua React Data Grid → AG Grid (ag-grid-community / ag-grid-react 36.x): Complete migration to AG Grid including centralized DataGrid component, custom styling, column state persistence, unified data loading overlays, and custom cell renderers (drill-down, info, progress)
  • Vite 7 → 8 (vite 7.x → 8.x, @vitejs/plugin-react 5.x → 6.x): Migrated to Vite 8 which replaces Rollup with Rolldown and esbuild with Oxc for bundling, transforms, and minification. Removed vite-plugin-eslint2 (redundant with editor-based linting and incompatible with Vite 8)
  • ESLint 10: Upgraded from ESLint 9 to 10 with modern React linting plugins (@eslint-react/eslint-plugin v5, eslint-plugin-react-hooks v7 — which now consolidates the React Compiler lint rules per the React Compiler 1.0 release), replacing eslint-plugin-react. Added dist/ ignore, fixed no-useless-assignment violations, and removed unused eslint-plugin-jest. Resolved all remaining lint warnings as part of the React 19 modernization — useContextuse, <Context.Provider><Context>, ref naming conventions, stable list keys, hoisted static styled components, migration of SnackbarUtils to notistack's standalone enqueueSnackbar, and moved "ref assigned during render" patterns into useEffect
  • MUI v7 → v9 (@mui/material 7.3.x → 9.0.x, @mui/icons-material 7.3.x → 9.0.x): Major two-version jump. Migrated deprecated component props to the unified slots/slotProps API (largely via @mui/codemod), moved deprecated system props into sx, replaced the removed Unstable_Grid2 with the new default Grid (using size={{ xs: N }}), renamed removed Outline (no "d") icon exports to their Outlined counterparts, and removed @mui/lab (no longer needed — LoadingButton's loading prop is now native to Button)
  • React Router 7 → 8 (react-router 7.x → 8.x): Major version upgrade. The UI uses declarative-mode routing (BrowserRouter / Routes / Route) with all imports already sourced from react-router, so no application code changes were required. v8 raises the minimum runtime to Node.js 22.22.0 (and React 19.2.7+)
  • Updated NPM packages across the board (Vite 7.3.x, React Router 7.13.x, MUI 7.3.x, etc.)

Engine & Backend

  • Azure Function Blueprints: Implemented Blueprint-based function naming for improved clarity
  • Python dependency cleanup: Removed msal, azure-common, azure-keyvault-secrets, and six; added azure-mgmt-resource-subscriptions to address Azure SDK module separation
  • Python linting: Added pyproject.toml with Ruff linter configuration (pycodestyle, pyflakes, isort). Resolved all lint violations across 18 engine files — bare except clauses, wildcard imports replaced with explicit names, unused imports/variables, invalid escape sequences, import sorting and grouping
  • Reservation logic hardening: Fixed CIDR overlap detection during auto-fulfillment, added validation for all in-block vNet prefix overlap checks, and made auto-fulfillment idempotent by deduping existing block vNet associations
  • Endpoint fix: Standalone NICs are now included in the Endpoint list (fixes Standalone NICs not listed in Endpoint node #371)
  • Network associations fix: Resolved improper handling of missing vNETs and vHUBs (fixes Error fetching available IP Block networks #350)

UI & UX Improvements

  • Drill-down navigation in Discover (fixes Navigation Improvements #183): Added hierarchical drill-down with multi-filter pass-through and hidden column auto-reveal when filters are active
  • Centralized authentication handling: New AuthHandler for MSAL error handling, centralized token acquisition via tokenService
  • Custom DraggablePaper component: Replaced react-draggable package with a purpose-built component
  • Associations UX: Shifted API work to Redux thunks, optimized data refresh, fixed infinite update loops by separating initial grid selection from user selection state
  • Planner data loading: Addressed issue where Planner would not load when data was incomplete (fixes Planner will not load #366)
  • Reservation UX: Fixed view reversion after cancelling a Reservation, fixed column sorting, and streamlined the interface
  • Unified grid experience: Centralized DataGrid and ConfigureGrid components with shared filter utilities, consistent loading overlays, and AG Grid custom styling (brightness filter for row hover)
  • Search bar: Fixed messaging when no resources are found in Azure IPAM scope

Notifications & Service Management

  • New notification framework (GET /api/notifications, POST /api/notifications/{id}/resolve): A self-describing, API-first advisory system. Server-side detectors emit notifications that the UI — or any API / IaC consumer — can read, while remediation is resolve-by-reference: the client asks the backend to resolve a notification by id and the backend owns all the logic (admin-gated, with an active-notification guard). Adding a new advisory is a single detector module registered in one list
  • Registry-migration detector: Flags deployments still pulling container images from the legacy public registry (azureipam.azurecr.io, critical) or the development registry (azureipamdev.azurecr.io, warning) and offers a one-click remediation that repoints the App Service / Function LinuxFxVersion to registry.azureipam.com. The target image is validated as anonymously pullable before any change is applied, then the app restarts to pull it — complementing the update script's auto-migration with an in-app path
  • Update-available detector: Compares the running IPAM_VERSION against the latest published GitHub release and surfaces an informational notice linking to the update guide. Fails safe (no notification, no error) on network or rate-limit failures
  • Notification center (UI): AppBar bell with a severity-colored unread badge, a severity-ordered list (popover on desktop, full-screen sheet on mobile), a detail dialog with server-driven actions and early admin gating, per-item dismiss, mark-all-read, and a manual refresh that resyncs to the server's truth
  • Service restart gate (UI): A full-screen overlay shown while the backend restarts (e.g. after a registry switch). The live SPA stays in memory, polls /api/status, and confirms recovery via a changed service start time before reloading — with calm, time-keyed messaging and a deliberate manual-reload escape hatch if recovery runs long. Background polling is paused while the gate is up

Deployment, Build & Infrastructure

  • Public container registry migrated: The publicly hosted Azure IPAM container images have moved from azureipam.azurecr.io to the new registry.azureipam.com endpoint. The deployment and migration Bicep templates now reference the new registry by default, while the update script continues to recognize the legacy azureipam.azurecr.io endpoint so existing deployments keep working. The Docker Compose migration tooling intentionally still targets the legacy endpoint, as it only ever processes pre-existing legacy deployments
  • Migration robustness: The Docker Compose migrate script now halts on a non-standard or unresolvable container registry with guidance to re-run using the -JsonFile override, instead of silently falling back to the public registry. A new -ContainerType (Debian|RHEL) override handles cases where the source app is stopped/unreachable and its distro can't be auto-detected
  • RHEL images updated to UBI9: Including the container-image overrides in the deploy, update, and migrate PowerShell scripts
  • Node.js minimum raised to 22.22.0: Required by React Router v8; enforced in the build.ps1 version gate and the UI package.json engines field
  • Dockerfiles optimized: Improved layering, removed unneeded steps across all container images. Resolved Hadolint lint violations (ADDCOPY, JSON notation for CMD/ENTRYPOINT, pipefail for piped RUN commands). Added centralized .hadolint.yaml for rule suppressions
  • KeyVault Soft Delete added to deployment and migration Bicep templates (fixes Enable soft delete for Key Vault #373)
  • Build flexibility: Added support for building with either current or latest NPM/Python packages
  • CI/CD path exclusions: Added exclusions to avoid unnecessary test/build runs for documentation-only changes
  • GitHub Actions modernized: Bumped all workflow actions to their latest versions (checkout@v6, setup-node@v6, setup-python@v6, github-script@v9, create-github-app-token@v3, azure/login@v3, hadolint-action@v3.3.0) to address the Node.js 20 runner deprecation
  • Azure PowerShell SDK v14 compatibility: Fixed Get-AzAccessToken breaking changes (fixes Breaking changes to Get-AzAccessToken #343); updated deploy & migrate scripts
  • OCI version labels on container images: All production images now carry standard OCI labels (org.opencontainers.image.version, .title, .source), stamped at build time via a new IPAM_VERSION build arg. This lets you determine the exact version behind a floating tag like latest by inspecting the registry — no pull or run required (e.g. docker buildx imagetools inspect or az acr manifest show). Covers all deb, rhel, and func variants across the root, engine, ui, and lb images, with the build workflow passing --build-arg IPAM_VERSION to every az acr build. Labels begin with the v4.0.0 release images
  • Release tag parsing hardened: Version extraction now strips only a leading v (^v) from the release tag, preserving suffixes such as -preview

Documentation Overhaul

  • Massively revamped How-To docs: authentication, exclusions, Discover, Reservations, External Networks, and Virtual Network Associations sections
  • New documentation: Comprehensive automation docs, API docs for vNet Associations, initial External Networks docs, detailed Reservations feature docs
  • 50+ screenshots added or replaced to reflect the current UI
  • Fixed all markdown warnings/errors and added .markdownlint.json configuration
  • Doc cleanup: Fixed stale links, grammar, spelling issues, deprecated folder descriptions, and undocumented switches across all sections

Examples

  • Terraform example revamped: Migrated from Shell scripts to the official Azure IPAM Terraform provider
  • Azure ESLZ example modernized: Updated resource API references, modern coding standards, and improved parameterization
  • Script examples reorganized: PowerShell and Shell scripts moved into a dedicated examples/scripts/ folder with new helper scripts and README
  • Token helper function: Standardized access token generation; removed legacy Microsoft Graph SDK v1 support

Testing

  • Added tests for Virtual Network Association permutations
  • Expanded overall testing coverage with numerous additional Pester tests
  • Updated test expectations to align with additionally created resources
  • CI lint gate: Added pre-deployment lint job to the testing workflow (ESLint, Vite build verification, Ruff for Python, Bicep template validation, Hadolint for Dockerfiles). Deploy is skipped if any check fails, preventing wasted Azure resources

Bug Fixes

[major]

…that were not being accounted for properly
…ue to improper handling of missing vNETs and vHUBs
DCMattyG added 30 commits June 8, 2026 14:01
Switch the lodash dependency to lodash-es and update all named imports
across 19 source files. The ES module build allows Vite/Rollup to
tree-shake unused lodash methods, reducing the production bundle size.

Also collapse two duplicate lodash-es imports in visualize.jsx into a
single statement. No functional changes; import names and call sites
are unchanged.
Moment.js is in maintenance mode and ships a large, non-tree-shakeable
bundle. Replace it with dayjs (a ~2KB drop-in) in reservations.jsx, the
only file using moment.

Add the localizedFormat plugin to preserve the 'lll' token used in the
reservation grid date columns. Output was verified identical to moment
for all call patterns (localized format, unix parsing, startOf('day')).
The browserslist field was inert: Vite uses esbuild/build.target for
browser targeting and there is no autoprefixer, postcss-preset-env, or
Babel config consuming it. Removing it eliminates misleading config
with no functional effect.
Add an engines field to package.json mirroring the minimum versions
already enforced in tools/build.ps1 (node >=22.12.0, npm >=10.9.2).
This documents the toolchain contract in the standard location for
Node-native tooling and contributors. Advisory only; not enforced at
install time.
Add a "preview" script (vite preview) to smoke-test the production
build locally, and a "lint:fix" script (eslint src/ --fix) for
auto-fixable lint issues. Existing start, build, and lint scripts are
unchanged.
Correct stale UBI8 container-image overrides in the deploy, update, and
migrate scripts so private-registry RHEL builds use UBI9, matching the
Dockerfile.rhel defaults. Raise the build-time Node.js floor to 22.22.0.

- deploy/deploy.ps1: ubi8/nodejs-22 and ubi8/python-311 -> ubi9 equivalents
- deploy/update.ps1: ubi8/nodejs-22 and ubi8/python-311 -> ubi9 equivalents
- migrate/migrate.ps1: ubi8/nodejs-22 and ubi8/python-311 -> ubi9 equivalents
- tools/build.ps1: bump MIN_NODE_VERSION 22.12.0 -> 22.22.0
Bump react-router 7.18.0 -> 8.0.1 (major). The UI uses declarative-mode
routing (BrowserRouter / Routes / Route) with all imports already sourced
from "react-router", so no application code changes were required.

React Router v8 requires Node.js 22.22.0+, so the UI package "engines"
floor is raised from 22.12.0 to 22.22.0 to match.
Migrate the publicly hosted Azure IPAM container registry from
azureipam.azurecr.io to the new registry.azureipam.com FQDN across the
deployment, migration, and update tooling.

- deploy and migrate Bicep modules now default the public ACR URI to
  registry.azureipam.com (appService.bicep, functionApp.bicep)
- update.ps1 now recognizes both the new and legacy public registries and
  auto-migrates existing App Service / Function deployments by rewriting the
  baked-in LinuxFxVersion to the new registry before restarting
- testing workflow replace-string updated to match the new Bicep default
- docs (update README) and the v4.0.0 release notes updated to reference both
  the new and legacy registries

The legacy Docker Compose migration path (migrate.ps1) intentionally continues
to reference azureipam.azurecr.io, since it only ever processes pre-existing
legacy Docker Compose deployments.

BREAKING CHANGE: public Azure IPAM images are now served from
registry.azureipam.com instead of azureipam.azurecr.io. New and existing
deployments continue to work (running update.ps1 auto-migrates existing ones),
but any custom automation or pipelines that hardcoded azureipam.azurecr.io
should be updated to registry.azureipam.com.
Replace the legacy AZURE_CREDENTIALS service-principal secret with
workload identity federation (OIDC) in the Azure IPAM testing workflow,
aligning it with the auth model already used by the build workflow.

- Add top-level `permissions` block (id-token: write, contents: read)
  so GitHub can issue the OIDC token
- Convert all three Azure Login steps (deploy, test, cleanup) to the
  client-id/tenant-id/subscription-id form using the new
  AZURE_TEST_CLIENT_ID, AZURE_TEST_TENANT_ID, and
  AZURE_TEST_SUBSCRIPTION_ID secrets
- Preserve enable-AzPSSession so deploy.ps1's Get-AzContext /
  Get-AzAccessToken -> Connect-MgGraph chain continues to work

Removes the last long-lived Azure credential secret from CI.
…delete

The federated client assertion (GitHub OIDC token) is valid only a few
minutes. The synchronous resource-group deletion could exceed that window,
so the later Microsoft Graph (identity) and ACR data-plane (container)
deletions failed with AADSTS700024 when acquiring tokens for their
audiences. Run the identity and container cleanup first, while the
assertion is still valid, and delete the resource group last (it reuses
the ARM token cached at login). Mark all cleanup steps `if: always()` so
resource, identity, and container teardown are best-effort and run
independently of each other.
Bump ag-grid-community and ag-grid-react from ^35.3.1 to ^36.0.0.
No app code changes required; verified build and grids in light/dark mode.
Stamp the org.opencontainers.image.version label (plus title and source)
onto all production images so the version behind the `latest` tag can be
queried from the registry without pulling or running the container.

- Add ARG IPAM_VERSION and OCI LABEL block to all production Dockerfiles
  (deb, rhel, and func variants for root, engine, ui, and lb)
- Pass --build-arg IPAM_VERSION to every az acr build in the build workflow
- Anchor the release-tag `v` strip to the start (^v) so version suffixes
  like -preview are preserved
…pdate

Provision a dormant `staging` deployment slot alongside production for both
native and container App Service and Function App deployments, laying the
groundwork for safer slot-based upgrades. Slots are created disabled, share
the production App Service Plan and managed identity, and mirror production
configuration.

Add an additive, auto-detected infrastructure update to the update workflow
that retrofits the staging slot onto existing deployments that predate slots.
The step is best-effort and never modifies the production site: it verifies
plan tier, managed identity, and baseline settings before deploying, preserves
all existing app-setting values (only backfilling missing baseline keys), and
gracefully falls back to a slot without vNet integration when regional vNet
replication cannot be applied automatically.

Relocate the update tooling from `deploy/update.ps1` to a dedicated `update/`
folder with its own subscription-scoped Bicep templates, and refresh the
console output for clearer, consistent status reporting.

* deploy/modules/appService.bicep: shared site config + dormant staging slot
* deploy/modules/functionApp.bicep: dormant staging slot + sticky slot settings
* migrate/modules/appService.bicep: dormant container staging slot
* update/main.bicep, update/modules/*: additive per-deployment slot templates
* update/update.ps1: moved from deploy/ and rewritten with the infra update
* docs/update/README.md, README.md: document slots, prerequisites, and layout

BREAKING CHANGE: the update script has moved from deploy/update.ps1 to
update/update.ps1; update any automation that references the old path.
The v2.0.0 release renamed the client class ManagementGroupsAPI to
ManagementGroupsMgmtClient, breaking the import in helper.py. Update the
import and instantiation to the new client name and unpin the dependency
so the latest version is used.
Drop the >=0.103.0 and >=2.3.0 minimum version constraints from
fastapi[all] and pydantic[email] so they match the unpinned style of
the rest of requirements.txt. The extras are preserved and exact
versions remain governed by requirements.lock.txt, so resolution
behavior is unchanged.
Reimagine the startup database upgrade to be safe for App Service
deployment slots. Instead of eagerly migrating on every boot, the engine
now converges data only on the production slot and leaves staging slots
inert, so the previous version keeps running unharmed until the swap —
and swap preview / rollback stay possible.

- Replace the eager startup upgrade_db() with app/schema: production-only
  convergence plus a compatibility gate; the v4 baseline step folds in all
  prior data fixups so upgrades from any older version still land correctly
- Record schema state in a Cosmos "schema" control doc as a compatibility
  floor { current, minReadable }; additive convergence never raises the
  floor, preserving swap-back / rollback
- Detect the slot via WEBSITE_SLOT_NAME (globals.IS_PRODUCTION_SLOT);
  unset / non-Azure hosts are treated as production
- Introduce production/staging/incompatible service modes with a gate that
  disables all /api routes except /api/status and /api/health when not in
  production
- Run convergence and the find_reservations scheduler only on the
  production slot; gate reservation reconciliation and the heartbeat to
  production across both the App Service and Functions entry points
- Add GET /api/health: an authenticated, live dependency checklist
  (config, cosmos, arm, schema) that stays reachable in staging for
  pre-swap verification
- Trim /api/status to a portable "mode"; drop Azure-specific slot detail
- Add Status/Health models and a Pester integration test for /api/health

BREAKING CHANGE: The engine now refuses to serve (503 on all API routes)
when the running build is older than the database's minimum readable
schema, and staging-slot instances return 503 on every route except
/api/status and /api/health until swapped to production.
…uction-accurate

Improve the update workflow's handling of container deployments and the
auto-provisioned staging slot so updates are safer, idempotent, and consistent.

- Tag built images with both the app version and `latest`, and pass
  IPAM_VERSION as a build arg, matching the CI build convention
- Skip the image build when the running version already matches the
  repository (unless -Force), using the bare version from /api/status
- Mirror production's actual ACR authentication method onto the staging
  slot (AcrUseManagedIdentityCreds) instead of inferring it from the
  registry name, so admin/anonymous and non-ACR registries are no longer
  forced to use a managed identity
- Treat a private registry outside the app's resource group as a clean,
  non-fatal manual-handoff instead of a hard failure
- Correct the staging slot for container function apps: match production's
  kind (functionapp,linux,container) and skip the content-share sticky
  settings that are not permitted as slot settings
- Normalize per-branch console output: consistent manual-handoff sections
  and async-completion guidance across the container and native paths
Bring the update guide in line with the current update.ps1 behavior.

- Note that private ACR container deployments now compare the running
  version and skip the image build when already up to date
- Document version+latest image tagging (ipam:<version> + ipam:latest,
  ipamfunc:<version> + ipamfunc:latest)
- Explain the same-resource-group requirement for automated builds and
  the manual image-update handoff when the registry is elsewhere
- Refresh the Docker Compose detection example to match the new output
- Split the multi-part Additional Requirements and Version Comparison
  items into child bullets for readability
Harden the SPA's MSAL redirect handling so a service restart no longer
triggers a cascading interaction_in_progress loop:

- Poll the public /api/status endpoint without a bearer token so the
  restart gate's recovery poll never triggers MSAL token acquisition.
- Reload the page only when no MSAL interaction is in progress, so an
  in-flight redirect is never interrupted mid-handshake.
- Replace the hand-rolled Login component with MSAL's native
  MsalAuthenticationTemplate for initial sign-in, removing the second
  interactive-redirect owner that raced with AuthHandler.
- Simplify AuthHandler to a single loginRedirect re-auth path and drop
  a dead acquireTokenRedirect branch (ACQUIRE_TOKEN_FAILURE events carry
  a null payload, so it never executed).
MsalAuthenticationTemplate internally calls acquireTokenSilent with the
default (iframe-capable) cache policy and throws on error, which crashed
the UI in privacy browsers that block third-party cookies. Restore the
AuthenticatedTemplate/UnauthenticatedTemplate + Login sign-in, which only
uses loginRedirect (no iframe). Keeps the unauthenticated /api/status poll
and the AuthHandler cleanup.
…d update detectors

Adds a self-describing notification framework (GET /api/notifications,
POST /api/notifications/{id}/resolve) with admin-gated, resolve-by-reference
remediation. Includes detectors for legacy/dev container-registry migration
(with a server-owned "switch to official registry" remediation) and for
available GitHub releases. Replaces the previous service router/models.
…art gate

Adds the AppBar notification center (bell, badge, list, manual refresh),
the detail dialog with server-driven resolve actions, and the full-screen
service restart gate that polls /api/status for recovery. Registers the
notifications/restart reducers and pauses background polling during restarts.
control.py was never staged in the slot-safe convergence commit (fcb3379),
even though app/schema/convergence.py, app/schema/steps.py, and
app/routers/health.py all import from it. Runtime deployments worked only
because they were built from the working tree; a clean checkout would fail
to import app.schema.control on startup. Commit the file to complete the
feature.
Order the app.schema.control import after app.routers.common.helper in
health.py and steps.py. Import ordering only; no functional change.
Adds a Notifications context to the Pester integration suite (ordered to match
the FastAPI router mount order). Covers GET /api/notifications (200 + response
shape validation) and the safe, non-mutating POST /api/notifications/{id}/resolve
guard paths: 404 for an unknown id, 404 for a link-only notification with no
remediation, and 409 for a remediable notification that isn't currently active.
The destructive resolve happy path is intentionally left to engine unit tests.
Mounts the notifications router before health so the route order in FastAPI
(and the generated Swagger docs) reads consistently.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants