Skip to content

Commit 2b4cd29

Browse files
docs: update CLAUDE.md, README, and all docs for two-part repo
Documents both the standalone scripts and the AzureLocalVMHydration PowerShell module that was added in the previous commit. Changes: - CLAUDE.md: add module file tree, export list, and module-specific notes - README.md: add PSGallery badge + module install/usage section; fix script parameter examples (SubnetId, StoragePathId); add module cmdlet examples - docs/index.md: surface both module cmdlets and standalone scripts - docs/getting-started.md: add module install path; fix script examples - docs/module.md: new — full reference for all three exported cmdlets with parameter tables and examples - docs/roadmap.md: move PSGallery module item to Completed - docs/contributing.md: fix broken GitHub link (hydration → vm-hydration) - mkdocs.yml: add Module Reference to nav; fix social GitHub link Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9fafb7c commit 2b4cd29

8 files changed

Lines changed: 376 additions & 70 deletions

File tree

CLAUDE.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,36 @@ Two operations are covered:
1111
- **VM Hydration** — onboarding an existing unmanaged Hyper-V VM *in place* into Azure Local management using `az stack-hci-vm disk create-from-local`
1212
- **VM Reconnect** — restoring a VM to a *different* Azure Local cluster and re-projecting it into Azure using `az stack-hci-vm reconnect-to-azure`
1313

14+
This is a **two-part repo**: standalone scripts that can be run directly on a cluster node, AND a PSGallery-publishable PowerShell module. Both parts implement the same logic; the module wraps everything in proper cmdlets for `Install-Module` workflows.
15+
16+
## PowerShell Module
17+
18+
```text
19+
AzureLocalVMHydration.psm1 # Root module — dot-sources Private + Public
20+
AzureLocalVMHydration.psd1 # Manifest (v0.1.0, GUID 83e5c34f, PS 7.0+, PSGallery-ready)
21+
Modules/
22+
├── Private/
23+
│ ├── Common-Functions.ps1 # Write-Step/OK/Warn/Fail, Invoke-AzCli, Invoke-ArmRestApi,
24+
│ │ # Assert-AdminElevation
25+
│ └── Test-HydrationPrerequisites.ps1 # Internal 10-check pre-flight (returns List[string])
26+
└── Public/
27+
├── Invoke-VMHydration.ps1 # Exported: hydrate unmanaged VM in-place (Gen1 + Gen2)
28+
├── Invoke-VMReconnect.ps1 # Exported: reconnect VM after cross-cluster restore
29+
└── Test-VMHydrationPrerequisites.ps1 # Exported: bool-returning pre-flight wrapper
30+
```
31+
32+
Exported functions: `Invoke-VMHydration`, `Invoke-VMReconnect`, `Test-VMHydrationPrerequisites`
33+
34+
Module uses `Assert-AdminElevation` (throws if not elevated) instead of `#Requires -RunAsAdministrator`.
35+
Module uses `throw` instead of `exit 1` — appropriate for module error handling.
36+
Both cmdlets support `-WhatIf` (`[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]`).
37+
1438
## Scripts
1539

