Troubleshoot Azure Application Gateway
Deploy and repair a highly available Azure web app with path-based routing.
Introduction
30 Second Summary
A public website can fail even when every cloud resource appears to be running. Finding the exact break means following each request through every layer.
In this project, you will deploy an intentionally broken web architecture on Microsoft Azure. You will diagnose an HTTP 502 outage before repairing the application layer plus the network layer through Bicep.
What You'll Build
You’ll demonstrate Application Gateway serving a main website plus a reports website from either healthy Windows Server backend in an availability set after recovering from a planned outage.
By the end of this project, you'll have:
- A path-routed public endpoint where the root URL displays the main Internet Information Services (IIS) site with its backend computer name. The /reports/ URL displays the reports site on port 8080.
- A repeatable incident repair that connects an HTTP 502 response to missing IIS plus a network security group (NSG) deny rule. You can show every backend health entry moving from Unhealthy to Healthy.
- A GitHub incident post-mortem backed by public route output plus health evidence. A continuation brief gives Project 06 an exact starting point.
- Secret Mission: Deallocate one backend virtual machine to prove both public routes stay available through the remaining healthy backend.
Are there any prerequisites?
This project assumes you have completed AZ-104 Portfolio Projects 01 through 04. Your established Windows environment needs Azure credits plus permission to deploy Microsoft.Network and Microsoft.Compute resources.
Before We Start
This opening checkpoint commits you to a public Application Gateway that routes default requests to IIS on port 80 while /reports/* requests use IIS on port 8080. The initial deployment deliberately returns HTTP 502 because IIS is absent while the backend NSG denies gateway HTTP traffic.
Set Up the Project Workspace
The next stage provisions a paid Azure Application Gateway architecture. A wrong subscription or unavailable virtual machine image would turn the planned outage into a preventable deployment failure.
This step creates a clean workspace before any project infrastructure exists. Each prerequisite gets a visible check before you create the deployment boundary.
In this step, get ready to:
- Create the empty 05-highly-available-app-gateway workspace inside your existing portfolio repository.
- Verify that Azure CLI, Bicep CLI, and Git print their installed versions.
- Confirm Azure access before creating the denmarkeast resource group.
Create the workspace and check the toolchain
An empty project folder keeps the upcoming template separate from your earlier portfolio work. The version checks confirm that every local command resolves before Azure resources enter the picture.
- Press the Windows key to open the search bar.
- Type Windows PowerShell into the search bar.
- Press Enter to open Windows PowerShell.
- Return to your existing portfolio repository by running this command:
Set-Location -Path "[[PORTFOLIO_REPO_PATH="path to your existing portfolio repository"]]" -PassThru
What does this command do?
- The location command moves PowerShell into your existing portfolio repository.
- The -PassThru parameter prints the resolved repository path for confirmation.
You should see the path to the same repository that contains Projects 01 through 04.
- Create the 05-highly-available-app-gateway folder and enter it by running these commands:
New-Item -Path .\05-highly-available-app-gateway -ItemType Directory
Set-Location -Path .\05-highly-available-app-gateway -PassThru
How is the workspace created?
- New-Item creates the project folder inside the current repository.
- Set-Location moves PowerShell into the new folder.
- The final path confirms that later files will land in Project 05.
Your PowerShell path should now end with 05-highly-available-app-gateway. The folder is empty because the infrastructure files belong to the next step.
Did the folder command fail?
Confirm that the printed repository path belongs to your existing portfolio. A folder with the same name may already exist if you previously attempted this project.
Help me fix the Project 05 workspace path.
- Verify the three installed tools by running these commands:
az version
az bicep version
git --version
What do these checks prove?
- The az check confirms that Azure CLI starts and reports its installed components.
- The Bicep check confirms that the template compiler is available through Azure CLI.
- The git check confirms that source control is available for the later portfolio commit.
Each command should print version information. Azure CLI must report 2.20.0 or later for local Bicep deployments.
Is a tool check failing?
A missing result usually means the tool is absent from your PowerShell path. An older Azure CLI installation needs an update before it can deploy the template.
Help me troubleshoot the local toolchain.
Confirm Azure access and image availability
Azure CLI needs an authenticated subscription context before it can query regional images. This sign-in touches your Azure credentials, but you enter them only through the interactive browser flow.
- Start the Azure CLI sign-in flow by running this command:
az login
What happens during sign-in?
Azure CLI starts an interactive sign-in flow for your Microsoft account. The completed flow gives later commands an active Azure subscription context.
- Complete the account sign-in in the browser window.
- Return to Windows PowerShell after Azure CLI lists your available subscriptions.
- Display the active subscription by running this command:
az account show --output table
What does this check show?
The table identifies the subscription currently selected for Azure CLI commands. Every resource created later uses this active context.
The table should show the Azure subscription you intend to use for Project 05.
✔️ The intended subscription is active
Your Azure context is ready for the regional image check.
ⓧ A different subscription is active
- Enter the intended subscription name or ID as your Azure subscription name or ID.
- Activate that subscription by running these commands:
az account set --subscription "[[AZURE_SUBSCRIPTION="your Azure subscription name or ID"]]"
az account show --output table
How is the subscription changed?
- The first line changes the active Azure subscription.
- The second line prints the new context for confirmation.
You should now see the intended subscription in the table.
A Windows Server image can vary by subscription or region. The live query confirms that the required 2025-datacenter image is visible in Denmark East.
- Query the required image in Denmark East by running this command:
az vm image list --location denmarkeast --publisher MicrosoftWindowsServer --offer windowsserver --sku 2025-datacenter --all --output table
What does the image query check?
- The location filter limits the live query to denmarkeast.
- The publisher plus offer filters identify the Windows Server image family.
- The SKU filter selects 2025-datacenter.
- The --all parameter queries the live Azure service.
Good, the image dependency is available before any paid deployment starts. You should see at least one image row in the table.
Did the image query return no rows?
Confirm that the intended subscription is still active. Check that the location and SKU match the command exactly.
Help me diagnose the empty image query.
Create the resource group
A resource group gives the project one deployment boundary for creation and cleanup. The gateway, virtual machines, and managed disks still do not exist.
- Create rg-az104-project05 in Denmark East by running this command:
az group create --name rg-az104-project05 --location denmarkeast
What does this command create?
Azure creates a resource group named rg-az104-project05 in denmarkeast. Upcoming deployments use this boundary to keep every Project 05 resource together.
The command returns the new resource group's details when creation succeeds.
Before you run the final check, do you expect the new resource group to appear in the active subscription?
- Verify the resource group by running this command:
az group show --name rg-az104-project05 --output table
What does the final check prove?
The table reads the resource group back from Azure. This proves that the active subscription contains the deployment boundary.
You should see rg-az104-project05 in denmarkeast. The workspace now has a confirmed Azure target.
Is the resource group missing?
Check the active subscription if the group cannot be found. Check the create command output for an Azure permission or location problem.
Help me find the Project 05 resource group.
✔️ Awesome, I've got everything!
Your empty Project 05 folder and Denmark East resource group match the target workspace state.
ⓧ I'd like to double check the full code
There is no project code to compare yet. Your end state should match both items below.
- The empty 05-highly-available-app-gateway folder exists inside the portfolio repository.
- The rg-az104-project05 resource group exists in denmarkeast.
Your Project 05 workspace is ready inside the portfolio repository. Next, you will deploy the deliberately broken Application Gateway architecture from Bicep.
Deploy the Broken Architecture
Your Project 05 workspace now has a verified Azure subscription and an empty resource group. The next challenge is proving that a successful infrastructure deployment does not guarantee a working delivery path.
This step uses Bicep to deploy two Windows backends behind Azure Application Gateway. You will test the public endpoint before changing the initial configuration so the delivery gap becomes visible.
In this step, get ready to:
- Add the complete infrastructure template and evidence files to the Project 05 folder.
- Preview the initial Azure deployment before creating its resources.
- Deploy the architecture and test its public endpoint.
Create the project files
One parameterized template controls both the broken state and the later repairs. The accompanying files keep generated parameter data out of Git and provide a structure for the incident evidence.
- Press the Windows key to open the Windows search bar.
- Type VS Code and press Enter to open it.
- Click File in the top menu bar.
- Click Open Folder.
- Select the existing portfolio repository from earlier.
- Click Select Folder.
The Explorer sidebar should show the existing 05-highly-available-app-gateway folder.
- Create main.bicep inside 05-highly-available-app-gateway using the New File button in the Explorer sidebar.
- Fill main.bicep with the complete template shown in the double-check tab below.
- Save main.bicep.
You should now see main.bicep beneath the Project 05 folder in the Explorer sidebar.
- Create .gitignore inside 05-highly-available-app-gateway using the New File button.
- Fill .gitignore with the three exclusion patterns shown in the double-check tab below.
- Save .gitignore.
The Explorer sidebar should now list both infrastructure files.
- Create README.md inside 05-highly-available-app-gateway using the New File button.
- Fill README.md with the incident template shown in the double-check tab below.
- Save README.md.
Your Project 05 folder should now show all three saved files.
✔️ Awesome, I've got everything!
Your main.bicep, .gitignore, and README.md files are ready for the deployment preview.
ⓧ I'd like to double check the full code
Compare each saved file with the complete reference below.
@description('Azure region for all resources.')
param location string = resourceGroup().location
@description('Administrator username for both Windows virtual machines.')
param adminUsername string = 'azureuser'
@secure()
@description('Administrator password for both Windows virtual machines.')
param adminPassword string
@description('Deploy the IIS configuration extension to both backend virtual machines.')
param deployIis bool = false
@description('Allow Application Gateway subnet traffic to backend HTTP ports.')
param allowBackendHttp bool = false
@description('Virtual machine size validated for the learner subscription.')
param vmSize string = 'Standard_B1s'
var vnetName = 'vnet-p05'
var appGatewaySubnetName = 'snet-appgw'
var backendSubnetName = 'snet-backend'
var backendNsgName = 'nsg-p05-backend'
var publicIpName = 'pip-p05-appgw'
var availabilitySetName = 'avset-p05-web'
var applicationGatewayName = 'agw-p05'
var backendPoolName = 'webBackendPool'
var mainProbeName = 'mainProbe'
var reportsProbeName = 'reportsProbe'
var mainHttpSettingsName = 'mainHttpSettings'
var reportsHttpSettingsName = 'reportsHttpSettings'
var frontendIpConfigurationName = 'publicFrontend'
var frontendPortName = 'frontendPort80'
var listenerName = 'httpListener'
var urlPathMapName = 'sitePathMap'
var routingRuleName = 'pathRoutingRule'
var vmNames = [
'vm-p05-01'
'vm-p05-02'
]
var nicNames = [
'nic-p05-01'
'nic-p05-02'
]
var backendPrivateIps = [
'10.50.2.4'
'10.50.2.5'
]
var backendPoolId = resourceId('Microsoft.Network/applicationGateways/backendAddressPools', applicationGatewayName, backendPoolName)
var mainProbeId = resourceId('Microsoft.Network/applicationGateways/probes', applicationGatewayName, mainProbeName)
var reportsProbeId = resourceId('Microsoft.Network/applicationGateways/probes', applicationGatewayName, reportsProbeName)
var mainHttpSettingsId = resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', applicationGatewayName, mainHttpSettingsName)
var reportsHttpSettingsId = resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', applicationGatewayName, reportsHttpSettingsName)
var frontendIpConfigurationId = resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', applicationGatewayName, frontendIpConfigurationName)
var frontendPortId = resourceId('Microsoft.Network/applicationGateways/frontendPorts', applicationGatewayName, frontendPortName)
var listenerId = resourceId('Microsoft.Network/applicationGateways/httpListeners', applicationGatewayName, listenerName)
var urlPathMapId = resourceId('Microsoft.Network/applicationGateways/urlPathMaps', applicationGatewayName, urlPathMapName)
var iisCommand = '''
powershell.exe -NoProfile -ExecutionPolicy Unrestricted -Command "Install-WindowsFeature -Name Web-Server -IncludeManagementTools; Import-Module WebAdministration; Set-Content -Path 'C:\inetpub\wwwroot\Default.htm' -Value ('<h1>Project 05 main site</h1><p>Served by ' + $env:COMPUTERNAME + '</p>'); Set-Content -Path 'C:\inetpub\wwwroot\health.htm' -Value 'healthy'; New-Item -Path 'C:\inetpub\reports\reports' -ItemType Directory -Force | Out-Null; Set-Content -Path 'C:\inetpub\reports\reports\Default.htm' -Value ('<h1>Project 05 reports site</h1><p>Served by ' + $env:COMPUTERNAME + '</p>'); Set-Content -Path 'C:\inetpub\reports\health.htm' -Value 'healthy'; if (-not (Test-Path 'IIS:\Sites\Reports')) { New-Website -Name 'Reports' -Port 8080 -PhysicalPath 'C:\inetpub\reports' }; if (-not (Get-NetFirewallRule -DisplayName 'Project 05 IIS ports' -ErrorAction SilentlyContinue)) { New-NetFirewallRule -DisplayName 'Project 05 IIS ports' -Direction Inbound -Protocol TCP -LocalPort 80,8080 -Action Allow }"
'''
resource backendNsg 'Microsoft.Network/networkSecurityGroups@2023-09-01' = {
name: backendNsgName
location: location
properties: {
securityRules: [
{
name: 'BackendHttpFromAppGateway'
properties: {
priority: 100
access: allowBackendHttp ? 'Allow' : 'Deny'
direction: 'Inbound'
protocol: 'Tcp'
sourcePortRange: '*'
destinationPortRanges: [
'80'
'8080'
]
sourceAddressPrefix: '10.50.1.0/24'
destinationAddressPrefix: '10.50.2.0/24'
description: 'Controlled Project 05 rule for Application Gateway to backend HTTP traffic.'
}
}
]
}
}
resource vnet 'Microsoft.Network/virtualNetworks@2023-09-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: [
'10.50.0.0/16'
]
}
subnets: [
{
name: appGatewaySubnetName
properties: {
addressPrefix: '10.50.1.0/24'
}
}
{
name: backendSubnetName
properties: {
addressPrefix: '10.50.2.0/24'
networkSecurityGroup: {
id: backendNsg.id
}
}
}
]
}
}
resource publicIp 'Microsoft.Network/publicIPAddresses@2023-09-01' = {
name: publicIpName
location: location
sku: {
name: 'Standard'
tier: 'Regional'
}
properties: {
publicIPAddressVersion: 'IPv4'
publicIPAllocationMethod: 'Static'
idleTimeoutInMinutes: 4
}
}
resource availabilitySet 'Microsoft.Compute/availabilitySets@2023-09-01' = {
name: availabilitySetName
location: location
sku: {
name: 'Aligned'
}
properties: {
platformFaultDomainCount: 2
platformUpdateDomainCount: 5
}
}
resource nics 'Microsoft.Network/networkInterfaces@2023-09-01' = [for i in range(0, 2): {
name: nicNames[i]
location: location
properties: {
enableAcceleratedNetworking: false
enableIPForwarding: false
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
primary: true
privateIPAddress: backendPrivateIps[i]
privateIPAllocationMethod: 'Static'
privateIPAddressVersion: 'IPv4'
subnet: {
id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, backendSubnetName)
}
}
}
]
}
dependsOn: [
vnet
]
}]
resource virtualMachines 'Microsoft.Compute/virtualMachines@2023-09-01' = [for i in range(0, 2): {
name: vmNames[i]
location: location
properties: {
availabilitySet: {
id: availabilitySet.id
}
hardwareProfile: {
vmSize: vmSize
}
osProfile: {
computerName: vmNames[i]
adminUsername: adminUsername
adminPassword: adminPassword
allowExtensionOperations: true
windowsConfiguration: {
provisionVMAgent: true
enableAutomaticUpdates: true
}
}
storageProfile: {
imageReference: {
publisher: 'MicrosoftWindowsServer'
offer: 'windowsserver'
sku: '2025-datacenter'
version: 'latest'
}
osDisk: {
createOption: 'FromImage'
caching: 'ReadWrite'
managedDisk: {
storageAccountType: 'StandardSSD_LRS'
}
}
}
networkProfile: {
networkInterfaces: [
{
id: nics[i].id
properties: {
primary: true
}
}
]
}
}
}]
resource iisExtensions 'Microsoft.Compute/virtualMachines/extensions@2023-09-01' = [for i in range(0, 2): if (deployIis) {
name: '${vmNames[i]}/iisSetup'
location: location
properties: {
publisher: 'Microsoft.Compute'
type: 'CustomScriptExtension'
typeHandlerVersion: '1.10'
autoUpgradeMinorVersion: true
protectedSettings: {
commandToExecute: iisCommand
}
}
dependsOn: [
virtualMachines
]
}]
resource applicationGateway 'Microsoft.Network/applicationGateways@2023-09-01' = {
name: applicationGatewayName
location: location
properties: {
sku: {
name: 'Standard_v2'
tier: 'Standard_v2'
}
autoscaleConfiguration: {
minCapacity: 0
maxCapacity: 2
}
gatewayIPConfigurations: [
{
name: 'gatewayIpConfiguration'
properties: {
subnet: {
id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, appGatewaySubnetName)
}
}
}
]
frontendIPConfigurations: [
{
name: frontendIpConfigurationName
properties: {
publicIPAddress: {
id: publicIp.id
}
}
}
]
frontendPorts: [
{
name: frontendPortName
properties: {
port: 80
}
}
]
backendAddressPools: [
{
name: backendPoolName
properties: {
backendAddresses: [for privateIp in backendPrivateIps: {
ipAddress: privateIp
}]
}
}
]
probes: [
{
name: mainProbeName
properties: {
protocol: 'Http'
host: '127.0.0.1'
path: '/health.htm'
port: 80
interval: 30
timeout: 10
unhealthyThreshold: 2
minServers: 0
pickHostNameFromBackendHttpSettings: false
match: {
statusCodes: [
'200-399'
]
}
}
}
{
name: reportsProbeName
properties: {
protocol: 'Http'
host: '127.0.0.1'
path: '/health.htm'
port: 8080
interval: 30
timeout: 10
unhealthyThreshold: 2
minServers: 0
pickHostNameFromBackendHttpSettings: false
match: {
statusCodes: [
'200-399'
]
}
}
}
]
backendHttpSettingsCollection: [
{
name: mainHttpSettingsName
properties: {
port: 80
protocol: 'Http'
cookieBasedAffinity: 'Disabled'
pickHostNameFromBackendAddress: false
requestTimeout: 20
probeEnabled: true
probe: {
id: mainProbeId
}
}
}
{
name: reportsHttpSettingsName
properties: {
port: 8080
protocol: 'Http'
cookieBasedAffinity: 'Disabled'
pickHostNameFromBackendAddress: false
requestTimeout: 20
probeEnabled: true
probe: {
id: reportsProbeId
}
}
}
]
httpListeners: [
{
name: listenerName
properties: {
frontendIPConfiguration: {
id: frontendIpConfigurationId
}
frontendPort: {
id: frontendPortId
}
protocol: 'Http'
requireServerNameIndication: false
}
}
]
urlPathMaps: [
{
name: urlPathMapName
properties: {
defaultBackendAddressPool: {
id: backendPoolId
}
defaultBackendHttpSettings: {
id: mainHttpSettingsId
}
pathRules: [
{
name: 'reportsPathRule'
properties: {
paths: [
'/reports/*'
]
backendAddressPool: {
id: backendPoolId
}
backendHttpSettings: {
id: reportsHttpSettingsId
}
}
}
]
}
}
]
requestRoutingRules: [
{
name: routingRuleName
properties: {
ruleType: 'PathBasedRouting'
priority: 100
httpListener: {
id: listenerId
}
urlPathMap: {
id: urlPathMapId
}
}
}
]
enableHttp2: false
}
dependsOn: [
vnet
]
}
output applicationGatewayName string = applicationGateway.name
output publicIpResourceName string = publicIp.name
output backendVmNames array = vmNames
output deployIisState bool = deployIis
output allowBackendHttpState bool = allowBackendHttp
What does this template define?
- The networking resources separate the gateway subnet from the two-node backend subnet.
- The availability set spreads the two virtual machines across fault and update domains.
- The gateway sends ordinary requests to port 80 and /reports/* requests to port 8080.
- The two Boolean parameters let later deployments change the application and network layers from the same source.
*.parameters.json
*.bicepparam
.azure/
What does this file protect?
These patterns keep local Azure data and generated parameter files out of Git. The secure VM password stays outside the repository.
# Project 05: Build and Repair a Highly Available Web Architecture with Application Gateway
## Scenario
A public Azure Application Gateway routes ordinary requests to an IIS site on port 80 and `/reports/*` requests to a second IIS site on port 8080. Both routes use two Windows Server backends in an availability set.
The first deployment intentionally omitted IIS and denied TCP ports 80 and 8080 from the Application Gateway subnet. This produced Unhealthy backends and an HTTP 502 response.
## Architecture
```mermaid
flowchart LR
Internet --> PIP[Standard public IPv4]
PIP --> AGW[Application Gateway Standard_v2]
AGW -->|Default route, port 80| Pool[Two-node backend pool]
AGW -->|/reports/*, port 8080| Pool
Pool --> VM1[vm-p05-01]
Pool --> VM2[vm-p05-02]
```
## Designed failure
- Application fault: IIS was absent, so neither VM listened on the probe ports.
- Network fault: `BackendHttpFromAppGateway` denied TCP 80 and 8080 from `10.50.1.0/24`.
- User impact: The public gateway returned HTTP 502 because no healthy backend was available.
## Evidence
### What-if
Add the important Create and Modify lines from `az deployment group what-if` here.
### Initial backend health
Add the server addresses, Unhealthy states, and probe messages here.
### Local VM test before IIS
Add the connection failure returned by Run Command here.
### Local VM test after IIS
Add the successful localhost content and health response here.
### Final backend health
Add the Healthy results for both servers on ports 80 and 8080 here.
### Public routing
- Root response: Add the returned main-site content and backend computer name.
- Reports response: Add the returned reports-site content and backend computer name.
## Incident post-mortem
### Impact
The public endpoint could not serve either route and returned HTTP 502.
### Detection
Application Gateway backend health reported both backend addresses as Unhealthy. A local request executed through Azure Run Command also showed that IIS was not listening.
### Root cause
Two independent controls failed the delivery path. IIS had not been installed, and the backend NSG explicitly denied Application Gateway traffic on the configured HTTP ports.
### Resolution
The same Bicep template was redeployed in two stages. The first stage added IIS and both web sites through Windows Custom Script Extension. The second stage changed the backend NSG rule from Deny to Allow.
### Prevention
Future deployments should include application bootstrap validation, explicit health endpoints, Bicep what-if review, and a post-deployment backend-health gate before traffic is announced as available.
## Secret Mission result
Add the health and public-response evidence collected while `vm-p05-02` was deallocated, then record its successful recovery.
## Cleanup
The Azure resources are isolated in `rg-az104-project05` so the entire lab can be removed with one resource-group deletion.
How will you use this README?
The evidence sections give each diagnostic result a permanent home. The post-mortem turns the outage into a complete incident story after the repairs are finished.
Preview the deployment
A Bicep what-if preview compares the template with the empty resource group without applying the changes. This gives you a chance to catch an unexpected deletion before paid resources exist.
The command requests the administrator password at runtime because adminPassword is a secure parameter. Keep the password private and do not save it in either project file.
- Switch back to Windows PowerShell from earlier.
- Move into the existing 05-highly-available-app-gateway folder inside your portfolio repository.
- Preview the initial deployment by running this command:
az deployment group what-if --resource-group rg-az104-project05 --template-file .\main.bicep --parameters deployIis=false allowBackendHttp=false
What does this preview check?
- The resource group scope keeps every new resource inside rg-az104-project05.
- The template file supplies the complete Azure architecture.
- The two false parameter values preserve the initial application and network states.
- Enter a compliant VM administrator password when Windows PowerShell prompts for the secure value.
- Review the preview for the expected resource creations.
- Confirm that the preview contains no resource deletion.
You should see planned creations for the network, compute, public IP, availability set, and Application Gateway resources. The absence of deletions confirms that this template only adds Project 05 infrastructure.
Preview failing before it finishes?
Confirm that Windows PowerShell is inside the 05-highly-available-app-gateway folder. Also check that main.bicep is saved and matches the complete reference.
If Azure rejects the preview for another reason, help me troubleshoot the Bicep what-if failure.
Deploy and reveal the outage
The preview has shown what Azure plans to create. The deployment now turns that plan into a live regional architecture with two backend virtual machines and one public gateway.
This deployment provisions several paid Azure resources. Expect Azure to spend several minutes creating the virtual machines and gateway, so a quiet terminal during that period does not mean it has stalled.
- Create the initial deployment by running this command:
az deployment group create --name p05-initial --resource-group rg-az104-project05 --template-file .\main.bicep --parameters deployIis=false allowBackendHttp=false
What does this deployment create?
The command creates the p05-initial deployment inside the existing resource group. Its two parameter values reproduce the same state you reviewed in the preview.
The gateway receives a public frontend and two path destinations. Both destinations share the same two backend virtual machines.
- Enter the same compliant VM administrator password when Windows PowerShell requests the secure value.
- Wait for the deployment command to finish successfully.
That is the longest provisioning task complete. Your resource group now contains the two-node backend and its public Application Gateway.
Deployment not completing?
Check the final deployment output for the first failed resource. A copied template line or a subscription capacity restriction can stop dependent resources from being created.
For targeted help, help me interpret the failed Azure deployment resource.
The gateway's public IP gives you the address for the first end-to-end test. Store it in $appGatewayIp so the same PowerShell session can reuse it.
- Retrieve the gateway IP by running this command:
$appGatewayIp = az network public-ip show --resource-group rg-az104-project05 --name pip-p05-appgw --query ipAddress --output tsv
What does this command capture?
The Azure CLI query returns only the public IP address from pip-p05-appgw. Windows PowerShell stores that address in $appGatewayIp for the request.
- Display the captured public IP by entering $appGatewayIp at the Windows PowerShell prompt.
You should see one public IPv4 address. That value confirms the gateway frontend is addressable from your PowerShell session.
Before you send the request, do you think a completed deployment guarantees that the gateway can serve the page?
- Test the public root endpoint by running this command:
Invoke-WebRequest -UseBasicParsing -Uri "http://$appGatewayIp/"
The HTTP 502 Is Intentional
Windows PowerShell reports an HTTP 502 error instead of returning the site page. The gateway has no healthy backend that can receive the request.
The deployment omitted Internet Information Services, so neither virtual machine listens on the probe ports. The backend network security group also denies gateway traffic to ports 80 and 8080.
Seeing a different result?
Confirm that the request uses the value stored in $appGatewayIp. Also confirm that both deployment parameters were set to false.
If the response still differs, help me compare my gateway response with the expected HTTP 502.
You have captured the designed outage from a live public endpoint. Next, you will inspect backend health and test the guest operating system so each failed layer is supported by evidence.
Diagnose the 502 Outage
Your deliberately broken Azure Application Gateway is live. The HTTP 502 response proves the incident is reproducible.
That gateway error alone cannot identify whether the fault sits in routing, the guest application, or network policy.
You will use Azure CLI evidence to trace the dependency chain from custom health probes to the local IIS listener. You will finish at the backend NSG rule without changing the deployment.
In this step, get ready to:
- Collect Application Gateway backend-health evidence.
- Test the IIS listener from inside vm-p05-01.
- Trace the backend NSG rule that blocks probe traffic.
Inspect Application Gateway backend health
Application Gateway evaluates each backend through its configured HTTP settings and probes. Only servers marked Healthy receive traffic.
Before you run the check, do you expect either backend to be available for requests?
- Inspect every backend from Windows PowerShell by running this command:
az network application-gateway show-backend-health --resource-group rg-az104-project05 --name agw-p05 --output json
What Does Backend Health Prove?
- The webBackendPool section reports the status of 10.50.2.4 and 10.50.2.5.
- The mainHttpSettings entries show probe results for port 80.
- The reportsHttpSettings entries show probe results for port 8080.
- An Unhealthy result means Application Gateway excludes that server from traffic.
You should see every server entry report "health": "Unhealthy".
The gateway has no healthy target for either route. That evidence explains the HTTP 502 response.
Backend Health Command Failed?
- Confirm your Azure CLI session still targets the intended subscription.
- Check that the resource group name is rg-az104-project05.
- Check that the Application Gateway name is agw-p05.
Help me troubleshoot the backend-health command.
- Return to README.md in VS Code.
- Locate the ### Initial backend health heading.
- Record the server addresses, health states, probe messages, and earlier HTTP 502 result beneath that heading.
- Save README.md.
Your incident record now connects the public HTTP 502 response to the absence of healthy backend targets.
Test the local IIS listener
A localhost request bypasses Application Gateway and the backend NSG path. Azure Run Command lets you test port 80 from inside vm-p05-01.
Before you run the test, do you expect the VM to answer the local HTTP request?
- Test the local web listener by running this command:
az vm run-command invoke --resource-group rg-az104-project05 --name vm-p05-01 --command-id RunPowerShellScript --scripts "try { (Invoke-WebRequest -UseBasicParsing -Uri http://localhost/).StatusCode } catch { Write-Output $_.Exception.Message }"
What Does This Test Isolate?
- Azure Run Command executes the PowerShell script inside vm-p05-01.
- The request targets the VM's own port 80.
- The error handler returns the local connection result as evidence.
- A local failure isolates the guest application before network policy enters the path.
You should see a connection failure in the Run Command output. The initial deployment omitted IIS, so no local HTTP listener is available.
Run Command Did Not Return a Result?
- Confirm that vm-p05-01 is running.
- Check that the command targets rg-az104-project05.
- Rerun the command after the previous Run Command operation finishes.
Help me troubleshoot Azure Run Command.
- Return to README.md.
- Locate the ### Local VM test before IIS heading.
- Replace the placeholder with the connection failure from the Run Command output.
- Save README.md.
You have now proved the first root cause. IIS is absent from the VM, so the probes have no application listener to reach.
Trace the backend NSG rule
The local connection failure proves an application-layer fault. The Bicep template also contains an independent network-layer fault.
- Return to main.bicep in VS Code.
- Find name: 'BackendHttpFromAppGateway'.
- Read priority: 100 in the rule properties.
- Read access: allowBackendHttp ? 'Allow' : 'Deny'.
- Trace sourceAddressPrefix: '10.50.1.0/24' to the Application Gateway subnet.
- Trace destinationAddressPrefix: '10.50.2.0/24' to the backend subnet.
- Confirm that destinationPortRanges contains '80' and '8080'.
Why Is This a Separate Fault?
The deployed value allowBackendHttp=false makes the conditional expression resolve to Deny. Priority 100 is evaluated before Azure's lower-priority default VNet rule.
The explicit rule blocks TCP ports 80 and 8080 from the gateway subnet. IIS alone cannot repair that network path.
- Return to README.md.
- Locate the NSG fault under ## Designed failure.
- Add the priority, source subnet, destination subnet, and blocked ports to your incident evidence.
- Save README.md.
Your evidence chain is complete. It now identifies a missing IIS listener plus an explicit NSG deny rule.
Before you rerun the health check, do you expect diagnosis alone to change the backend state?
- Verify that every backend remains unhealthy by running:
az network application-gateway show-backend-health --resource-group rg-az104-project05 --name agw-p05 --output json
What Does the Repeated Check Prove?
This query reads the current Application Gateway state. It does not change the deployment.
The repeated result gives you a clean boundary between diagnosis and repair. Your evidence is complete while the incident remains intact.
You should still see "health": "Unhealthy" for every server entry under the port 80 and port 8080 settings.
That unchanged state confirms this step preserved the original failure for the controlled repairs ahead.
Seeing a Different Health State?
- Confirm that no deployment command ran during this step.
- Wait for the next probe cycle.
- Rerun the backend-health command.
Help me explain the unexpected backend state.
✔️ My evidence chain is complete
Strong work. You have isolated the application fault and the network fault without changing the broken deployment.
ⓧ I'd like to double check the project state
- Confirm that README.md records the HTTP 502 result plus every Unhealthy backend.
- Confirm that README.md records the local connection failure from vm-p05-01.
- Confirm that the incident evidence identifies BackendHttpFromAppGateway as the priority 100 deny rule.
- Confirm that main.bicep remains unchanged with deployIis=false and allowBackendHttp=false for the initial deployment.
You have isolated both failures without disturbing the evidence. Next, you will install IIS while the NSG deny stays in place so each repair proves its own impact.
Repair the Application and Network Layers
Your diagnosis exposed two independent breaks in the delivery chain. Both virtual machines lack Internet Information Services (IIS).
Layered troubleshooting repairs one dependency at a time. You will prove the application layer works before you change the network path.
In this step, get ready to:
- Enable IIS on both backend virtual machines through the existing Bicep template.
- Isolate the application repair with local response evidence.
- Repair the Application Gateway network path through a second controlled deployment.
Install IIS on both backends
The deployIis parameter controls whether your template configures the web servers. A value of true adds the iisSetup Windows Custom Script Extension to each backend.
The deployment can take several minutes while Azure configures both Windows servers. A quiet terminal during extension setup does not mean the command is stuck.
The password prompt is expected. Your secure Bicep parameter keeps the value out of the template.
- Keep the compliant virtual machine administrator password from the initial deployment ready.
- Install IIS on both backends by running this command:
az deployment group create --name p05-install-iis --resource-group rg-az104-project05 --template-file .\main.bicep --parameters deployIis=true allowBackendHttp=false
What does this deployment change?
- The p05-install-iis deployment reuses the existing main.bicep template.
- The deployIis=true parameter installs IIS on both virtual machines.
- The allowBackendHttp=false parameter preserves the restrictive backend rule for the next diagnostic check.
When the command finishes, you should see deployment details for p05-install-iis. Both deployed virtual machines now carry the iisSetup extension.
Did the IIS deployment fail?
If the deployment stops at the secure prompt, enter the same compliant administrator password used for the initial deployment.
If extension provisioning fails, inspect the deployment output for the resource that did not complete.
Help me troubleshoot the IIS deployment.
Prove the application layer works
A localhost request stays inside the virtual machine. This gives you a clean test of the guest application before gateway traffic enters the picture.
- Inspect the extension on vm-p05-01 by running this command:
az vm extension show --resource-group rg-az104-project05 --vm-name vm-p05-01 --name iisSetup --output table
What does this extension check prove?
The extension query confirms that Azure attached iisSetup to vm-p05-01. A completed extension means the IIS configuration command ran inside the guest operating system.
You should see extension details for iisSetup without a provisioning failure.
Can't inspect the extension?
Confirm that the earlier deployment completed before checking the extension. Check that the virtual machine name is vm-p05-01.
Help me inspect the IIS extension.
- Test the main listener plus the reports health endpoint from inside vm-p05-01 by running this command:
az vm run-command invoke --resource-group rg-az104-project05 --name vm-p05-01 --command-id RunPowerShellScript --scripts "(Invoke-WebRequest -UseBasicParsing -Uri http://localhost/).Content; (Invoke-WebRequest -UseBasicParsing -Uri http://localhost:8080/health.htm).Content"
What does this local test check?
- The first request reads the main IIS site through port 80.
- The second request reads the reports health endpoint through port 8080.
- Both requests originate inside the virtual machine, so the gateway subnet rule is outside this test path.
You should see main-site HTML containing the backend computer name. The next response should contain healthy.
Did a localhost request fail?
Confirm that iisSetup completed before repeating the local request. A failure on port 8080 points to the reports site configuration inside the extension.
Help me troubleshoot the local IIS responses.
Before you ask Application Gateway for its view, do you think successful localhost responses are enough to make its probes healthy?
- Check the gateway's backend health by running this command:
az network application-gateway show-backend-health --resource-group rg-az104-project05 --name agw-p05 --output json
What does this health report check?
This command asks Application Gateway to report the probe result for every backend address. The JSON output separates the port 80 path from the port 8080 path.
Every server entry still reports "health": "Unhealthy". This is the planned shortfall.
IIS now works inside the virtual machine. The gateway path remains blocked by the unchanged rule.
Backend health result look different?
If the report does not load, confirm that Azure CLI still has access to the intended subscription. If any server is already Healthy, confirm that the IIS deployment kept allowBackendHttp=false.
Help me compare the unexpected backend-health result.
- Switch back to README.md in VS Code.
- Find the ### Local VM test after IIS heading.
- Replace its placeholder with the successful localhost output plus the still-unhealthy gateway result.
- Save README.md.
Repair the network path
The BackendHttpFromAppGateway rule sits at priority 100 in the backend network security group (NSG). The allowBackendHttp parameter changes its access value from Deny to Allow.
This repair opens ports 80 plus 8080 for traffic from the gateway subnet. The rule remains limited to the backend subnet.
The secure password prompt appears again because this is a new deployment. Enter the same compliant administrator password without saving it in the repository.
- Repair the backend network rule by running this command:
az deployment group create --name p05-open-backend --resource-group rg-az104-project05 --template-file .\main.bicep --parameters deployIis=true allowBackendHttp=true
What does the second deployment change?
- The deployIis=true parameter preserves both configured IIS servers.
- The allowBackendHttp=true parameter changes the controlled NSG rule to Allow.
- The p05-open-backend deployment keeps the gateway configuration unchanged.
When the command finishes, you should see deployment details for p05-open-backend.
Did the network repair fail?
Confirm that the command uses deployIis=true plus allowBackendHttp=true. Inspect the deployment output for the resource update that did not complete.
Help me troubleshoot the network repair deployment.
Probe recovery is not instant. Two probe intervals take about one minute because the template checks each backend every 30 seconds.
- Wait for two probe intervals.
Before you run the final check, what should change now that the application listeners have a reachable network path?
- Verify the repaired backend health by running this command:
az network application-gateway show-backend-health --resource-group rg-az104-project05 --name agw-p05 --output json
What does the final report prove?
The report now tests the complete path from Application Gateway to each IIS health endpoint. It covers both backend addresses through both configured ports.
You should find both backend addresses listed as Healthy for port 80 plus port 8080.
That is the layered repair complete: the gateway can now reach every IIS listener.
Are any backends still Unhealthy?
Wait for another probe interval before repeating the health command. Confirm that p05-open-backend completed successfully.
Check that iisSetup exists on both virtual machines. Confirm that the backend rule now uses Allow.
Help me diagnose the remaining Unhealthy backend.
Your delivery chain is healthy from the gateway through both IIS sites. Next, you will prove both public routes before publishing the incident evidence.
Verify, Document, and Publish
Your repaired Azure Application Gateway now reports healthy custom health probes. Probe health alone does not prove that users reach the intended IIS site.
This step turns the runtime evidence into a complete incident record. You will publish that record with Git to your GitHub portfolio.
In this step, get ready to:
- Verify the public root route plus the public /reports/ route.
- Complete the Project 05 evidence package.
- Publish the finished Project 05 record.
Verify both public routes
A public request tests the listener plus path-based routing. It also proves that the correct backend settings reach the intended site.
- Switch back to Windows PowerShell from earlier.
- Refresh $appGatewayIp by rerunning the public IP lookup from the broken-deployment test.
Before you send the requests, which site heading do you expect each URL to return?
- Verify both public routes by running these commands:
(Invoke-WebRequest -UseBasicParsing -Uri "http://$appGatewayIp/").Content
(Invoke-WebRequest -UseBasicParsing -Uri "http://$appGatewayIp/reports/").Content
What do these requests prove?
- The root request uses the default backend setting on port 80.
- The /reports/ request matches the path rule.
- The matched path rule uses the backend setting on port 8080.
The first response should contain Project 05 main site plus one backend computer name.
The second response should contain Project 05 reports site plus one backend computer name.
That closes the outage loop. The repaired gateway now serves both IIS sites through its public frontend.
Is a public route failing?
- Confirm that $appGatewayIp contains the current public IP address.
- Confirm that both backend settings remain healthy with the backend-health check from the previous step.
- Inspect reportsProbe if only the /reports/ route fails.
Help me diagnose which Application Gateway layer is blocking one of my public routes.
Complete the incident record
An incident record is useful when evidence supports each conclusion. Your README.md already contains the structure plus the observations collected in earlier steps.
- Return to README.md in VS Code.
The evidence headings form a timeline from planned deployment to repaired service.
- Replace the What-if placeholder with the important Create plus Modify lines from your Bicep what-if preview.
- Replace the Initial backend health placeholder with both server addresses plus their Unhealthy states.
These sections now connect the planned infrastructure changes to the first unhealthy result.
- Record the observed HTTP 502 in the initial backend-health evidence.
- Replace the Local VM test before IIS placeholder with the connection failure from vm-p05-01.
The public failure now has supporting evidence from inside the backend VM.
- Replace the Local VM test after IIS placeholder with the successful main-site content plus the health response.
- Replace the Final backend health placeholder with the Healthy results for both servers on ports 80 plus 8080.
The before-and-after local tests isolate the application repair. The final health report proves that the separate network repair restored the gateway path.
- Complete the Root response evidence with the returned main-site content plus its backend computer name.
- Complete the Reports response evidence with the returned reports-site content plus its backend computer name.
How does the evidence fit together?
- The what-if evidence records the intended infrastructure changes.
- The initial health result records the outage impact.
- The local tests separate the application fault from the network fault.
- The final health report plus the public responses prove the resolution.
- Review the Incident post-mortem section against your completed evidence.
- Save README.md.
- Search README.md for Add the.
The search should return no unfinished evidence placeholders.
The continuation brief carries this architecture into Project 06. Copying it exactly keeps the next project grounded in the environment you built.
- Return to the repository root in the Explorer sidebar.
- Select the repository-root NEXT-PROJECT-BRIEF.md file.
This file becomes the source of truth for the next portfolio project.
- Replace the entire contents of NEXT-PROJECT-BRIEF.md with the full file in the double-check tab below.
- Save NEXT-PROJECT-BRIEF.md.
✔️ Awesome, I've got everything!
Your completed README.md now tells the full incident story. Your repository-root NEXT-PROJECT-BRIEF.md now carries that story into Project 06.
ⓧ I'd like to double check the full code
Your final repository state should match these checks.
- The .gitignore file still contains *.parameters.json, *.bicepparam, plus .azure/.
- The main.bicep file remains unchanged from the repaired deployment.
- The README.md file contains your completed failure plus repair evidence.
- The repository-root NEXT-PROJECT-BRIEF.md file matches the full reference below.
# AZ-104 Portfolio Continuation Brief
## Purpose
This file is the source of truth for continuing the AZ-104 portfolio after Project 05. Use it to design Project 06 in the same numbered, troubleshooting-first format.
## Learner and environment
- Device: Windows
- Primary shell: Windows PowerShell
- Editor: VS Code
- Infrastructure as code: Bicep
- Cloud tooling: Azure CLI and Bicep CLI
- Source control: Git and GitHub
- Azure region: Denmark East (`denmarkeast`)
- Cost model: Azure credits are available, but every project must include explicit pause and delete options
- Repository pattern: One numbered folder per project, plus this continuation brief at the repository root
- Preferred workflow: Azure CLI for deployment and runtime checks, with the Azure portal used only for visual confirmation
## Completed portfolio projects
### Project 01
Established the Azure portfolio foundation and repeatable repository workflow.
### Project 02
Built and validated reusable Azure infrastructure through Bicep.
### Project 03
Expanded the portfolio with operational Azure resource management and validation.
### Project 04
Built the secured hub-and-spoke network, created a deliberate connectivity problem, diagnosed the network path, repaired the configuration, and recorded the incident as portfolio evidence.
### Project 05
Folder: `05-highly-available-app-gateway`
Built a redundant regional web delivery architecture with:
- Azure Application Gateway Standard_v2
- A dedicated Application Gateway subnet and backend subnet
- Two Windows Server virtual machines in an availability set
- A shared backend pool with custom health probes
- Default routing to IIS on port 80
- `/reports/*` path-based routing to IIS on port 8080
- A Standard public IPv4 frontend
- Bicep what-if before deployment
Troubleshooting scenario:
- The initial Bicep deployment intentionally omitted IIS.
- The backend NSG intentionally denied ports 80 and 8080 from the gateway subnet.
- Application Gateway reported Unhealthy backends and returned HTTP 502.
- IIS was installed on both VMs through Windows Custom Script Extension.
- Local VM requests succeeded while gateway probes still failed, isolating the remaining NSG fault.
- The NSG rule was changed from Deny to Allow through the same Bicep template.
- Backend health recovered to Healthy and both public routes were verified.
- The optional failover test deallocated one backend and proved both routes stayed available through the remaining VM.
Portfolio evidence created:
- Complete `main.bicep`
- Layered backend-health and HTTP evidence
- Incident post-mortem in `README.md`
- Git history showing the completed numbered project folder
## Standards to preserve for Project 06
- Keep the project under 60 minutes and at no more than six counted steps.
- Use `denmarkeast` unless an official availability check requires another region.
- Keep Azure CLI and Bicep as the primary deployment workflow.
- Run Bicep what-if before deployment.
- Include one deterministic failure on the main path.
- Diagnose before repairing.
- End every step with a command or action that produces observable evidence.
- Keep secrets out of Git and GitHub.
- Include resource cleanup and cost warnings.
- Finish by updating this continuation brief for the next project.
## Recommended Project 06 direction
Title: Monitor and Respond to Application Gateway Failures with Azure Monitor
Goal: Extend the application-delivery and incident-response skills from Project 05 by making failures observable before a user reports them.
Recommended scope:
- Deploy a compact Application Gateway test environment through Bicep.
- Send Application Gateway diagnostic data to a Log Analytics workspace.
- Create an Azure Monitor alert for unhealthy backend behavior or failed requests.
- Trigger a controlled backend failure.
- Use logs, metrics, and alert evidence to identify the affected layer.
- Restore service, document the incident timeline, clean up resources, and push Project 06 to GitHub.
The project should focus on monitoring and incident detection. Do not add WAF, custom domains, TLS certificates, or global routing to the same project. Those belong in later projects.
## Instruction for the next planning session
Use this continuation brief as the source of truth. Research current Microsoft documentation and pricing before designing Project 06. Preserve the same portfolio format, Windows PowerShell command style, troubleshooting narrative, GitHub evidence requirements, and final copy-ready update to `NEXT-PROJECT-BRIEF.md`.
What does this brief preserve?
The brief records the Project 05 architecture plus its troubleshooting lifecycle. It also fixes the scope and working standards for Project 06.
Your documentation now preserves the complete incident lifecycle. The next project can begin from evidence instead of assumptions.
Publish the finished project
A local file change becomes portfolio evidence after Git records it. Publishing the commit makes the completed project visible in your connected repository.
- Switch back to the repository root in Windows PowerShell.
- Stage the Project 05 files plus the continuation brief by running these commands:
git add .\05-highly-available-app-gateway .\NEXT-PROJECT-BRIEF.md
git commit -m "Complete AZ-104 Project 05 Application Gateway troubleshooting"
git push
What do these commands do?
- The first line stages the Project 05 folder plus the continuation brief.
- The second line creates a commit with the required message.
- The third line sends the commit to your existing remote repository.
You should see Git create the commit before the push completes.
Did the commit or push fail?
- Read the Git message for a missing local identity setting.
- Complete any authentication request before retrying the push.
- Confirm that Windows PowerShell is at the repository root.
Help me troubleshoot why my Project 05 Git commit or push failed.
Before the final check, do you expect Git to report any pending files after a successful commit?
- Verify the repository state by running:
git status
What does this check prove?
Git compares the current files with the recorded commit. A clean result proves that the Project 05 folder plus the continuation brief are fully committed.
Git should report a clean working tree with no pending changes.
- Return to your portfolio repository on GitHub.
- Confirm that 05-highly-available-app-gateway appears at the repository root.
- Confirm that NEXT-PROJECT-BRIEF.md contains the Project 05 continuation brief.
You have published the full incident lifecycle. Your portfolio now shows the outage, the diagnosis, the layered repair, plus the working public routes.
Secret mission
Prove Backend Failover
A healthy diagram cannot prove failover. Take `vm-p05-02` offline. Prove both public routes remain available through `vm-p05-01` before restoring the two-backend state.
Clean Up Your Resources
Clean Up Your Resources
Your deployed Microsoft Azure architecture continues to incur charges while its billable resources remain provisioned. Decide whether to keep the live demonstration running, pause its main services, or delete the resource group entirely.
Cost warning
Application Gateway v2 has fixed charges plus capacity-unit charges while active. A partial gateway hour is billed as a full hour.
The virtual machines, managed disks, and public IPv4 address also use billable resources. Choose Pause or Delete if you no longer need the live demonstration.
Every deployed resource is contained in rg-az104-project05. Deleting that group leaves your committed GitHub evidence intact.
Resources you used:
- Azure resource group rg-az104-project05 in denmarkeast.
- Azure virtual network vnet-p05.
- Application Gateway subnet snet-appgw using 10.50.1.0/24.
- Backend subnet snet-backend using 10.50.2.0/24.
- Network security group nsg-p05-backend with the BackendHttpFromAppGateway rule.
- Standard static public IPv4 address pip-p05-appgw.
- Availability set avset-p05-web.
- Network interfaces nic-p05-01 and nic-p05-02.
- Windows Server virtual machines vm-p05-01 and vm-p05-02 with managed OS disks.
- Custom Script Extension instances named iisSetup on both virtual machines.
- Application Gateway agw-p05 using the Standard_v2 SKU.
Keep everything running
No action needed. Choose this if you are still actively demonstrating the architecture or collecting evidence from its public routes.
- Leave rg-az104-project05 deployed with both virtual machines running.
- Budget for continued Application Gateway charges plus compute and storage charges.
- Retain the committed Project 05 files in your existing portfolio repository.
Pause - I'll come back to this later
Shut down the gateway plus both virtual machines to reduce ongoing charges. Azure preserves the configuration for a later return.
Managed disks and the public IP can still incur charges while the architecture is paused.
- Pause the gateway plus both virtual machines by running these commands:
az network application-gateway stop --resource-group rg-az104-project05 --name agw-p05
az vm deallocate --resource-group rg-az104-project05 --name vm-p05-01
az vm deallocate --resource-group rg-az104-project05 --name vm-p05-02
What do these commands pause?
- The first command stops agw-p05 while preserving its configuration.
- The final two commands deallocate the virtual machines while preserving their managed disks.
- Wait for all three commands to return control to Windows PowerShell.
- Confirm that Azure CLI reports no failure for any resource.
Pause commands failed?
- Check that your Azure CLI session still uses the subscription containing rg-az104-project05.
- Check that the commands use agw-p05, vm-p05-01, and vm-p05-02 exactly.
- Get help troubleshooting the pause commands.
Your gateway plus both virtual machines are now paused. The infrastructure configuration remains available for your next session.
Delete - I don't want to use this again
Deleting the resource group is permanent, but your committed GitHub evidence stays safe. This option removes the complete Azure architecture.
- Delete rg-az104-project05 and every Azure resource inside it by running this command:
az group delete --name rg-az104-project05 --yes --no-wait
What does deletion remove?
This command starts deleting the resource group plus its networking, compute, extension, public IP, and Application Gateway resources. The --no-wait flag returns control while Azure continues the deletion.
- Confirm that Windows PowerShell returns to the prompt without reporting a permissions or resource-lock failure.
Resource group deletion failed?
- Check that your Azure account can delete resources in the current subscription.
- Check that the command uses rg-az104-project05 exactly.
- Get help troubleshooting the resource group deletion.
Resource group deletion can take a while because Azure removes every dependent resource. Check whether the group still exists after Azure has had time to finish.
- Verify the deletion by running this command:
az group exists --name rg-az104-project05
What does the existence check prove?
The command returns false when Azure can no longer find the resource group. That result confirms the paid Project 05 architecture has been removed.
Still finding the resource group?
- Wait for Azure to finish removing the dependent resources.
- Run the existence check again.
- Get help checking the deletion status.
Nice Work!
Nice Work!
You did it! You built a redundant web delivery architecture with Azure Application Gateway Standard_v2. You proved both public routes survive a backend outage.
What you can now do:
- Built a two-node Windows backend across an Azure availability set. Served the main site through IIS on port 80. Served the Reports site on port 8080.
- Configured custom health probes to control which backends receive traffic. Used path-based routing to direct /reports/* requests to port 8080.
- Diagnosed an HTTP 502 with backend-health evidence. Isolated missing IIS at the application layer. Isolated the NSG deny rule at the network layer. Repaired both controls through repeatable Bicep deployments. Published the incident post-mortem to GitHub.
- Completed the optional Secret Mission by deallocating vm-p05-02. Proved both public routes stayed available through vm-p05-01. Restarted vm-p05-02. Confirmed both backends returned to Healthy.
Ready to quiz yourself?