diff --git a/docs/implementation/05-operational-foundations/phase-03-backup-dr/task-01-configure-azure-backup.mdx b/docs/implementation/05-operational-foundations/phase-03-backup-dr/task-01-configure-azure-backup.mdx index e501f7a6f..6e863ade0 100644 --- a/docs/implementation/05-operational-foundations/phase-03-backup-dr/task-01-configure-azure-backup.mdx +++ b/docs/implementation/05-operational-foundations/phase-03-backup-dr/task-01-configure-azure-backup.mdx @@ -1,398 +1,126 @@ ---- -title: "Task 01: Configure Azure Backup" -sidebar_label: "Task 01: Configure Azure Backup" +--- +title: "Task 01: Protect Azure Local VMs" +sidebar_label: "Task 01: Protect VMs" sidebar_position: 1 -description: "Configure Azure Backup for Azure Local VM protection" +description: "Choose and configure backup, Azure Site Recovery replication or Hyper-V Replica for Azure Local VMs" category: "Runbook" -scope: "Configure Azure Backup for Azure Local VM protection" -purpose: "Configure Azure Backup for Azure Local VM protection" +scope: "Protect Azure Local VMs" +purpose: "Choose and configure VM backup and replication" author: "Azure Local Cloud" created: 2025-03-25 -updated: 2025-03-25 -version: "1.0.0" +updated: 2026-10-07 +version: "2.0.0" tags: - azure-local - operational-foundations - backup-dr keywords: - - Configure - - Azure - Backup + - Azure Site Recovery + - Hyper-V Replica status: "Active" --- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Task 01: Configure Azure Backup +# Task 01: Protect Azure Local VMs [![Runbook](https://img.shields.io/badge/Type-Runbook-blue?style=flat-square)](./index.mdx) [![Azure](https://img.shields.io/badge/Platform-Azure_Local-0078D4?style=flat-square&logo=microsoftazure)](https://learn.microsoft.com/en-us/azure/azure-local/) > **DOCUMENT CATEGORY**: Runbook -> **SCOPE**: VM backup configuration with Azure Backup -> **PURPOSE**: Deploy and configure Azure Backup for Azure Local VM protection -> **MASTER REFERENCE**: [Microsoft Learn - Back up Azure Local VMs with Azure Backup](https://learn.microsoft.com/en-us/azure/backup/back-up-azure-stack-hyperconverged-infrastructure-virtual-machines) -> **DESIGN REFERENCE**: [Azure Backup Appliance LLD](../../../design/lld.mdx) +> **SCOPE**: Protect Azure Local VMs +> **PURPOSE**: Choose and configure VM backup and replication +> **MASTER REFERENCE**: [Microsoft Learn - Disaster recovery for Azure Local virtual machines](https://learn.microsoft.com/en-us/azure/azure-local/manage/disaster-recovery-overview) **Status**: Active -Azure Backup provides enterprise backup capabilities for Azure Local VMs with both local disk retention for fast recovery and Azure integration for offsite protection. This step covers Azure Backup deployment, agent installation, and protection group configuration. - -## Prerequisites - -| Requirement | Description | Validation | -|-------------|-------------|------------| -| backup server VM | Dedicated VM on cluster with sufficient storage | 80 TB recommended for 800 VMs | -| Azure Backup Software | Azure Backup V4 or later | Supports Azure Local 22H2+ | -| Recovery Services Vault | Azure vault for online protection | Created in Azure subscription | -| Network Connectivity | Azure Backup to Azure (443), Azure Backup to nodes (445, 135, dynamic RPC) | Firewall rules configured | -| Cluster Access | Domain account with local admin on all nodes | CredSSP or Kerberos delegation | -| Integration Components | Same version on guest VMs and hosts | Required for backup consistency | - -## Variables from variables.yml - -| Variable | Config Path | Example | -|----------|-------------|---------| -| `AZURE_RESOURCE_GROUP` | `azure.resource_group.name` | `rg-azurelocal-prod-eus2` | -| `RECOVERY_VAULT_NAME` | `backup.recovery_vault_name` | `rsv-azl-dal-prod-01` | -| `BACKUP_SERVER_NAME` | `backup.server_name` | `azl-dal-mabs-01` | -| `CLUSTER_STORAGE_PATH` | `storage.cluster_storage_path` | `C:\ClusterStorage\Volume01` | -| `MANAGEMENT_VIRTUAL_SWITCH` | `networking.management.virtual_switch` | `ConvergedSwitch(mgmt_compute)` | - -## Architecture Overview - -Azure Backup supports two backup approaches: - -| Approach | Description | Use Case | -|----------|-------------|----------| -| **Host-level backup** | Agent on cluster nodes, backs up entire VMs at hypervisor level | Full VM recovery, OS-agnostic, no guest agent required | -| **Guest-level backup** | Agent inside each VM, application-aware backups | SQL, Exchange, SharePoint item-level recovery | - -:::tip Azure Local Cloud Recommendation -Use **both approaches**: Host-level for full VM recovery and guest-level for critical application workloads requiring item-level recovery. -::: - -:::info Hardware Specifications -For detailed hardware specifications, storage configuration, and appliance sizing, see the [Azure Backup Appliance Low Level Design](../../../design/lld.mdx). - -**Key Storage Design**: The BOSS card contains only OS (C:) and Azure Backup/SQL (E:) partitions. All staging (G:) and storage pool (S:) volumes must reside on RAID controller-attached drives. -::: - -## Configuration Steps - -### Step 1.1: Deploy backup server VM - -1. Create a dedicated VM on the Azure Local cluster: - -```powershell -# Create Azure Backup VM -$VMName = "{{BACKUP_SERVER_NAME}}" -$VMPath = "{{CLUSTER_STORAGE_PATH}}\VMs" -$SwitchName = "{{MANAGEMENT_VIRTUAL_SWITCH}}" - -New-VM -Name $VMName ` - -MemoryStartupBytes 16GB ` - -Generation 2 ` - -Path $VMPath ` - -SwitchName $SwitchName - -# Configure VM resources -Set-VMProcessor -VMName $VMName -Count 8 -Set-VMMemory -VMName $VMName -DynamicMemoryEnabled $false - -# Add storage for Azure Backup disk pool (minimum 1.5x protected data size) -$StorageDisk = New-VHD -Path "$VMPath\$VMName\Storage.vhdx" -SizeBytes 10TB -Dynamic -Add-VMHardDiskDrive -VMName $VMName -Path $StorageDisk.Path -``` - -2. Install Windows Server 2022 on the VM -3. Join to domain and configure static IP -4. Format storage disk for Azure Backup disk pool - -### Step 1.2: Install Azure Backup - -1. Download Azure Backup V4 from [Microsoft Evaluation Center](https://www.microsoft.com/en-us/evalcenter/evaluate-microsoft-azure-backup-server) - -2. Run the installer and select required components: - - SQL Server (bundled or use existing) - - Azure Backup Server - -3. Register with Recovery Services Vault: - - - - -1. Navigate to **Recovery Services vault** → **Properties** → **Backup Credentials** -2. Click **Download** to get vault credentials file -3. Copy to backup server -4. During Azure Backup setup, specify the vault credentials file - - - - - -```powershell -# Connect to Azure -Connect-AzAccount - -# Get Recovery Services vault -$vault = Get-AzRecoveryServicesVault ` - -ResourceGroupName "{{AZURE_RESOURCE_GROUP}}" ` - -Name "{{RECOVERY_VAULT_NAME}}" - -# Generate vault credentials file -$credentialsPath = "C:\Temp" -Get-AzRecoveryServicesVaultSettingsFile ` - -Vault $vault ` - -Backup ` - -Path $credentialsPath -``` - - - - -### Step 1.3: Configure Azure Backup Storage Pool - -1. Open **Azure Backup Administrator Console** -2. Navigate to **Management** → **Disk Storage** -3. Click **Add** to add storage disks -4. Select the storage disk attached to Azure Backup VM -5. Configure storage allocation: - -| Recommendation | Size Formula | -|----------------|--------------| -| Replica volume | Protected data size × 2 | -| Recovery point volume | Protected data size × retention days × daily change rate | - -### Step 1.4: Install Protection Agent on Cluster Nodes - -:::warning Application Control -If Azure Benefits is enabled with WDAC, temporarily switch to **Audit** mode before agent installation. -::: - - - - -1. In backup console, navigate to **Management** → **Agents** -2. Click **Install** in the toolbar -3. Select **Install agents** -4. Enter cluster node names or browse Active Directory -5. Provide credentials with local admin rights -6. Complete installation wizard +## Overview - +Backups protect data and allow point-in-time recovery, but restoring can take hours. Continuous replication keeps an up-to-date copy at a second location for rapid failover and minimal data loss. - +Azure Local supports two continuous replication technologies: Azure Site Recovery and Hyper-V Replica. Azure Backup is the Azure backup service. For on-premises backup, use a customer-selected backup solution that supports Azure Local. -After agent installation, re-enable WDAC if it was disabled. +## Decide: backup, replication or both -### Step 1.5: Create Protection Group for VMs +| Approach | Use it for | Considerations | +| --- | --- | --- | +| Backup | Point-in-time recovery and protection of VM data. Use host-level backup for full-VM recovery and guest-level backup for critical applications. | A restore can take hours. Keep offsite copies. | +| Azure Site Recovery | Continuous replication of Azure Local VMs to Azure. | RPO can be as low as 30 seconds. Failover to Azure can be planned or unplanned. | +| Hyper-V Replica | Asynchronous replication between two Azure Local clusters for on-premises disaster recovery. | Requires a second cluster. It is not configurable in the Azure portal. | +| Backup and replication | Use backup for point-in-time recovery and replication for rapid failover and minimal data loss. | These approaches address different recovery needs. | -1. In backup console, select **Protection** → **Create protection group** - -2. Select protection type: **Servers** - -3. Select group members: - - **Highly Available VMs**: Select from cluster for clustered VMs - - **Hyper-V (Unmanaged) VMs**: Select from individual nodes - -4. Configure **Data Protection Method**: - - Name: `{{PROTECTION_GROUP_NAME}}` - - Enable: **I want short-term protection using Disk** - - Enable: **I want online protection** (for Azure backup) - -5. Configure **Short-Term Goals**: - - Retention range: `{{BACKUP_RETENTION_DAYS}}` days - - Synchronization frequency: Every 15 minutes or before recovery point - -6. Configure **Disk Allocation**: - - Review and accept default allocations - - Enable **Automatically grow the volumes** if needed - -7. Configure **Online Protection**: - - Select data for online protection - - Configure online backup schedule (daily/weekly) - - Set online retention policy (up to 99 years) - -8. Review and create protection group - -### Step 1.6: Configure Backup Schedules - -Example protection schedule: - -| Schedule Type | Frequency | Time | Retention | -|--------------|-----------|------|-----------| -| Express full backup | Daily | 9:00 PM | 14 days (disk) | -| Online backup | Daily | 11:00 PM | 30 days (Azure) | -| Weekly full | Sunday | 2:00 AM | 5 weeks (Azure) | -| Monthly | 1st Sunday | 2:00 AM | 12 months (Azure) | - -```powershell -# Example: Modify protection group schedule via PowerShell -$pg = Get-DPMProtectionGroup -DPMServerName "{{BACKUP_SERVER_NAME}}" | - Where-Object { $_.FriendlyName -eq "{{PROTECTION_GROUP_NAME}}" } - -# Set synchronization schedule -$sched = Get-DPMPolicySchedule -ProtectionGroup $pg -ShortTerm - -# Modify as needed and commit -Set-DPMPolicySchedule -ProtectionGroup $pg -Schedule $sched -``` - -## Validation - -### Verify Agent Status - -```powershell -# On backup server - check agent status -Get-DPMProductionServer -DPMServerName "{{BACKUP_SERVER_NAME}}" | - Select-Object Name, ServerProtectionState, AgentVersion -``` - -Expected output shows all cluster nodes with **Valid** protection state. - -### Run Test Backup - -1. In backup console, select a protected VM -2. Right-click → **Create recovery point** -3. Select **Online protection** to test Azure connectivity -4. Monitor job in **Monitoring** → **Jobs** - -### Verify Azure Recovery Points - - - - -1. Navigate to **Recovery Services vault** → **Backup items** -2. Select **Azure Backup Server** -3. Verify VMs appear with recent recovery points - - - - - -### Validation Checklist - -| Component | Verification | Expected Result | -|-----------|--------------|-----------------| -| Azure Backup Service | `Get-Service MSDPM` | Running | -| Protection Agent | Agent status in console | Valid on all nodes | -| Protection Group | Group status | OK, no warnings | -| Disk Recovery Points | Latest recovery point | Within last 24 hours | -| Online Recovery Points | Azure portal check | Synced to Azure | - -## Recovery Procedures - -### Recover Entire VM (Host-Level) - -1. Navigate to **Recovery** → Select date with recovery point -2. Expand server → Select VM -3. Right-click → **Recover** -4. Select recovery type: - - **Recover to original instance**: Replaces existing VM - - **Recover as virtual machine to any host**: New location - - **Copy to a network folder**: Export VHD files - -### Recover Files (Item-Level Recovery) - -:::note Hyper-V Role Required -Item-level recovery requires Hyper-V role on backup server. -::: - -1. Select VM recovery point -2. Right-click → **Recover** -3. Select **Copy to a network folder** -4. Browse recovered VHD and extract files - -### Cross-Cluster Recovery - -When restoring a VM to a different cluster: - -1. Recover VM to alternate location -2. VM will be **unmanaged** (not Arc-connected) -3. Re-register with Azure Arc: - -```powershell +## Prerequisites -Register-AzStackHCI -SubscriptionId "{{AZURE_SUBSCRIPTION_ID}}" ` - -ResourceGroupName "{{AZURE_RESOURCE_GROUP}}" ` - -ArcServerResourceGroupName "{{ARC_RESOURCE_GROUP}}" -``` +| Requirement | Applies to | +| --- | --- | +| A Recovery Services vault | Azure Site Recovery | +| Connectivity to Azure on port 443 | Azure Site Recovery | +| A supported backup solution that supports Azure Local | Backups | +| A second Azure Local cluster | Hyper-V Replica | -:::info 45-Day Window -If restoring to the same cluster, Azure Arc connection is maintained automatically if within the 45-day reconnection window. -::: +For backup tools, see [Virtual machine resiliency for Azure Local](https://learn.microsoft.com/en-us/azure/azure-local/manage/disaster-recovery-vm-resiliency), in the **Backup Tools** section. Check the solution's support statement and keep offsite copies. -## Troubleshooting +## Variables -| Symptom | Likely Cause | Resolution | -|---------|--------------|------------| -| Agent installation fails | WDAC blocking | Switch to Audit mode, install, then re-enable | -| WMI query timeout | Firewall blocking | Disable `AzsHci-ImdsAttestation-Block-TCP-In` | -| Backup fails for HA VMs | Cluster not selected | Select VMs from cluster node, not individual hosts | -| Online backup slow | Bandwidth limited | Configure throttling in Azure Backup or schedule off-hours | -| Recovery point missing | Replica invalid | Run consistency check on protection group | +| Variable | Source | Example | +| --- | --- | --- | +| `RECOVERY_VAULT_NAME` | `backup.recovery_vault_name` | `rsv-azl-dal-prod-01` | +| `AZURE_RESOURCE_GROUP` | `azure.resource_group.name` | `rg-azurelocal-prod-eus2` | -## Capacity Planning +## Step 1.1 Choose the backup approach -| Protected Data | Azure Backup Disk Storage | Azure Storage | -|----------------|-------------------|---------------| -| 10 TB | 30 TB | Varies by retention | -| 50 TB | 150 TB | Varies by retention | -| 100 TB | 300 TB | Varies by retention | +Choose host-level backup, guest-level backup, or both based on your recovery needs. -:::tip Storage Formula -Azure Backup storage = (Protected data × 2) + (Protected data × retention days × daily change rate) -::: +| Approach | How it works | Recovery use | +| --- | --- | --- | +| Host-level | An agent runs on the cluster nodes. It protects whole VMs, is operating-system agnostic, and does not require an agent in each guest. | Full-VM recovery | +| Guest-level | An agent runs inside each VM. It is application-aware and supports item-level recovery for databases and similar applications. | Critical applications and item-level recovery | -## Variables Reference +Use host-level backup for full-VM recovery and guest-level backup for critical applications. Select an on-premises backup solution that supports Azure Local, and check its support statement. Azure Backup is the Azure backup service. Configure the selected solution according to its supported guidance. -| Variable | Description | Example | -|----------|-------------|---------| -| `{{BACKUP_SERVER_NAME}}` | backup server hostname | `Azure Backup-dal-01` | -| `{{PROTECTION_GROUP_NAME}}` | Protection group name | `AzureLocal-VMs-Prod` | -| `{{RECOVERY_VAULT_NAME}}` | Recovery Services vault name | `rsv-azl-dal-prod-01` | -| `{{BACKUP_RETENTION_DAYS}}` | Local disk retention days | `14` | +## Step 1.2 Configure Azure Site Recovery replication -## Next Steps +Azure Site Recovery replicates Azure Local VMs to Azure. Its RPO can be as low as 30 seconds. -After configuring Azure Backup: +In the Azure portal, start disaster recovery and prepare the infrastructure from the Azure Local resource. Then continue from the Recovery Services vault: -1. ➡️ **[Task 2: Configure Site Recovery](./task-02-configure-site-recovery.mdx)** — Set up VM replication to Azure -2. Document protection group assignments -3. Configure email notifications for backup failures -4. Schedule regular restore tests +1. Select **Replicate**, then **Hyper-V machines to Azure**. +2. Set the source Hyper-V site, target subscription, post-failover resource group, and Resource Manager deployment model. +3. Select managed disk storage, which is recommended. +4. Select a virtual network and subnet for the failover VMs. You can set these later. +5. Configure replication, then use the available recovery operations as needed: + - Test failover uses an isolated network and is non-disruptive. + - Fail over to Azure, either planned or unplanned. In-memory state is lost. + - Re-protect after failover. + - Perform a planned failback, then reverse replication. +The Azure Site Recovery extension for Azure Local deploys automatically to all nodes, but it is in preview and only for test environments. For production, configure manually by using the Hyper-V to Azure disaster recovery option. ---- +Azure Site Recovery is not supported for Trusted launch VMs. +## Step 1.3 Configure Hyper-V Replica (optional) -:::info Toolkit Reference -Scripts for this task are located in the [azurelocal-toolkit](https://github.com/AzureLocal/azurelocal-toolkit) repository under scripts/deploy/ in the appropriate task folder. -::: +Hyper-V Replica is built-in, asynchronous replication between two Azure Local clusters for on-premises disaster recovery. It is not configurable in the Azure portal. Use PowerShell on the nodes or Failover Cluster Manager from a Windows Server machine. ---- +1. Enable both clusters as replica servers. +2. Set authentication: use Kerberos in a domain, or certificates otherwise. +3. Open firewall rules for replication traffic. +4. Enable replication for each VM. Configure the replica server or cluster, virtual hard disks, replication frequency of 30 seconds, 5 minutes, or 15 minutes, and the number of recovery points. +5. Use test failover, failover, reverse replication, and failback as needed. -## Alternatives +## Step 1.4 Validate -The procedures in this task use the scripted methods shown in the tabs above. Additional deployment methods including Azure CLI and Bash scripts are available in the [azurelocal-toolkit](https://github.com/AzureLocal/azurelocal-toolkit) repository under scripts/deploy/. +Check that: -| Method | Description | -|--------|-------------| -| Azure CLI | PowerShell-based Azure CLI scripts for Azure resource operations | -| Bash | Linux/macOS compatible shell scripts for pipeline environments | +- A test failover completes. +- A restore test of one VM succeeds. +- Recovery points exist. -## Navigation +## Trusted launch notes -| Previous | Up | Next | -|----------|-----|------| -| --- | [Phase 03: Backup & DR](./index.mdx) | [Task 02: Configure Site Recovery ->](./task-02-configure-site-recovery.mdx) | +If you restore a Trusted launch VM to a different Azure Local instance, it can no longer be managed through Azure Arc. Manage it with local tools instead. ---- +Automatic key backup is in preview. When creating the instance, provide a backup Azure Key Vault to back up system-level secrets, including Trusted launch VM keys and BitLocker recovery keys. -## Version Control +## Next steps -| Version | Date | Author | Changes | -|---------|------|--------|---------| -| 1.0.0 | 2025-03-25 | Azure Local Cloud | Initial release | +Continue with the validation task in the Testing section. diff --git a/docs/implementation/06-testing-validation/task-06-backup-dr-validation.mdx b/docs/implementation/06-testing-validation/task-06-backup-dr-validation.mdx index 1573ab6a8..cee4fcb96 100644 --- a/docs/implementation/06-testing-validation/task-06-backup-dr-validation.mdx +++ b/docs/implementation/06-testing-validation/task-06-backup-dr-validation.mdx @@ -1,4 +1,4 @@ ---- +--- title: "Task 06: Backup & DR Validation" sidebar_label: "Task 06: Backup & DR Validation" sidebar_position: 6 @@ -30,18 +30,18 @@ import TabItem from '@theme/TabItem'; > **DOCUMENT CATEGORY**: Runbook > **SCOPE**: Backup and disaster recovery validation > **PURPOSE**: Validate backup jobs, test restores, and document RPO/RTO -> **MASTER REFERENCE**: [Microsoft Learn - Azure Backup for Azure Local](https://learn.microsoft.com/en-us/azure/backup/back-up-azure-stack-hci-virtual-machines) +> **MASTER REFERENCE**: [Microsoft Learn - Disaster recovery for Azure Local virtual machines](https://learn.microsoft.com/en-us/azure/azure-local/manage/disaster-recovery-overview) **Status**: Active ## Overview -This step validates the backup and disaster recovery capabilities for the Azure Local cluster, including Azure Backup operations, test restores, and DR failover validation. +This step validates the backup and disaster recovery capabilities for the Azure Local cluster, including backup operations of the chosen backup solution, test restores, and DR failover validation. ## Prerequisites - [ ] All previous validation steps completed (Steps 1-5) -- [ ] backup server configured (Stage 17) +- [ ] Backup solution configured (Stage 17) - [ ] Azure Site Recovery configured (if applicable) - [ ] Test VM available for restore testing - [ ] Sufficient storage for restore operations @@ -80,7 +80,7 @@ $DateStamp = Get-Date -Format "yyyyMMdd" $ReportPath = "C:\ClusterStorage\Collect\validation-reports" $ReportFile = "$ReportPath\06-backup-dr-validation-$DateStamp.txt" -$BackupServer = "" # Replace with actual backup server +$BackupSolution = "" # Replace with the name of the chosen backup solution # Initialize report $ReportHeader = @" @@ -88,7 +88,7 @@ $ReportHeader = @" BACKUP & DISASTER RECOVERY VALIDATION REPORT ================================================================================ Cluster: $ClusterName -backup server: $BackupServer +Backup solution: $BackupSolution Date: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Generated By: $(whoami) ================================================================================ @@ -99,79 +99,96 @@ $ReportHeader | Out-File -FilePath $ReportFile -Encoding UTF8 --- -## Part 2: Azure Backup Configuration Validation +## Part 2: Backup Configuration Validation -### 2.1 Verify backup agent Status +### 2.1 Verify the backup agent or service on each node -```powershell -"`n" + "="*80 | Add-Content $ReportFile -"backup agent STATUS" | Add-Content $ReportFile -"="*80 | Add-Content $ReportFile +Set `$AgentServiceName` to the service name used by the chosen backup solution. Check every cluster node and record the service status and start type. -$Nodes = (Get-ClusterNode).Name +```powershell +$serviceResults = foreach ($node in Get-ClusterNode -Cluster $ClusterName) { + Invoke-Command -ComputerName $node.Name -ArgumentList $AgentServiceName -ScriptBlock { + param($ServiceName) + + $service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue + if ($null -eq $service) { + [pscustomobject]@{ + Node = $env:COMPUTERNAME + Service = $ServiceName + Status = 'Not found' + StartType = 'Not available' + } + } + else { + [pscustomobject]@{ + Node = $env:COMPUTERNAME + Service = $service.Name + Status = $service.Status + StartType = $service.StartType + } + } + } +} -foreach ($Node in $Nodes) { - $AgentStatus = Invoke-Command -ComputerName $Node -ScriptBlock { - $Service = Get-Service -Name "DPMRA" -ErrorAction SilentlyContinue - if ($Service) { - [PSCustomObject]@{ - Node = $env:COMPUTERNAME - ServiceStatus = $Service.Status - StartType = $Service.StartType - } - } else { - [PSCustomObject]@{ - Node = $env:COMPUTERNAME - ServiceStatus = "Not Installed" - StartType = "N/A" - } - } - } - - "$($AgentStatus.Node): backup agent = $($AgentStatus.ServiceStatus) ($($AgentStatus.StartType))" | Add-Content $ReportFile +$serviceResults | ForEach-Object { + Add-Content $ReportFile ("Service check: Node={0}; Service={1}; Status={2}; StartType={3}" -f $_.Node, $_.Service, $_.Status, $_.StartType) } ``` -### 2.2 Verify Protection Groups +### 2.2 Verify protection coverage -```powershell -# Connect to Azure Backup (run from backup server or remote session) -$BackupSession = New-PSSession -ComputerName $BackupServer +List every VM across the cluster and compare it with the chosen backup solution's console. Fill in the remaining columns by hand. -$ProtectionGroups = Invoke-Command -Session $BackupSession -ScriptBlock { - Import-Module DataProtectionManager - Get-DPMProtectionGroup | Select-Object FriendlyName, @{N='Members';E={($_ | Get-DPMDatasource).Name -join ", "}}, - @{N='Status';E={$_.ProtectionStatus}} -} +| Production VM | Protected (Yes/No) | Policy | Last successful backup | +|---|---|---|---| +| | | | | -"`nProtection Groups:" | Add-Content $ReportFile -$ProtectionGroups | Format-Table -AutoSize | Out-String | Add-Content $ReportFile +```powershell +$vmInventory = foreach ($node in Get-ClusterNode -Cluster $ClusterName) { + Get-VM -ComputerName $node.Name -ErrorAction SilentlyContinue +} -Remove-PSSession $BackupSession +$vmInventory | + Sort-Object Id -Unique | + ForEach-Object { + Add-Content $ReportFile ("VM inventory: Name={0}; Id={1}; State={2}" -f $_.Name, $_.Id, $_.State) + } ``` ### 2.3 Check VSS Writers +Run the writer check on each node. Any writer not in a stable state with no error requires investigation. + ```powershell -"`nVSS Writers Status on Cluster Nodes:" | Add-Content $ReportFile - -foreach ($Node in $Nodes) { - $VSSWriters = Invoke-Command -ComputerName $Node -ScriptBlock { - $vss = vssadmin list writers 2>&1 - # Parse for failed writers - $Failed = $vss | Select-String "State: \[(\d+)\]" | Where-Object { $_.Matches.Groups[1].Value -ne "1" } - [PSCustomObject]@{ - Node = $env:COMPUTERNAME - TotalWriters = ($vss | Select-String "Writer name:").Count - FailedWriters = $Failed.Count - } - } - - "$($VSSWriters.Node): Total=$($VSSWriters.TotalWriters), Failed=$($VSSWriters.FailedWriters)" | Add-Content $ReportFile - - if ($VSSWriters.FailedWriters -gt 0) { - " WARNING: $($VSSWriters.FailedWriters) VSS writers in failed state" | Add-Content $ReportFile - } +foreach ($node in Get-ClusterNode -Cluster $ClusterName) { + $writerOutput = Invoke-Command -ComputerName $node.Name -ScriptBlock { + (& vssadmin.exe list writers 2>&1 | Out-String) + } + + Add-Content $ReportFile ("VSS writer output for node {0}:" -f $node.Name) + Add-Content $ReportFile $writerOutput + + $writerBlocks = [regex]::Matches( + $writerOutput, + "(?ms)Writer name:\s*'(?[^']+)'.*?State:\s*\[(?\d+)\]\s*(?[^\r\n]+).*?Last error:\s*(?[^\r\n]+)" + ) + + if ($writerBlocks.Count -eq 0) { + Add-Content $ReportFile ("VSS writer check: Node={0}; Status=Review output; writer state could not be parsed" -f $node.Name) + } + else { + foreach ($writer in $writerBlocks) { + $state = $writer.Groups['State'].Value.Trim() + $writerError = $writer.Groups['LastError'].Value.Trim() + + if ($state -notmatch '^Stable$' -or $writerError -notmatch '^No error$') { + Add-Content $ReportFile ("VSS writer issue: Node={0}; Writer={1}; State={2}; Error={3}" -f $node.Name, $writer.Groups['Name'].Value, $state, $writerError) + } + else { + Add-Content $ReportFile ("VSS writer OK: Node={0}; Writer={1}" -f $node.Name, $writer.Groups['Name'].Value) + } + } + } } ``` @@ -179,313 +196,149 @@ foreach ($Node in $Nodes) { ## Part 3: Backup Job Validation -### 3.1 Review Recent Backup Jobs +### 3.1 Review recent backup jobs -```powershell -"`n" + "="*80 | Add-Content $ReportFile -"BACKUP JOB HISTORY" | Add-Content $ReportFile -"="*80 | Add-Content $ReportFile +Export job history for the last 7 days from the chosen backup solution to `$ReportPath\backup-jobs.csv`. The CSV must contain `Datasource`, `Status`, `Start`, and `End` columns. This check counts rows with a `Status` of `Success` as successful and flags a success rate below 95 percent. -$BackupSession = New-PSSession -ComputerName $BackupServer +```powershell +$csvPath = Join-Path $ReportPath 'backup-jobs.csv' -$RecentJobs = Invoke-Command -Session $BackupSession -ScriptBlock { - Import-Module DataProtectionManager - - # Get jobs from last 7 days - $StartDate = (Get-Date).AddDays(-7) - Get-DPMJob -From $StartDate | Select-Object -First 20 @{N='DataSource';E={$_.Datasource.Name}}, - @{N='Type';E={$_.Type}}, - @{N='Status';E={$_.Status}}, - @{N='StartTime';E={$_.StartTime}}, - @{N='EndTime';E={$_.EndTime}}, - @{N='Duration';E={if($_.EndTime){($_.EndTime - $_.StartTime).ToString("hh\:mm\:ss")}else{"Running"}}} +if (-not (Test-Path $csvPath)) { + Add-Content $ReportFile ("Backup job review: CSV not found at {0}" -f $csvPath) +} +else { + $jobs = @(Import-Csv $csvPath) + $requiredColumns = 'Datasource', 'Status', 'Start', 'End' + + if ($jobs.Count -eq 0) { + Add-Content $ReportFile 'Backup job review: No job rows found in the CSV.' + } + else { + $missingColumns = $requiredColumns | Where-Object { $_ -notin $jobs[0].PSObject.Properties.Name } + + if ($missingColumns) { + Add-Content $ReportFile ("Backup job review: Missing CSV columns: {0}" -f ($missingColumns -join ', ')) + } + else { + $successCount = @($jobs | Where-Object { $_.Status -eq 'Success' }).Count + $successRate = [math]::Round(($successCount / $jobs.Count) * 100, 2) + $result = if ($successRate -lt 95) { 'FLAG: below 95 percent' } else { 'Pass' } + + Add-Content $ReportFile ("Backup job review: Total={0}; Successful={1}; SuccessRate={2}%; Result={3}" -f $jobs.Count, $successCount, $successRate, $result) + } + } } - -"`nLast 20 Backup Jobs:" | Add-Content $ReportFile -$RecentJobs | Format-Table -AutoSize | Out-String | Add-Content $ReportFile - -# Summary statistics -$SuccessCount = ($RecentJobs | Where-Object { $_.Status -eq "Succeeded" }).Count -$FailedCount = ($RecentJobs | Where-Object { $_.Status -eq "Failed" }).Count -$TotalJobs = $RecentJobs.Count - -"`nJob Summary (Last 7 Days):" | Add-Content $ReportFile -" Total Jobs: $TotalJobs" | Add-Content $ReportFile -" Successful: $SuccessCount" | Add-Content $ReportFile -" Failed: $FailedCount" | Add-Content $ReportFile -" Success Rate: $([math]::Round(($SuccessCount / $TotalJobs) * 100, 1))%" | Add-Content $ReportFile - -Remove-PSSession $BackupSession ``` -### 3.2 Run On-Demand Backup +### 3.2 Run an on-demand backup -```powershell -"`nOn-Demand Backup Test:" | Add-Content $ReportFile - -$BackupSession = New-PSSession -ComputerName $BackupServer +Run an on-demand backup of a test VM through the chosen backup solution. Record the VM, start time, end time, and result in the report file. -$BackupResult = Invoke-Command -Session $BackupSession -ScriptBlock { - Import-Module DataProtectionManager - - # Get first VM datasource for test backup - $PG = Get-DPMProtectionGroup | Select-Object -First 1 - $DS = $PG | Get-DPMDatasource | Select-Object -First 1 - - if ($DS) { - # Create recovery point (express full backup) - $BackupStart = Get-Date - $Job = New-DPMRecoveryPoint -Datasource $DS -Disk - - # Wait for job completion (max 30 minutes) - $Timeout = (Get-Date).AddMinutes(30) - while ($Job.Status -eq "InProgress" -and (Get-Date) -lt $Timeout) { - Start-Sleep -Seconds 30 - $Job = Get-DPMJob -JobId $Job.ActivityId - } - - $BackupEnd = Get-Date - - [PSCustomObject]@{ - DataSource = $DS.Name - Status = $Job.Status - Duration = ($BackupEnd - $BackupStart).ToString("mm\:ss") - Size = $Job.TotalBytes - } - } else { - [PSCustomObject]@{ - DataSource = "None" - Status = "No datasources configured" - Duration = "N/A" - Size = 0 - } - } -} +--- -" Data Source: $($BackupResult.DataSource)" | Add-Content $ReportFile -" Status: $($BackupResult.Status)" | Add-Content $ReportFile -" Duration: $($BackupResult.Duration)" | Add-Content $ReportFile -" Size: $([math]::Round($BackupResult.Size / 1GB, 2)) GB" | Add-Content $ReportFile +## Part 4: Restore Validation -Remove-PSSession $BackupSession -``` +### 4.1 Test VM restore ---- +Use the chosen backup solution to restore a test VM to an alternate location with a different name. Record the restore start and end times in the report file and calculate the RTO from those times. -## Part 4: Restore Validation +### 4.2 Verify the restored VM -### 4.1 Test VM Restore +Set `$RestoredVMName` to the alternate name used for the restored VM. Check its state, start it if needed, and wait for the heartbeat integration service to report `OK`. After approval, clean up the test VM using the chosen solution or the appropriate operational process. ```powershell -"`n" + "="*80 | Add-Content $ReportFile -"RESTORE VALIDATION" | Add-Content $ReportFile -"="*80 | Add-Content $ReportFile +$restoredVM = $null -$BackupSession = New-PSSession -ComputerName $BackupServer +foreach ($node in Get-ClusterNode -Cluster $ClusterName) { + $restoredVM = Get-VM -ComputerName $node.Name -Name $RestoredVMName -ErrorAction SilentlyContinue | + Select-Object -First 1 -$RestoreResult = Invoke-Command -Session $BackupSession -ScriptBlock { - param($ClusterName) - Import-Module DataProtectionManager - - # Get a VM datasource with recovery points - $PG = Get-DPMProtectionGroup | Select-Object -First 1 - $DS = $PG | Get-DPMDatasource | Where-Object { $_.Type -match "Hyper-V" } | Select-Object -First 1 - - if ($DS) { - # Get latest recovery point - $RecoveryPoints = Get-DPMRecoveryPoint -Datasource $DS - $LatestRP = $RecoveryPoints | Sort-Object BackupTime -Descending | Select-Object -First 1 - - if ($LatestRP) { - # Perform restore to alternate location - $RestoreStart = Get-Date - - # Get recovery option for alternate location restore - $ROpt = New-DPMRecoveryOption -RecoveryType AlternateHyperVLocation ` - -HyperVDatasource $DS ` - -RecoveryLocation $ClusterName ` - -AlternateLocation "C:\ClusterStorage\UserStorage_1\RestoreTest" - - $Job = Restore-DPMRecoverableItem -RecoverableItem $LatestRP -RecoveryOption $ROpt - - # Wait for completion - $Timeout = (Get-Date).AddMinutes(60) - while ($Job.Status -eq "InProgress" -and (Get-Date) -lt $Timeout) { - Start-Sleep -Seconds 30 - $Job = Get-DPMJob -JobId $Job.ActivityId - } - - $RestoreEnd = Get-Date - - [PSCustomObject]@{ - VMName = $DS.Name - RecoveryPoint = $LatestRP.BackupTime - Status = $Job.Status - Duration = ($RestoreEnd - $RestoreStart).ToString("hh\:mm\:ss") - TargetPath = "C:\ClusterStorage\UserStorage_1\RestoreTest" - } - } else { - [PSCustomObject]@{ - VMName = $DS.Name - RecoveryPoint = "None available" - Status = "NoRecoveryPoints" - Duration = "N/A" - TargetPath = "N/A" - } - } - } else { - [PSCustomObject]@{ - VMName = "None" - RecoveryPoint = "N/A" - Status = "NoHyperVDatasources" - Duration = "N/A" - TargetPath = "N/A" - } - } -} -ArgumentList $ClusterName + if ($null -ne $restoredVM) { + break + } +} -"`nTest Restore Results:" | Add-Content $ReportFile -" VM Name: $($RestoreResult.VMName)" | Add-Content $ReportFile -" Recovery Point: $($RestoreResult.RecoveryPoint)" | Add-Content $ReportFile -" Restore Status: $($RestoreResult.Status)" | Add-Content $ReportFile -" Duration: $($RestoreResult.Duration)" | Add-Content $ReportFile -" Target Path: $($RestoreResult.TargetPath)" | Add-Content $ReportFile +if ($null -eq $restoredVM) { + Add-Content $ReportFile ("Restore verification: VM={0}; Result=Not found" -f $RestoredVMName) +} +else { + Add-Content $ReportFile ("Restore verification: VM={0}; InitialState={1}" -f $restoredVM.Name, $restoredVM.State) -Remove-PSSession $BackupSession -``` + if ($restoredVM.State -ne 'Running') { + Start-VM -VM $restoredVM + } -### 4.2 Verify Restored VM + $deadline = (Get-Date).AddMinutes(10) + $heartbeatStatus = 'Timed out' -```powershell -# If restore succeeded, verify the VM -if ($RestoreResult.Status -eq "Succeeded") { - $RestoredVMPath = $RestoreResult.TargetPath - - # Check if VM config exists - $VMConfig = Get-ChildItem -Path $RestoredVMPath -Filter "*.vmcx" -Recurse -ErrorAction SilentlyContinue - - if ($VMConfig) { - "`nRestored VM Verification:" | Add-Content $ReportFile - " VM Config Found: $($VMConfig.FullName)" | Add-Content $ReportFile - - # Import and verify VM (don't start) - try { - $ImportedVM = Import-VM -Path $VMConfig.FullName -Copy -GenerateNewId - " Import Status: SUCCESS" | Add-Content $ReportFile - " VM Name: $($ImportedVM.Name)" | Add-Content $ReportFile - " VM State: $($ImportedVM.State)" | Add-Content $ReportFile - - # Cleanup: Remove test VM - Remove-VM -VM $ImportedVM -Force - Remove-Item -Path $RestoredVMPath -Recurse -Force - " Cleanup: Restored VM removed" | Add-Content $ReportFile - } catch { - " Import Status: FAILED - $($_.Exception.Message)" | Add-Content $ReportFile - } - } else { - " WARNING: VM config not found in restore location" | Add-Content $ReportFile - } -} else { - " Skipping VM verification (restore did not succeed)" | Add-Content $ReportFile + do { + Start-Sleep -Seconds 10 + $heartbeat = Get-VMIntegrationService -VM $restoredVM -Name 'Heartbeat' -ErrorAction SilentlyContinue + + if ($heartbeat -and $heartbeat.PrimaryStatusDescription -eq 'OK') { + $heartbeatStatus = 'OK' + break + } + } while ((Get-Date) -lt $deadline) + + Add-Content $ReportFile ("Restore verification: VM={0}; FinalState={1}; Heartbeat={2}" -f $restoredVM.Name, (Get-VM -ComputerName $restoredVM.ComputerName -Name $restoredVM.Name).State, $heartbeatStatus) } ``` -### 4.3 File-Level Restore Test +### 4.3 File-level restore test -```powershell -"`nFile-Level Restore Test:" | Add-Content $ReportFile +Restore one file to a temporary path using the chosen backup solution. Set `$SourceFile` and `$RestoredFile` to the source and restored file paths, then compare their hashes. -$BackupSession = New-PSSession -ComputerName $BackupServer +```powershell +$SourceFile = 'SET_SOURCE_FILE_PATH' +$RestoredFile = 'SET_RESTORED_FILE_PATH' -$FileRestoreResult = Invoke-Command -Session $BackupSession -ScriptBlock { - Import-Module DataProtectionManager - - # Get a file system datasource - $DS = Get-DPMDatasource | Where-Object { $_.Type -eq "FileSystem" } | Select-Object -First 1 - - if ($DS) { - $RP = Get-DPMRecoveryPoint -Datasource $DS | Select-Object -Last 1 - - if ($RP) { - # Browse recovery point - $Items = Get-DPMRecoverableItem -RecoveryPoint $RP - - [PSCustomObject]@{ - DataSource = $DS.Name - RecoveryPoint = $RP.BackupTime - ItemCount = $Items.Count - Status = "Browsable" - } - } else { - [PSCustomObject]@{ - DataSource = $DS.Name - RecoveryPoint = "None" - ItemCount = 0 - Status = "NoRecoveryPoints" - } - } - } else { - [PSCustomObject]@{ - DataSource = "None" - RecoveryPoint = "N/A" - ItemCount = 0 - Status = "NoFileSystemDatasources" - } - } +if (-not (Test-Path $SourceFile)) { + Add-Content $ReportFile ("File restore check: Source file not found: {0}" -f $SourceFile) } +elseif (-not (Test-Path $RestoredFile)) { + Add-Content $ReportFile ("File restore check: Restored file not found: {0}" -f $RestoredFile) +} +else { + $sourceHash = Get-FileHash -Path $SourceFile + $restoredHash = Get-FileHash -Path $RestoredFile + $result = if ($sourceHash.Hash -eq $restoredHash.Hash) { 'Pass' } else { 'Fail' } -" Data Source: $($FileRestoreResult.DataSource)" | Add-Content $ReportFile -" Recovery Point: $($FileRestoreResult.RecoveryPoint)" | Add-Content $ReportFile -" Items Available: $($FileRestoreResult.ItemCount)" | Add-Content $ReportFile -" Status: $($FileRestoreResult.Status)" | Add-Content $ReportFile - -Remove-PSSession $BackupSession + Add-Content $ReportFile ("File restore check: Source={0}; Restored={1}; Match={2}; Result={3}" -f $SourceFile, $RestoredFile, ($sourceHash.Hash -eq $restoredHash.Hash), $result) +} ``` --- ## Part 5: Recovery Point Verification -### 5.1 Check Recovery Point Inventory - -```powershell -"`n" + "="*80 | Add-Content $ReportFile -"RECOVERY POINT INVENTORY" | Add-Content $ReportFile -"="*80 | Add-Content $ReportFile +### 5.1 Check the recovery point inventory -$BackupSession = New-PSSession -ComputerName $BackupServer +For three sample VMs, record the number and age of recovery points shown in the chosen backup solution's console. Enter the newest recovery point time for each VM in `$RecoveryPointTimes`. The check compares each entered time with `$RpoHours`. -$RPInventory = Invoke-Command -Session $BackupSession -ScriptBlock { - Import-Module DataProtectionManager - - $AllDataSources = Get-DPMDatasource - - $AllDataSources | ForEach-Object { - $DS = $_ - $RPs = Get-DPMRecoveryPoint -Datasource $DS - - [PSCustomObject]@{ - DataSource = $DS.Name - Type = $DS.Type - TotalRecoveryPoints = $RPs.Count - OldestRP = if ($RPs) { ($RPs | Sort-Object BackupTime | Select-Object -First 1).BackupTime } else { "None" } - NewestRP = if ($RPs) { ($RPs | Sort-Object BackupTime -Descending | Select-Object -First 1).BackupTime } else { "None" } - RetentionDays = if ($RPs.Count -gt 1) { - (($RPs | Sort-Object BackupTime -Descending | Select-Object -First 1).BackupTime - - ($RPs | Sort-Object BackupTime | Select-Object -First 1).BackupTime).Days - } else { 0 } - } - } +```powershell +$RpoHours = 24 # set to the agreed RPO in hours +$RecoveryPointTimes = @{ + # 'VM name' = [datetime]'YYYY-MM-DD HH:MM:SS' } -"`nRecovery Point Summary:" | Add-Content $ReportFile -$RPInventory | Format-Table DataSource, Type, TotalRecoveryPoints, OldestRP, NewestRP, RetentionDays -AutoSize | Out-String | Add-Content $ReportFile - -Remove-PSSession $BackupSession +if ($RecoveryPointTimes.Count -eq 0) { + Add-Content $ReportFile 'Recovery point check: No recovery point times entered.' +} +else { + foreach ($vmName in $RecoveryPointTimes.Keys) { + $recoveryPointTime = [datetime]$RecoveryPointTimes[$vmName] + $ageHours = ((Get-Date) - $recoveryPointTime).TotalHours + $result = if ($ageHours -le $RpoHours) { 'Within RPO' } else { 'Outside RPO' } + + Add-Content $ReportFile ("Recovery point check: VM={0}; NewestRecoveryPoint={1}; AgeHours={2}; RpoHours={3}; Result={4}" -f $vmName, $recoveryPointTime, ([math]::Round($ageHours, 2)), $RpoHours, $result) + } +} ``` --- + ## Part 6: RPO/RTO Documentation ### 6.1 Calculate Actual RPO @@ -523,7 +376,7 @@ RECOVERY TIME OBJECTIVE (RTO): | Recovery Type | Measured RTO | Target RTO | Status | |--------------------------|------------------|------------|--------| -| Single VM Restore | $($RestoreResult.Duration) | < 2 hours | $(if($RestoreResult.Status -eq "Succeeded"){"PASS"}else{"VERIFY"}) | +| Single VM Restore | Record from 4.1 | < 2 hours | VERIFY | | File/Folder Restore | < 15 minutes | < 30 min | PASS | | Full Cluster Recovery | 4-8 hours | < 8 hours | N/A | | Azure Site Recovery (DR) | < 2 hours | < 4 hours | N/A | @@ -601,25 +454,18 @@ $Summary = @" BACKUP & DR VALIDATION SUMMARY ================================================================================ -Azure Backup CONFIGURATION: - Agent Status: All nodes - VERIFY - Protection Groups: $($ProtectionGroups.Count) configured - VSS Writers: Check report for failures +BACKUP CONFIGURATION: + Backup solution: $BackupSolution + Agent or service status, protection coverage and VSS writers: see Part 2 entries in this report BACKUP VALIDATION: - Recent Job Success Rate: $([math]::Round(($SuccessCount / [math]::Max($TotalJobs, 1)) * 100, 1))% - On-Demand Backup: $($BackupResult.Status) - Backup Duration: $($BackupResult.Duration) + Recent job success rate and on-demand backup result: see Part 3 entries in this report RESTORE VALIDATION: - VM Restore Test: $($RestoreResult.Status) - Restore Duration (RTO): $($RestoreResult.Duration) - File-Level Restore: $($FileRestoreResult.Status) + VM restore, restored VM check and file-level restore: see Part 4 entries in this report RECOVERY POINTS: - Total Data Sources: $($RPInventory.Count) - Sources with RPs: $(($RPInventory | Where-Object { $_.TotalRecoveryPoints -gt 0 }).Count) - + Newest recovery point against the RPO: see Part 5 entries in this report DISASTER RECOVERY: Azure Site Recovery: $(if($RecoveryVault){"Configured"}else{"Not Configured"}) @@ -645,9 +491,9 @@ Write-Host $Summary | Category | Requirement | Status | |----------|-------------|--------| -| **Azure Backup** | Agent running on all nodes | ☐ | -| **Azure Backup** | Protection groups configured | ☐ | -| **Azure Backup** | VSS writers healthy | ☐ | +| **Backup** | Agent or service running on all nodes | ☐ | +| **Backup** | All production VMs protected | ☐ | +| **Backup** | VSS writers healthy | ☐ | | **Backup** | Job success rate ≥ 95% | ☐ | | **Backup** | On-demand backup succeeds | ☐ | | **Restore** | VM restore test passes | ☐ | diff --git a/docs/planning/01-naming-standards.mdx b/docs/planning/01-naming-standards.mdx index 5ebcf27be..11f2b7d28 100644 --- a/docs/planning/01-naming-standards.mdx +++ b/docs/planning/01-naming-standards.mdx @@ -960,7 +960,7 @@ The following table shows how Azure resource names map to Windows hostnames for | Certificate Authority | `vm-azlocal-prod-eus-ca-01` | `nyc-ca-01` | 9 | | Windows Admin Center | `vm-azlocal-prod-eus-wac-01` | `nyc-wac-01` | 10 | | Jumpbox / Tools | `vm-azlocal-prod-eus-jmp-01` | `nyc-jmp-01` | 10 | -| backup server | `vm-azlocal-prod-eus-Azure Backup-01` | `nyc-bkup-01` | 11 | +| Backup solution VM (if one is used) | `vm-azlocal-prod-eus-backup-01` | `nyc-bkup-01` | 11 | | Utility Server | `vm-azlocal-prod-eus-util-01` | `nyc-util-01` | 11 | | NDM Server | `vm-azlocal-prod-eus-ndm-01` | `nyc-ndm-01` | 10 | diff --git a/docs/planning/02-discovery-checklist.mdx b/docs/planning/02-discovery-checklist.mdx index 1591088ad..b4e89b2d6 100644 --- a/docs/planning/02-discovery-checklist.mdx +++ b/docs/planning/02-discovery-checklist.mdx @@ -57,6 +57,23 @@ This section covers discovery activities performed **once per deployment program - Conditional access policies that may block service principal authentication: ☐ Yes ☐ No ☐ Unknown - Policy names affecting automation: `________________________________` +#### Identity Model for the Cluster + +Decide the identity model before the cluster is deployed. It is chosen during deployment, and the planning and design inputs differ. + +- [ ] **Identity model** + - ☐ Active Directory (domain-joined machines; Active Directory preparation and DNS are prerequisites) + - ☐ Local identity with Azure Key Vault (previously called AD-less; no Active Directory dependency) + - Reason for the choice: `________________________________` +- [ ] **If local identity with Azure Key Vault** + - Key Vault in the landing zone subscription, with public network access enabled so the instance can reach it: ☐ Yes ☐ No + - DNS: ☐ Existing enterprise DNS ☐ Internal DNS (preview; for cluster operations only, not for VMs or applications) + - Management tools agreed with the operations team: Azure portal, PowerShell and Azure Monitor are supported. **Windows Admin Center is not supported** in this model; Hyper-V Manager and Failover Cluster Manager may not work in all scenarios, and System Center Virtual Machine Manager has limited or no support. ☐ Accepted + - Local administrator management: the PowerShell-based local identity management module with certificate-based authentication and Azure Key Vault ☐ Planned +- [ ] **If Active Directory** + - Complete Section 8 (Active Directory Planning) before deployment. + +**Reference:** [Microsoft Learn - Local identity with Azure Key Vault overview](https://learn.microsoft.com/en-us/azure/azure-local/deploy/deployment-local-identity-with-key-vault-overview) ### Section 2: Source Control and CI/CD Requirements **Reference:** [CI/CD Infrastructure Setup](../implementation/01-cicd-infra/)