1640
```text
1741
scripts/
1842
├── helpers/
19-
│ ├── Common-Functions.ps1 # Write-Step/OK/Warn/Fail, Invoke-AzCli, Invoke-ArmRestApi
43+
│ ├── Common-Functions.ps1 # Shared logging and Azure CLI wrappers
2044
│ └── Test-HydrationPrerequisites.ps1 # Pre-flight checks (dot-sourced by both main scripts)
2145
├── Invoke-VMHydration.ps1 # Hydrate unmanaged VM in-place (Gen1 + Gen2)
2246
└── Invoke-VMReconnect.ps1 # Reconnect VM after cross-cluster restore

README.md

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
![Azure Local VM Hydration — Revive. Reconnect. Reclaim.](docs/assets/images/azurelocal-vm-hydration-banner.svg)
44

55
[![Azure Local](https://img.shields.io/badge/Azure%20Local-azurelocal.cloud-0078D4?logo=microsoft-azure)](https://azurelocal.cloud)
6+
[![PSGallery](https://img.shields.io/badge/PSGallery-AzureLocalVMHydration-3b82f6?logo=powershell)](https://www.powershellgallery.com/packages/AzureLocalVMHydration)
67
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7-
[![Docs: MkDocs Material](https://img.shields.io/badge/docs-MkDocs%20Material-0f766e)](docs/index.md)
8+
[![Docs: MkDocs Material](https://img.shields.io/badge/docs-MkDocs%20Material-0f766e)](https://azurelocal.github.io/azurelocal-vm-hydration/)
89
[![PowerShell: 7.x](https://img.shields.io/badge/PowerShell-7.x-3b82f6)](https://github.com/PowerShell/PowerShell)
910

10-
Documentation: [azurelocal.cloud](https://azurelocal.cloud) | Solutions: [Azure Local Solutions](https://azurelocal.cloud)
11+
Documentation: [azurelocal.github.io/azurelocal-vm-hydration](https://azurelocal.github.io/azurelocal-vm-hydration/) | Solutions: [azurelocal.cloud](https://azurelocal.cloud)
1112

1213
> *Revive. Reconnect. Reclaim.*
1314
@@ -27,47 +28,73 @@ Covers two scenarios:
2728
## Prerequisites
2829

2930
- Azure Local cluster running **2602 or later**
30-
- `az stack-hci-vm` extension **≥ 1.11.9** (`az extension show --name stack-hci-vm --query version`)
31-
- VMs must reside in a storage path GUID subfolder (e.g., `C:\ClusterStorage\Volume1\<guid>\`)
31+
- `az stack-hci-vm` extension **≥ 1.11.9**
32+
- VMs must reside in a storage path **GUID subfolder** (e.g., `C:\ClusterStorage\Volume1\<guid>\`)
3233
- VMs must be configured as **Highly Available** in Failover Cluster Manager
33-
- **Hyper-V Data Exchange Service (KVP)** enabled inside the VM
34-
- Run scripts as **Administrator** on a cluster node
34+
- **Hyper-V Data Exchange Service (KVP)** and **Guest Service Interface** enabled on the VM
35+
- Run as **Administrator** on a cluster node
3536

3637
---
3738

38-
## Scripts
39+
## PowerShell Module (recommended)
3940

40-
```text
41-
scripts/
42-
├── helpers/
43-
│ ├── Common-Functions.ps1 # Shared logging and Azure CLI wrappers
44-
│ └── Test-HydrationPrerequisites.ps1 # Pre-flight checks (dot-sourced by both scripts)
45-
├── Invoke-VMHydration.ps1 # Hydrate an unmanaged VM in-place
46-
└── Invoke-VMReconnect.ps1 # Reconnect a VM after cross-cluster restore
41+
Install from PSGallery — works anywhere PowerShell 7 is available:
42+
43+
```powershell
44+
Install-Module AzureLocalVMHydration -Scope CurrentUser
4745
```
4846

