Stage 1 — MVP¶
Trigger: "We need a real public web app online this week."
Stage 1 stands up the smallest thing that is genuinely production-shaped: a public ASP.NET Core web app on Azure App Service, backed by Azure SQL Database, with Application Insights wired in from the first deploy. Everything lives in one resource group so the whole stage tears down with a single command.
This stage deploys the Practical Storefront sample app using the foundation Bicep modules composed by stages/stage-01-mvp/main.bicep.
Before you start¶
Read these foundations first — Stage 1 applies the decisions they describe:
- Compute selection basics — why App Service (managed PaaS) over VMs.
- Data selection basics — why Azure SQL Database for a relational MVP.
- Resource organization — why a single resource group per stage.
- Observability foundations — why telemetry ships on day one.
- Service selection patterns — the selection logic behind this baseline.
What you'll build¶
flowchart TD
User[Public user] -->|HTTPS 443| WebApp[App Service Web App<br/>Linux B1, DOTNETCORE 8.0]
WebApp -->|SQL 1433, TLS 1.2| SQL[(Azure SQL Database<br/>Basic tier)]
WebApp -->|Telemetry| AI[Application Insights]
AI --> LA[Log Analytics workspace<br/>7-day retention]
subgraph RG[Single resource group]
WebApp
SQL
AI
LA
end | Resource | SKU / Tier | Role |
|---|---|---|
| App Service plan | Linux B1 (Basic) | Compute host |
| Web App | DOTNETCORE\|8.0, HTTPS-only | Runs the Storefront app |
| Azure SQL logical server | v12.0, TLS 1.2 | Database server |
| SQL Database | Basic (2 GB) | Catalog and order data |
| Application Insights | Workspace-based | Request and dependency telemetry |
| Log Analytics workspace | 7-day retention | Telemetry backend |
Cost: ~$0.09–$0.13/hour. Time: 20–30 minutes.
Prerequisites¶
- Azure CLI logged in (
az login) with rights to create resource groups. - A strong SQL administrator password exported as
SQL_ADMIN_PASSWORD(never commit it).
Deploy¶
The generic driver scripts under scripts/practical/ wrap the Bicep deployment with a repeatable deploy → verify → destroy flow:
The script creates the resource group, runs the deployment, and prints the web app name, URL, and Application Insights name.
To deploy the Bicep directly instead:
az group create --resource-group rg-practical-storefront-stage01 --location koreacentral
az deployment group create \
--resource-group rg-practical-storefront-stage01 \
--template-file infra/bicep/stages/stage-01-mvp/main.bicep \
--parameters infra/bicep/stages/stage-01-mvp/main.bicepparam \
--parameters sqlAdministratorLoginPassword="$SQL_ADMIN_PASSWORD"
| Command | Purpose |
|---|---|
az group create --resource-group rg-practical-storefront-stage01 --location koreacentral | Creates the resource group that holds every Stage 1 resource. |
--resource-group rg-practical-storefront-stage01 | Names the resource group to create. |
--location koreacentral | Sets the Azure region for the resource group. |
az deployment group create | Deploys the Bicep template into the resource group. |
--resource-group rg-practical-storefront-stage01 | Targets the resource group that receives the deployment. |
--template-file infra/bicep/stages/stage-01-mvp/main.bicep | Points to the Bicep template to deploy. |
--parameters infra/bicep/stages/stage-01-mvp/main.bicepparam | Supplies deployment parameters from the .bicepparam file. |
--parameters sqlAdministratorLoginPassword="$SQL_ADMIN_PASSWORD" | Overrides the SQL administrator password inline from the exported variable. |
Verify¶
This runs two smoke tests:
- HTTP smoke —
GET /returns200,GET /healthzreturns{"status":"Healthy"},GET /ops/inforeturns JSON with aversionfield. - SQL smoke — confirms the database is reachable on TCP 1433.
Confirm telemetry is flowing after a few requests:
az monitor app-insights metrics show \
--app <appInsightsName> \
--resource-group rg-practical-storefront-stage01 \
--metric requests/count \
--interval PT5M
| Command | Purpose |
|---|---|
az monitor app-insights metrics show | Reads an aggregated metric from the Application Insights resource. |
--app <appInsightsName> | Identifies the Application Insights component to query. |
--resource-group rg-practical-storefront-stage01 | Locates the Application Insights resource. |
--metric requests/count | Selects the request-count metric to return. |
--interval PT5M | Aggregates the metric over 5-minute (ISO-8601) buckets. |
A value greater than 0 confirms Application Insights is capturing requests.
See labs/trunk/stage-01-mvp/ for the full checklist, sample requests, and expected results.
Best practices embedded in this stage¶
- Managed PaaS over VMs — no OS to patch; App Service and Azure SQL are fully managed.
- Stateless app design — all durable state is in SQL, so the app can scale out later without change.
- Telemetry on day 1 — the Application Insights connection string is injected at deploy time, not bolted on later.
- Single resource group — the entire stage is created and destroyed as one unit.
Stage 1 intentionally uses SQL authentication and a public SQL endpoint to keep the first deploy simple. This is not a production target. Later stages migrate to managed identity, Key Vault, and private endpoints.
Clean up¶
This deletes the resource group and everything in it.
Go deeper¶
- Public web and API — baseline — the workload blueprint this stage instantiates.
- Compute selection cheatsheet
- Data selection cheatsheet
- Design Lab 01 — public web baseline
See Also¶
- Compute selection basics
- Data selection basics
- Observability foundations
- Public web and API — baseline