Skip to content

chore(infra): run test on the free App Service tier (D105) - #235

Open
Lethabarb wants to merge 4 commits into
mainfrom
chore/test-on-free-tier
Open

Lethabarb wants to merge 4 commits into
mainfrom
chore/test-on-free-tier

Conversation

@Lethabarb

@Lethabarb Lethabarb commented Nov 6, 2026 •

Copy link
Copy Markdown
Owner

What

Test's App Service plan moves from Linux B1 to the free F1 tier. Production is unchanged. This saves AUD 19.71 a month, about half of today's bill (D105, 6 November 2026).

  • infra/main.bicep gains appServicePlanSku, 'B1' by default. infra/test.bicepparam sets 'F1'. On F1 the web app asks for neither VNet integration nor Always On, because F1 offers neither (infra/modules/app.bicep).
  • F1 has no subnet. So the new infra/modules/sql-app-firewall.bicep gives test's SQL server one rule per IPv4 outbound address of the app's App Service stamp. It is deployed after the app, whose addresses are known only then. Test's Key Vault takes every network (allowAllNetworks), because the vault's network rules are part of the vault and the app's settings already depend on it.
  • Entra-only sign-in and the identities' roles stay the only way in. The stamp's addresses are shared with other customers' apps, which is recorded as ops/security-review.md F-27. Test holds made-up data only (A35).
  • InfraTemplateTests now checks that main.bicep defaults to B1, test picks F1, and production never does.
  • Production's database is not changed now. It stays on S0, which the Azure account's first-year free offer covers. D105 records the owner's plan to move to Basic when that year ends: 7-day point-in-time restore, plus weekly long-term backups kept 5 weeks next to the monthly 12. The three deploys are in design.md §10.9.
  • Docs: D105 in design/decisions.md; design/design.md §10.2 (re-priced from the 6 November 2026 Retail Prices API and the bill), §10.3, §10.7, §10.9, A36 and the layout; infra/README.md (Test on the free plan); two comments in deploy.yml.

Why

The owner asked to scale down for about 12 users. The 6 November capacity test measured B1 at about 4.7 req/s within p95 200 ms / p99 500 ms. That is roughly 8× the peak of 12 users. B1 is also the smallest tier with VNet integration and Always On, so production keeps it. The bill showed the two B1 plans as the whole cost. A VPS, other clouds and Container Apps were weighed: they would save at most AUD 10–20 a month more and give up the managed identity, Key Vault and managed geo-redundant backups.

Before the first test deploy (one time)

Azure will not move a plan to F1 while its app has Always On or VNet integration, and a deploy updates the plan before the app. So right after merging, while main's CI runs, run the two commands in infra/README.md → Test on the free plan. Test is then down until its deploy finishes. Afterwards, run the README's check that every outbound address has a rule.

Checks

  • az bicep build and az bicep lint are clean for main.bicep, bootstrap.bicep, locks.bicep and both .bicepparam files. dotnet build of the test project gives 0 warnings. Tests run in CI.
  • az deployment group what-if (read-only), with the real parameters:
    • prod: no change to its plan, web app, VNet integration or Key Vault network rules. The other Modify lines are the usual what-if noise.
    • test: plan B1 → F1, alwaysOn true → false, vault defaultAction Deny → Allow. The firewall rules depend on runtime outputs, so what-if cannot show them.
  • An independent review found no faults in the Bicep's behaviour. Its documentation findings are fixed in this branch.

To confirm on the first test deploy

  • F1 can be created in Australia Southeast on this subscription.
  • The user-assigned managed identity, the SSH console and az webapp exec work on F1. Break-glass and the onboarding rehearsal use the last two.
  • Cold starts after about 20 idle minutes can outlast the SPA's 10-second timeout on a non-sign-in call; a retry then works.
  • If the plan's capacity: 1 reads back differently on F1, every later what-if will show a capacity diff.

If any of these fail, appServicePlanSku = 'B1' puts test back as production is (infra/README.md).

🤖 Generated with Claude Code

Lethabarb and others added 2 commits November 6, 2026 18:56
Test's App Service plan moves from Linux B1 to the free F1 tier, saving
AUD 19.71 a month, about half the current bill. Production is unchanged.

- main.bicep gains appServicePlanSku ('B1' by default; test.bicepparam
  sets 'F1'). On F1 the app asks for neither VNet integration nor Always On.
- F1 has no subnet, so the new sql-app-firewall.bicep gives the SQL server
  one rule per outbound address of the app's App Service stamp, and test's
  Key Vault takes every network. Entra-only sign-in and the identities'
  roles stay the only way in (security review F-27).
- InfraTemplateTests checks that production never picks F1.
- infra/README.md has the one-time step before the first test deploy:
  turn off Always On and remove VNet integration, which Azure requires
  before it moves a plan to F1.
- D102 also records the owner's plan for production's database: S0 stays
  while the Azure account's free S0 year lasts, then moves to Basic with
  7-day point-in-time restore and weekly long-term backups kept 5 weeks.
  design.md §10.9 has the steps; nothing changes in production now.
- design.md §10.2 re-prices both environments from the 6 November 2026
  Retail Prices API and bill; §10.3, §10.7 and A36 follow D102.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main took D102 for the first provider's quick start (#240), so this
branch's hosting-cost decision becomes D103. Both rows are kept in
design/decisions.md, and this branch's citations of the decision are
renamed in the files it changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Lethabarb Lethabarb changed the title chore(infra): run test on the free App Service tier (D102) chore(infra): run test on the free App Service tier (D103) Nov 9, 2026
Main took D104 for the Word-template decision (#246), and open PR #241
also numbers its decision D103, so this branch's hosting-cost decision
becomes D105, the next number no branch claims. design/decisions.md keeps
main's D104 row and adds this one after it; this branch's citations are
renamed in the files it changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Lethabarb Lethabarb changed the title chore(infra): run test on the free App Service tier (D103) chore(infra): run test on the free App Service tier (D105) Nov 10, 2026
Main added D106 (#248), which also amends D104. design/decisions.md keeps
main's amended D104 row and its D106 row, with this branch's D105 between
them; design.md's decision range now reads D1-D106.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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