Azure VM Backup and File Recovery

Back up a Windows VM, recover a file, and verify its SHA-256 hash.

Introduction

30 Second Summary

A backup setting can look reassuring until an important file disappears. The real test is whether you can recover the right version with evidence you trust.

In this project, you will build a Denmark East file recovery drill in Microsoft Azure using Bicep plus Azure Backup. You will delete a known file from a protected Windows Server VM before restoring it to Windows 11 with matching SHA-256 evidence.

What You'll Build

Picture showing a deleted server file safely recovered on Windows 11 with proof that every byte matches the original.

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

  • A repeatable recovery drill that lets you redeploy the same protected Windows Server VM from Bicep.
  • A completed backup job tied to a named recovery point you can show before simulating data loss.
  • An evidence-backed README that records file integrity. It proves the recovery mount was disconnected. It records the actual cleanup state.
  • Secret Mission: Overwrite the live file. Recover its prior version from the original recovery point. Prove the hashes differ before disconnecting the second mount.

Are there any prerequisites?

You need Azure portal access from Windows 11 through a subscription with credits plus create or delete permissions. File recovery requires outbound access on TCP ports 443 plus 3260.

Before We Start

The business risk is deletion or overwrite of a critical application configuration file. Before touching Azure, commit to evidence-led recovery across preflight, deployment, backup, recovery points, file existence, SHA-256 integrity, mount disconnection, cost, and cleanup.

Prepare the Workstation and Preflight

A missing tool or blocked regional identifier can stall this recovery drill after paid resources already exist. An incomplete cost review can also hide charges from backup storage or instant-restore snapshots.

You will prepare Windows PowerShell. You will verify the Azure CLI and Bicep. You will then test the planned Microsoft Azure deployment before creating the lab.

In this step, get ready to:
  • Verify Azure CLI access to the active subscription.
  • Create the local project files and build the Bicep template.
  • Estimate costs and complete the Denmark East deployment preflight.
Verify Azure CLI and sign in

Azure CLI provides the commands used throughout this drill. Version 2.91.0 is the project baseline.

  • Press the Windows key to open Windows search.
  • Type PowerShell into the search field.
  • Press Enter to open Windows PowerShell.
  • Check the installed Azure CLI version by running this command:
az version

What does this command check?

The command prints the installed Azure CLI components and their versions. Use the Azure CLI version to choose the matching tab below.

✔️ I see version 2.91.0 or higher

Your Azure CLI meets the project baseline. Keep this PowerShell window open for the remaining checks.

  • Continue to the sign-in instructions below.

ⓧ I see an older version

The installed Azure CLI needs an update before you continue. The upgrade keeps your existing installation while replacing the older release.

  • Upgrade Azure CLI by running this command:
winget upgrade --exact --id Microsoft.AzureCLI

What does this command do?

Windows Package Manager finds the package with the exact Microsoft Azure CLI identifier. It upgrades that package to the latest available release.

  • Close PowerShell after the upgrade completes.
  • Reopen PowerShell through Windows search.
  • Confirm the upgraded version by running this command:
az version

What should you confirm?

The Azure CLI version should now show 2.91.0 or higher. This confirms the upgrade is available in the new PowerShell session.

Upgrade did not complete?

Close any PowerShell windows that may be using Azure CLI. Run the upgrade again from a PowerShell window with permission to install applications.

Still blocked? Help me troubleshoot an Azure CLI upgrade that failed in Windows PowerShell.

ⓧ Command not found

Azure CLI is missing from this workstation. Windows Package Manager can install the current Microsoft package.

  • Install Azure CLI by running this command:
winget install --exact --id Microsoft.AzureCLI

What does this command do?

Windows Package Manager installs the package with the exact Microsoft Azure CLI identifier. The command may display an installation approval prompt.

  • Approve the installation if Windows requests permission.
  • Close PowerShell after the installation completes.
  • Reopen PowerShell through Windows search.
  • Confirm the installed version by running this command:
az version

What should you confirm?

The output should now include Azure CLI version 2.91.0 or higher. This proves the executable is available in the new PowerShell session.

Azure CLI still unavailable?

Make sure you opened a new PowerShell window after installation. The earlier session may not contain the updated executable path.

Still blocked? Help me diagnose why Azure CLI is still unavailable after a winget installation.

Authentication connects Azure CLI to your subscription. The sign-in command opens a browser flow so you can use your Azure account without placing credentials in the terminal.

  • Start the Azure sign-in flow by running this command:
az login

What happens during sign-in?

Azure CLI opens a browser authentication flow. Complete the prompts with the account that has permission to create and delete resources for this drill.

  • Complete the browser sign-in flow.
  • Return to the PowerShell window from earlier.
  • Inspect the active subscription by running this command:
az account show

What does the account output prove?

The output identifies the subscription that later commands target. Check the subscription name before any paid resources are created.

  • Record the active subscription as your active Azure subscription.
Create the project files and verify Bicep

The local project keeps infrastructure code separate from recovery scripts and evidence. The README records expected results separately from observations gathered during the drill.

  • Move to your Desktop by running this command:
cd ~/Desktop

Why start on the Desktop?

The project folder now has a predictable location on this Windows workstation. You can find the evidence quickly during the recovery drill.

  • Create the project folder structure by running these commands:
New-Item -Path .\07-backup-and-restore -ItemType Directory -Force
New-Item -Path .\07-backup-and-restore\infra, .\07-backup-and-restore\scripts, .\07-backup-and-restore\evidence, .\07-backup-and-restore\restored -ItemType Directory -Force

What does this structure separate?

  • The infra folder stores the Bicep deployment template.
  • The scripts folder stores the file creation and recovery checks.
  • The evidence folder stores captured output from each stage.
  • The restored folder receives the recovered file later.
  • Switch into the new project folder by running this command:
cd .\07-backup-and-restore

What changes now?

PowerShell now resolves relative paths from the 07-backup-and-restore folder. The remaining project commands assume this location.

  • Create the five project files by running this command:
New-Item -Path .\README.md, .\infra\main.bicep, .\scripts\create-sample.ps1, .\scripts\delete-sample.ps1, .\scripts\verify-restored.ps1 -ItemType File -Force

What should PowerShell list?

PowerShell should list each new file with its full location. These empty files are ready for the exact project source below.

  • Press the Windows key to open Windows search.
  • Type Notepad into the search field.
  • Press Enter to open the editor.
  • Use the full-code tab below to fill each matching file.

✔️ Awesome, I've got everything!

Great. Confirm that all five files are saved inside the exact folders shown above.

  • Keep the README observations limited to results you have actually seen.
  • Leave deployment and recovery observations unfinished until those actions occur.

ⓧ I'd like to double check the full code

  • Replace the contents of README.md with this template:
# Project 07: Azure VM Backup and File Recovery Drill

Status: In progress
Region: Denmark East (`denmarkeast`)

## Scenario and goals

Problem: A critical file can be deleted or overwritten, and backup configuration alone does not prove recovery.

Goals: deploy, prove a point, restore one file, compare SHA-256, disconnect the mount, and record cleanup.

## What this demonstrates

Expected capabilities: Bicep deployment, backup inspection, on-demand backup, point validation, item-level recovery, and cleanup awareness.

Observed capabilities: [Add only after real output.]

## Architecture and key decisions

| Decision | Planned | Observed |
|---|---|---|
| Region | Denmark East | [After preflight] |
| VM | Windows Server 2022 Azure Edition, `Standard_B2s` if available | [After preflight] |
| Network | No custom inbound allow; Standard IPv4 for explicit outbound | [After inspection] |
| Backup | Standard `V1`, 23:00 UTC, seven days, LRS | [After inspection] |

Evidence: [Preflight](evidence/preflight.txt), [What-If](evidence/what-if.txt), [estimate](evidence/cost-estimate.png)

## Deployment and baseline

Expected result: Bicep deploys the resources and the sample file exists.

Observed result: [Record actual names, policy, path, and original hash.]

