Skip to content

Modernize templates, fix cloud-init provisioning race, reorganize repo and rewrite docs - #6

Merged
dmauser merged 7 commits into
masterfrom
dmauser-supreme-engine
Aug 13, 2026
Merged

Modernize templates, fix cloud-init provisioning race, reorganize repo and rewrite docs#6
dmauser merged 7 commits into
masterfrom
dmauser-supreme-engine

Conversation

@dmauser

@dmauser dmauser commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Modernizes the templates after several Azure platform retirements broke them, fixes a latent provisioning bug, reorganizes the repository, and rewrites the documentation.

Every template in this PR was validated by actually deploying it to Azure (subscription DMAUSER-FDPO) and inspecting the result, not just by building it. That is what surfaced the cloud-init bug below.

Fixes

Retired platform dependencies

  • Basic SKU public IPs (retired Sept 2025) → Standard SKU, static across all templates and lab scripts.
  • Retired UbuntuLTS / ubuntults CLI aliases → Ubuntu 24.04 LTS (default) / 22.04 LTS.
  • Windows Server 2019 → Server 2025 Core, small disk, Gen 2, with Trusted Launch (Secure Boot + vTPM) and current API versions.

Standard SKU is deny-by-default inbound, so switching allocation method alone would have silently made the NVAs unreachable. Added allowSshFromAddressPrefix / allowRdpFromAddressPrefix to all three templates, driving a conditional NSG with the management rule (pri 200) plus an RFC 1918 rule (pri 300) so forwarded traffic still flows. deploylinuxnva.azcli and deploylinuxnvabgp.azcli previously created no NSG at all and now do.

NSG placement is deliberate: the existing-subnet template attaches to the NIC so a user's pre-existing subnet NSG is never clobbered; the new-subnet template appends to the subnet NSG it already creates.

Cloud-init race (pre-existing on master, not introduced by any recent PR)

A deployment failed with Unable to locate package netfilter-persistent. The Custom Script Extension can start while cloud-init is still rewriting /etc/apt/sources.list from archive.ubuntu.com to azure.archive.ubuntu.com. Apt indexes are keyed by mirror hostname, so after the rewrite the on-disk indexes belong to the superseded mirror and apt knows zero packages. It is timing-dependent — 24.04 passed while 22.04 failed on the same run, which is why it went unnoticed. All package-installing scripts now cloud-init status --wait first; redeploying the exact failing configuration then succeeded.

Repository reorganization

infra/bicep/   template sources          scripts/linux/    CSE payloads (.sh) + cloud-init
infra/arm/     generated ARM JSON        scripts/windows/  CSE payload (.ps1)
labs/          .azcli lab builds + conf/ docs/             notes

scriptUri defaults now use relative paths (../../scripts/linux/linuxrouter.sh), so they still resolve against whatever branch or fork a template is deployed from. I verified ARM's uri() handles ../ traversal before relying on it, then confirmed it end-to-end by deploying both templates from this branch's raw URLs.

⚠️ Breaking: the ARM JSON moved out of the repo root, so previously bookmarked Deploy-to-Azure links will 404. The README buttons are updated. The three master-based raw URLs in the README return 404 until this merges — the equivalent branch URLs were verified 200.

Quality gates

  • GitHub Actions: bicep lint, rebuild every template and fail on drift between infra/bicep/ and infra/arm/, plus shell syntax and a CRLF check.
  • .gitattributes pins *.sh to LF — CRLF breaks the shebang when the CSE runs a script.

Documentation

README rewritten: table of contents, template comparison and full parameter tables, network-security guidance, the --template-file/scriptUri caveat, coverage of all 15 previously undocumented files, a recent-improvements summary, and an expanded roadmap detailing the VMSS design (Flexible orchestration behind an internal LB with HA Ports, UDR pointing at the frontend IP, and the flow-symmetry constraint that affects the SNAT scenario) and Accelerated Networking (SR-IOV benefits, and that the Standard_B2s default is burstable and cannot support it).

Validation