49-
Both scripts support `-WhatIf` for dry runs.
47+
### VM Hydration
48+
49+
```powershell
50+
Invoke-VMHydration `
51+
-VMName 'WEBSRV01' `
52+
-ResourceGroup 'rg-azlocal-prod' `
53+
-CustomLocation '/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.ExtendedLocation/customLocations/<cl-name>' `
54+
-StoragePathId '/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.AzureStackHCI/storageContainers/<name>' `
55+
-NicName 'WEBSRV01-nic1' `
56+
-SubnetId 'lnet-prod-vlan10' `
57+
-Location 'eastus'
58+
```
5059

51-
### VM Hydration (in-place onboarding)
60+
### VM Reconnect
5261

5362
```powershell
54-
.\scripts\Invoke-VMHydration.ps1 `
55-
-VMName "MyVM" `
56-
-ResourceGroup "rg-azurelocal" `
57-
-CustomLocation "/subscriptions/.../resourceGroups/.../providers/Microsoft.ExtendedLocation/customLocations/cl-site1" `
58-
-LogicalNetwork "lnet-prod"
63+
Invoke-VMReconnect `
64+
-VMName 'APPSRV01' `
65+
-LocalVMName 'APPSRV01_restored' `
66+
-ResourceGroup 'rg-azlocal-prod' `
67+
-CustomLocation '/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.ExtendedLocation/customLocations/<dest-cl-name>' `
68+
-NicName 'APPSRV01-nic2' `
69+
-SubnetId 'lnet-prod-vlan10' `
70+
-Location 'eastus' `
71+
-RemoveSourceVM
5972
```
6073

61-
### VM Reconnect (cross-cluster restore)
74+
### Pre-flight Check Only
6275

6376
```powershell
64-
.\scripts\Invoke-VMReconnect.ps1 `
65-
-VMName "MyVM" `
66-
-ResourceGroup "rg-azurelocal" `
67-
-CustomLocation "/subscriptions/.../resourceGroups/.../providers/Microsoft.ExtendedLocation/customLocations/cl-site2" `
68-
-LogicalNetwork "lnet-prod"
77+
Test-VMHydrationPrerequisites -VMName 'WEBSRV01'
78+
# Returns $true if all checks pass, $false if any fail
79+
```
80+
81+
---
82+
83+
## Standalone Scripts
84+
85+
No install required — run directly on a cluster node:
86+
87+
```text
88+
scripts/
89+
├── helpers/
90+
│ ├── Common-Functions.ps1 # Shared logging and Azure CLI wrappers
91+
│ └── Test-HydrationPrerequisites.ps1 # Pre-flight checks (dot-sourced by both scripts)
92+
├── Invoke-VMHydration.ps1 # Hydrate an unmanaged VM in-place
93+
└── Invoke-VMReconnect.ps1 # Reconnect a VM after cross-cluster restore
6994
```
7095

96+
Both scripts support `-WhatIf` for dry runs. See [Getting Started](https://azurelocal.github.io/azurelocal-vm-hydration/getting-started/) for full parameter reference and examples.
97+
7198
---
7299

73100
## Configuration

docs/contributing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Contributing Guide
22

3-
See the root [CONTRIBUTING.md](https://github.com/AzureLocal/azurelocal-hydration/blob/main/CONTRIBUTING.md) for full contribution guidelines.
3+
See the root [CONTRIBUTING.md](https://github.com/AzureLocal/azurelocal-vm-hydration/blob/main/CONTRIBUTING.md) for full contribution guidelines.

docs/getting-started.md

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Prerequisites
44

5-
All operations require the following before running any script:
5+
All operations require the following before running any script or cmdlet:
66

77
### Azure Local Cluster
88

@@ -27,9 +27,9 @@ az extension show --name stack-hci-vm --query version
2727

2828
### VM Requirements
2929

30-
Before running either script, confirm the target VM:
30+
Before running either operation, confirm the target VM:
3131

32-
- Is in **Running** state in Hyper-V
32+
- Is in **Running** state in Hyper-V (required for reconnect; hydration can run on stopped VMs)
3333
- Is configured as a **Highly Available VM** in Failover Cluster Manager
3434
- Has the **Hyper-V Data Exchange Service (KVP)** integration service enabled
3535
- Has the **Hyper-V Guest Service Interface** integration service enabled
@@ -42,32 +42,54 @@ Before running either script, confirm the target VM:
4242
Get-VM -Name <VMName> | Select-Object Name, ConfigurationLocation
4343
```
4444

45+
Run a pre-flight check at any time:
46+
47+
```powershell
48+
# Module
49+
Test-VMHydrationPrerequisites -VMName 'WEBSRV01'
50+
51+
# Script
52+
.\scripts\Test-VMHydrationPrerequisites.ps1 -VMName 'WEBSRV01'
53+
```
54+
4555
---
4656

47-
## Which Script to Use
57+
## Which Operation to Use
4858

