Build an Azure Incident Intake MVP
Build an Azure Functions app that queues and processes incident reports.
Introduction
30 Second Summary
A campus system failure can quickly become a trail of messages with no clear reference number. That uncertainty makes it hard to prove what support received or what happened next.
In this project, you will build a deployed incident intake dashboard on Microsoft Azure that returns a traceable incident ID immediately. Azure Queue Storage carries each report to a separate worker in Azure Functions with its activity visible in Application Insights.
What You'll Build
A deployed dashboard where a mentor submits a realistic incident, receives a traceable ID, then watches the background worker process it in Azure telemetry.
By the end of this project, you'll have:
- A responsive incident form that accepts a title, category, severity, and description.
- An HTTP 202 response that gives every accepted report a traceable incident ID with a Queued status.
- A repeatable web-to-queue-to-worker demonstration that shows request handling and background processing as separate activities.
- Secret Mission: Add a visual escalation state that makes Critical incidents impossible to miss.
Are there any prerequisites?
You need an Azure account with an active subscription. Your account also needs permission to create resources.
This guide walks you through installing and verifying Python, Visual Studio Code, Azure CLI, and Azure Functions Core Tools on Windows.
Before We Start
Before any hands-on work, this checkpoint commits you to a focused incident intake MVP. Your system needs a named incident submitter plus a clear asynchronous role for the background worker.
Set Up the Windows Toolchain
Your mentor demo can fail before it reaches Microsoft Azure when local versions or sign-in state are unknown.
Visual Studio Code gives you one workspace for the project. Python runs the function code.
Azure Functions Core Tools handles function deployment. Azure CLI connects your terminal to Azure. Windows PowerShell gives each tool a shared command line.
In this step, get ready to:
- Install the four tools required for Azure Functions development.
- Prepare the incident-intake-mvp workspace with an activated virtual environment.
- Sign in to Azure before verifying the complete toolchain.
Install and verify the required tools
Each version check tells you whether the correct tool already exists on your computer. Choose the tab that matches the result you see.
- Press the Windows key to open Windows search.
- Type Windows PowerShell and press Enter.
Visual Studio Code provides the editor for every project file. Its Windows User setup also makes the code command available to PowerShell.
- Check the installed Visual Studio Code version by running this command:
code --version
What does this check show?
The command prints the Visual Studio Code version on its first output line. It also confirms that PowerShell can find the editor.
✔️ I see the required version
The first output line shows 1.143.1. Your editor is ready.
ⓧ I see an older version
- Open the official Visual Studio Code updates page.
- Download the Windows User setup for version 1.143.1.
- Run the downloaded installer.
- Keep the default installation options.
ⓧ Command not found
- Open the official Visual Studio Code Windows setup guide.
- Download the Windows User setup for version 1.143.1.
- Run the downloaded installer.
- Keep the default installation options.
Python provides the runtime for your Azure Functions code. The project uses version 3.13.17.
- Check the installed Python version by running this command:
python --version
What does this check show?
The command prints the Python version selected by PowerShell. This catches older installations that could run incompatible dependencies.
✔️ I see the required version
PowerShell reports Python 3.13.17. Your runtime is ready.
ⓧ I see an older version
- Open the official Python 3.13.17 release page.
- Download the recommended 64-bit Windows installer.
- Run the downloaded installer.
- Enable the installer option that adds Python to your command path.
ⓧ Command not found
- Open the official Python 3.13.17 release page.
- Download the recommended 64-bit Windows installer.
- Run the downloaded installer.
- Enable the installer option that adds Python to your command path.
Core Tools packages your local function project for Azure. The required release is 4.16.0.
- Check the installed Core Tools version by running this command:
func --version
What does this check show?
The command prints the Azure Functions Core Tools version. It proves the func deployment command is available to PowerShell.
✔️ I see the required version
PowerShell reports 4.16.0. Core Tools is ready.
ⓧ I see an older version
- Open the official Core Tools release page.
- Download the 64-bit MSI for version 4.16.0.
- Run the downloaded MSI.
- Complete the installation with the default options.
ⓧ Command not found
- Open the official Core Tools release page.
- Download the 64-bit MSI for version 4.16.0.
- Run the downloaded MSI.
- Complete the installation with the default options.
Azure CLI provides the authentication commands used in this step. The required release is 2.91.0.
- Check the installed Azure CLI version by running this command:
az version
What does this check show?
The command prints version details for Azure CLI. It confirms that the az command is available before authentication.
✔️ I see the required version
The output includes Azure CLI version 2.91.0. Your cloud command line is ready.
ⓧ I see an older version
The upgrader can request confirmation before it changes the local installation. Approve that prompt so the update can finish.
- Upgrade Azure CLI by running this command:
az upgrade
What does this command do?
The command upgrades Azure CLI to the latest supported release. It also checks installed Azure CLI extensions.
ⓧ Command not found
Windows may request permission or package agreement confirmation during this installation. Approve those prompts to continue.
- Install Azure CLI by running this command:
winget install --exact --id Microsoft.AzureCLI
What does this command do?
WinGet installs the official Microsoft Azure CLI package. The installation adds the az command to your computer.
Having trouble with an installer?
Use the 64-bit Windows installer for each tool. Corporate devices may require administrator approval before an installer can change your computer.
If an installation or version check still fails, ask for help diagnosing the Windows toolchain.
- Close every PowerShell window after all installers finish.
- Press the Windows key to open Windows search.
- Type Windows PowerShell and press Enter.
The reopened session now reads the command path changes made by each installer.
Create the project workspace
A dedicated folder keeps the dashboard code beside the Azure Functions files you add later. A virtual environment keeps this project's Python dependency separate from other projects.
Visual Studio Code may show a Workspace Trust prompt after opening the folder. You created this empty folder yourself, so you can trust its contents.
- Create the incident-intake-mvp folder on your Desktop before opening it in Visual Studio Code by running these commands:
cd ~/Desktop
mkdir incident-intake-mvp
cd incident-intake-mvp
code .
What do these commands do?
- The first command moves PowerShell to your Desktop.
- The next command creates the incident-intake-mvp folder.
- The third command makes that folder the active PowerShell location.
- The final command opens the active folder as a Visual Studio Code workspace.
- Select Yes, I trust the authors if Visual Studio Code shows the Workspace Trust prompt.
- Confirm that the Explorer sidebar shows incident-intake-mvp as the open folder.
Did the folder fail to open?
Return to the reopened PowerShell window. Confirm its current path ends with incident-intake-mvp before running the editor command again.
If PowerShell cannot find the editor command, ask for help fixing the Visual Studio Code command path.
- Create the virtual environment before activating it by running these commands in the reopened PowerShell window:
python -m venv .venv
.venv\Scripts\Activate.ps1
What do these commands do?
The first command creates an isolated Python environment in the .venv folder. The second command directs this PowerShell session to use that environment.
- Confirm that (.venv) appears at the start of the PowerShell prompt.
- Confirm that .venv appears in the Visual Studio Code Explorer sidebar.
Virtual environment not activated?
Confirm that PowerShell is inside the incident-intake-mvp folder. The activation path only works after the environment has been created there.
If PowerShell blocks the activation script, ask for help with the exact policy message.
The requirements.txt file pins the Azure Functions Python library used by the project. Pinning it gives your local environment one repeatable dependency version.
- Select the New File icon in the Visual Studio Code Explorer sidebar.
- Enter requirements.txt as the file name.
- Add the pinned dependency by copying this line into the new file:
azure-functions==2.3.0
What does this dependency do?
The azure-functions package provides the Python types and decorators used by the function app. Version 2.3.0 supports the Python runtime installed in this step.
- Save requirements.txt by pressing Ctrl+S.
- Confirm that requirements.txt appears beneath incident-intake-mvp in the Explorer sidebar.
✔️ Awesome, I've got everything!
Your dependency file is complete. Double-check that you saved it before continuing.
ⓧ I'd like to double check the full code
azure-functions==2.3.0
What should match?
Your file contains one dependency pin. The package name and version must match this reference exactly.
Does the file look different?
Confirm the file is named requirements.txt without another extension. Windows can hide file extensions in some views.
If the dependency installation later rejects the file, ask for help checking requirements.txt.
- Install the pinned dependency into the activated virtual environment by running this command:
pip install -r requirements.txt
What does this command do?
The command reads requirements.txt before installing its package into .venv. Your global Python installation stays separate.
- Wait for the installation to return to the (.venv) PowerShell prompt.
- Confirm that the installation finishes without a dependency error.
Did the dependency fail to install?
Confirm the PowerShell prompt starts with (.venv). Confirm that the Python version reported earlier is 3.13.17.
If the install still fails, ask for help with the pip output.
Authenticate and verify the toolchain
Azure CLI authentication connects local commands to your Azure account. Checking the active subscription prevents later resources from landing in the wrong place.
Interactive sign-in opens an authentication flow outside PowerShell. Complete it with the Azure account that has the subscription for this project.
- Start the Azure sign-in flow by running this command in the activated PowerShell session:
az login
What does this command do?
The command starts interactive Azure authentication. PowerShell receives your available subscription context after sign-in succeeds.
- Complete the sign-in flow with the Azure account you intend to use.
- Return to the activated PowerShell session after authentication finishes.
PowerShell now has an authenticated Azure session. The next check reveals which subscription is active.
- Display the active Azure subscription by running this command:
az account show
What does this command show?
The output describes the subscription currently selected by Azure CLI. Its subscription name should match the one you intend to use for the deployment.
- Confirm that the displayed subscription name matches your intended Azure subscription.
Seeing the wrong subscription?
Run the sign-in command again with the Azure account that owns the intended subscription. Stop before creating resources if the subscription still looks wrong.
If your account has several subscriptions, ask for help selecting the correct Azure subscription.
A successful import proves that the dependency was installed into the Python environment currently used by PowerShell.
- Verify the Azure Functions dependency by running this command:
python -c "import azure.functions; print('azure-functions import OK')"
What should this print?
Python imports the azure.functions module before printing azure-functions import OK. Seeing that message proves the activated environment can load the dependency.
Import did not succeed?
Confirm that (.venv) appears in the PowerShell prompt. Run the dependency installation command again from the folder containing requirements.txt.
If Python still cannot load the module, ask for help diagnosing the active environment.
Before you run the final checks, which three version numbers do you expect PowerShell to report?
- Verify the complete Windows toolchain by running these commands:
python --version
func --version
az version
What should you see?
- The Python check reports 3.13.17.
- The Core Tools check reports 4.16.0.
- The Azure CLI output includes version 2.91.0.
That is the fragile setup work handled. Your editor, runtime, deployment tools, Azure session, virtual environment, and Python dependency now work together.
Your Windows toolchain is ready. Next, you will build the dashboard before deliberately testing what happens when no backend exists.
See the Dashboard Without a Backend
Your Windows toolchain is verified. Visual Studio Code already has the incident-intake-mvp folder open.
An incident form proves its value when a submission reaches a backend. This step tests that boundary before the server-side work exists.
In this step, get ready to:
- Create the Campus Incident Intake dashboard.
- Inspect the responsive form plus its architecture flow.
- Submit a High incident to test the current request path.
Create the dashboard structure
The page structure gives the dashboard two clear areas. One explains the system flow while the other collects the incident details.
- Select the Explorer view in the left Activity Bar.
- Select New File in the Explorer toolbar.
- Enter dashboard.html as the file name.
- Build the dashboard structure by replacing the blank file with this code:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Campus Incident Intake</title>
</head>
<body>
<main>
<section class="card"><p>Final-year Azure project</p><h1>Campus Incident Intake</h1><p>Submit a technical incident and receive a traceable ID immediately. Background processing continues independently through Azure Queue Storage.</p><div class="flow" aria-label="Architecture flow"><span>1. Browser dashboard</span><span>2. HTTP-triggered Azure Function</span><span>3. Azure Queue Storage</span><span>4. Queue-triggered worker and telemetry</span></div></section>
<section class="card"><h2>Report an incident</h2><form id="incident-form"><label for="title">Title</label><input id="title" name="title" required maxlength="120" placeholder="Cannot access student portal"><label for="category">Category</label><select id="category" name="category"><option>Access</option><option>Network</option><option>Software</option><option>Hardware</option><option>Other</option></select><label for="severity">Severity</label><select id="severity" name="severity"><option>Low</option><option selected>Medium</option><option>High</option><option>Critical</option></select><label for="description">Description</label><textarea id="description" name="description" placeholder="Describe the impact and what you already tried."></textarea><button id="submit-button" type="submit">Queue incident</button><div id="result" class="result" role="status" aria-live="polite"></div></form></section>
</main>
</body>
</html>
What does this structure do?
- The first .card introduces the dashboard plus the intended architecture.
- The flow shows a browser handing work to an HTTP-triggered Azure Function.
- The next stage uses Azure Queue Storage to separate intake from worker processing.
- The incident-form collects the title, category, severity, plus description.
- The result panel provides one place for submission feedback.
- Save dashboard.html by pressing Ctrl+S.
- Right-click dashboard.html in the Explorer view.
- Select Reveal in File Explorer.
- Double-click dashboard.html in Windows File Explorer.
You should see the plain Campus Incident Intake heading above the incident form. You should also see the four architecture steps.
Dashboard file not opening?
- Confirm that the file is named dashboard.html.
- Check that dashboard.html appears directly inside the incident-intake-mvp folder.
Help me check why my dashboard file does not open correctly in the browser.
Style and inspect the dashboard
CSS turns the plain document into a dashboard that works across wide plus narrow browser windows. The result panel starts hidden so feedback only appears after a submission.
- Return to dashboard.html in Visual Studio Code.
- Find the <title>Campus Incident Intake</title> line.
- Add the dashboard styling immediately below that line by pasting this code:
<style>
:root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, sans-serif; background: #07111f; color: #e5edf7; }
* { box-sizing: border-box; }
body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top left, #17345f 0, transparent 36%), #07111f; }
main { width: min(980px, 92vw); margin: 0 auto; padding: 48px 0; display: grid; gap: 24px; grid-template-columns: 1fr 1.15fr; }
.card { background: rgba(12, 26, 46, 0.9); border: 1px solid #264366; border-radius: 18px; padding: 28px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.28); }
h1, h2 { margin-top: 0; } h1 { font-size: clamp(2rem, 6vw, 3.5rem); line-height: 1; } p { color: #a9bad0; line-height: 1.6; }
.flow { display: grid; gap: 10px; margin-top: 24px; } .flow span { padding: 12px 14px; border-radius: 10px; background: #102b4c; border-left: 4px solid #5ba7ff; }
label { display: block; margin: 16px 0 6px; color: #cbd8e8; font-weight: 650; }
input, select, textarea, button { width: 100%; border-radius: 10px; border: 1px solid #34577d; font: inherit; }
input, select, textarea { padding: 12px; color: #f4f8ff; background: #08182b; } textarea { min-height: 110px; resize: vertical; }
button { margin-top: 20px; padding: 13px 18px; border: 0; color: #06101d; background: #69b2ff; font-weight: 800; cursor: pointer; } button:disabled { opacity: 0.6; cursor: wait; }
.result { display: none; margin-top: 18px; padding: 14px; border-radius: 10px; line-height: 1.5; }
.result.success { display: block; color: #bff7d5; background: #0f3d2c; border: 1px solid #267c57; }
.result.error { display: block; color: #ffd0d0; background: #4b1f28; border: 1px solid #984052; }
@media (max-width: 760px) { main { grid-template-columns: 1fr; padding: 24px 0; } }
</style>
How does the layout work?
- The main grid places the architecture card beside the form card on wide screens.
- The .card rules give each section its dark panel plus border.
- The form controls use the full card width to keep each field easy to scan.
- The media query changes the grid to one column when the browser is 760px wide or narrower.
- The success plus error classes give the result panel distinct visual states.
- Save dashboard.html by pressing Ctrl+S.
- Return to the browser window containing the dashboard.
- Refresh the dashboard page.
You should see two dark cards against a navy background. The architecture flow should appear beside the form on a wide window.
- Open the Category list.
You should see Access, Network, Software, Hardware, plus Other.
- Open the Severity list.
You should see Low, Medium, High, plus Critical.
- Narrow the browser window until the cards stack vertically.
That is the visual layer complete: your dashboard now presents every incident field plus the architecture flow.
Dashboard still looks unstyled?
- Confirm that the <style> block sits below the page title.
- Check that the closing </style> tag appears before </head>.
- Refresh the browser after saving dashboard.html.
Help me find why the styles in my dashboard.html file are not appearing.
Submit a High incident
JavaScript connects the form submission to the relative incidents endpoint. The result panel reports what happens when the browser attempts that request.
- Return to dashboard.html in Visual Studio Code.
- Find the closing </main> line.
- Add the submission behavior immediately below that line by pasting this code:
<script>
const form = document.querySelector("#incident-form");
const result = document.querySelector("#result");
const button = document.querySelector("#submit-button");
form.addEventListener("submit", async (event) => {
event.preventDefault(); button.disabled = true; result.className = "result"; result.textContent = "Submitting incident..."; result.style.display = "block";
const payload = Object.fromEntries(new FormData(form).entries());
try {
const response = await fetch("incidents", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) });
const data = await response.json();
if (!response.ok) { throw new Error(data.error || "The incident could not be submitted."); }
result.className = "result success"; result.innerHTML = `<strong>${data.incident_id}</strong><br>${data.status}: ${data.message}`; form.reset();
} catch (error) { result.className = "result error"; result.textContent = `Backend unavailable: ${error.message}`; } finally { button.disabled = false; }
});
</script>
What does the submission code do?
- The event listener takes control when the learner submits incident-form.
- The payload gathers the current values from every named form control.
- The request sends the payload to the relative incidents endpoint as JSON.
- The success branch displays the incident ID plus its status.
- The error branch displays Backend unavailable inside the result panel.
- Save dashboard.html by pressing Ctrl+S.
- Return to the browser window containing the dashboard.
- Refresh the dashboard page.
Before you submit the form, do you think a page opened directly from disk can complete the request to incidents?
- Enter Cannot access student portal in the Title field.
- Select High from the Severity list.
- Click Queue incident.
You should see a red result panel beneath the form beginning with Backend unavailable.
Why is this failure useful?
The form can collect an incident plus attempt a request. The visible failure proves that no backend currently serves the incidents endpoint.
This gives you a concrete baseline for the serverless intake path you build next.
Error panel not appearing?
- Confirm that the <script> block sits below </main>.
- Check that #incident-form, #result, plus #submit-button match the IDs in the page structure.
- Refresh the browser after saving the script.
Help me debug why submitting my dashboard form does not show the Backend unavailable panel.
✔️ Awesome, I've got everything!
Your dashboard.html file now matches the complete dashboard.
ⓧ I'd like to double check the full code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Campus Incident Intake</title>
<style>
:root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, sans-serif; background: #07111f; color: #e5edf7; }
* { box-sizing: border-box; }
body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top left, #17345f 0, transparent 36%), #07111f; }
main { width: min(980px, 92vw); margin: 0 auto; padding: 48px 0; display: grid; gap: 24px; grid-template-columns: 1fr 1.15fr; }
.card { background: rgba(12, 26, 46, 0.9); border: 1px solid #264366; border-radius: 18px; padding: 28px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.28); }
h1, h2 { margin-top: 0; } h1 { font-size: clamp(2rem, 6vw, 3.5rem); line-height: 1; } p { color: #a9bad0; line-height: 1.6; }
.flow { display: grid; gap: 10px; margin-top: 24px; } .flow span { padding: 12px 14px; border-radius: 10px; background: #102b4c; border-left: 4px solid #5ba7ff; }
label { display: block; margin: 16px 0 6px; color: #cbd8e8; font-weight: 650; }
input, select, textarea, button { width: 100%; border-radius: 10px; border: 1px solid #34577d; font: inherit; }
input, select, textarea { padding: 12px; color: #f4f8ff; background: #08182b; } textarea { min-height: 110px; resize: vertical; }
button { margin-top: 20px; padding: 13px 18px; border: 0; color: #06101d; background: #69b2ff; font-weight: 800; cursor: pointer; } button:disabled { opacity: 0.6; cursor: wait; }
.result { display: none; margin-top: 18px; padding: 14px; border-radius: 10px; line-height: 1.5; }
.result.success { display: block; color: #bff7d5; background: #0f3d2c; border: 1px solid #267c57; }
.result.error { display: block; color: #ffd0d0; background: #4b1f28; border: 1px solid #984052; }
@media (max-width: 760px) { main { grid-template-columns: 1fr; padding: 24px 0; } }
</style>
</head>
<body>
<main>
<section class="card"><p>Final-year Azure project</p><h1>Campus Incident Intake</h1><p>Submit a technical incident and receive a traceable ID immediately. Background processing continues independently through Azure Queue Storage.</p><div class="flow" aria-label="Architecture flow"><span>1. Browser dashboard</span><span>2. HTTP-triggered Azure Function</span><span>3. Azure Queue Storage</span><span>4. Queue-triggered worker and telemetry</span></div></section>
<section class="card"><h2>Report an incident</h2><form id="incident-form"><label for="title">Title</label><input id="title" name="title" required maxlength="120" placeholder="Cannot access student portal"><label for="category">Category</label><select id="category" name="category"><option>Access</option><option>Network</option><option>Software</option><option>Hardware</option><option>Other</option></select><label for="severity">Severity</label><select id="severity" name="severity"><option>Low</option><option selected>Medium</option><option>High</option><option>Critical</option></select><label for="description">Description</label><textarea id="description" name="description" placeholder="Describe the impact and what you already tried."></textarea><button id="submit-button" type="submit">Queue incident</button><div id="result" class="result" role="status" aria-live="polite"></div></form></section>
</main>
<script>
const form = document.querySelector("#incident-form");
const result = document.querySelector("#result");
const button = document.querySelector("#submit-button");
form.addEventListener("submit", async (event) => {
event.preventDefault(); button.disabled = true; result.className = "result"; result.textContent = "Submitting incident..."; result.style.display = "block";
const payload = Object.fromEntries(new FormData(form).entries());
try {
const response = await fetch("incidents", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) });
const data = await response.json();
if (!response.ok) { throw new Error(data.error || "The incident could not be submitted."); }
result.className = "result success"; result.innerHTML = `<strong>${data.incident_id}</strong><br>${data.status}: ${data.message}`; form.reset();
} catch (error) { result.className = "result error"; result.textContent = `Backend unavailable: ${error.message}`; } finally { button.disabled = false; }
});
</script>
</body>
</html>
Your dashboard now makes the missing backend visible. Next, you will add the serverless intake path plus the separate queue worker.
Add the Serverless Intake and Queue Worker
Your Campus Incident Intake dashboard already captures a realistic report. Its Backend unavailable result proved that the form needs a destination.
In this step, Azure Functions supplies that destination through an HTTP trigger.
Azure Queue Storage carries each accepted incident. A queue trigger starts the separate worker.
In this step, get ready to:
- Create the Azure Functions configuration files.
- Connect the HTTP handler to an output binding.
- Add the queue worker and verify the Python app.
Create the Functions project files
The host.json file tells the Functions runtime which extension bundle supplies the queue bindings. The .funcignore file keeps local development files out of the deployment package.
- Create host.json inside the incident-intake-mvp folder with the file controls in the Visual Studio Code sidebar.
- Add the Functions host configuration by pasting this code:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.0.0, 5.0.0)"
}
}
What Does This Configuration Do?
- The version value selects the 2.0 host configuration format.
- The extension bundle gives the Python app access to the Queue Storage trigger and binding definitions.
- Save host.json.
- Confirm host.json appears beside dashboard.html in the Visual Studio Code sidebar.
Host Configuration Not Recognized?
- Check that the filename is exactly host.json.
- Check that every opening brace has a matching closing brace.
Help me check my host.json configuration.
- Create .funcignore inside the incident-intake-mvp folder with the file controls in the Visual Studio Code sidebar.
- Add the deployment exclusions by pasting this code:
.venv/
.vscode/
__pycache__/
*.pyc
local.settings.json
mentor-screenshots/
What Does This File Exclude?
- The .venv/ entry keeps local dependency files out of the cloud package.
- The __pycache__/ entry keeps generated Python cache files out of the package.
- The remaining entries exclude editor settings and local-only project evidence.
- Save .funcignore.
- Confirm host.json and .funcignore are both visible beside dashboard.html.
Cannot See the Ignore File?
- Check that the filename starts with a full stop.
- Remove any extra .txt extension from the filename.
Help me find why my .funcignore file is missing.
The first Python slice registers the app and serves the existing dashboard through the Dashboard route. It also defines the severity values that the intake handler accepts.
- Create function_app.py inside the incident-intake-mvp folder with the file controls in the Visual Studio Code sidebar.
- Add the app declaration and dashboard handler by pasting this code:
from datetime import UTC, datetime
from pathlib import Path
import json
import logging
import uuid
import azure.functions as func
app = func.FunctionApp()
DASHBOARD_PATH = Path(__file__).with_name("dashboard.html")
VALID_SEVERITIES = {"Low", "Medium", "High", "Critical"}
@app.function_name(name="Dashboard")
@app.route(route="dashboard", methods=["GET"], auth_level=func.AuthLevel.ANONYMOUS)
def dashboard(req: func.HttpRequest) -> func.HttpResponse:
return func.HttpResponse(DASHBOARD_PATH.read_text(encoding="utf-8"), mimetype="text/html", status_code=200)
What Does This Code Do?
- The imports provide timestamps and file access for the incident flow.
- The app object registers every function with the Azure Functions runtime.
- The DASHBOARD_PATH value points to the existing dashboard.html file.
- The dashboard() handler returns the dashboard as an HTML response.
- Save function_app.py.
- Check the first runnable slice from the PowerShell terminal by running:
python -m py_compile function_app.py
What Does This Check Prove?
Python compiles function_app.py without running a cloud deployment. A successful check returns you to the PowerShell prompt without a syntax error.
Seeing a Python Syntax Error?
- Compare the reported line with the matching line in the code block.
- Check the decorator lines for missing quotation marks or parentheses.
Help me diagnose the syntax error in my dashboard handler.
Build the HTTP producer path
The producer accepts the form data and validates its required fields. It creates a traceable incident ID before handing the serialized incident to the queue.
The HTTP function returns 202 after the queue binding accepts the message. This keeps the browser request separate from background processing.
- Scroll to the end of dashboard() in function_app.py.
- Add the incident producer below dashboard() by pasting this code:
@app.function_name(name="SubmitIncident")
@app.route(route="incidents", methods=["POST"], auth_level=func.AuthLevel.ANONYMOUS)
@app.queue_output(arg_name="incident_message", queue_name="incident-intake", connection="AzureWebJobsStorage")
def submit_incident(req: func.HttpRequest, incident_message: func.Out[str]) -> func.HttpResponse:
try:
request_body = req.get_json()
except ValueError:
return json_response({"error": "Request body must be valid JSON."}, 400)
title = str(request_body.get("title", "")).strip()
category = str(request_body.get("category", "Other")).strip() or "Other"
severity = str(request_body.get("severity", "Medium")).strip()
description = str(request_body.get("description", "")).strip()
if not title:
return json_response({"error": "Title is required."}, 400)
if severity not in VALID_SEVERITIES:
return json_response({"error": "Severity is invalid."}, 400)
incident = {"incident_id": f"INC-{uuid.uuid4().hex[:8].upper()}", "title": title, "category": category, "severity": severity, "description": description, "status": "Queued", "submitted_at": datetime.now(UTC).isoformat()}
incident_message.set(json.dumps(incident))
return json_response({"incident_id": incident["incident_id"], "status": incident["status"], "message": "Incident accepted for asynchronous processing."}, 202)
How Does the Producer Work?
- The incidents route accepts the dashboard's request body.
- The validation checks reject invalid JSON and unsupported incident values.
- The incident_message binding serializes the accepted incident into the incident-intake queue.
- The response sends the incident ID and Queued status back to the dashboard immediately.
- Save function_app.py.
- Check the expanded file for valid Python syntax by running:
python -m py_compile function_app.py
What Does This Check Confirm?
A quiet return to the prompt confirms that Python can compile the dashboard and producer definitions. The queue binding decorators are now part of the registered app.
Producer Code Fails to Compile?
- Check that submit_incident() starts at the left edge of the file.
- Check that every statement inside the function uses four spaces of indentation.
- Compare the closing parentheses on the final json_response() call.
Help me debug the producer code in function_app.py.
Add the queue worker and verify the app
The consumer runs independently when a message reaches incident-intake. It logs only the incident ID and routing fields needed for the mentor demonstration.
- Scroll to the end of submit_incident() in function_app.py.
- Add the worker and response helper below submit_incident() by pasting this code:
@app.function_name(name="ProcessIncident")
@app.queue_trigger(arg_name="msg", queue_name="incident-intake", connection="AzureWebJobsStorage")
def process_incident(msg: func.QueueMessage) -> None:
incident = json.loads(msg.get_body().decode("utf-8"))
logging.info("INCIDENT_PROCESSED id=%s category=%s severity=%s", incident["incident_id"], incident["category"], incident["severity"])
def json_response(payload: dict, status_code: int) -> func.HttpResponse:
return func.HttpResponse(json.dumps(payload), mimetype="application/json", status_code=status_code)
How Does the Consumer Work?
- The ProcessIncident function starts when a message arrives in incident-intake.
- The QueueMessage body is decoded from UTF-8 before the JSON is parsed.
- The log entry records the incident ID and routing fields without including the description.
- The json_response() helper gives every API response the same JSON format.
- Save function_app.py.
Before you run the final checks, do you expect Python to compile the file and load every decorated function?
- Verify the complete file has valid Python syntax by running:
python -m py_compile function_app.py
What Does the Syntax Check Prove?
A successful command returns to the PowerShell prompt without an error. This confirms that every function definition is valid Python.
- Verify the complete Functions app imports successfully by running:
python -c "import function_app; print('Function app loaded')"
What Does the Import Check Prove?
Importing the module evaluates the app declaration and its decorators. The printed message confirms that Python loaded the complete module.
The first command returns without a syntax error. The second command prints Function app loaded.
Function App Does Not Load?
- Confirm the PowerShell prompt shows the virtual environment from earlier.
- Confirm requirements.txt still contains azure-functions==2.3.0.
- Use the reported line number to compare function_app.py with the full reference below.
Help me diagnose why function_app.py does not import.
✔️ Awesome, I've got everything!
Great work. Save each project file before moving on.
ⓧ I'd like to double check the full code
Compare your host.json file with this reference:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.0.0, 5.0.0)"
}
}
What to Compare in host.json
Confirm the extension bundle identifier and version range match the reference. These values enable the queue binding definitions.
Compare your .funcignore file with this reference:
.venv/
.vscode/
__pycache__/
*.pyc
local.settings.json
mentor-screenshots/
What to Compare in .funcignore
Confirm every exclusion appears on its own line. The first character of the filename must be a full stop.
Compare your function_app.py file with this complete reference:
from datetime import UTC, datetime
from pathlib import Path
import json
import logging
import uuid
import azure.functions as func
app = func.FunctionApp()
DASHBOARD_PATH = Path(__file__).with_name("dashboard.html")
VALID_SEVERITIES = {"Low", "Medium", "High", "Critical"}
@app.function_name(name="Dashboard")
@app.route(route="dashboard", methods=["GET"], auth_level=func.AuthLevel.ANONYMOUS)
def dashboard(req: func.HttpRequest) -> func.HttpResponse:
return func.HttpResponse(DASHBOARD_PATH.read_text(encoding="utf-8"), mimetype="text/html", status_code=200)
@app.function_name(name="SubmitIncident")
@app.route(route="incidents", methods=["POST"], auth_level=func.AuthLevel.ANONYMOUS)
@app.queue_output(arg_name="incident_message", queue_name="incident-intake", connection="AzureWebJobsStorage")
def submit_incident(req: func.HttpRequest, incident_message: func.Out[str]) -> func.HttpResponse:
try:
request_body = req.get_json()
except ValueError:
return json_response({"error": "Request body must be valid JSON."}, 400)
title = str(request_body.get("title", "")).strip()
category = str(request_body.get("category", "Other")).strip() or "Other"
severity = str(request_body.get("severity", "Medium")).strip()
description = str(request_body.get("description", "")).strip()
if not title:
return json_response({"error": "Title is required."}, 400)
if severity not in VALID_SEVERITIES:
return json_response({"error": "Severity is invalid."}, 400)
incident = {"incident_id": f"INC-{uuid.uuid4().hex[:8].upper()}", "title": title, "category": category, "severity": severity, "description": description, "status": "Queued", "submitted_at": datetime.now(UTC).isoformat()}
incident_message.set(json.dumps(incident))
return json_response({"incident_id": incident["incident_id"], "status": incident["status"], "message": "Incident accepted for asynchronous processing."}, 202)
@app.function_name(name="ProcessIncident")
@app.queue_trigger(arg_name="msg", queue_name="incident-intake", connection="AzureWebJobsStorage")
def process_incident(msg: func.QueueMessage) -> None:
incident = json.loads(msg.get_body().decode("utf-8"))
logging.info("INCIDENT_PROCESSED id=%s category=%s severity=%s", incident["incident_id"], incident["category"], incident["severity"])
def json_response(payload: dict, status_code: int) -> func.HttpResponse:
return func.HttpResponse(json.dumps(payload), mimetype="application/json", status_code=status_code)
What to Compare in function_app.py
Confirm all three decorated functions appear in the same order as the reference. Confirm json_response() remains at the bottom of the file.
That is the serverless application assembled. Your dashboard now has a producer route and a separate queue worker ready for Azure.
Your code is ready for a cloud runtime. Next up, you will deploy it and replace the dashboard's designed failure with a queued incident ID.
Deploy the End-to-End Azure Flow
Your Azure Functions source now imports cleanly. The missing piece is a cloud runtime that can receive real browser requests.
In this step, you deploy the dashboard to Microsoft Azure.
Azure Queue Storage hands each incident to the background worker. Application Insights records the resulting invocations.
In this step, get ready to:
- Provision the Azure resources that run the project.
- Deploy the local incident intake project.
- Trace a test incident from the dashboard to the queue worker.
Create the Azure runtime
A Function App provides the managed runtime for the three functions in function_app.py. A resource group keeps the related Azure resources together.
Know the Tutorial Cost
Creating these resources starts the billable part of the project. Official guidance estimates a few USD cents or less for a low-volume Flex Consumption tutorial deployment.
Storage, networking, and telemetry can still generate small charges.
- Go to the Azure portal in a browser using your signed-in Azure account.
- Select Create a resource.
- Under Function App, select Create.
- Select Flex Consumption as the hosting option.
- Create a new resource group for the incident intake project.
- Record the resource group name here: <RESOURCE_GROUP>.
- Enter a unique name for the Function App.
- Record the Function App name here: <APP_NAME>.
What Will Azure Create?
Flex Consumption supplies the serverless runtime that starts when requests or queue messages arrive. The resource group also receives the related deployment resources.
The associated storage account provides the default AzureWebJobsStorage connection used by the queue binding. Application Insights receives the app's telemetry.
- Choose an Azure region for the deployment.
- On the Runtime stack page, select Python.
- On the Version page, select Python 3.13.
- On the Storage page, keep the automatically associated storage account.
- Record the associated storage account name here: your associated storage account name.
- On the Monitoring page, keep Application Insights enabled.
- Complete the creation wizard to start the deployment.
Provisioning can take a few minutes while Azure creates the Function App and its supporting resources. A quiet deployment screen during that wait is expected.
- Open the Function App resource after the portal reports that deployment is complete.
You should see the Function App name beside its resource group. The overview should also link to its associated storage account.
Why Azure Functions With a Queue?
A conventional App Service can host an HTTP API. Azure Functions makes both the request trigger and the queue trigger visible as separate units of work.
The queue lets the HTTP function return quickly. The worker can then process the incident independently.
Create the incident queue
The output binding in submit_incident points to incident-intake. The associated storage account now needs a queue with that exact name.
- Return to the Function App Overview page.
- Open the associated storage account from the Function App overview.
- Select Queue Storage in the storage account.
- Select Queues.
- Select + Queue.
- Enter incident-intake as the queue name.
- Select OK.
- Confirm that incident-intake is listed under Queues.
That connection point is ready. Your HTTP function now has a real queue for handing work to ProcessIncident.
How Does the Handoff Work?
SubmitIncident serializes each incident as JSON. Its output binding writes that message to incident-intake.
ProcessIncident runs when the queue receives a message. It logs the incident ID, category, and severity for the demonstration.
Publish and test the complete flow
Azure Functions Core Tools sends the existing project from Visual Studio Code to the Function App. The remote build installs the packages pinned in requirements.txt.
- Switch back to the PowerShell terminal in Visual Studio Code.
- Check that the terminal is inside the incident-intake-mvp folder.
The deployment can take a few minutes while Azure performs the remote build. Short pauses in the terminal are expected.
- Deploy the local project by replacing <APP_NAME> with your recorded Function App name in this command:
func azure functionapp publish <APP_NAME>
What Does This Command Do?
- The publish command sends the files allowed by .funcignore to the existing Function App.
- The remote build installs azure-functions==2.3.0 from requirements.txt.
- The Azure Functions host discovers Dashboard, SubmitIncident, and ProcessIncident from function_app.py.
- Wait for the command to report that the publish operation succeeded.
- Confirm that the deployment output lists Dashboard, SubmitIncident, and ProcessIncident.
Publish Command Failed?
- Confirm that the command uses the exact Function App name shown in the Azure portal.
- Confirm that host.json sits beside function_app.py in the incident-intake-mvp folder.
- Confirm that your Azure session still uses the subscription containing the Function App.
Help me diagnose my Azure Functions deployment failure.
The publish output normally includes the function invocation URLs. You can ask Core Tools to list them if they are missing.
- List the deployed function URLs by replacing <APP_NAME> with your recorded Function App name in this command:
func azure functionapp list-functions <APP_NAME> --show-keys
What Does This Command Show?
The command returns endpoint URLs for the deployed functions. Use the URL associated with Dashboard for the browser test.
Cannot List the Function URLs?
- Check that the Function App name matches the name in its Azure overview.
- Check that the publish operation completed before requesting the URLs.
- Check that your active Azure subscription contains the Function App.
Help me retrieve the invocation URLs for my deployed Azure Functions.
- Copy the invocation URL associated with Dashboard.
- Record the deployed dashboard URL here: your deployed dashboard URL.
Keep the Demo Data Fictional
The demonstration routes allow anonymous requests. This access model is suitable only for the short mentor demonstration.
- Use fictional incident details throughout the test.
- Open the recorded dashboard URL in your browser.
- Enter Cannot access student portal in the Title field.
- Select Access from the Category field.
- Select High from the Severity field.
- Enter Students cannot sign in to the portal. in the Description field.
Before you submit, decide whether the queued response or the worker completion should appear first.
- Select Queue incident.
You will see an incident ID shaped like INC-XXXXXXXX with the status Queued. This is the browser evidence that the HTTP function returned its asynchronous 202 response.
The full browser-to-queue handoff now works. Your mentor can submit an incident and receive a traceable ID immediately.
The Monitor view can take up to five minutes to show invocation data. That delay comes from telemetry batching.
Before you refresh Monitor, predict which function should have a separate invocation for the queued message.
- Return to the Function App overview in the Azure portal.
- Select ProcessIncident from the function list.
- Select Monitor.
- Refresh the Monitor view until the latest invocation appears.
- Confirm that the latest ProcessIncident invocation completed successfully.
- Select its Date (UTC) link to inspect the invocation logs.
- Confirm that the log records the incident ID, category, and severity.
You will see a successful worker invocation corresponding to the incident you submitted. That separate invocation proves the queue handed the message to ProcessIncident.
Worker Invocation Missing?
- Allow up to five minutes for the Monitor view to receive batched telemetry.
- Confirm that the storage account still contains the incident-intake queue.
- Confirm that the dashboard showed a queued incident ID after submission.
Help me trace a missing ProcessIncident invocation.
Your Azure-hosted flow now accepts incidents and processes them asynchronously. Next, you will turn the working system into a repeatable mentor demonstration.
Build the Mentor Evidence Pack
Your deployed Microsoft Azure flow already returns a queued incident ID. The separate worker processes the message.
A live demo can fail or hide the engineering decisions behind the result. You will package the flow into a mentor guide backed by Application Insights telemetry.
In this step, get ready to:
- Create a mentor guide with the architecture diagram and demonstration order.
- Observe HTTP request activity and the separate worker invocation.
- Prepare a repeatable demonstration with three backup screenshots.
Create the mentor demo guide
A Markdown guide gives your demonstration a fixed order. Its Mermaid diagram also makes the asynchronous boundary visible before you submit an incident.
- In the file sidebar of Visual Studio Code from earlier, create mentor-demo.md beside function_app.py.
- Add the project details and architecture by pasting this first section into mentor-demo.md:
# Mentor Demo: Campus Incident Intake
Project URL: Replace with the deployed dashboard URL
Function App: Replace with the Function App name
Resource group: Replace with the resource group name
## Problem
Students need one place to report technical incidents. Support staff need a traceable submission and a reliable path for background processing.
## Architecture
```mermaid
flowchart LR
M[Mentor browser] --> D[Incident dashboard]
D --> H[HTTP-triggered Azure Function]
H --> Q[incident-intake queue]
Q --> W[Queue-triggered worker]
H --> A[Application Insights]
W --> A
```
What does this section show?
- The first three lines identify the deployed project that your mentor sees.
- The problem statement connects the technical flow to the people who need it.
- The diagram shows the browser handing work to an HTTP function before Azure Queue Storage passes it to the worker.
- Both functions connect to Application Insights so you can show activity from each stage.
- Save mentor-demo.md.
- Press Ctrl+Shift+V to open the Markdown preview.
You should see the project heading followed by a left-to-right architecture diagram.
Diagram missing from the preview?
- Confirm that the opening fence says ```mermaid.
- Confirm that the diagram ends with a separate closing fence.
- Save mentor-demo.md before checking the preview again.
Help me fix the Mermaid diagram in my mentor guide.
- Replace Replace with the deployed dashboard URL with your deployed dashboard URL.
- Replace Replace with the Function App name with your Function App name.
- Save mentor-demo.md.
The preview should now show your real deployment URL and Function App name.
- Replace Replace with the resource group name with your resource group name.
- Save mentor-demo.md.
The guide should now identify the exact resource group that contains your deployed evidence.
- Add the demonstration sequence and evidence checklist by pasting this content after the architecture diagram:
## Live demo order
1. Open the deployed dashboard.
2. Submit `Cannot access student portal` with High severity.
3. Show the returned incident ID and Queued status.
4. Open Application Insights Live Metrics Stream.
5. Show the HTTP request and the separate ProcessIncident execution.
6. Explain that the request handler and worker can scale and fail independently.
## Evidence checklist
- Deployed dashboard URL
- Incident ID screenshot
- Successful ProcessIncident invocation
- Resource group screenshot
- Architecture diagram
- Cost and cleanup note
Why use a fixed demo order?
The sequence moves from the visible dashboard result to the separate worker evidence. The checklist gives you a backup when live telemetry is slow during a mentor review.
- Save mentor-demo.md.
The preview should show six numbered demonstration actions followed by six evidence items.
List formatting looks broken?
- Keep a blank line between the numbered demo order and the Evidence checklist heading.
- Keep each evidence item on its own line beginning with a single hyphen.
Help me repair the lists in my mentor guide.
- Add the mentor questions and honest limitations by pasting this final section below the evidence checklist:
## Questions to answer
- Why use a queue instead of processing everything in the HTTP request?
- What happens if the worker fails?
- Why return 202 instead of 200?
- What data should not be written to logs?
- Which production features are intentionally deferred?
## Honest limitations
This MVP does not yet persist incident history, authenticate users, assign staff, send notifications, or implement a full status workflow. Those belong in follow-on projects after the asynchronous core is proven.
What does this final section protect?
The questions prepare you to explain the design choices behind the demo. The limitations keep the anonymous MVP framed as a focused architecture proof.
- Save mentor-demo.md.
The preview should end with five mentor questions and one paragraph describing the deferred production features.
Missing the final sections?
- Confirm that both headings begin with two hash characters.
- Confirm that the limitations paragraph sits below the Honest limitations heading.
Help me compare my mentor guide with the expected structure.
Use this reference to compare the complete structure. Keep your actual deployment values in the first three lines of your saved file.
✔️ Awesome, I've got everything!
Your mentor guide now has the complete architecture, demonstration order, evidence checklist, questions, and limitations.
ⓧ I'd like to double check the full code
This reference keeps the original replacement prompts. Your saved file should contain your actual deployment values in those three lines.
# Mentor Demo: Campus Incident Intake
Project URL: Replace with the deployed dashboard URL
Function App: Replace with the Function App name
Resource group: Replace with the resource group name
## Problem
Students need one place to report technical incidents. Support staff need a traceable submission and a reliable path for background processing.
## Architecture
```mermaid
flowchart LR
M[Mentor browser] --> D[Incident dashboard]
D --> H[HTTP-triggered Azure Function]
H --> Q[incident-intake queue]
Q --> W[Queue-triggered worker]
H --> A[Application Insights]
W --> A
```
## Live demo order
1. Open the deployed dashboard.
2. Submit `Cannot access student portal` with High severity.
3. Show the returned incident ID and Queued status.
4. Open Application Insights Live Metrics Stream.
5. Show the HTTP request and the separate ProcessIncident execution.
6. Explain that the request handler and worker can scale and fail independently.
## Evidence checklist
- Deployed dashboard URL
- Incident ID screenshot
- Successful ProcessIncident invocation
- Resource group screenshot
- Architecture diagram
- Cost and cleanup note
## Questions to answer
- Why use a queue instead of processing everything in the HTTP request?
- What happens if the worker fails?
- Why return 202 instead of 200?
- What data should not be written to logs?
- Which production features are intentionally deferred?
## Honest limitations
This MVP does not yet persist incident history, authenticate users, assign staff, send notifications, or implement a full status workflow. Those belong in follow-on projects after the asynchronous core is proven.
Capture live telemetry and backup evidence
The guide explains the design on paper. Live telemetry proves that the HTTP request and queue worker execute as separate parts of the deployed flow.
The telemetry link is easy to miss because it sits under Configured features on the Function App overview page.
- Return to the Azure portal overview page for your Function App name.
- Under Configured features, select the Application Insights link.
- Select Live Metrics Stream.
- In the Visual Studio Code file sidebar, create mentor-screenshots beside mentor-demo.md.
- In another browser tab, open your deployed dashboard URL.
- Enter Cannot access student portal in the Title field.
- Select High in the Severity field.
- Select Queue incident.
Your dashboard should show a generated incident ID with the Queued status. That result proves the intake function accepted the submission.
- Press Windows logo key + Shift + S to start a screen capture.
- Drag over the success panel containing the incident ID.
- Save the capture as mentor-screenshots\incident-id.png.
- Return to Live Metrics Stream.
You should see request activity and separate worker activity under Sample Telemetry.
No activity in Live Metrics?
- Keep Live Metrics Stream open while you submit a fresh incident.
- Confirm that you opened the Application Insights resource associated with your Function App name.
- Use the Function Monitor view below if the stream does not show the worker event.
Help me find my HTTP and ProcessIncident telemetry.
The Function Monitor view can take up to five minutes to display an invocation. A short delay here is normal because this view batches telemetry.
- Return to the Azure portal page for your Function App name.
- Select the ProcessIncident function.
- Select Monitor.
- Select Refresh until the successful invocation appears.
- Select the invocation's Date (UTC) link.
- Press Windows logo key + Shift + S to start a screen capture.
- Drag over the successful invocation details.
- Save the capture as mentor-screenshots\process-incident.png.
Why keep invocation evidence?
The incident ID screenshot proves that the browser received an accepted response. The invocation screenshot proves that the independent worker consumed the queued message.
- Return to the Azure portal overview page for your resource group name.
- Press Windows logo key + Shift + S to start a screen capture.
- Drag over the resource list containing the Function App and its related resources.
- Save the capture as mentor-screenshots\resource-group.png.
Your mentor-screenshots folder should now contain evidence of the incident ID, worker invocation, and deployed resource group.
Rehearse the asynchronous story
The strongest demonstration connects the visible result to the architecture behind it. Your explanation should make the fast 202 response and independent worker feel like one coherent flow.
How should you explain the 202 response?
The HTTP function validates the request. It creates the incident ID before placing the serialized incident on the queue.
The 202 response confirms that the incident was accepted for asynchronous processing. The queue worker completes its part independently after the response returns.
Before you begin the rehearsal, can you move through every proof point without searching for the next screen?
- Follow the Live demo order in mentor-demo.md from top to bottom.
- Open your deployed dashboard URL.
- Submit Cannot access student portal with High severity.
- Point to the generated incident ID.
- Point to the Queued status.
At this point, your mentor has seen the accepted request. The remaining evidence shows what happened after the browser received its response.
- Show the HTTP request activity in Application Insights.
- Show the separate ProcessIncident invocation.
- Display the architecture diagram in mentor-demo.md.
- Explain why the HTTP function returns 202 while the worker processes independently.
You should now be able to show the deployed dashboard, a queued incident ID, a successful ProcessIncident invocation, and the architecture diagram without improvising.
That is the evidence pack ready. Your mentor can now inspect the running system and question the design using the same repeatable path.
Secret mission
Highlight Critical Incidents
Every accepted incident currently uses the same success styling. Add a red escalation state that appears only for Critical submissions while preserving the incident ID and Queued status.
Clean Up Your Resources
Clean Up Your Resources
Your mentor demonstration is ready, so you can keep the deployment available, pause your testing, or remove every project resource. This tutorial deployment usually costs a few USD cents or less.
Cost warning
Azure Functions Flex Consumption includes a monthly free grant of 250,000 on-demand executions plus 100,000 GB-s on eligible pay-as-you-go subscriptions.
Azure Storage plus networking sit outside this grant. Application Insights telemetry can also generate charges.
The Delete option is the only choice that removes these cloud resources and ends their ongoing project costs.
Resources you used:
- One Azure resource group containing the Azure Functions app <APP_NAME>, its associated storage account, the incident-intake Azure Queue Storage queue, and Application Insights.
- The local incident-intake-mvp folder containing the dashboard, function code, mentor evidence pack, and optional .venv folder.
Keep everything running
No action is needed. Choose this option while the deployed dashboard is still useful for mentor reviews.
- Use the deployed dashboard whenever you want to repeat the mentor demonstration.
- Review the Azure resource group's accumulated costs after each demonstration.
- Keep the incident-intake-mvp folder so the source code remains available.
Pause - I'll come back to this later
Pausing means ending test traffic while leaving the deployment available on demand. Persistent storage plus telemetry can still incur small charges.
- Stop submitting test incidents through the deployed dashboard.
- Keep the Function App at zero always-ready instances.
- Keep the incident-intake-mvp folder for your next session.
Delete - I don't want to use this again
Deleting the resource group is intentionally broad. It removes the Function App, storage account, queue, and telemetry resource in one operation.
- Find your actual resource group name in mentor-demo.md.
- Return to Windows PowerShell from earlier.
- Replace <RESOURCE_GROUP> in the command below with your actual resource group name.
- Expect Azure CLI to ask you to confirm the resource group deletion.
- Delete the Azure resource group by running this command:
az group delete --name <RESOURCE_GROUP>
What does this command delete?
The command deletes the named resource group plus every resource inside it. For this project, that includes the Function App, storage account, incident-intake queue, and Application Insights.
- Confirm the deletion when Azure CLI asks.
- Wait for PowerShell to return to its prompt.
- Return to the Azure portal resource groups list to confirm that your project resource group no longer appears.
Resource group still visible?
- Refresh the resource groups list after the deletion command finishes.
- Check the resource group name used in the command against mentor-demo.md.
- Renew your Azure CLI sign-in if the previous session expired.
Ask for help diagnosing the resource group deletion.
Your local folder contains the source code plus the mentor evidence pack. Permanent removal also deletes the optional .venv folder.
Windows may request confirmation because permanent deletion removes the folder without normal recovery.
- Press the Windows key to open search.
- Type File Explorer into search.
- Press Enter to open Windows File Explorer.
- Locate the incident-intake-mvp folder you created earlier.
- Select the incident-intake-mvp folder.
- Use Windows File Explorer's permanent delete option to remove the selected folder.
- Confirm that incident-intake-mvp no longer appears in its previous location.
Nice Work!
Nice Work!
You made it! Your mentor-ready incident intake system is live on Microsoft Azure.
What you learned:
- Built a responsive incident intake dashboard backed by an Azure Functions HTTP endpoint. Each accepted request returns a traceable incident ID with a 202 response.
- Connected the request path to Azure Queue Storage. The incident-intake queue hands each message to the independent ProcessIncident worker.
- Used Application Insights to show separate HTTP activity. You also captured the worker activity in a repeatable mentor demonstration.
- Secret Mission: Added a conditional Critical escalation state to the dashboard. Critical submissions now turn the result panel red while lower severities keep the standard success state.
Ready to quiz yourself?