Evidence: [Deployment](evidence/deployment-output.txt), [original hash](evidence/original-file-hash.txt)

## Troubleshooting and fix

Expected incident: The live file is deleted after a recovery point exists.

Observed incident: [Record actual symptoms.]

Investigation: [Record checks and findings.]

Fix: [Record only after recovery completes.]

## Results and evidence

| Check | Expected | Observed |
|---|---|---|
| Backup job | `Completed` | [Actual] |
| Recovery point | Present | [Actual name and time] |
| File after deletion | Absent | [Actual] |
| Original SHA-256 | Recorded | [Actual] |
| Restored SHA-256 | Matches | [Actual] |
| Comparison | `true` | [Actual] |
| Recovery mount | Disconnected | [Actual evidence] |

Evidence: [Job](evidence/backup-job.txt), [point](evidence/recovery-point.txt), [deletion](evidence/deletion-check.txt), [hashes](evidence/hash-comparison.txt), [unmount](evidence/unmount.txt)

## AZ-104 skills mapped

| Skill | Evidence |
|---|---|
| Deploy compute | [Observed link] |
| Configure VM backup | [Observed link] |
| Monitor jobs | [Observed link] |
| Restore data | [Observed link] |
| Manage cost and lifecycle | [Observed link] |

## Cost and cleanup

Pre-deployment estimate: [Record currency, assumptions, amount, and date.]

Observed cleanup: [Record deletions, soft-deleted vault, snapshots, group existence, and billing observation.]

Evidence: [Cleanup](evidence/cleanup.txt), [soft-deleted vault](evidence/soft-deleted-vault.txt)

## Reflection

- What proves this was a recovery test?
- Which step is most fragile in a real incident?
- What changes for private-only production networking?
- [Add observed lessons.]

How does the README work?

Expected fields describe the planned drill. Observed fields stay incomplete until actual command output or screenshots provide evidence.

  • Replace the contents of infra/main.bicep with this template:
targetScope = 'resourceGroup'

param location string = 'denmarkeast'
param adminUsername string = 'localadmin'
@minLength(12)
@secure()
param adminPassword string
param vmSize string = 'Standard_B2s'

var vmName = 'vm-p07-backup'
var vnetName = 'vnet-p07-backup'
var subnetName = 'snet-workload'
var vaultName = 'rsv-p07-${uniqueString(resourceGroup().id)}'
var policyName = 'policy-p07-daily-7d'
var scheduleTime = '2026-11-03T23:00:00Z'
var containerName = 'iaasvmcontainer;iaasvmcontainerv2;${resourceGroup().name};${vmName}'
var itemName = 'vm;iaasvmcontainerv2;${resourceGroup().name};${vmName}'

resource nsg 'Microsoft.Network/networkSecurityGroups@2022-05-01' = {
  name: 'nsg-p07-backup'
  location: location
  properties: {
    securityRules: []
  }
}

resource publicIp 'Microsoft.Network/publicIPAddresses@2022-05-01' = {
  name: 'pip-p07-backup'
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    publicIPAllocationMethod: 'Static'
  }
}

resource vnet 'Microsoft.Network/virtualNetworks@2022-05-01' = {
  name: vnetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.7.0.0/16'
      ]
    }
    subnets: [
      {
        name: subnetName
        properties: {
          addressPrefix: '10.7.1.0/24'
          networkSecurityGroup: {
            id: nsg.id
          }
        }
      }
    ]
  }
}

resource nic 'Microsoft.Network/networkInterfaces@2022-05-01' = {
  name: 'nic-p07-backup'
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          publicIPAddress: {
            id: publicIp.id
          }
          subnet: {
            id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, subnetName)
          }
        }
      }
    ]
  }
  dependsOn: [
    vnet
  ]
}

resource vm 'Microsoft.Compute/virtualMachines@2024-03-01' = {
  name: vmName
  location: location
  properties: {
    hardwareProfile: {
      vmSize: vmSize
    }
    osProfile: {
      computerName: vmName
      adminUsername: adminUsername
      adminPassword: adminPassword
      windowsConfiguration: {
        provisionVMAgent: true
        enableAutomaticUpdates: true
      }
    }
    storageProfile: {
      imageReference: {
        publisher: 'MicrosoftWindowsServer'
        offer: 'WindowsServer'
        sku: '2022-datacenter-azure-edition'
        version: 'latest'
      }
      osDisk: {
        name: '${vmName}-osdisk'
        createOption: 'FromImage'
        caching: 'ReadWrite'
        managedDisk: {
          storageAccountType: 'StandardSSD_LRS'
        }
      }
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: nic.id
          properties: {
            primary: true
          }
        }
      ]
    }
    diagnosticsProfile: {
      bootDiagnostics: {
        enabled: true
      }
    }
  }
}

resource vault 'Microsoft.RecoveryServices/vaults@2026-05-01' = {
  name: vaultName
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    publicNetworkAccess: 'Enabled'
    redundancySettings: {
      standardTierStorageRedundancy: 'LocallyRedundant'
      crossRegionRestore: 'Disabled'
    }
  }
}

resource policy 'Microsoft.RecoveryServices/vaults/backupPolicies@2024-10-01' = {
  name: policyName
  parent: vault
  location: location
  properties: {
    backupManagementType: 'AzureIaasVM'
    policyType: 'V1'
    instantRpRetentionRangeInDays: 1
    schedulePolicy: {
      schedulePolicyType: 'SimpleSchedulePolicy'
      scheduleRunFrequency: 'Daily'
      scheduleRunDays: []
      scheduleRunTimes: [
        scheduleTime
      ]
    }
    retentionPolicy: {
      retentionPolicyType: 'LongTermRetentionPolicy'
      dailySchedule: {
        retentionTimes: [
          scheduleTime
        ]
        retentionDuration: {
          count: 7
          durationType: 'Days'
        }
      }
    }
    tieringPolicy: {
      ArchivedRP: {
        tieringMode: 'DoNotTier'
        duration: 0
        durationType: 'Invalid'
      }
    }
    timeZone: 'UTC'
  }
}

resource protectedItem 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems@2023-02-01' = {
  name: '${vault.name}/Azure/${containerName}/${itemName}'
  properties: {
    protectedItemType: 'Microsoft.Compute/virtualMachines'
    policyId: policy.id
    sourceResourceId: vm.id
  }
}

output vmName string = vm.name
output vaultName string = vault.name
output backupPolicyName string = policy.name
output publicIpAddress string = publicIp.properties.ipAddress
output sampleFilePath string = 'C:\\RecoveryLab\\critical-app-config.txt'

What does the Bicep template define?

The template describes the VM network and backup configuration as one resource-group deployment. Its outputs expose the resource names needed by later recovery commands.

  • Replace the contents of scripts/create-sample.ps1 with this script:
$path = 'C:\RecoveryLab\critical-app-config.txt'
New-Item -Path (Split-Path -Path $path -Parent) -ItemType Directory -Force | Out-Null
Set-Content -Path $path -Value @('Application=ContosoOrders','Environment=RecoveryDrill','Version=1','RecoveryOwner=AZ104Learner')
$hash = Get-FileHash -Path $path -Algorithm SHA256
[pscustomobject]@{ Path=$path; Exists=(Test-Path -Path $path); Length=(Get-Item -Path $path).Length; Algorithm=$hash.Algorithm; Hash=$hash.Hash } | ConvertTo-Json

What does the creation script record?

The script creates Version 1 of the known configuration file on the VM. Its JSON output records the path and original file hash.

  • Replace the contents of scripts/delete-sample.ps1 with this script:
$path = 'C:\RecoveryLab\critical-app-config.txt'
if (Test-Path -Path $path) { Remove-Item -Path $path -Force }
[pscustomobject]@{ Path=$path; Exists=(Test-Path -Path $path) } | ConvertTo-Json

What does the deletion script prove?

The script removes the protected sample file during the incident drill. Its JSON output reports whether the file remains at the expected path.

  • Replace the contents of scripts/verify-restored.ps1 with this script:
