Skip to main content

Deploy your first web app

In this lab, you deploy a simple web app to Azure App Service and open it in your browser. App Service is a fully managed platform for hosting web apps, so you can ship code without managing servers.

You'll build the same result three ways so you can pick the workflow that fits you:

  • Azure Developer CLI (azd) - an opinionated, repeatable workflow that provisions infrastructure and deploys code in one step.
  • Azure CLI (az) - explicit commands that create each resource and deploy your code.
  • Azure portal - a visual, click-through experience.

You can follow the lab in your language of choice: .NET, Node.js, Python, Java, or PHP. Each path calls out the differences between Linux and Windows App Service plans.

App Service Labs complements Microsoft Learn

This lab is a hands-on, end-to-end walkthrough. For reference depth on any concept, follow the "Learn more" links to the official Microsoft Learn quickstarts.

Estimated time: 15-20 minutes

What you'll build

A single web app running on an App Service plan (the compute that hosts your app), reachable at a default https://<app-name>.azurewebsites.net hostname that returns HTTP 200.

Prerequisites

Before you begin, you will need an Azure subscription with Owner permissions and a GitHub account.

In addition, you will need the following tools installed on your local machine:

Setup Azure CLI

Start by logging into Azure by run the following command and follow the prompts:

az login --use-device-code
tip

You can log into a different tenant by passing in the --tenant flag to specify your tenant domain or tenant ID.

Choose a region and low-cost tier

This lab uses the East US region and the B1 (Basic) pricing tier, a low-cost option that's ideal for learning. The F1 (Free) tier also works for the code paths, with some limits. You can change the region to one near you.

Deploy the app

Choose your path
Set these once - every matching step and code sample below follows your choice.
Tooling
Language
OS

Pick a deployment mechanism, then choose your language. The steps produce an identical running app.

The Azure Developer CLI packages your app code and infrastructure together, then provisions and deploys in one flow. In this lab you use a minimal project: a small app plus a Bicep file that defines the App Service plan and web app.

1. Sign in

azd auth login

2. Create the project structure

Create a new folder and add the four files below. The infra/main.bicep and infra/resources.bicep files are shared across every language - only the app code and two values change per language (the language value in azure.yaml and linuxFxVersion in infra/main.parameters.json).

mkdir my-first-web-app && cd my-first-web-app
mkdir infra src

Create azure.yaml in the project root:

# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
name: my-first-web-app
services:
web:
project: ./src
language: js # change per language: js, dotnet, python, java
host: appservice

Create infra/main.parameters.json:

{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": { "value": "${AZURE_ENV_NAME}" },
"location": { "value": "${AZURE_LOCATION}" },
"resourceGroupName": { "value": "${AZURE_RESOURCE_GROUP}" },
"linuxFxVersion": { "value": "NODE|22-lts" }
}
}

Create infra/main.bicep. It runs at subscription scope so azd creates and owns the resource group for this environment - the reliable pattern that avoids reusing a shared group:

targetScope = 'subscription'

@description('Name of the azd environment; used to derive resource names.')
param environmentName string

@description('Azure region for all resources.')
param location string

@description('Resource group to create for this environment.')
param resourceGroupName string

@description('Runtime stack for the web app, for example NODE|22-lts or DOTNETCORE|8.0.')
param linuxFxVersion string = 'NODE|22-lts'

resource rg 'Microsoft.Resources/resourceGroups@2024-03-01' = {
name: resourceGroupName
location: location
}

module resources 'resources.bicep' = {
name: 'resources'
scope: rg
params: {
location: location
environmentName: environmentName
linuxFxVersion: linuxFxVersion
}
}

output WEB_URI string = resources.outputs.webUri

Create infra/resources.bicep:

@description('Azure region for all resources.')
param location string

@description('azd environment name used to derive globally unique names.')
param environmentName string

@description('Runtime stack for the web app, for example NODE|22-lts or DOTNETCORE|8.0.')
param linuxFxVersion string = 'NODE|22-lts'