49-
| Scenario | Script |
50-
| --- | --- |
51-
| VM is on this cluster, never registered with Azure | `Invoke-VMHydration.ps1` |
52-
| VM was registered with Azure but restored to a different cluster | `Invoke-VMReconnect.ps1` |
59+
| Scenario | Module cmdlet | Script |
60+
| --- | --- | --- |
61+
| VM is on this cluster, never registered with Azure | `Invoke-VMHydration` | `scripts/Invoke-VMHydration.ps1` |
62+
| VM was registered with Azure but restored to a different cluster | `Invoke-VMReconnect` | `scripts/Invoke-VMReconnect.ps1` |
5363

5464
---
5565

56-
## Running Invoke-VMHydration.ps1
66+
## Using the PowerShell Module
67+
68+
Install once from PSGallery:
5769

58-
Hydrates an unmanaged Hyper-V VM into Azure Local management in-place.
70+
```powershell
71+
Install-Module AzureLocalVMHydration -Scope CurrentUser
72+
```
5973

60-
Run on a cluster node (directly or via remote PowerShell):
74+
See [Module Reference](module.md) for complete parameter documentation and examples for all three exported cmdlets.
75+
76+
---
77+
78+
## Using the Standalone Scripts
79+
80+
Run directly on a cluster node — no install required.
81+
82+
### VM Hydration
6183

6284
```powershell
6385
.\scripts\Invoke-VMHydration.ps1 `
64-
-VMName 'WEBSRV01' `
86+
-VMName 'WEBSRV01' `
6587
-ResourceGroup 'rg-azlocal-prod' `
66-
-CustomLocation '/subscriptions/<sub>/resourcegroups/<rg>/providers/microsoft.extendedlocation/customlocations/<cl-name>' `
67-
-StoragePathId '/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.AzureStackHCI/storageContainers/<name>' `
68-
-NicName 'WEBSRV01-nic1' `
69-
-SubnetId 'lnet-prod-vlan10' `
70-
-Location 'eastus'
88+
-CustomLocation '/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.ExtendedLocation/customLocations/<cl-name>' `
89+
-StoragePathId '/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.AzureStackHCI/storageContainers/<name>' `
90+
-NicName 'WEBSRV01-nic1' `
91+
-SubnetId 'lnet-prod-vlan10' `
92+
-Location 'eastus'
7193
```
7294

7395
**Dry run first:**
@@ -76,42 +98,43 @@ Run on a cluster node (directly or via remote PowerShell):
7698
.\scripts\Invoke-VMHydration.ps1 -VMName 'WEBSRV01' ... -WhatIf
7799
```
78100

79-
### Gen1 VMs
101+
#### Gen1 VMs
80102

81103
Add `-HyperVGeneration V1`. This uses the ARM REST API directly (the Azure CLI does not expose `hyperVGeneration`), and automatically disables vTPM and Secure Boot which are incompatible with Gen1:
82104

83105
```powershell
84-
.\scripts\Invoke-VMHydration.ps1 -VMName 'LEGACYAPP' -HyperVGeneration V1 ...
106+
.\scripts\Invoke-VMHydration.ps1 -VMName 'LEGACYAPP' -HyperVGeneration V1 `
107+
-ResourceGroup 'rg-azlocal-prod' `
108+
-CustomLocation '...' -StoragePathId '...' `
109+
-NicName 'LEGACYAPP-nic1' -SubnetId 'lnet-prod-vlan10' -Location 'eastus'
85110
```
86111

87112
---
88113

89-
## Running Invoke-VMReconnect.ps1
90-
91-
Reconnects a VM restored to a different Azure Local cluster back to its Azure resource.
114+
### VM Reconnect
92115

93116
Run on a node of the **destination** cluster:
94117