param([Parameter(Mandatory=$true)][string]$RestoredFile,[Parameter(Mandatory=$true)][string]$ExpectedHash)
if (-not (Test-Path -Path $RestoredFile)) { throw "Restored file not found: $RestoredFile" }
$hash = Get-FileHash -Path $RestoredFile -Algorithm SHA256
[pscustomobject]@{ RestoredFile=$RestoredFile; Algorithm=$hash.Algorithm; ExpectedHash=$ExpectedHash.ToUpperInvariant(); RestoredHash=$hash.Hash; MatchesExpectedHash=($hash.Hash -eq $ExpectedHash) } | ConvertTo-Json

What does the verification script compare?

The script calculates the restored file's SHA-256 hash. It returns a Boolean comparison against the original hash recorded earlier.

  • Save every project file.
  • Return to the PowerShell window from earlier.
  • Install Bicep through Azure CLI by running this command:
az bicep install

Why install Bicep through Azure CLI?

Azure CLI manages the Bicep executable used by later commands. This keeps template compilation inside the same command-line workflow.

Bicep installation failed?

Confirm that Azure CLI is available in this PowerShell session. Check that the workstation has internet access before retrying the installation.

Still blocked? Help me troubleshoot an az bicep install failure on Windows 11.

  • Display the installed Bicep version by running this command:
az bicep version

What should you record?

The output identifies the exact Bicep release used to compile this project. Recording it makes the preflight reproducible.

  • Record the output as your observed Bicep version.
  • Build the infrastructure template by running this command:
az bicep build --file .\infra\main.bicep

What does the build prove?

Bicep parses the template and compiles it into an Azure Resource Manager template. A completed build confirms the local source has no Bicep compilation error.

Seeing a Bicep build error?

Confirm that PowerShell is inside the 07-backup-and-restore folder. Compare infra/main.bicep with the full-code tab for missing braces or altered values.

Still blocked? Help me diagnose my Bicep build error without changing the planned Azure resources.

Estimate costs and complete the preflight

This project can incur charges for compute and storage. The Azure Pricing Calculator gives you a subscription-specific estimate before any lab infrastructure is deployed.

Cost review can feel risky because the estimate includes several billable components. You are only calculating the planned cost at this point.

  • Open your web browser through Windows search.
  • Find the Azure Pricing Calculator on the Microsoft Azure website.
  • Set the estimate region to Denmark East.
  • Add Windows VM compute for Standard_B2s.
  • Add the managed OS disk estimate.
  • Add the Standard public IPv4 estimate.
  • Add Azure Backup protected-instance usage.
  • Include backup storage and instant-restore snapshot usage in the estimate.

Why include every cost component?

Azure VM backup charges include protected-instance usage and consumed backup storage. Instant-restore snapshots are billed as disk snapshots.

The final amount depends on the region and your subscription pricing. Your saved estimate becomes the pre-deployment cost evidence.

  • Record the estimate amount as your calculated Azure estimate.
  • Record the estimate currency as your estimate currency.
  • Save a screenshot of the complete estimate as evidence/cost-estimate.png.

Regional availability is subscription-sensitive. The next commands check the exact image and VM size before the resource group receives a deployment.

  • Return to the PowerShell window from earlier.
  • Query the planned image and VM size by running these commands:
az vm image list --location denmarkeast --publisher MicrosoftWindowsServer --offer WindowsServer --sku 2022-datacenter-azure-edition --all --output table
az vm list-skus --location denmarkeast --size Standard_B2s --all --output table

What do these queries test?

  • The image query checks Windows Server 2022 Azure Edition in Denmark East.
  • The SKU query includes restrictions that apply to your active subscription.
  • Copy both command outputs into evidence/preflight.txt.
  • Record the image result as your image preflight result.
  • Record the SKU result as your VM size preflight result.

Seeing a restriction or no matching image?

Stop before creating the lab resources. Save the actual output in evidence/preflight.txt so the restriction remains part of your evidence.

Need help reading the result? Help me interpret my Denmark East VM image and SKU preflight output.

An empty resource group provides the scope required by deployment validation. Creating the group does not deploy the VM or backup resources.

  • Create the empty Denmark East resource group by running this command:
az group create --name rg-az104-p07-backup --location denmarkeast

What does this create?

The command creates rg-az104-p07-backup as the deployment scope. The group remains empty until the later deployment step.

What-If asks Azure providers to validate the planned identifiers and resource changes. It previews the deployment without creating the VM or backup resources.

  • Prepare a temporary password that satisfies the template's minimum length.
  • Keep that password private when PowerShell requests the secure adminPassword parameter.

Before you run the validation, do you expect Azure to create the planned resources or only describe them?

  • Run the provider-level deployment preview with this command:
az deployment group what-if --resource-group rg-az104-p07-backup --template-file .\infra\main.bicep --validation-level Provider

What does the What-If result mean?

The output describes the resources Azure would create. A completed Provider validation confirms that the planned identifiers passed the preflight without deploying them.

  • Enter the temporary password only when the secure prompt appears.
  • Copy the completed preview into evidence/what-if.txt.
  • Return to README.md in your editor.
  • Update Architecture and key decisions with the observed image and SKU preflight results.
  • Update Deployment and baseline with the observed build and What-If status.
  • Update Cost and cleanup with the estimate amount, currency, assumptions, and date.
  • Keep the existing relative evidence links unchanged.
  • Leave future deployment and recovery observations incomplete.

Record observations once

Use the saved evidence files as the source for each README observation. This keeps expected outcomes separate from what your subscription actually returned.

The final check covers every local tool required before deployment. Predict whether all three commands now complete without an error.

  • Run the end-of-step verification from the 07-backup-and-restore folder:
az version
az bicep version
az bicep build --file .\infra\main.bicep

What does this final check prove?

  • The first command prints the installed Azure CLI components and versions.
  • The second command prints the observed Bicep version.
  • The third command builds infra/main.bicep again from the prepared project folder.

You should see both version outputs. The Bicep build should finish without an error.

That is the paid-resource safety gate complete. Your tools work, the project files are in place, and Azure has validated the planned deployment without creating the lab.

Your workstation and preflight evidence are ready. Next, you will deploy the protected lab and record the sample file's original hash.

Deploy the Protected Lab

Your preflight proved that the template fits your subscription before paid resources exist. The next move turns that reviewed Bicep design into a live recovery lab.

You will deploy the Windows VM through Azure CLI. You will inspect its network plus its Recovery Services vault. You will then create a known file whose original SHA-256 hash becomes the integrity baseline for the recovery drill.

In this step, get ready to:
  • Deploy the reviewed infrastructure through the secure password prompt.
  • Confirm the network configuration plus the Azure Backup protection state.
  • Create the Version 1 sample file plus record its original hash.
Deploy the infrastructure with a secure prompt

The successful What-If result showed the planned changes without creating the lab. A resource-group deployment now applies that same template to rg-az104-p07-backup.

This action starts resources that can incur Azure charges. Your saved estimate gives you a subscription-specific baseline before the first billable resource appears.

Keep the Password Private

Azure CLI asks for the secure adminPassword parameter after the command starts. The password stays out of the command plus the evidence file.

Prepare a password with at least 12 characters. Never add it to README.md or a screenshot.

  • Switch back to the PowerShell session from the preflight.
  • Confirm that the prompt points to the existing 07-backup-and-restore folder.
  • Deploy the template while saving the output by running this command:
az deployment group create --resource-group rg-az104-p07-backup --template-file .\infra\main.bicep | Tee-Object -FilePath .\evidence\deployment-output.txt

What Does This Command Do?

  • The deployment command applies infra/main.bicep to the existing resource group.
  • PowerShell sends the output to the terminal plus evidence/deployment-output.txt.
  • The template requests the administrator password through a secure parameter.
  • Enter your prepared password when Azure CLI requests the secure parameter.
  • Wait for the deployment to finish.
  • Review the outputs for vm-p07-backup.
  • Review the outputs for policy-p07-daily-7d.
  • Record the unique vault name here: rsv-p07-....