Check Result
linux-router @ 24.04, existing subnet Succeeded
linux-router-newsubnet @ 22.04, new subnet Succeeded
windows-router @ Server 2025 Core, Trusted Launch Succeeded
Deploy from relocated paths via branch raw URL (Linux + Windows) Succeeded — relative scriptUri resolved correctly
CSE provisioning state Succeeded on all
SSH 22 / RDP 3389 reachable through new NSG True
Linux in-guest: ip_forward, IPv6 fwd, NAT rules, persisted rules 1 / 1 / 5 / 22 lines, netfilter-persistent present
Windows in-guest: forwarding interfaces, ICMP rule 2 / enabled
End-to-end egress through NVA via UDR, and reboot persistence Verified

All validation resource groups have been torn down.

dmauser and others added 7 commits August 12, 2026 20:23
az network public-ip create now defaults to --sku Standard, which
rejects Dynamic allocation, so these scripts fail at the public IP
step. Switch to Static/Standard explicitly.

Standard SKU public IPs are also closed to inbound traffic until an
NSG allows it, so deploylinuxnva and deploylinuxnvabgp now create the
same default-nsg (SSH from the caller's public IP + VirtualNetwork
breakout) that deploylinuxnvabgpnp already used, otherwise the NVA
would be unreachable after deployment.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cd9ed807-66b2-43e3-8bf9-ed7eb24a46dd
- deploylinuxnvabgpnp.azcli: the 'ubuntults' alias was removed from the
  Azure CLI, so both az vm create calls failed. Use Ubuntu2404 and match
  the new image offer in the nettools VM filter.
- deploylinuxnva.azcli: drop the second subnet create, which tried to
  recreate the subnet already created by az network vnet create under a
  different prefix and failed with 'already exists'.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cd9ed807-66b2-43e3-8bf9-ed7eb24a46dd
The Custom Script Extension can start while cloud-init is still rewriting
/etc/apt/sources.list. apt is then left holding package indexes for the
superseded mirror, so installs fail with 'Unable to locate package
netfilter-persistent' and the script dies writing /etc/iptables/rules.v4.

Reproduced on a 22.04 deployment in Azure: the extension failed, and the
identical commands succeeded on that same VM once cloud-init reported done.
Redeploying with this wait in place succeeded.

linuxrouter.sh here also carries the apt changes from PR #5 so this branch
matches the configuration that was validated end to end.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cd9ed807-66b2-43e3-8bf9-ed7eb24a46dd
…lates

Standard SKU public IPs deny inbound traffic by default, so a one-click
deployment produced a VM that could not be reached over SSH. Both
templates now accept an optional source prefix for TCP 22.

LinuxRouter.bicep creates a NIC-level NSG when the parameter is set,
leaving the pre-existing subnet's own NSG untouched. It carries the same
RFC 1918 allow rule as the new-subnet template so forwarded traffic keeps
flowing. LinuxRouter-newsubnet.bicep appends the SSH rule to the NSG it
already creates.

The parameter defaults to empty, which preserves the previous behaviour.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cd9ed807-66b2-43e3-8bf9-ed7eb24a46dd
Restructure the repository into infra/ (bicep sources + generated ARM),
scripts/ (linux + windows CSE payloads), labs/ (azcli lab builds) and docs/,
with consistent kebab-case file naming. Template scriptUri defaults now use
relative paths (../../scripts/...) so they continue to resolve against the
branch or fork a template is deployed from.

Add WinRouter as a Bicep source, moving it off the retired Basic SKU public
IP and onto Windows Server 2025 Core (small disk, Gen 2) with Trusted Launch,
an allowRdpFromAddressPrefix parameter and current API versions.

Add a GitHub Actions workflow that lints the Bicep, rebuilds the ARM JSON and
fails on drift, and checks shell scripts for syntax errors and CRLF endings.
Add .gitattributes pinning shell scripts to LF, since CRLF breaks the shebang
when the Custom Script Extension runs them.

Rewrite README.md with a table of contents, template comparison and parameter
tables, network security guidance, full coverage of every script in the repo,
a summary of recent improvements and an expanded roadmap.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: cd9ed807-66b2-43e3-8bf9-ed7eb24a46dd
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: cd9ed807-66b2-43e3-8bf9-ed7eb24a46dd
@dmauser
dmauser merged commit ead28b2 into master Aug 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant