Deploy a Web App to Azure App Service

Deploy a live web app on Azure App Service using the CLI and governance controls.

Introduction

30 Second Summary

Every website needs a home on the internet. Cloud platforms like Azure let you launch a site in minutes without managing any physical servers.

In this project, you will deploy a web app to Azure App Service using the Azure CLI in Cloud Shell, then apply governance controls like tags and resource locks that map directly to the AZ-900 certification exam.

What You'll Build

By the end of this project, you will open a browser tab, visit a public URL, and see your own custom web page running on Azure's platform. You built it, tagged it, locked it, and monitored it entirely from a cloud-based terminal.

By the end of this project, you'll have:

  • A live web app at a public azurewebsites.net URL you can open in any browser and share with anyone.
  • A resource group with metadata tags organizing your resources, plus a resource lock you will test by attempting to delete your app and watching Azure refuse.
  • Real traffic data in Azure Monitor showing proof that your page visits are being tracked, alongside Service Health dashboards showing the status of Azure itself.
  • Secret Mission: create a deployment slot and perform a zero-downtime swap to update your live site without any interruption.

Are there any prerequisites?

You need an Azure free account to complete this project. Everything runs in Azure Cloud Shell (a browser-based terminal), so you do not need to install anything on your computer.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters.

Open Cloud Shell and Create a Resource Group

Every Azure project starts with two things: a way to talk to Azure, and a place to put your resources.

In this step, you will open Cloud Shell (a browser-based terminal that is already connected to your Azure account) and create a resource group (the container that holds every resource in this project).

In this step, get ready to:
  • Open Cloud Shell in the Azure Portal.
  • Confirm your active Azure subscription.
  • Create a resource group in the eastus region.
Launch Cloud Shell

Cloud Shell is a terminal built right into the Azure Portal. It comes with the Azure CLI preinstalled and automatically authenticates with your account, so there is nothing to install on your computer.

  • Open a new browser tab and navigate to portal.azure.com.
  • Sign in with the Microsoft account you used to create your Azure free account.
  • In the top navigation bar, click the Cloud Shell icon (it looks like a terminal/command prompt icon).
  • Select Bash as your shell environment.

First-time storage prompt

If this is your first time opening Cloud Shell, you will be prompted to create an Azure Storage account for your file share. Select your subscription, then click Create storage. This takes about 30 seconds.

After setup completes, you should see a Bash command prompt that looks like yourname@Azure:~$. This confirms Cloud Shell is ready.

Cloud Shell not loading?

Make sure you are using a supported browser (Edge, Chrome, Firefox, or Safari). If the shell panel stays blank for more than a minute, try refreshing the full page. Help me get Cloud Shell working.

Verify your subscription and create a resource group

Before creating resources, confirm which subscription Cloud Shell is using. Then you will create the resource group that holds everything for this project.

  • Check your active subscription by running this command:
az account show --query name -o tsv

You should see your subscription name printed (for example, Azure subscription 1 or Free Trial). This confirms Cloud Shell is talking to the right account.

  • Create your resource group by running this command:
az group create --name az900-webapp-rg --location eastus

What does this command do?

  • az group create tells the Azure CLI to create a new resource group.
  • --name az900-webapp-rg gives it a descriptive name. Every resource you create in this project will live inside this group.
  • --location eastus sets the Azure region. East US is a common default with broad service availability.

The command returns a block of JSON. Look for "provisioningState": "Succeeded" near the bottom. That confirms the resource group was created successfully.

Seeing a permission or subscription error?

If you see a message about authorization or subscription not found, your Cloud Shell may be pointed at a different subscription. Run az account list -o table to see all subscriptions, then run az account set --subscription "your-subscription-name" to switch. Help me fix my subscription issue.

Verify the resource group exists

Before you move on, take a moment to predict what you expect. You asked Azure to create a resource group named az900-webapp-rg in eastus. What columns do you think a table query will show?

  • Confirm the resource group details by running this command:
az group show --name az900-webapp-rg --query "{Name:name, Location:location, State:properties.provisioningState}" -o table

You should see a table with three columns: Name showing az900-webapp-rg, Location showing eastus, and State showing Succeeded.

What is a resource group?

A resource group is the fundamental organizational unit in Azure. It acts as a logical container that groups related resources together so you can manage, monitor, and delete them as a single unit.

The AZ-900 exam tests this concept heavily. Every Azure resource must belong to exactly one resource group, and the group's location determines where its metadata is stored.

Your command-line environment is ready and your first Azure resource is live. Next up, you will use this resource group to deploy a web app and see what Platform as a Service looks like in practice.

Deploy a Web App to App Service

Your resource group is created and ready to hold resources. Now you need a running application to see what PaaS means in practice.

Azure App Service handles the server, operating system, and runtime for you. You just push your code and the platform does the rest.

What is PaaS vs IaaS?

With IaaS (Infrastructure as a Service), you rent a virtual machine and manage everything yourself: the OS, patches, runtime, and scaling. With PaaS (Platform as a Service), the cloud provider handles all of that for you.

App Service is PaaS. You focus on your code. Azure handles the infrastructure. This distinction is the single most important concept on the AZ-900 exam.

In this step, get ready to:
  • Create an App Service plan on the Free tier.
  • Create a web app with a globally unique name.
  • Visit your live app at its public URL.
Create an App Service plan on the Free tier

An App Service plan defines the compute resources (CPU, memory, storage) that your web app runs on. The F1 SKU is the Free tier.

  • Create your App Service plan by running this command in Cloud Shell:
az appservice plan create --name az900-plan --resource-group az900-webapp-rg --sku F1

What does this command do?

  • --name az900-plan names the App Service plan.
  • --resource-group az900-webapp-rg places it in your existing resource group.
  • --sku F1 selects the Free tier: shared compute, 60 CPU minutes/day, 1 GB RAM, 1 GB storage, $0 cost.
  • You should see JSON output in your terminal confirming the plan was created. Look for "provisioningState": "Succeeded" in the response.

Seeing an error about the SKU?

  • Make sure you typed F1 in uppercase. The SKU name is case-sensitive.
  • If you see a message about the subscription not supporting the tier, confirm you are using the Azure free account subscription (not a sandbox or expired trial).

Help me troubleshoot this App Service plan error.

Create the web app

The web app resource is the actual application that runs on your plan. It gets a public URL at azurewebsites.net.

  • Create your web app by running this command:
az webapp create --name [[WEBAPP_NAME="yourname-az900-app"]] --resource-group az900-webapp-rg --plan az900-plan

What does this command do?

  • --name sets the globally unique name for your app. This becomes the subdomain of your public URL.
  • --plan az900-plan attaches the app to the Free tier plan you just created.
  • Valid characters are lowercase letters, numbers, and hyphens only.
  • You should see JSON output with your new app's details. Look for "state": "Running" in the response.

Seeing a conflict or naming error?

  • Web app names must be globally unique across all of Azure. If you see a conflict error, try adding more characters to make your name unique (for example, adding your initials and a number).
  • Make sure your name contains only lowercase letters (a-z), numbers (0-9), and hyphens (-).

Help me fix my web app naming error.

Visit your live app

Your web app is now running on Azure. Before you open it, what do you think you will see at the URL?

  • Open a new browser tab.
  • Navigate to your app's URL:
https://[[WEBAPP_NAME="yourname-az900-app"]].azurewebsites.net

You should see the Azure App Service default landing page. It displays the Azure logo and the message "Your web app is running and waiting for your content".

Why is this just a default page?

You created the web app resource, but you have not deployed any code yet. Azure shows this placeholder to confirm the infrastructure is working. In the next step, you will deploy your own HTML content.

Seeing a 404 or error page?

  • Double check that the URL matches your app name exactly. It should be https://your-app-name.azurewebsites.net with no typos.
  • Wait 30 seconds and refresh. New apps can take a moment to become available.
  • Make sure the previous az webapp create command completed without errors.

Help me troubleshoot my web app not loading.

Your web app is live and accessible to anyone with the URL. Next up, you will deploy your own custom content to replace the default landing page.

Deploy Your Own Content

Your web app is live with Azure's default landing page. This proves the infrastructure works, but you haven't deployed any of your own code yet.

In this step, you'll push a custom HTML page to your app. This is where the PaaS model shines. You provide the code, and Azure App Service handles the rest. No server configuration, no OS patching, no runtime setup.

In this step, get ready to:
  • Create a custom HTML file in Cloud Shell.
  • Package and deploy the file to your web app.
  • Verify your custom page is live at your public URL.
Create a custom HTML file in Cloud Shell

Azure App Service serves whatever files you deploy to it. You'll create a simple index.html file that replaces the default landing page.

  • Create a new directory for your project files and write the HTML by running these commands:
mkdir ~/myapp && cd ~/myapp
echo '<!DOCTYPE html><html><head><title>AZ-900 Project</title></head><body><h1>Hello from Azure App Service!</h1><p>This is my AZ-900 hands-on project.</p></body></html>' > index.html

What do these commands do?

  • mkdir ~/myapp creates a folder called myapp in your home directory.
  • cd ~/myapp moves you into that new folder.
  • echo '...' > index.html writes the HTML content into a file called index.html.
  • Confirm the file was created by running:
ls

You should see index.html listed in the output.

Don't see index.html?

  • Make sure you ran both lines together. If you only ran mkdir without the && and second command, run cd ~/myapp first, then the echo line again.
  • If you see an error about the directory already existing, that's fine. Just run cd ~/myapp and then the echo command.

Help me troubleshoot my Cloud Shell file creation.

Zip and deploy the file to your web app

Azure App Service accepts zip deployments. You'll package your HTML file into a zip archive and push it to your app with a single CLI command.

  • Package your file and deploy it by running these commands:
cd ~/myapp
zip deploy.zip index.html
az webapp deploy --resource-group az900-webapp-rg --name [[APPNAME="yourname-az900-app"]] --src-path deploy.zip --type zip

What do these commands do?

  • zip deploy.zip index.html packages your HTML file into a zip archive called deploy.zip.
  • az webapp deploy uploads the zip to your web app and unpacks it. The --type zip flag tells Azure to extract the archive's contents as your app's files.

This is the PaaS deployment model in action. You provide the code, and Azure handles everything else (web server, networking, TLS certificate, DNS).

The deploy command takes 10-20 seconds to complete. You'll see a JSON response confirming the deployment succeeded.

Seeing a 'ResourceNotFound' error?

  • Double-check that the --name value matches the exact app name you created in the previous step. It must be lowercase with hyphens only.
  • Make sure --resource-group is az900-webapp-rg (the resource group you created in Step 1).

Help me fix my az webapp deploy command.

Verify your custom page is live

Your deployment is complete. Before you check the result, think about what you expect to see.

What do you think will appear when you refresh your app's page? Will it still show the Azure default, or something different?

  • Switch to the browser tab showing your app (the one you opened in the previous step).
  • Refresh the page.

You should now see your custom page with the heading "Hello from Azure App Service!" and the paragraph "This is my AZ-900 hands-on project." instead of the Azure default landing page.

What just happened?

You pushed code to a fully managed platform and it went live instantly. No web server to install, no firewall rules to configure, no SSL certificate to generate. That's PaaS.

On the AZ-900 exam, you'll be asked to distinguish PaaS from IaaS (where you manage the VM and OS yourself). You just experienced firsthand why PaaS is simpler for deploying web apps.

Still seeing the default page?

  • Try a hard refresh: Cmd+Shift+R (macOS) or Ctrl+Shift+R (Windows). Your browser may be caching the old page.
  • Go back to Cloud Shell and confirm the deploy command's JSON output shows a status of 4 (which means succeeded). If not, re-run the deploy command.

Help me troubleshoot my App Service deployment.

Your custom content is live on the internet. Next up, you'll protect this app from accidental deletion and add metadata tags to organize your resources.

Apply Governance Controls

Your web app is live and serving content to the world. But right now, there is no metadata describing what this resource is for. Anyone with access to your subscription could also accidentally delete it.

The AZ-900 exam dedicates 30-35% of its weight to management and governance. Resource tags and resource locks are explicitly listed in the exam objectives. In this step, you will apply both to organize and protect your resources.

In this step, get ready to:
  • Add metadata tags to your resource group.
  • Apply a delete lock to prevent accidental deletion.
  • Verify the lock blocks deletion attempts.
Add resource tags

Tags are key-value pairs you attach to Azure resources. They help you organize resources, track costs, and apply policies across your subscription.

  • Add tags to your resource group by running this command in Cloud Shell:
az group update --name az900-webapp-rg --tags environment=learning project=az900-prep owner=[[YOURNAME="your name"]]

What does this command do?

  • az group update modifies an existing resource group without recreating it.
  • --tags sets key-value pairs as metadata. Here you are tagging the group with its environment, project purpose, and owner.
  • Tags do not affect how resources behave. They are purely organizational metadata that shows up in billing reports, policy filters, and the portal.

The command returns JSON output showing your updated resource group. You should see your three tags listed inside a tags object in the response.