Expect Azure to spend several minutes provisioning the VM plus the backup resources. A quiet PowerShell window during that period does not mean the deployment has stopped.

That is a major milestone. Your reviewed template now controls a live VM plus its recovery configuration.

Did the Deployment Stop?

  • Check that PowerShell is inside the existing 07-backup-and-restore folder so the relative template path resolves.
  • Check that the password satisfies the 12-character minimum from the template.
  • Read the final deployment message for a restricted SKU or provider issue that appeared after preflight.

Still blocked? Help me diagnose my Azure Bicep deployment failure.

Inspect the deployed protection baseline

A completed deployment proves that Azure accepted the template. The resource views prove that the live network plus the Azure Backup configuration match the reviewed design.

Capture the resource inventory plus the protection state during this inspection. These observations belong in the existing architecture plus deployment sections of your README.

  • Switch to your Azure portal session.
  • Search for rg-az104-p07-backup.
  • Open the matching resource group.
  • Confirm that the resource list contains vm-p07-backup plus vm-p07-backup-osdisk.
  • Confirm that the resource list contains nic-p07-backup plus pip-p07-backup.
  • Confirm that the resource list contains vnet-p07-backup plus nsg-p07-backup.
  • Confirm that the resource list contains rsv-p07-....

You should see the VM plus its disk. You should also see the network resources plus the uniquely named vault.

  • Open vnet-p07-backup from the resource list.
  • Inspect snet-workload to confirm the address prefix is 10.7.1.0/24.
  • Return to rg-az104-p07-backup.
  • Open nsg-p07-backup from the resource list.
  • Inspect its inbound rules to confirm that no custom inbound security rule exists.
  • Return to rg-az104-p07-backup.
  • Open pip-p07-backup to confirm the Standard SKU plus static allocation.

Why This Network Shape?

The Standard public IPv4 gives this short-lived lab an explicit outbound method. The empty custom rule list leaves unmatched inbound traffic blocked by the NSG defaults.

This keeps the recovery lab reachable for Azure management without creating a custom inbound path.

  • Return to rg-az104-p07-backup.
  • Open rsv-p07-... from the resource list.
  • Inspect the vault properties for the Standard SKU.
  • Confirm that the vault uses locally redundant storage.
  • Confirm that public network access is enabled.
  • Confirm that cross-region restore is disabled.
  • Open the backup policy named policy-p07-daily-7d.
  • Inspect the policy schedule plus its retention settings.

You should see a Standard V1 daily policy scheduled for 23:00 UTC. Its retention is seven days plus one day of instant recovery-point retention.

  • Open the vault's protected items view.
  • Select the Azure virtual machine protection category.
  • Confirm that vm-p07-backup appears as a protected item.
  • Capture a screenshot showing the protected VM plus the applied policy.

The live configuration now matches the design. The VM has explicit outbound connectivity plus no custom inbound access.

  • Return to the existing README.md file.
  • Update Architecture and key decisions with the observed region plus VM details.
  • Preview the table to confirm that planned values remain separate from observed values.
  • Update Deployment and baseline with the actual VM name plus the vault name.
  • Record the observed policy schedule plus retention in the same section.
  • Keep the existing deployment evidence link relative to README.md.

Does the Portal Show Different Values?

Record the portal values as observed evidence before changing anything. A difference between the template plus the live state is a troubleshooting signal.

Need help interpreting a mismatch? Help me compare my deployed Azure resources with infra/main.bicep.

Create the sample file and record its hash

A recovery drill needs a known source file before any backup starts. The existing scripts/create-sample.ps1 script creates Version 1 on the VM without requiring RDP.

The next output is your baseline evidence. Capture its path plus its length. Capture the existence result plus the original hash.

Before you run the script, do you expect the result to show an existing file with a populated hash?

  • Invoke the sample script on vm-p07-backup while saving the result by running this command:
az vm run-command invoke --command-id RunPowerShellScript --name vm-p07-backup -g rg-az104-p07-backup --scripts @scripts/create-sample.ps1 | Tee-Object -FilePath .\evidence\original-file-hash.txt

What Does This Command Do?

  • Azure Run Command sends the local PowerShell script to the VM agent.
  • The script creates C:\RecoveryLab\critical-app-config.txt with Version=1 content.
  • The script calculates the SHA-256 hash plus returns file metadata as JSON.
  • PowerShell saves the command response in evidence/original-file-hash.txt.

Run Command can take a few minutes while the VM agent receives the script. Keep the PowerShell session open until the response returns.

  • Confirm that the returned path is C:\RecoveryLab\critical-app-config.txt.
  • Confirm that the returned existence value is true.
  • Confirm that the returned algorithm is SHA256.
  • Record the returned hash here: your observed SHA-256 hash.
  • Record the observed file length in Deployment and baseline.
  • Record your observed SHA-256 hash as the original hash in the same section.
  • Confirm that the relative original-hash evidence link points to evidence/original-file-hash.txt.

You now have a protected VM plus a known Version 1 file. Its recorded hash gives the later recovery a precise integrity test.

Did Run Command Fail?

  • Confirm that the VM deployment has finished before invoking the script.
  • Check that scripts/create-sample.ps1 exists inside the current 07-backup-and-restore folder.
  • Check the VM agent status plus outbound connectivity if Azure cannot return the script result.

Still missing the JSON result? Help me diagnose my Azure VM Run Command failure.

✔️ Awesome, I've got everything!

Great. Confirm that the deployed template remains unchanged. Confirm that the sample script returned an existing file plus its SHA-256 hash.

ⓧ I'd like to double check the full code

Compare your deployed infra/main.bicep with this complete reference file.

targetScope = 'resourceGroup'

param location string = 'denmarkeast'
param adminUsername string = 'localadmin'
@minLength(12)
@secure()
param adminPassword string
param vmSize string = 'Standard_B2s'

var vmName = 'vm-p07-backup'
var vnetName = 'vnet-p07-backup'
var subnetName = 'snet-workload'
var vaultName = 'rsv-p07-${uniqueString(resourceGroup().id)}'
var policyName = 'policy-p07-daily-7d'
var scheduleTime = '2026-11-03T23:00:00Z'
var containerName = 'iaasvmcontainer;iaasvmcontainerv2;${resourceGroup().name};${vmName}'
var itemName = 'vm;iaasvmcontainerv2;${resourceGroup().name};${vmName}'

resource nsg 'Microsoft.Network/networkSecurityGroups@2022-05-01' = {
  name: 'nsg-p07-backup'
  location: location
  properties: {
    securityRules: []
  }
}

resource publicIp 'Microsoft.Network/publicIPAddresses@2022-05-01' = {
  name: 'pip-p07-backup'
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    publicIPAllocationMethod: 'Static'
  }
}

resource vnet 'Microsoft.Network/virtualNetworks@2022-05-01' = {
  name: vnetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.7.0.0/16'
      ]
    }
    subnets: [
      {
        name: subnetName
        properties: {
          addressPrefix: '10.7.1.0/24'
          networkSecurityGroup: {
            id: nsg.id
          }
        }
      }
    ]
  }
}

resource nic 'Microsoft.Network/networkInterfaces@2022-05-01' = {
  name: 'nic-p07-backup'
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          publicIPAddress: {
            id: publicIp.id
          }
          subnet: {
            id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, subnetName)
          }
        }
      }
    ]
  }
  dependsOn: [
    vnet
  ]
}