var suffix = uniqueString(subscription().id, resourceGroup().id, environmentName)
var planName = 'plan-${suffix}'
var webName = 'app-${suffix}'

resource plan 'Microsoft.Web/serverfarms@2023-12-01' = {
name: planName
location: location
sku: {
name: 'B1'
}
kind: 'linux'
properties: {
reserved: true // required for Linux plans
}
}

resource web 'Microsoft.Web/sites@2023-12-01' = {
name: webName
location: location
kind: 'app,linux'
tags: {
'azd-service-name': 'web' // links this site to the "web" service in azure.yaml
}
properties: {
serverFarmId: plan.id
httpsOnly: true
siteConfig: {
linuxFxVersion: linuxFxVersion
appSettings: [
{
name: 'SCM_DO_BUILD_DURING_DEPLOYMENT'
value: 'true' // let App Service build the app on deploy (installs dependencies)
}
]
}
}
}

output webUri string = 'https://${web.properties.defaultHostName}'

3. Add your app code

Choose your language and add the sample code under src/. Update the language value in azure.yaml and linuxFxVersion in infra/main.parameters.json to match.

Requires the .NET SDK. Set language: dotnet and linuxFxVersion to DOTNETCORE|8.0.

cd src
dotnet new webapp
cd ..

4. Create an environment and provision

Create an azd environment with a unique suffix (so the resource group and app names don't collide with earlier runs), then let azd create the resource group and deploy:

SUFFIX=$(openssl rand -hex 3) # 6 lowercase hex chars
azd env new "my-first-web-app-${SUFFIX}" --location eastus
azd env set AZURE_RESOURCE_GROUP "rg-firstwebapp-${SUFFIX}"

Provision the infrastructure and deploy your code in one step:

azd up

When it finishes, azd prints the app endpoint:

- Endpoint: https://app-<random>.azurewebsites.net/
SUCCESS: Your application was deployed to Azure in 3 minutes 46 seconds.
First azd up can't find the tagged resource

On the very first azd up, azd occasionally reports unable to find a resource tagged with 'azd-service-name: web' because the provisioning outputs aren't cached yet. If that happens, run azd deploy once more - the resources already exist and the code deploy completes.

One azd service per resource group

azd finds your app by the azd-service-name: web tag. If you deploy more than one azd app into the same resource group, deployment fails with expecting only '1' resource tagged with 'azd-service-name: web', but found '2'. The unique suffix above gives each run its own resource group, which avoids this.

Windows plans with azd

The Bicep in infra/resources.bicep targets Linux. For a Windows plan, remove reserved: true, change kind to app, and replace linuxFxVersion with a Windows setting - for example netFrameworkVersion: 'v8.0' for .NET or nodeVersion: '~22' for Node.js. Python and PHP aren't available on Windows plans.

Verify your app is running

Open the app's URL in a browser, or test it from the command line and confirm you get an HTTP 200 response:

curl -I https://<your-app-name>.azurewebsites.net

Expected output (validated during authoring):

HTTP/1.1 200 OK
Content-Type: text/html

You should see your page - for example, Hello from Azure App Service! - or the framework's default start page.

The running app in the browser

First request can be slow

The first request after a deployment may take a few seconds while the app starts. If you see a "starting" or 503 page, wait a moment and refresh.

Clean up resources

To avoid ongoing charges, delete the resources when you're done. Deleting the resource group removes the web app and its App Service plan.

azd down --purge --force

Summary

In this lab, you deployed your first web app to Azure App Service and confirmed it returns an HTTP 200 response. You learned how to:

  • Create an App Service plan and web app on both Linux and Windows.
  • Deploy code three ways - with the Azure Developer CLI, the Azure CLI, and the Azure portal.
  • Choose a runtime stack for .NET, Node.js, Python, Java, or PHP, and where language and OS support differ (Python and PHP are Linux-only).
  • Clean up resources to avoid charges.

Learn more