Deploy and Monitor a Web App on Azure
Deploy a Node.js app and static site to Azure with monitoring.
Introduction
30 Second Summary
Every website you visit runs on a server somewhere in the cloud. When teams deploy their apps, they need to choose where to host them, how to organize their resources, and how to know if something breaks.
In this project, you will deploy a Node.js web app and a static website to Azure, organize them under a tagged resource group, and set up monitoring alerts to watch for errors.
What You'll Build
You will build a two-part Azure deployment: a Node.js Express app running on Azure App Service (PaaS) and a static website hosted on Azure Blob Storage, both monitored and organized through Azure's governance tools.
By the end of this project, you'll have:
- A live Express.js web app deployed to Azure App Service, accessible at a public .azurewebsites.net URL.
- A static website hosted on Azure Blob Storage, served from a public web endpoint with no compute resources needed.
- A resource group with consistent tags and an Azure Monitor alert rule watching your app for errors.
- Secret Mission: Secure your Blob Storage files with a time-limited SAS token for controlled access.
Are there any prerequisites?
You need an Azure account (a free account with $200 credit works perfectly), Node.js installed on your Mac, and Homebrew ready to install the Azure CLI. Step 1 walks you through verifying everything.
Before We Start
Before you can deploy anything to Azure, you need the right tools on your machine. The Azure CLI lets you create, configure, and manage cloud resources directly from your terminal.
In this step, you'll install the Azure CLI, sign in to your Azure account, and create a resource group to organize everything you build in this project.
In this step, get ready to:
- Install and verify the Azure CLI.
- Sign in to Azure and confirm Node.js is ready.
- Create a tagged resource group for your project.
Install Azure CLI and sign in to Azure
The Azure CLI is a command-line tool that talks to Azure's APIs on your behalf. You'll use it throughout this project to create resources, deploy code, and manage storage.
- Open your terminal by pressing Cmd+Space to open Spotlight, then typing Terminal and pressing Enter.
- Install the Azure CLI by running this command:
brew update && brew install azure-cli
What does this command do?
`brew update` refreshes Homebrew's list of available packages so you get the latest version. `brew install azure-cli` downloads and installs the Azure CLI itself.
- This may take a minute or two. When it finishes, verify the installation by running:
az version
You should see JSON output that includes a version number for azure-cli.
✔️ I see a version number
The Azure CLI is installed and ready. Move on to the next command below.
ⓧ Command not found
If your terminal says the command is not found, try closing and reopening your terminal. Homebrew sometimes needs a fresh shell session to register new commands.
If it still doesn't work, confirm Homebrew is working by running:
brew doctor
Follow any instructions Homebrew provides, then re-run brew install azure-cli.
- Sign in to your Azure account by running:
az login
What happens when you run this?
Your default browser will open to a Microsoft sign-in page. Sign in with the same credentials you use for the Azure Portal.
After you sign in, your terminal will display your subscription information as JSON. This confirms the CLI is connected to your account.
Browser didn't open?
If the browser-based login fails, the CLI may fall back to device code flow. It will display a code and a URL in your terminal. Open the URL in any browser, enter the code, and sign in manually.
You can also force this mode by running az login --use-device-code.
help me troubleshoot my az login issue
- Confirm Node.js is installed by running:
node --version
You should see a version number like v20.x.x or higher. This confirms Node.js is ready for the Express app you'll build later.
Don't see a version number?
If you get a "command not found" error, you need to install Node.js before continuing. Visit the official Node.js website and download the LTS installer for macOS.
help me install Node.js on macOS
Create a resource group with tags
A resource group is a container that holds related Azure resources together. Every resource you create in this project will live inside one group, making it easy to manage and delete everything at once.
- Create your resource group by running:
az group create --name az900-project-rg --location eastus
What does this command do?
`--name az900-project-rg` gives your resource group a descriptive name. `--location eastus` places it in the East US data center region.
You should see JSON output with "provisioningState": "Succeeded" confirming it was created.
- Add tags to your resource group by running:
az group update --name az900-project-rg --tags environment=learning project=az900-webapp owner=[[YOURNAME="enter your name"]]
Why add tags?
Tags are key-value labels you attach to Azure resources. They help you organize, search, and track costs across projects. In a real workplace, tags like environment and owner help teams identify who owns what.
Seeing a permission or subscription error?
Make sure az login completed successfully before running these commands. You can verify by running az account show to confirm your subscription is active.
help me fix my Azure CLI permission error
Verify your setup in the Azure Portal
The Azure Portal is the web-based dashboard where you can visually inspect everything you created with the CLI. Verifying here confirms your commands worked correctly.
- Open your browser and go to the Azure Portal.
- In the search bar at the top, type Resource groups and select it from the results.
- Click on az900-project-rg in the list.
- Click Tags in the left sidebar to confirm your three tags (environment, project, owner) are listed.
You should see all three tags with their values displayed on the tags page.
Don't see your resource group?
Make sure you're signed in to the Portal with the same account you used for az login. If you have multiple subscriptions, check that the correct one is selected in the Portal's subscription filter.
help me find my resource group in the Azure Portal
Your tools are installed and your Azure environment is organized. Next up, you'll build a Node.js web app and deploy it live to Azure App Service.
Deploy a Node.js Web App to Azure App Service
In the previous step, you set up the Azure CLI and created a tagged resource group to organize your project resources. Now you're ready to deploy real application code to the cloud.
You'll build a simple Node.js web app using Express.js and deploy it to Azure App Service. App Service is Azure's PaaS (Platform as a Service) offering for hosting web applications without managing any underlying servers.
In this step, get ready to:
- Create a local Express.js web application.
- Deploy it to Azure App Service on the Free F1 tier.
- Verify the live deployment in your browser and the Azure Portal.
Create a local Express.js app
Express.js is a lightweight framework for building web servers in Node.js. You'll create a simple app that serves an HTML page showing information about the Azure services you're using.
- Move to your Desktop by running this command in your terminal:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir az900-webapp
cd az900-webapp
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Visual Studio Code and press Enter to open it.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop, select the az900-webapp folder, and click Open.
Why VS Code?
VS Code gives you a code editor and integrated terminal in one window. You can edit files and run commands without switching between apps.
- Create a new file by clicking the new file icon in the Explorer sidebar (or press Cmd+N on macOS, Ctrl+N on Windows).
- Name the file package.json.
- Paste the following code into package.json:
{
"name": "az900-webapp",
"version": "1.0.0",
"description": "AZ-900 learning project - Azure App Service demo",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.21.2"
}
}
What does this file do?
The package.json file defines your Node.js project. It lists the project name, version, and dependencies (libraries your app needs).
The "start": "node index.js" script tells Azure how to run your app. The express dependency is the web framework you'll use to serve your page.
- Save package.json by pressing Cmd+S (macOS) or Ctrl+S (Windows).
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
{
"name": "az900-webapp",
"version": "1.0.0",
"description": "AZ-900 learning project - Azure App Service demo",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.21.2"
}
}
- Open the integrated terminal in VS Code by pressing Ctrl+` (Control + backtick) on macOS or Windows.
- Install the project dependencies by running:
npm install
You should see output confirming that packages were added, including Express.
Seeing a permission error or command not found?
Make sure Node.js is installed by running node --version in your terminal. If it's not found, return to Step 1 to verify your Node.js installation.
Also check that your terminal is in the az900-webapp folder. Run pwd to confirm your current directory.
Help me troubleshoot npm install
- Create another new file in VS Code and name it index.js.
- Paste the following code into index.js:
const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
app.get("/", (req, res) => {
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AZ-900 Azure Web App</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 700px; margin: 50px auto; padding: 20px; background: #f0f4f8; }
h1 { color: #0078d4; }
.card { background: white; border-radius: 8px; padding: 20px; margin: 15px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.label { font-weight: bold; color: #333; }
.value { color: #0078d4; }
</style>
</head>
<body>
<h1>My Azure Web App</h1>
<p>This app is running on Azure App Service (PaaS).</p>
<div class="card">
<p><span class="label">Service Type:</span> <span class="value">Azure App Service (PaaS)</span></p>
<p><span class="label">Runtime:</span> <span class="value">Node.js</span></p>
<p><span class="label">Tier:</span> <span class="value">Free F1</span></p>
<p><span class="label">Timestamp:</span> <span class="value">${new Date().toISOString()}</span></p>
</div>
<div class="card">
<h2>AZ-900 Concepts Demonstrated</h2>
<ul>
<li><strong>PaaS:</strong> You deployed code without managing the underlying OS or server</li>
<li><strong>Shared Responsibility:</strong> Microsoft manages the infrastructure; you manage the app</li>
<li><strong>Scalability:</strong> You could scale this from Free to Premium without rewriting code</li>
</ul>
</div>
</body>
</html>
`);
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
What does this code do?
This Express app listens for web requests and responds with a styled HTML page. The page displays information about the Azure service hosting it (App Service, Node.js runtime, Free F1 tier) along with a live timestamp.
The process.env.PORT || 3000 line lets Azure assign the port automatically in production, while falling back to port 3000 for local testing.
The bottom section of the HTML highlights three AZ-900 concepts: PaaS, the shared responsibility model, and scalability.
- Save index.js by pressing Cmd+S (macOS) or Ctrl+S (Windows).
- Test your app locally by running this in the integrated terminal:
node index.js
You should see Server running on port 3000 in the terminal output.
- Stop the server by pressing Ctrl+C in the terminal.
Seeing a module not found error?
If you see an error mentioning the express module cannot be found, make sure you ran npm install successfully before starting the server. Check that a node_modules folder exists in your az900-webapp directory.
Help me fix my module error
✔️ Awesome, I've got everything!
Great. Double check both files are saved before moving on to deployment.
ⓧ I'd like to double check the full code
{
"name": "az900-webapp",
"version": "1.0.0",
"description": "AZ-900 learning project - Azure App Service demo",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.21.2"
}
}
const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
app.get("/", (req, res) => {
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AZ-900 Azure Web App</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 700px; margin: 50px auto; padding: 20px; background: #f0f4f8; }
h1 { color: #0078d4; }
.card { background: white; border-radius: 8px; padding: 20px; margin: 15px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.label { font-weight: bold; color: #333; }
.value { color: #0078d4; }
</style>
</head>
<body>
<h1>My Azure Web App</h1>
<p>This app is running on Azure App Service (PaaS).</p>
<div class="card">
<p><span class="label">Service Type:</span> <span class="value">Azure App Service (PaaS)</span></p>
<p><span class="label">Runtime:</span> <span class="value">Node.js</span></p>
<p><span class="label">Tier:</span> <span class="value">Free F1</span></p>
<p><span class="label">Timestamp:</span> <span class="value">${new Date().toISOString()}</span></p>
</div>
<div class="card">
<h2>AZ-900 Concepts Demonstrated</h2>
<ul>
<li><strong>PaaS:</strong> You deployed code without managing the underlying OS or server</li>
<li><strong>Shared Responsibility:</strong> Microsoft manages the infrastructure; you manage the app</li>
<li><strong>Scalability:</strong> You could scale this from Free to Premium without rewriting code</li>
</ul>
</div>
</body>
</html>
`);
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Deploy to Azure App Service using the CLI
Your app works locally. Now you'll push it to Azure with a single command that creates everything needed to host it in the cloud.
- Choose a globally unique name for your app. This name becomes part of your public URL, so it must be unique across all of Azure. A good format is your name followed by a project identifier (for example, janedoe-az900-webapp).
- Deploy your app to Azure App Service by running this command in your integrated terminal:
az webapp up --name [[APP_NAME="your-unique-app-name"]] --resource-group az900-project-rg --sku F1
What does this command do?
The az webapp up command does several things at once: it creates an App Service plan, creates a web app resource, and deploys your code as a ZIP package.
The --sku F1 flag specifies the Free F1 pricing tier, which costs $0. The --resource-group flag places the app inside your existing tagged resource group.
This command takes 1-2 minutes to complete. When it finishes, look for a URL in the output that ends in .azurewebsites.net.
Seeing a name conflict or authentication error?
If you see an error about the app name already being taken, choose a more unique name and run the command again. App names must be globally unique across all Azure customers.
If you see an authentication error, run az login again to refresh your session, then retry the deploy command.
Help me troubleshoot my deployment
Verify your live app
- Copy the URL from the command output (it looks like https://your-app-name.azurewebsites.net).
- Open the URL in your browser.
You should see your styled web page with the blue heading "My Azure Web App" and two cards showing service information and AZ-900 concepts.
- Now explore your app in the Azure Portal. Go to portal.azure.com in your browser.
- Search for App Services in the top search bar.
- Click on your app name in the list.
- Explore the Overview page to see your app's URL, status, and resource group.
- Click Configuration in the left sidebar to see your app's runtime settings.
- Click App Service Plan in the left sidebar to confirm you're on the Free F1 tier.
Why explore the Portal?
The AZ-900 exam tests your familiarity with both CLI and Portal interfaces. Navigating the Portal now helps you recognize where settings live when exam questions reference them.
What is the shared responsibility model?
With App Service (PaaS), Microsoft manages the physical servers, operating system, networking, and patching. You are responsible only for your application code and its configuration.
This is different from running a Virtual Machine (IaaS), where you would also manage the OS, updates, and security patches yourself.
Your Node.js app is live on Azure App Service. Next up, you'll host a static website on Azure Blob Storage to see how Azure handles pure file hosting without any server compute.
Host a Static Website on Azure Blob Storage
Your Node.js web app is live on App Service, proving you can deploy code to Azure's PaaS compute layer.
Now you'll host a static website on Azure Blob Storage. This approach uses pure storage with no server at all. Comparing the two side by side is one of the clearest ways to understand the difference between compute and storage services on Azure.
In this step, get ready to:
- Create a storage account with proper tags in your resource group.
- Enable static website hosting and upload HTML files via the CLI.
- Access your static site at its public Blob Storage endpoint.
Create a storage account in your resource group
A storage account is the top-level container for all Azure Storage services. You need one before you can store blobs, files, or host a static website.
- Create your storage account by running this command in your terminal:
az storage account create --name [[STORAGENAME="your-unique-storage-name"]] --resource-group az900-project-rg --location eastus --sku Standard_LRS --kind StorageV2
What does this command do?
- --name sets a globally unique name for your storage account. It must be 3-24 characters, lowercase letters and numbers only.
- --sku Standard_LRS selects Locally Redundant Storage, the cheapest replication option.
- --kind StorageV2 creates a general-purpose v2 account, which supports static website hosting.
Pick a memorable storage name
Try something like az900yourname or yournamestoragesite. No hyphens or uppercase letters are allowed.
- You should see a large JSON response in your terminal. Look for "provisioningState": "Succeeded" to confirm the account was created.
Seeing a name-already-taken error?
Storage account names must be globally unique across all of Azure. If your chosen name is taken, try adding a few numbers to the end.
Also confirm your name uses only lowercase letters and numbers, with no hyphens or special characters.
Help me fix my storage account creation error.
- Add tags to your storage account by running:
az storage account update --name [[STORAGENAME="your-unique-storage-name"]] --resource-group az900-project-rg --tags environment=learning project=az900-webapp
Why tag every resource?
Tags let you filter, search, and track costs across all your Azure resources. In a real organization, consistent tagging is a governance requirement. Practicing it now builds a professional habit.
- You should see another JSON response. Confirm that the tags section shows environment and project keys in the output.
Enable static website hosting and upload files
With your storage account ready, you can turn on the static website feature. This tells Azure to serve files from a special container called $web as a public website.
- Enable static website hosting by running:
az storage blob service-properties update --account-name [[STORAGENAME="your-unique-storage-name"]] --static-website --index-document index.html --404-document 404.html
What does this command do?
- --static-website enables the static website feature on your storage account.
- --index-document index.html tells Azure which file to serve when someone visits the root URL.
- --404-document 404.html tells Azure which file to show when someone visits a page that does not exist.
- You should see a JSON response with "staticWebsite": { "enabled": true } confirming the feature is active.
Seeing a permissions error?
Make sure you are still signed into the Azure CLI. Run az account show to confirm your session is active. If it expired, run az login again.
Help me fix a permissions error when enabling static website hosting.
Now you need HTML files to upload. You will create a static-site folder inside your project directory with two pages.
- Create the static-site folder by running:
mkdir static-site
- Confirm the folder exists by running:
ls
- You should see static-site listed alongside index.js, package.json, and node_modules.
- Create the static-site/index.html file in your code editor with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Azure Blob Storage Static Site</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 700px; margin: 50px auto; padding: 20px; background: #e8f5e9; }
h1 { color: #2e7d32; }
.card { background: white; border-radius: 8px; padding: 20px; margin: 15px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
</style>
</head>
<body>
<h1>Static Website on Azure Blob Storage</h1>
<p>This page is served directly from Azure Blob Storage with no web server.</p>
<div class="card">
<h2>How This Works</h2>
<ul>
<li>Files are stored as blobs in the $web container</li>
<li>Azure serves them via a public web endpoint</li>
<li>No compute resources needed (no App Service, no VM)</li>
<li>You pay only for storage and bandwidth used</li>
</ul>
</div>
<div class="card">
<h2>Storage vs Compute</h2>
<p>This static site costs fractions of a cent per month because it uses only storage. Your App Service app uses compute (CPU time) which is why it has quotas on the Free tier.</p>
</div>
</body>
</html>
What does this page show?
This HTML page explains how Blob Storage static hosting works and contrasts it with your App Service app. It highlights that static sites need zero compute resources, which is why they cost almost nothing.
- Save static-site/index.html.
- Create the static-site/404.html file in your code editor with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - Page Not Found</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 700px; margin: 50px auto; padding: 20px; text-align: center; }
h1 { color: #d32f2f; font-size: 4em; }
</style>
</head>
<body>
<h1>404</h1>
<p>This page does not exist. Navigate back to the homepage.</p>
</body>
</html>
What is the 404 page for?
When someone visits a URL that does not match any file in your $web container, Azure serves this page instead. It provides a friendly error rather than a generic browser message.
- Save static-site/404.html.
✔️ Awesome, I've got everything!
Great. Double check you have saved both files inside the static-site folder.
ⓧ I'd like to double check the full code
Here is static-site/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Azure Blob Storage Static Site</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 700px; margin: 50px auto; padding: 20px; background: #e8f5e9; }
h1 { color: #2e7d32; }
.card { background: white; border-radius: 8px; padding: 20px; margin: 15px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
</style>
</head>
<body>
<h1>Static Website on Azure Blob Storage</h1>
<p>This page is served directly from Azure Blob Storage with no web server.</p>
<div class="card">
<h2>How This Works</h2>
<ul>
<li>Files are stored as blobs in the $web container</li>
<li>Azure serves them via a public web endpoint</li>
<li>No compute resources needed (no App Service, no VM)</li>
<li>You pay only for storage and bandwidth used</li>
</ul>
</div>
<div class="card">
<h2>Storage vs Compute</h2>
<p>This static site costs fractions of a cent per month because it uses only storage. Your App Service app uses compute (CPU time) which is why it has quotas on the Free tier.</p>
</div>
</body>
</html>
Here is static-site/404.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - Page Not Found</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 700px; margin: 50px auto; padding: 20px; text-align: center; }
h1 { color: #d32f2f; font-size: 4em; }
</style>
</head>
<body>
<h1>404</h1>
<p>This page does not exist. Navigate back to the homepage.</p>
</body>
</html>
- Upload both files to your storage account's $web container by running:
az storage blob upload-batch --account-name [[STORAGENAME="your-unique-storage-name"]] --destination '$web' --source ./static-site
What does this command do?
- --destination '$web' targets the special container Azure created when you enabled static website hosting.
- --source ./static-site tells the CLI to upload every file from your local static-site folder.
- You should see output listing each uploaded file (index.html and 404.html) with upload progress.
Seeing a 'container not found' error?
The $web container only exists after you successfully enable static website hosting. Re-run the az storage blob service-properties update command from earlier in this substep and confirm you see "enabled": true in the output.
Help me fix a container not found error when uploading to $web.
Access your static website
Your files are uploaded. Now you need the public URL that Azure assigned to your static site.
- Get your static website URL by running:
az storage account show --name [[STORAGENAME="your-unique-storage-name"]] --resource-group az900-project-rg --query "primaryEndpoints.web" --output tsv
What does this command do?
The --query flag uses JMESPath to extract just the web endpoint from the full account details. The --output tsv flag strips quotes so you get a clean URL you can paste directly into your browser.
- Copy the URL from the output. It will look something like https://yourstoragename.z13.web.core.windows.net/.
- Paste the URL into your browser and press Enter.
You should see a green-themed page titled "Static Website on Azure Blob Storage" with two cards explaining how static hosting works and how it compares to compute.
Why use Blob Storage instead of App Service for static files?
App Service runs a server process that uses CPU time. For pages that are just HTML, CSS, and images, no server-side processing is needed. Blob Storage serves the files directly from storage at a fraction of the cost.
This distinction between compute and storage is a core concept on the AZ-900 exam.
- Test the 404 page by adding a fake path to your URL, like /nonexistent-page. You should see a red "404" heading and the message "This page does not exist."
Seeing a generic error instead of your site?
Confirm that your static-site folder contains index.html (not a subfolder with the file inside). The upload-batch command mirrors the folder structure into the $web container.
Also check that you spelled index.html exactly as specified in the --index-document flag. The filename is case-sensitive.
Help me troubleshoot my static website not loading on Blob Storage.
You now have two live websites on Azure. One runs on a managed server (App Service) and one serves files straight from storage (Blob Storage). Next up, you will set up monitoring and alerts to keep an eye on your resources.
Configure Monitoring and Cost Alerts
Your web app and static site are both live and accessible to the world. The next question is: how do you know when something breaks?
Azure Monitor gives you visibility into your app's health by tracking metrics and firing alerts when thresholds are breached. In this step, you'll create an alert rule, explore performance metrics, and check your costs in the Cost Management dashboard.
In this step, get ready to:
- Set up an Azure Monitor alert rule for your App Service.
- Explore metrics charts to see your app's performance baseline.
- Review Cost Management to confirm your free-tier resources cost $0.
Set up an Azure Monitor alert for your App Service
An alert rule watches a metric on your resource and fires a notification when a condition is met. You'll create one that emails you whenever your App Service returns any HTTP 5xx server errors.
- In the Azure Portal, type your App Service name in the top search bar and click on it to open the resource.
- In the left sidebar, scroll down to the Monitoring section and click Alerts.
- Click + Create and select Alert rule.
What is an alert rule?
An alert rule combines three things: the resource to monitor, a condition that triggers the alert, and an action (like sending an email). Azure Monitor evaluates the condition at regular intervals and fires the alert when the threshold is breached.
- On the Condition tab, click the Signal name field.
- Select Http5xx from the list of popular signals (this is the HTTP Server Errors metric).
- In the Alert logic section, set the Operator to Greater than.
- Set the Threshold value to 0.
- Leave the other defaults (aggregation type, check frequency) as they are.
This means the alert fires whenever even a single 5xx error occurs.
- Click the Actions tab.
- Click Create action group.
- Give the action group a name like az900-email-group.
- Select the Notifications tab inside the action group wizard.
- For Notification type, select Email/SMS message/Push/Voice.
- Check the Email box and enter your email address.
- Click OK, then name the notification (e.g. email-me).
- Click Review + create and then Create to save the action group.
- Back on the alert rule wizard, click the Details tab.
- Enter an Alert rule name like http-5xx-alert.
- Set the Severity to 2 - Warning.
- Make sure Enable upon creation is checked.
- Click Review + create and then Create to save the alert rule.
You should see a confirmation that the alert rule was created successfully.
Why monitor HTTP 5xx errors?
HTTP 5xx responses mean your server hit an unrecoverable error. Even one 5xx can indicate a crash, a misconfiguration, or a resource exhaustion issue. Setting the threshold to 0 means you'll know immediately if something goes wrong.
On the AZ-900 exam, monitoring falls under the "Azure management and governance" domain, which covers 30-35% of questions.
Don't see Http5xx in the signal list?
Make sure you navigated to the alert from your App Service resource (not from Monitor in the top-level menu). When you create the alert from the resource itself, the scope is automatically set to your App Service and the relevant signals appear.
Help me find the Http5xx signal for my App Service alert.
Explore Azure Monitor metrics
Metrics give you a real-time view of how your app is performing. Your earlier test visits to the web app generated some baseline activity you can now see charted.
- In the Azure Portal, navigate back to your App Service resource.
- In the left sidebar under Monitoring, click Metrics.
- In the Metric dropdown, select CPU Time.
- You should see a chart showing CPU usage over time.
- Click Add metric to add another line to the chart.
- Select Requests from the Metric dropdown.
- Click Add metric one more time and select Response Time.
You should now see three metrics charted together. The small spikes correspond to your earlier browser visits to the app.
What do these metrics tell you?
CPU Time shows how much compute your app consumed. On the Free F1 tier, you're limited to 60 CPU minutes per day. Requests counts how many HTTP calls your app received. Response Time shows how long each request took to process.
Together, these three metrics give you a quick health check. A spike in response time with flat requests might indicate a slow dependency. A spike in CPU time with high requests might mean you need to scale up.
Charts showing no data?
Make sure the time range at the top of the chart is set to at least the last hour. If you just deployed, it may take a few minutes for metric data to appear. Try refreshing the page or visiting your app URL once more in the browser to generate fresh data.
My Azure Monitor metrics chart is empty for my App Service.
Check Cost Management
Understanding Azure billing is a core skill for the AZ-900 exam. You'll now confirm that your free-tier resources are costing nothing and see how Azure tracks spending over time.
- In the Azure Portal search bar at the top, type Cost Management + Billing and select the matching result.
- In the left sidebar, click Cost Management.
- Click Cost Analysis.
You should see an overview of your accumulated costs. Since your App Service runs on the Free F1 tier and your Blob Storage holds only a few tiny HTML files, the total should show $0.00 (or a negligible fraction of a cent).
- Explore the default Accumulated costs view. This shows how your spending trends over the current billing period.
- Try selecting Cost by resource from the views to see which specific resources contribute to your bill.
- Notice how your az900-project-rg resource group appears, grouping all your project resources together.
Why does it show $0.00?
The App Service Free F1 tier costs $0 because Microsoft provides shared compute at no charge (limited to 60 CPU minutes per day). Blob Storage charges for storage and bandwidth used, but a few KB of HTML files costs fractions of a cent.
In a production environment, Cost Management becomes essential for catching unexpected spend. The Cost Analysis view helps you spot anomalies before they become expensive surprises.
Don't see Cost Analysis?
Cost Analysis requires your Azure subscription to have the Cost Management feature enabled (it is by default for most subscription types). If you're on a Visual Studio or sponsored subscription, you may need to navigate via your subscription directly: search for Subscriptions, select yours, then click Cost analysis in the left menu under Cost Management.
I can't find Cost Analysis in the Azure Portal.
Secret mission
Secure Blob Access with a SAS Token
You deployed a public static website, but what if you have files that should only be accessible for a limited time? In this secret mission, you will generate a Shared Access Signature (SAS) token to create a time-limited, read-only URL for a specific blob. This demonstrates Azure's security model for granting temporary access without exposing your storage account keys.
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. The App Service Free F1 tier costs $0, and Blob Storage for a few HTML files costs fractions of a cent per month. This project is safe to leave running at no meaningful cost.
Resources you used:
- Resource group az900-project-rg (contains all Azure resources below).
- App Service plan and web app on Free F1 tier.
- Storage account your-unique-storage-name with static website ($web container) and private container (private-files).
- Azure Monitor alert rule on your App Service.
- Local project files (az900-webapp folder, static-site folder, secret-report.txt).
- Azure CLI (installed via Homebrew).
Keep everything running
No action needed. Choose this if you are still actively building or plan to reference these resources for AZ-900 study.
- The App Service Free F1 tier has no charges. Your web app stays live at its .azurewebsites.net URL.
- Blob Storage for a few HTML files costs fractions of a cent per month.
- The Azure Monitor alert rule is free.
- Azure CLI remains installed on your Mac for future projects.
Pause - I'll come back to this later
Stop your App Service to free up the shared compute slot, even though the Free tier has no charges regardless.
- In the Azure Portal, navigate to your App Service.
- Click Stop in the top toolbar to halt the web app.
- Your storage account and static website remain active but cost nothing meaningful.
- To resume later, return to the App Service and click Start.
Delete - I don't want to use this again
Remove all project resources completely. Deleting the resource group removes everything inside it in one command.
- Delete all Azure resources by running this command in your terminal:
az group delete --name az900-project-rg --yes --no-wait
What does this command do?
The --yes flag skips the confirmation prompt. The --no-wait flag returns control to your terminal immediately while Azure deletes resources in the background.
This single command removes your App Service plan, web app, storage account (including all containers and blobs), and the Azure Monitor alert rule.
- Delete your local project files by running:
cd ~/Desktop
rm -rf az900-webapp
rm -rf static-site
rm -f secret-report.txt
- Uninstall Azure CLI by running:
brew uninstall azure-cli
- Verify the resource group is gone by running:
az group show --name az900-project-rg
You should see a "ResourceGroupNotFound" error, confirming deletion is complete.
Nice Work!
Nice Work!
You just deployed two live websites to Azure and set up real cloud governance around them. You now have hands-on experience with the core services covered in the AZ-900 exam.
Here's what you accomplished:
- Deployed a live Node.js web app to Azure App Service on the Free F1 tier, demonstrating PaaS compute in action.
- Hosted a static website on Azure Blob Storage using the CLI, contrasting pure storage with compute-based hosting.
- Organized all resources under a tagged resource group and configured Azure Monitor alerts to track application health and costs.
- Secret Mission: Generated a time-limited SAS token for secure blob access, proving you can control who sees your files and for how long.
Ready to quiz yourself?