resource vm 'Microsoft.Compute/virtualMachines@2024-03-01' = {
  name: vmName
  location: location
  properties: {
    hardwareProfile: {
      vmSize: vmSize
    }
    osProfile: {
      computerName: vmName
      adminUsername: adminUsername
      adminPassword: adminPassword
      windowsConfiguration: {
        provisionVMAgent: true
        enableAutomaticUpdates: true
      }
    }
    storageProfile: {
      imageReference: {
        publisher: 'MicrosoftWindowsServer'
        offer: 'WindowsServer'
        sku: '2022-datacenter-azure-edition'
        version: 'latest'
      }
      osDisk: {
        name: '${vmName}-osdisk'
        createOption: 'FromImage'
        caching: 'ReadWrite'
        managedDisk: {
          storageAccountType: 'StandardSSD_LRS'
        }
      }
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: nic.id
          properties: {
            primary: true
          }
        }
      ]
    }
    diagnosticsProfile: {
      bootDiagnostics: {
        enabled: true
      }
    }
  }
}

resource vault 'Microsoft.RecoveryServices/vaults@2026-05-01' = {
  name: vaultName
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    publicNetworkAccess: 'Enabled'
    redundancySettings: {
      standardTierStorageRedundancy: 'LocallyRedundant'
      crossRegionRestore: 'Disabled'
    }
  }
}

resource policy 'Microsoft.RecoveryServices/vaults/backupPolicies@2024-10-01' = {
  name: policyName
  parent: vault
  location: location
  properties: {
    backupManagementType: 'AzureIaasVM'
    policyType: 'V1'
    instantRpRetentionRangeInDays: 1
    schedulePolicy: {
      schedulePolicyType: 'SimpleSchedulePolicy'
      scheduleRunFrequency: 'Daily'
      scheduleRunDays: []
      scheduleRunTimes: [
        scheduleTime
      ]
    }
    retentionPolicy: {
      retentionPolicyType: 'LongTermRetentionPolicy'
      dailySchedule: {
        retentionTimes: [
          scheduleTime
        ]
        retentionDuration: {
          count: 7
          durationType: 'Days'
        }
      }
    }
    tieringPolicy: {
      ArchivedRP: {
        tieringMode: 'DoNotTier'
        duration: 0
        durationType: 'Invalid'
      }
    }
    timeZone: 'UTC'
  }
}

resource protectedItem 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems@2023-02-01' = {
  name: '${vault.name}/Azure/${containerName}/${itemName}'
  properties: {
    protectedItemType: 'Microsoft.Compute/virtualMachines'
    policyId: policy.id
    sourceResourceId: vm.id
  }
}

output vmName string = vm.name
output vaultName string = vault.name
output backupPolicyName string = policy.name
output publicIpAddress string = publicIp.properties.ipAddress
output sampleFilePath string = 'C:\\RecoveryLab\\critical-app-config.txt'

Compare scripts/create-sample.ps1 with this complete reference file.

$path = 'C:\RecoveryLab\critical-app-config.txt'
New-Item -Path (Split-Path -Path $path -Parent) -ItemType Directory -Force | Out-Null
Set-Content -Path $path -Value @('Application=ContosoOrders','Environment=RecoveryDrill','Version=1','RecoveryOwner=AZ104Learner')
$hash = Get-FileHash -Path $path -Algorithm SHA256
[pscustomobject]@{ Path=$path; Exists=(Test-Path -Path $path); Length=(Get-Item -Path $path).Length; Algorithm=$hash.Algorithm; Hash=$hash.Hash } | ConvertTo-Json

Your protected lab now has a known source file plus a recorded integrity baseline. Next, you will trigger a backup job and prove that Azure created a usable recovery point.

Create and Prove a Recovery Point

Your protected VM now holds the Version=1 sample file. Its original SHA-256 hash gives this recovery drill a baseline.

A configured policy shows protection intent. In this step, Azure Backup creates an on-demand copy that you can identify as a usable recovery point.

In this step, get ready to:
  • Preserve the pre-backup evidence for the protected VM.
  • Monitor the on-demand backup job until it reaches Completed.
  • Select a recovery point with recorded creation details.
Capture the pre-backup state

Recovery evidence needs a clear before-state. You already recorded the protected item, policy, file path, existence result, and original hash.

The next check confirms that the known file still exists immediately before the backup starts. This ties the recovery point to the baseline you plan to recover.

  • Switch back to Windows PowerShell in your 07-backup-and-restore folder.
  • Review the protected item for vm-p07-backup in your saved deployment evidence.
  • Review policy-p07-daily-7d in your saved policy evidence.
  • Review the original hash in evidence/original-file-hash.txt.
  • Reconfirm the live file by running this command:
az vm run-command invoke --command-id RunPowerShellScript --name vm-p07-backup -g rg-az104-p07-backup --scripts @scripts/create-sample.ps1

What Does This Check Prove?

  • The command runs the existing scripts/create-sample.ps1 script on vm-p07-backup.
  • The script reapplies the known Version=1 content before calculating its current hash.
  • The returned JSON connects the file path to its existence result.

You should see Exists set to true. The returned hash should match your original recorded hash.

  • Create evidence/backup-job.txt inside the 07-backup-and-restore folder using your text editor.
  • Paste the current file-check output under a Pre-backup state label.
  • Record the protected item name in the same evidence file.
  • Record policy-p07-daily-7d in the same evidence file.
  • Record the original SHA-256 hash in the same evidence file.

Does the Live Check Look Wrong?

  • Check that PowerShell is inside the 07-backup-and-restore folder before resolving the script path.
  • Check that the command targets vm-p07-backup in rg-az104-p07-backup.
  • Investigate a changed hash before starting the backup because it breaks the evidence chain.

Need help comparing the live output with your baseline? Help me diagnose the pre-backup file check.

Start and monitor the backup job

An on-demand backup creates evidence independently of the daily schedule. The Recovery Services vault returns a backup-job name that lets you follow the operation.

  • Record the deployed vault name from your baseline evidence here: your deployed rsv-p07 vault name.
  • Start the on-demand backup by running this command:
az backup protection backup-now --resource-group rg-az104-p07-backup --vault-name [[VAULT_NAME="your deployed rsv-p07 vault name"]] --container-name vm-p07-backup --item-name vm-p07-backup --backup-management-type AzureIaasVM

What Does This Command Start?

  • The command requests an immediate backup for the protected vm-p07-backup item.
  • The AzureIaasVM value identifies the backup-management type.
  • The returned backup-job name becomes the handle for every status check.

That is the first proof point captured. Azure has accepted a real backup operation for your protected VM.

  • Record the returned backup-job name here: your backup job name.
  • Copy the complete start response into evidence/backup-job.txt.

Did the Backup Request Fail?

  • Check that the vault name matches the unique deployed rsv-p07 name in your baseline evidence.
  • Check that vm-p07-backup still appears as a protected item in the vault.
  • Confirm that your Azure CLI session still uses the subscription from the deployment step.

Need help with the failed request? Help me diagnose the Azure VM backup command.

Azure processes this backup asynchronously. Snapshot data can take hours to copy at peak times, so a quiet interval does not mean the job has stalled.

Before you check, do you expect the job to be finished already? Make your prediction now.

  • Check the returned backup job by running this command:
az backup job show --name [[BACKUP_JOB_NAME="your backup job name"]] --resource-group rg-az104-p07-backup --vault-name [[VAULT_NAME="your deployed rsv-p07 vault name"]]

How to Read the Status

  • A status of InProgress means Azure is still processing the backup.
  • A status of Completed confirms that the backup job finished successfully.
  • A status of CompletedWithWarnings requires investigation before the drill continues.
  • Copy the first status response into evidence/backup-job.txt.
  • Record the observation time beside that response.
  • Wait for a visible interval when the status is InProgress.
  • Run the same status command again after the interval.
  • Record another observed status when the job remains active after ten minutes.
  • Resume this step later when Azure still reports an active job.
  • Continue the visible checks until the status reaches Completed.
  • Copy the completed response into evidence/backup-job.txt.

You have crossed the longest part of the drill. The vault now reports a completed backup job for the protected VM.

Does the Job Show a Failure?

  • Preserve the full Failed response in evidence/backup-job.txt.
  • Check that the VM still reports a healthy agent before retrying the backup.
  • Investigate the job details before moving to the recovery-point check.

Need help interpreting the job details? Help me diagnose the failed Azure Backup job.

Select and record the recovery point

The completed job proves that Azure finished the operation. A listed recovery point gives the drill a named point in time for the later file recovery.

Before you list the points, do you expect the completed job to have produced at least one usable entry? Make your prediction now.

  • List the VM recovery points by running this command:
az backup recoverypoint list --resource-group rg-az104-p07-backup --vault-name [[VAULT_NAME="your deployed rsv-p07 vault name"]] --container-name vm-p07-backup --item-name vm-p07-backup --backup-management-type AzureIaasVM

What Does This List Prove?

  • The command asks the vault for recovery points belonging to the protected VM.
  • Each returned point has an identity that the later file-recovery command can target.
  • The creation time connects the point to the completed backup job.
  • The tier records where Azure currently holds the recovery data.

You should see at least one recovery point. Its creation details should align with the completed on-demand backup.

  • Create evidence/recovery-point.txt inside the 07-backup-and-restore folder using your text editor.
  • Copy the complete recovery-point output into evidence/recovery-point.txt.
  • Select the point associated with the completed backup job.
  • Record its recovery-point name here: your selected recovery-point name.
  • Record its creation time here: your selected recovery-point creation time.
  • Record its tier here: your selected recovery-point tier.

The evidence chain now connects the selected point to the live Version=1 file check. The recorded original hash remains the integrity value for the later restore.

  • In README.md, update Deployment and baseline with the actual backup-job name.
  • Save README.md.
  • Confirm that the saved section identifies the job as an observed Completed result.
  • Update Results and evidence with the selected point's actual name.
  • Save README.md.
  • Confirm that the saved results include the observed creation time.
  • Confirm that the saved results include the observed tier.
  • Leave the expected values separate from every observed value.

Is the Recovery-Point List Empty?

  • Confirm that the saved backup-job response reached Completed.
  • Check that the command uses the same vault as the completed job.
  • Check that the container name and item name both target vm-p07-backup.

Need help tracing the missing point? Help me diagnose an empty recovery-point list.

Your backup evidence now reaches from the live Version 1 file to a completed job and a named recovery point. Next, you will create the data-loss incident that this point is ready to resolve.

Delete and Diagnose the File

The completed Azure Backup job proves protection ran successfully. The selected recovery point gives you a recoverable copy of the original file.

A recovery drill needs a real failure state after protection is proven. You will create that incident through Azure CLI before documenting why the protected copy remains recoverable.

In this step, get ready to:
  • Confirm the pre-incident evidence for the live file plus its protected copy.
  • Delete the live sample file through Azure VM Run Command.
  • Diagnose the incident before documenting the planned recovery.
Capture the pre-incident evidence

Your earlier evidence forms the control state for this incident. It proves that Version 1 existed before the deletion.

  • Switch back to README.md in the editor from earlier.
  • Confirm the recorded path is C:\RecoveryLab\critical-app-config.txt.
  • Confirm the original SHA-256 hash is recorded under Deployment and baseline.
  • Confirm the backup job evidence shows its observed name plus Completed status.
  • Confirm the selected recovery-point name is recorded under Results and evidence.
  • Confirm the selected recovery-point creation time is recorded.
  • Confirm the selected recovery-point tier is recorded.

Why capture this evidence first?

The path plus original hash establish what existed before the incident. The completed job plus selected point establish that a protected copy already existed.

This sequence separates a recoverable data-loss event from a missing-backup problem.

Delete the live file

The existing delete-sample.ps1 script creates a controlled data-loss incident on vm-p07-backup. Run Command executes that local script through the VM agent.

The VM agent can take a few minutes to return the result. A busy Windows PowerShell terminal during that wait is normal.

Before you run this, do you expect the live file to share the recovery point's fate? Hold that prediction for the result.

  • Invoke the deletion script while saving the response by running this command from the 07-backup-and-restore folder:
az vm run-command invoke --command-id RunPowerShellScript --name vm-p07-backup -g rg-az104-p07-backup --scripts '@scripts/delete-sample.ps1' | Tee-Object -FilePath .\evidence\deletion-check.txt

What does this command do?

  • The az vm run-command invoke command sends the existing PowerShell script to the VM agent.
  • The RunPowerShellScript command ID runs the script on the Windows VM.
  • The script removes the known path when that path exists.
  • The script tests the same path after the removal.
  • The Tee-Object cmdlet displays the response while saving it to evidence/deletion-check.txt.

The returned script data should identify C:\RecoveryLab\critical-app-config.txt with Exists: false.

That false value is the intended incident state. The current VM file is now absent.

Still seeing Exists: true?

  • Confirm PowerShell is currently inside the 07-backup-and-restore folder.
  • Confirm the command targets vm-p07-backup.
  • Confirm scripts/delete-sample.ps1 still contains the complete deletion script from earlier.
  • Run the command again after correcting the mismatched path or VM name.

Still stuck? Help me diagnose why the deletion output still reports that the file exists.

Diagnose and document the incident

Deleting the live file changes the VM's current state. The selected recovery point preserves the protected Version 1 state from earlier.

Before you rerun the recovery-point check, do you expect the selected point to remain available? Keep your prediction in mind.

  • Return to the Windows PowerShell terminal from earlier.
  • Press the Up Arrow until the recovery-point list command from the previous step appears.
  • Press Enter to rerun that command.

Your selected recovery-point name should still appear.

Its creation time plus tier should match the evidence from earlier. This proves the protected copy remains available after the live-file deletion.

  • Switch back to README.md in the editor from earlier.
  • Find the ## Troubleshooting and fix heading.
  • Select the existing section content through the line before ## Results and evidence.
  • Replace the selected content by pasting this incident record:
## Troubleshooting and fix

Expected incident: The live `C:\RecoveryLab\critical-app-config.txt` file is deleted after a usable recovery point exists.

Observed incident: `scripts/delete-sample.ps1` reported `Exists: false` for `C:\RecoveryLab\critical-app-config.txt`.

Evidence: [Deletion output](evidence/deletion-check.txt). The completed backup job remains recorded in [Job](evidence/backup-job.txt). The selected recovery point remains recorded in [point](evidence/recovery-point.txt).

Investigation: The deletion script removed the live VM file. The selected recovery point remained listed with its recorded name, creation time, and tier.

Diagnosis: The incident affects the current VM file. The protected Version 1 copy remains available in the selected recovery point.

Planned recovery: Mount the selected recovery point as read-only volumes. Copy `RecoveryLab\critical-app-config.txt` to the local `restored/` folder.

Expected recovery outcome: The restored copy exists separately. Its SHA-256 matches the recorded original hash. Recovery access is disconnected after verification.

Observed pre-fix state: The live VM file is absent. Recovery has not started.

Fix: Planned only. Record completion after the restore, hash comparison, and mount disconnection.

What does this incident record show?

  • The expected incident describes the controlled failure you planned.
  • The observed incident records the actual Exists: false result.
  • The diagnosis limits the failure to the live VM file.
  • The planned recovery names the next action without claiming that recovery is complete.
  • The expected outcome defines the evidence required to close the incident.
  • Save README.md.
  • Confirm the incident section links to evidence/deletion-check.txt.
  • Confirm the observed pre-fix state says that recovery has not started.
  • Confirm the ## Results and evidence heading still follows the updated section.

Did the next README section disappear?

  • Undo the replacement if ## Results and evidence was removed.
  • Select only the content between the two existing headings.
  • Paste the incident record again after restoring the second heading.

Need help repairing the document? Help me restore the README section boundary without losing my recorded evidence.

Your deletion evidence now shows Exists: false for the live file. Your recovery-point check still lists the protected Version 1 copy.

You now have a documented data-loss incident with a clear recovery plan. Next, you will recover the separate copy before proving its hash and closing recovery access.

Recover, Verify, and Disconnect

The completed Azure Backup job gave you a usable recovery point. The deletion drill proved that the live file is gone.