Don't see your tags in the output?

  • Make sure each tag follows the key=value format with no spaces around the equals sign.
  • Confirm the resource group name is exactly az900-webapp-rg. A typo creates a new group instead of updating the existing one.

Why aren't my tags showing up on my resource group?

Apply a delete lock to the resource group

A resource lock prevents accidental changes. The CanNotDelete lock type allows reading and modifying a resource, but blocks anyone from deleting it.

  • Create a delete lock on your resource group by running this command:
az group lock create --name PreventDelete --resource-group az900-webapp-rg --lock-type CanNotDelete

What does this command do?

  • --name PreventDelete gives the lock a descriptive name you can reference later.
  • --lock-type CanNotDelete means users can still read and modify resources in this group, but cannot delete the group or any resource inside it.
  • The lock applies to the entire resource group and everything it contains, including your web app and App Service plan.

You should see JSON output confirming the lock was created with a level of CanNotDelete.

Now, before you move on, what do you think will happen if you try to delete this resource group? Take a moment to predict the outcome.

  • Test the lock by attempting to delete the resource group:
az group delete --name az900-webapp-rg --yes --no-wait

You should see an error message indicating the resource group cannot be deleted because of the lock. The error references CanNotDelete. This is exactly what you want. The lock is doing its job.

Why is a failed delete a good thing here?

In production environments, accidental deletions can take down entire applications. A CanNotDelete lock acts as a safety net. Even someone with full permissions cannot remove the resource group without first deliberately removing the lock.

This is one of the governance controls the AZ-900 exam tests. You just saw it work in practice.

Did the delete succeed instead of failing?

  • If the delete went through without an error, the lock was not applied correctly. Run az group lock list --resource-group az900-webapp-rg -o table to check whether the lock exists.
  • If no lock is listed, re-run the az group lock create command from above and make sure the resource group name matches exactly.

My resource group was deleted even though I created a lock. What went wrong?

Verify the lock is listed
  • Confirm your lock is in place by running:
az group lock list --resource-group az900-webapp-rg -o table

You should see a table with one row showing your PreventDelete lock with a level of CanNotDelete.

What does the -o table flag do?

The -o table flag formats CLI output as a human-readable ASCII table instead of the default JSON. This makes it easy to scan results at a glance. You can use -o table with any Azure CLI command.

Your resources are now organized with tags and protected from accidental deletion. Next up, you will explore Azure's monitoring tools to see how your app is performing and whether Azure itself is healthy.

Explore Monitoring Tools

Your app is live and protected by governance controls. Right now, though, you have no way to see how it's performing or whether Azure itself is having issues in your region.

Azure provides three monitoring tools that are explicitly tested on the AZ-900 exam: Azure Advisor, Service Health, and Azure Monitor. In this step, you'll explore all three to close the loop on the management and governance domain.

In this step, get ready to:
  • Check Azure Advisor recommendations for your subscription.
  • View Service Health status for your region.
  • View your app's HTTP metrics in Azure Monitor.
Check Azure Advisor recommendations

Azure Advisor analyzes your deployed resources and gives personalized recommendations across five categories: Reliability, Security, Performance, Cost, and Operational Excellence.

  • In the Azure Portal, click the search bar at the top of the page.
  • Type Advisor and select Advisor from the results.
  • On the Advisor Overview page, confirm you can see category tiles for Reliability, Security, Performance, Cost, and Operational Excellence.

What is Azure Advisor?

Azure Advisor is a free, personalized recommendations engine. It continuously evaluates your resources against Azure best practices and suggests improvements across the five categories you see on the Overview page.

You may see zero recommendations on a fresh Free-tier app. That's normal. Advisor will surface issues as your environment grows and you add more resources.

View Service Health

Service Health tells you whether Azure itself is having problems in the regions where your resources live. This separates "my code is broken" from "Azure is having an outage."

  • In the Azure Portal search bar, type Service Health and select Service Health from the results.
  • In the left menu, click Service issues to see if any Azure services in the East US region are currently affected.
  • Note whether you see active issues or an empty list (an empty list means Azure is healthy in your region right now).
  • In the left menu, click Health history to view past incidents that may have impacted your subscription.

Why does Service Health matter for AZ-900?

The AZ-900 exam explicitly tests your knowledge of Service Health as a monitoring tool. It helps you distinguish between a problem in your code and a platform-level outage that Microsoft is responsible for fixing.

