Releases: libre-devops/FindNextCIDRRange
Release list
2.3.0
CheckCidr: check any CIDR against Azure's subnet rules, no Azure access needed
A new endpoint that answers with arithmetic alone, so it involves no identity, no Reader grant, and no Azure call, and it works the moment a bare deployment lands:
GET /api/CheckCidr?cidr=10.0.0.0/29
The answer covers the total and usable address counts, the five addresses Azure reserves in every subnet (the network address, the default gateway, two addresses for Azure DNS, and the broadcast address), the first and last address Azure would actually hand out, and whether the subnet can exist at all. Azure subnets run from /2 to /29, so a /30 answers validAzureSubnet: false with the reason spelled out, and that is a 200: the question was answered. Host bits are dropped and reported through the normalized field.
The endpoint postdates the 1.x contract, so it carries none of the preserved quirks: responses are application/json and wire statuses are always truthful, with 400 reserved for input that cannot be parsed (garbage, a missing prefix length, or IPv6, where Azure subnets are always /64). GetCidr and its preserved contract are completely untouched.
Proven live before release: /29 valid with 3 usable addresses, /30 rejected with the reason, garbage input a true wire 400, host bits normalized. 76 unit tests now guard both endpoints; the smoke recipe probes both verdicts on every deploy; the landing page, OpenAPI description, and Swagger UI document the endpoint.
Full diff: 2.2.0...2.3.0
2.2.0
Opt-in truthful HTTP wire statuses
The headline: a deployment can now opt out of the oldest wart in the contract. By default every error still travels as HTTP 400 on the wire with the meaningful status in the body's code field, exactly as 1.x consumers expect. Set the HONOR_HTTP_STATUS app setting to true (honor_http_status = true in tfvars) and errors travel with the body's code as the wire status instead: a missing vnet is a real 404, an unexpected failure a real 500. The bodies are byte-identical in both modes, so opting in is safe for anything that parses the JSON. Proven live before release: a deployment with the setting enabled answered a missing vnet with wire 404, an unsatisfiable address space with wire 404, and bad input with 400.
The contract is now guarded by tests
56 unit tests drive the real function end to end through hand-rolled isolated-worker fakes and pin the preserved contract byte for byte: the always-400 default, the text/plain; charset=utf-8 bodies, the exact historical error shapes, cidr accepted only from 2 through 29, and POST reading the query string. CI runs them on every push and PR, so a contribution can no longer break the contract silently.
Hardening
- Empty or whitespace name parameters now count as missing and return the clean historical "X is null" body, instead of leaking an unstable SDK exception as a 500-coded body (an idea from a community PR against the 1.x code).
- Trivy config scanning in CI, gating on HIGH and CRITICAL, with a single audited waiver mirrored in the README's security scan exceptions table.
- CodeQL scanning (C# and the workflows themselves), weekly and on every push and PR.
- Dependabot on nuget, github-actions, and terraform; all first-sweep updates are merged.
- The justfile now runs in PowerShell throughout, and its live recipes fail fast with a clear message when no stack is deployed.
Full diff: 2.1.0...2.2.0
2.1.0
The API now presents itself properly. The GetCidr contract is untouched and verified live before release, exactly as in 2.0.x.
What is new
- OpenAPI, served by the app itself. A hand-written OpenAPI 3 description lives at
openapi.yamlin the repo root, is embedded into the assembly at build time so spec and code cannot drift, and is served at/api/openapi.yaml. It documents the contract warts and all: every error carries HTTP 400 on the wire with the meaningful status in the body'scodefield, responses are JSON served as text/plain, and POST reads the query string. An interactive Swagger UI (Try It Out enabled) is served at/api/swagger. - A landing page at the root, replacing the Azure Functions default. A single self-contained HTML document (inline CSS, system fonts, light and dark schemes, no external assets) introducing the API with links to the docs, GitHub, and libredevops.org. Under the hood the global route prefix is emptied and every function pins its full historical
api/...path, so all existing URLs are byte-identical. - justfile rounded out:
smokeexercises every live endpoint including the preserved error contract,shipchains package and deploy,initjoins the terraform lifecycle,packageno longer lets deleted files linger in a stale zip, andset tempdirfixes shebang recipes failing with permission denied on systems where the runtime directory is mounted noexec (WSL2 among them).
Since 2.0.x
2.0.0 rebuilt the function on .NET 10 isolated (supported on Flex Consumption to November 2028) with two inherited bugs fixed contract-safely, and rebased the terraform onto the Libre DevOps registry modules with standalone and bring-your-own-network paths; 2.0.1 fixed a fresh-deploy plan failure. See those releases for detail.
Issues and PRs against the new codebase are welcome.
2.0.1
Fixes a fresh-deploy blocker in the terraform: the Reader grant built its principal list with distinct(compact()) over identity ids that do not exist until the app does, which made the list length unknown at plan time and failed terraform apply on a clean deployment with "Invalid for_each argument" (incremental applies against existing state masked it). The list is statically sized now, and a clean-tenant deploy has been verified end to end: apply, package, deploy, and the API answering with the expected next free /26. No function code changes; the 2.0.0 binaries are unchanged.
2.0.0
The v-next rewrite. The HTTP contract is held byte-stable with 1.0.0 (the code exactly as handed over by Gary L. Mullen-Schultz, preserved in that tag): same route, methods, and query parameters, same indented JSON string bodies, same text/plain content type, and the long-standing behaviour that every error travels with HTTP 400 on the wire while the meaningful status lives in the body's code field.
What changed
- .NET 10 isolated worker, replacing .NET 6 in-process. The old runtime is EOL and the in-process model retires in November 2026; dotnet-isolated 10.0 is supported on Flex Consumption until November 2028.
- Two inherited bugs fixed, contract intact:
- The working status code was a static field shared across invocations, so one request's error code could bleed into another's error body under concurrency. It is per-request now.
- The subscription lookup required
subscriptions/readat subscription scope, so narrowly scoped Reader grants (a resource group or a single vnet) failed with a 403 dressed as a 500. The resource group ID is now constructed directly, so Reader on the vnet's scope is genuinely enough. This is the likely cause of the 500s reported against 1.x.
- Terraform rebuilt on the Libre DevOps registry modules (Flex Consumption function app, keyless storage, managed identity host storage) with two deployment paths chosen by tfvars alone: standalone (a pre-seeded test vnet so a fresh deploy proves itself with
just try) and bring your own network (reader_scopespointing Reader at the networks you already have). - justfile, CI, and README brought up to the Libre DevOps house standards.
Verified live
Deployed to Azure and exercised end to end before release: the happy path proposes the expected next free /26, the addressSpace filter matches exactly, invalid CIDR sizes return the identical 1.x error body, and a missing vnet now reports code 404 in the body rather than the permission-induced 500.
Issues and PRs
Everything open against 1.x was closed with a handover note when the project changed hands. If your issue still reproduces on 2.0.0, or you would like your PR considered against the new codebase, please resubmit; contributions are welcome.