This step resolves the incident through item-level recovery. You will recover a separate copy, prove its SHA-256 integrity, then close recovery access.

In this step, get ready to:
  • Mount the selected recovery point as a read-only recovery drive.
  • Copy the deleted file into the local restored folder.
  • Verify the restored hash before disconnecting the recovery mount.
Mount the selected recovery point

Item-level recovery exposes the backed-up volumes as read-only drives for 12 hours. The read-only mount protects the recovery point while you copy the missing file.

  • Return to Windows File Explorer from earlier.
  • Select This PC in the left pane.

You should see only your normal workstation drives before the recovery mount starts.

  • Record the current drive letters in evidence/unmount.txt using the editor from earlier.
  • Bring forward your recorded original hash: your original Version=1 SHA-256 hash.

The recovery password is temporary, so this sensitive step stays contained. Keep the password out of screenshots, evidence files, and README.md.

  • Return to the Microsoft Azure portal from earlier.
  • Return to the protected VM in the Recovery Services vault from earlier.
  • Open File Recovery for the protected VM.
  • Select the recovery point recorded in your evidence.
  • Use the Windows download option to save the recovery executable.
  • Copy the generated recovery password without saving it in your evidence.

The executable connects your Windows 11 workstation to the read-only recovery volumes. The mount can take a few minutes while Azure establishes the recovery session.

  • Return to the Downloads folder in Windows File Explorer.
  • Right-click the downloaded recovery executable.
  • Select Run as administrator.
  • Approve the Windows permission prompt.
  • Enter the temporary recovery password when the executable requests it.
  • Wait for the executable to report that the recovery volumes are connected.
  • Return to This PC in Windows File Explorer.
  • Refresh the drive list.

You should see at least one new recovery drive. That drive is the read-only view of the selected recovery point.

The backup is now accessible without restoring the whole VM. You have reached the original file through a controlled recovery session.

Recovery drive missing?

  • Confirm that the executable is still running with administrator permissions.
  • Confirm that Windows 11 allows outbound TCP traffic on ports 443 and 3260.
  • Use a direct network connection if a proxy blocks iSCSI traffic.

Still stuck? Help me diagnose why my Azure Backup file-recovery executable completed without showing a recovery drive in Windows File Explorer.

Copy and verify the restored file

A separate local copy preserves the recovered evidence after the temporary mount closes. The hash comparison proves that the copied bytes match the original file.

  • Open the new recovery drive in Windows File Explorer.
  • Open the RecoveryLab folder on the mounted volume.
  • Select critical-app-config.txt.
  • Copy the selected file.
  • Return to 07-backup-and-restore\restored in Windows File Explorer.
  • Paste the file into the restored folder.
  • Open 07-backup-and-restore\restored\critical-app-config.txt.

You should see Version=1 in the recovered copy. This confirms that the selected point contains the original version.

  • Return to the PowerShell window in 07-backup-and-restore from earlier.

Before you run the comparison, do you expect the restored copy to produce the same hash as the original file?

  • Verify the restored copy against the recorded original hash by running this command:
.\scripts\verify-restored.ps1 -RestoredFile '.\restored\critical-app-config.txt' -ExpectedHash '[[ORIGINAL_VERSION_1_HASH="your original Version=1 SHA-256 hash"]]'

What does this verification prove?

  • The RestoredFile parameter points to the separate local copy.
  • The ExpectedHash parameter carries the original Version 1 hash from your evidence.
  • The script calculates a new SHA-256 hash for the restored file.
  • The final JSON property reports whether both hash values match.

You should see both hashes in the JSON output. The result should include "MatchesExpectedHash": true.

That match closes the integrity question. The recovered copy contains the same bytes protected by the original recovery point.

  • Save the complete JSON output as evidence/hash-comparison.txt using the editor from earlier.
  • Open evidence/hash-comparison.txt to confirm that it contains both hashes.
  • Confirm that the saved output contains "MatchesExpectedHash": true.

Hash comparison failed?

  • Confirm that RestoredFile points to restored\critical-app-config.txt.
  • Confirm that ExpectedHash contains the original Version 1 hash without extra spaces.
  • Return to the mounted volume if the copied file shows a different version.

Need help? Help me diagnose why verify-restored.ps1 reports that my Azure Backup recovery file does not match the original SHA-256 hash.

✔️ Awesome, I've got everything!

Great. Confirm that the restored file is inside 07-backup-and-restore\restored and that the comparison result is true.

ⓧ I'd like to double check the full code

Compare scripts/verify-restored.ps1 with this complete reference file:

param([Parameter(Mandatory=$true)][string]$RestoredFile,[Parameter(Mandatory=$true)][string]$ExpectedHash)
if (-not (Test-Path -Path $RestoredFile)) { throw "Restored file not found: $RestoredFile" }
$hash = Get-FileHash -Path $RestoredFile -Algorithm SHA256
[pscustomobject]@{ RestoredFile=$RestoredFile; Algorithm=$hash.Algorithm; ExpectedHash=$ExpectedHash.ToUpperInvariant(); RestoredHash=$hash.Hash; MatchesExpectedHash=($hash.Hash -eq $ExpectedHash) } | ConvertTo-Json

What does this script check?

  • The parameter declaration requires a restored file path.
  • The parameter declaration also requires the expected original hash.
  • The file check stops verification when the restored copy is missing.
  • The final object records both hashes plus the comparison result.
Disconnect the mount and document the result

Recovery access should stay open only while you need it. Disconnecting the mount invalidates the temporary session while leaving the copied file inside restored untouched.

Closing recovery access can feel risky after a successful restore. Your verified local copy remains available after the read-only drive disappears.

  • Return to the File Recovery menu in the Azure portal.
  • Open the active recovery session.
  • Confirm that the session references your selected recovery point.

Before you disconnect the session, which drive do you expect to disappear from This PC?

  • Select Unmount Disks.
  • Wait for the portal to report that the unmount operation completed.

The completed response confirms that Azure has closed access to the recovery point.

  • Save the observed unmount response in evidence/unmount.txt.
  • Return to This PC in Windows File Explorer.
  • Refresh the drive list.

You should see that the recovery drive has disappeared. The verified file should remain at 07-backup-and-restore\restored\critical-app-config.txt.

  • Record the post-unmount drive list in evidence/unmount.txt.
  • Confirm that 07-backup-and-restore\restored\critical-app-config.txt still opens from the local folder.

Recovery drive still visible?

  • Refresh This PC after the portal reports completion.
  • Return to the active session in File Recovery to confirm its current status.
  • Use Unmount Disks if the recovery session still shows as active.

Still connected? Help me verify why an Azure Backup recovery drive remains visible after I selected Unmount Disks.

The technical recovery is complete, so the README can now record observed results. Keep expected statements separate from the evidence you actually captured.

  • Return to README.md in the editor from earlier.
  • Update ## Troubleshooting and fix with the observed restored path.
  • Replace Fix: [Record only after recovery completes.] with the observed recovery outcome.
  • Update the Restored SHA-256 row using evidence/hash-comparison.txt.
  • Update the Comparison row with the observed true result.
  • Update the Recovery mount row with the observed unmount response.

Your results table now tells the complete incident story from deletion through verified recovery. The evidence links keep every claim traceable.

  • Confirm that the hash evidence link points to evidence/hash-comparison.txt.
  • Confirm that the unmount evidence link points to evidence/unmount.txt.
  • Update the Restore data row under ## AZ-104 skills mapped with your observed evidence link.
  • Add an observed recovery lesson under ## Reflection.
  • Save README.md.

That closes the incident with evidence. You recovered the deleted file, proved its integrity, and removed temporary recovery access.

Secret mission

Recover the Prior Version After an Overwrite

A deleted file is easy to notice. An overwritten file can look healthy while its trusted contents are gone. Recover Version 1 from the original recovery point after the VM creates Version 2 at the same path.

Clean Up Your Resources

Clean Up Your Resources