View your app's metrics in Azure Monitor

Azure Monitor collects performance data from your running resources automatically. Your earlier page visits to the web app generated HTTP requests that Azure Monitor recorded.

  • In the Azure Portal search bar, type your app name (yourname-az900-app) and select your web app from the results.
  • In the left menu of your web app, click Monitoring, then select Metrics.
  • In the Metric dropdown, select Http 2xx.

Before you look at the chart, think about how many times you visited your web app's URL during this project. Do you expect to see data points on the graph?

You should see at least one data point on the chart. Each time you loaded your app's URL and received a successful response, Azure recorded it as an Http 2xx event.

What does Http 2xx mean?

Http 2xx refers to HTTP status codes in the 200 range, which indicate successful responses. Every time your browser loaded the web app and received the page content, the server returned a 200 OK status.

Azure Monitor tracks these automatically for App Service apps. No extra configuration is needed.

Don't see any data points?

Metrics can take a few minutes to appear. Try adjusting the time range in the top-right corner of the Metrics blade to Last hour.

If you still see nothing, open a new browser tab and visit your app URL again. Wait 2-3 minutes, then refresh the Metrics page.

Still stuck? Why am I not seeing Http 2xx data points in Azure Monitor for my App Service?

Secret mission

Create a Deployment Slot and Perform a Swap

Ready for a real-world deployment pattern? Deployment slots let you push updates to a staging environment and swap them into production with zero downtime. In this bonus challenge, you will upgrade your plan, create a staging slot, deploy different content, and swap it live.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses the Azure App Service Free F1 tier, so there are no ongoing costs as long as you stay on that tier.

Resources you used:

  • Resource group az900-webapp-rg (contains all resources below).
  • App Service plan az900-plan (Free F1 tier, $0).
  • Web app yourname-az900-app with public URL.
  • Resource lock PreventDelete (CanNotDelete type).
  • Cloud Shell files in ~/myapp/ directory.

What about the deployment slot?

If you completed the Secret Mission, you also have a staging deployment slot on your web app. Deleting the resource group removes everything inside it, including the slot.

Keep everything running

No action needed. Choose this if you are still actively building or want to keep your live app available.

  • The Free F1 tier costs $0 indefinitely. Your app stays live at its azurewebsites.net URL.
  • The resource lock remains in place, preventing accidental deletion.
  • Cloud Shell storage persists between sessions (5 GB included at no cost).

Pause - I'll come back to this later

The Free F1 tier does not accumulate charges, so there is nothing to pause. Your app and resources remain in place at no cost.

  • If you want to stop the app from being publicly accessible without deleting it, you can stop the web app from the Azure Portal by navigating to your app and clicking Stop in the top toolbar.
  • Your resource lock and tags remain untouched for when you return.

Delete - I don't want to use this again

Remove all project resources and start fresh. Deleting the resource group removes the plan, web app, lock, tags, and any deployment slots inside it.

  • First, remove the resource lock (you cannot delete a locked resource group). Run this in Cloud Shell:
az group lock delete --name PreventDelete --resource-group az900-webapp-rg
  • Now delete the resource group and all its contents by running:
az group delete --name az900-webapp-rg --yes --no-wait

What does --no-wait do?

The --no-wait flag returns control of your terminal immediately instead of waiting for Azure to finish deleting all resources. Deletion continues in the background and typically completes within a few minutes.

  • Clean up the local files in Cloud Shell by running:
rm -rf ~/myapp
  • Verify the resource group is gone (or being deleted) by running:
az group list -o table

You should no longer see az900-webapp-rg in the list. If it still shows with a Deleting state, wait a minute and run the command again.

Nice Work!

Nice Work!

You just deployed a live web app to Azure App Service using the Azure CLI in Cloud Shell, then layered governance and monitoring on top of it.

You've learned how to:

  • Deploy a web app to Azure App Service from the CLI and see it live at a public azurewebsites.net URL, experiencing the PaaS model firsthand (no server management, just push code).
  • Organize resources with a resource group and metadata tags, then protect them with a CanNotDelete resource lock to prevent accidental deletion.
  • Explore Azure Advisor, Service Health, and Azure Monitor metrics, covering all three AZ-900 exam domains: cloud concepts, Azure architecture and services, and management and governance.
  • Secret Mission: Create a deployment slot and perform a zero-downtime swap to simulate production deployments.

Ready to quiz yourself?