95118
```powershell
96119
.\scripts\Invoke-VMReconnect.ps1 `
97-
-VMName 'APPSRV01' `
98-
-LocalVMName 'APPSRV01_restored' `
120+
-VMName 'APPSRV01' `
121+
-LocalVMName 'APPSRV01_restored' `
99122
-ResourceGroup 'rg-azlocal-prod' `
100-
-CustomLocation '/subscriptions/<sub>/resourcegroups/<rg>/providers/microsoft.extendedlocation/customlocations/<dest-cl-name>' `
101-
-NicName 'APPSRV01-nic2' `
102-
-SubnetId 'lnet-prod-vlan10' `
103-
-Location 'eastus' `
123+
-CustomLocation '/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.ExtendedLocation/customLocations/<dest-cl-name>' `
124+
-NicName 'APPSRV01-nic2' `
125+
-SubnetId 'lnet-prod-vlan10' `
126+
-Location 'eastus' `
104127
-DataDiskLocalPaths @('C:\ClusterStorage\Volume1\<guid>\APPSRV01\data1.vhdx') `
105-
-DataDiskNames @('APPSRV01-data1') `
128+
-DataDiskNames @('APPSRV01-data1') `
106129
-RemoveSourceVM
107130
```
108131

109132
!!! danger "If Reconnect Fails"
110133
**Do NOT delete the VM resource from the Azure portal or CLI.**
111134
A VM resource may be created in a failed state. Deleting it can destroy the original VM.
112-
Fix the root cause, then re-run `Invoke-VMReconnect.ps1` to repair it.
135+
Fix the root cause, then re-run `Invoke-VMReconnect` to repair it.
113136

114-
### After Reconnect — NIC IP Configuration
137+
#### After Reconnect — NIC IP Configuration
115138

116139
- **SDN-enabled clusters:** The guest OS IP is configured automatically.
117140
- **Non-SDN clusters:** Manually configure the IP inside the guest OS via RDP or VM Connect:

docs/index.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,34 @@ Takes an existing, unmanaged Hyper-V VM running on an Azure Local cluster and br
1616

1717
The VM becomes a `Microsoft.AzureStackHCI/virtualMachineInstances` resource, fully manageable from the Azure portal, with lifecycle operations (start/stop, disk attach, extensions, policy).
1818

19-
**Script:** `scripts/Invoke-VMHydration.ps1`
19+
- **Module cmdlet:** `Invoke-VMHydration`
20+
- **Standalone script:** `scripts/Invoke-VMHydration.ps1`
2021

2122
## VM Reconnect
2223

23-
Reconnects an Azure Local VM to its Azure resource after the VM has been restored to a *different* Azure Local cluster (e.g. via Veeam backup restore, export/import). The Azure resource becomes orphaned after such a restore; this script re-projects it onto the destination cluster.
24+
Reconnects an Azure Local VM to its Azure resource after the VM has been restored to a *different* Azure Local cluster (e.g. via Veeam backup restore, export/import). The Azure resource becomes orphaned after such a restore; this tooling re-projects it onto the destination cluster.
2425

25-
**Script:** `scripts/Invoke-VMReconnect.ps1`
26+
- **Module cmdlet:** `Invoke-VMReconnect`
27+
- **Standalone script:** `scripts/Invoke-VMReconnect.ps1`
28+
29+
---
30+
31+
## Two Ways to Use This Repo
32+
33+
### PowerShell Module (recommended)
34+
35+
Install from PSGallery — works anywhere PowerShell 7 is available:
36+
37+
```powershell
38+
Install-Module AzureLocalVMHydration -Scope CurrentUser
39+
```
40+
41+
Full module reference, parameters, and examples: [Module Reference](module.md)
42+
43+
### Standalone Scripts
44+
45+
No install required — dot-source helpers and run directly on a cluster node.
46+
Full usage: [Getting Started](getting-started.md)
2647

2748
---
2849

@@ -37,6 +58,7 @@ The term comes from Microsoft's own naming of the underlying CLI command: `az st
3758
## Related
3859

3960
- [Getting Started](getting-started.md)
61+
- [Module Reference](module.md)
4062
- [Roadmap](roadmap.md)
4163
- [AzureLocal Solutions](https://azurelocal.cloud)
4264
- [GitHub Repository](https://github.com/AzureLocal/azurelocal-vm-hydration)

0 commit comments

Comments
 (0)