Choose a cleanup path based on whether you want to keep using this recovery lab. Paid Microsoft Azure resources can continue incurring charges until you deallocate or delete them.

Cost warning

Charges can continue after the recovery drill finishes. The cleanup options below make each tradeoff explicit before you remove anything.

  • VM compute can incur charges while the VM runs.
  • The managed OS disk can incur storage charges.
  • The Standard public IPv4 can incur charges.
  • Azure Backup bills protected-instance usage plus consumed backup storage.
  • Instant-restore snapshots can incur snapshot charges.

Azure Backup retains deleted backup data for 14 days by default through soft delete. This default vaulted-backup retention has no extra retention cost.

Resources you used:

  • Azure resource group rg-az104-p07-backup.
  • Windows Server VM vm-p07-backup.
  • Managed OS disk vm-p07-backup-osdisk.
  • Network interface nic-p07-backup.
  • Virtual network vnet-p07-backup.
  • Subnet snet-workload.
  • Network security group nsg-p07-backup.
  • Standard public IPv4 resource pip-p07-backup.
  • The observed Recovery Services vault recorded in 07-backup-and-restore/README.md.
  • Standard V1 backup policy policy-p07-daily-7d.
  • Protected backup item for vm-p07-backup.
  • Original recovery point containing Version 1.
  • Instant-restore snapshot created for the protected VM.
  • Local folder 07-backup-and-restore containing the Bicep source files.
  • Recovered files stored under 07-backup-and-restore/restored.

Keep everything running

No action needed. Choose this option if you want the protected VM available for more backup drills.

  • The VM remains available for further recovery tests.
  • The backup policy continues protecting the VM.
  • The recovery point remains available for another item-level recovery session.
  • The local evidence remains available for review.
  • Applicable Azure charges continue.

Keep monitoring your subscription costs while these resources remain deployed.

Pause - I'll come back to this later

This option pauses VM compute while preserving the recovery lab. Other Azure resources remain deployed.

  • Return to the Azure portal session from earlier.
  • Return to the VM page for vm-p07-backup.
  • Use the VM page's stop control to deallocate the VM.
  • Refresh the VM status until it identifies the VM as deallocated.
  • Record the observed deallocation status under ## Cost and cleanup in 07-backup-and-restore/README.md.

You should see a deallocated VM status. This pauses VM compute.

Other resources can remain billable.

  • The managed OS disk remains stored.
  • The Standard public IPv4 remains deployed.
  • The protected-instance backup remains configured.
  • Backup storage remains retained.
  • Instant-restore snapshots can remain present.

Delete - I don't want to use this again

This option removes the Azure lab resources plus the local project folder. Deletion is final for the active resources covered below.

Capture the pre-delete state:

  • Return to Windows File Explorer from your recovery work.
  • Confirm that neither temporary recovery drive is present.
  • Return to the rg-az104-p07-backup page in the Azure portal.
  • Record the current resource inventory in 07-backup-and-restore/evidence/cleanup.txt.
  • Find the actual vault name under ## Deployment and baseline in 07-backup-and-restore/README.md.
  • Record the actual vault name here: your observed Recovery Services vault name.
  • Identify any separate resource group containing the instant-restore snapshot.
  • Record that resource group here if one exists: your observed snapshot resource group name.

Stop protection and request backup-data deletion:

  • Stop protection for vm-p07-backup by running this command:
az backup protection disable --backup-management-type AzureIaasVM --container-name vm-p07-backup --item-name vm-p07-backup --resource-group rg-az104-p07-backup --vault-name [[VAULT_NAME="your observed Recovery Services vault name"]] --delete-backup-data true --yes

Protection cleanup blocked?

This command stops protection for the VM. It also requests deletion of its backup data.

Confirm the vault name if the command fails. Confirm that the protected item still uses vm-p07-backup. Help me diagnose this protection cleanup failure.

  • Append the actual protection response to 07-backup-and-restore/evidence/cleanup.txt.

Delete the Recovery Services vault:

  • Delete the observed vault by running this command:
az backup vault delete --name [[VAULT_NAME="your observed Recovery Services vault name"]] --resource-group rg-az104-p07-backup --yes

Vault deletion blocked?

This command removes the vault after its active dependencies are cleared. The backup policy belongs to that vault.

A remaining protected item can block deletion. Record the actual response before investigating it. Help me diagnose this vault deletion failure.

  • Append the actual vault response to 07-backup-and-restore/evidence/cleanup.txt.

Delete the main resource group:

Resource group deletion can take several minutes. The wait reflects Azure removing each dependent resource.

  • Delete rg-az104-p07-backup by running this command:
az group delete -n rg-az104-p07-backup --yes

Resource group deletion blocked?

  • This command removes vm-p07-backup.
  • This command removes vm-p07-backup-osdisk.
  • This command removes nic-p07-backup.
  • This command removes vnet-p07-backup.
  • This command removes snet-workload.
  • This command removes nsg-p07-backup.
  • This command removes pip-p07-backup.

If deletion fails, capture the blocking resource from the command output. Help me diagnose this resource group deletion failure.

  • Append the actual resource group response to 07-backup-and-restore/evidence/cleanup.txt.

Remove a separate snapshot resource group:

Instant-restore snapshots can exist outside the main resource group. Their resource charges can continue after the main group disappears.

  • Skip this cleanup command if your inventory showed no separate snapshot resource group.
  • Delete the observed snapshot resource group by running these commands:
az group delete -n [[SNAPSHOT_RESOURCE_GROUP="your observed snapshot resource group name"]] --yes
az group exists -n [[SNAPSHOT_RESOURCE_GROUP="your observed snapshot resource group name"]]

Snapshot group still present?

The first command removes the snapshot resource group. The second command checks whether that group remains.

The existence check should return false. Help me diagnose a remaining snapshot group.

Verify actual cleanup:

  • Verify the final Azure cleanup state by running these commands:
az group exists -n rg-az104-p07-backup
az backup deleted-vault list --location denmarkeast

Cleanup verification failed?

The first command checks whether the main resource group remains. The second command lists soft-deleted vault state in denmarkeast.

Confirm that your Azure CLI session still targets the subscription used for this lab if either check fails. Help me diagnose this cleanup verification failure.

You should see false for the main resource group. The deleted-vault output records the observed soft-delete state.

  • Return to Cost Management in the Azure portal.
  • Inspect whether charges remain for the deleted lab resources.
  • Inspect whether charges remain for any instant-restore snapshot.
  • Append the main resource group result under ## Cost and cleanup in 07-backup-and-restore/README.md.
  • Append the observed soft-delete state under the same heading.
  • Append the observed snapshot state under the same heading.
  • Append the actual billing observation under the same heading.
  • Keep these observations under the existing ## Cost and cleanup heading.
  • Copy any evidence you want to retain to a separate location outside 07-backup-and-restore.
  • Return to the parent location of 07-backup-and-restore in Windows File Explorer.
  • Select the 07-backup-and-restore folder.
  • Use Windows File Explorer's delete action to remove the selected folder.
  • Confirm that 07-backup-and-restore no longer appears in its parent location.

Nice Work!

Nice Work!

You completed it! You built an evidence-led Azure VM backup and file recovery drill.

Your proof follows the incident from a healthy file to a verified recovery. Both recovery mounts are disconnected.

You've learned how to:

  • Deploy a Windows Server 2022 Azure Edition recovery lab with Bicep. Protect the VM in a Recovery Services vault through Azure Backup.
  • Monitor a real backup job until it produces a named recovery point. Recover a deleted file to a separate location. Use SHA-256 evidence to prove its integrity. Disconnect recovery access after the copy is secure.
  • Maintain an evidence-led recovery report in 07-backup-and-restore/README.md. Separate expected outcomes from observed results. Record the real cleanup state.
  • Complete the Secret Mission by overwriting the live file with Version 2. Recover Version 1 from the original recovery point. Prove the version change with different hashes. Disconnect the second recovery mount.

Ready to quiz yourself?