diff --git a/HandsOnLabs/01-CreatingFirstLab/media/explorer01.png b/HandsOnLabs/01-CreatingFirstLab/media/explorer01.png
new file mode 100644
index 00000000..13d39a9f
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/explorer01.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/explorer02.png b/HandsOnLabs/01-CreatingFirstLab/media/explorer02.png
new file mode 100644
index 00000000..88beccba
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/explorer02.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/explorer03.png b/HandsOnLabs/01-CreatingFirstLab/media/explorer03.png
new file mode 100644
index 00000000..49677d45
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/explorer03.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/explorer04.png b/HandsOnLabs/01-CreatingFirstLab/media/explorer04.png
new file mode 100644
index 00000000..a754598a
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/explorer04.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/explorer05.png b/HandsOnLabs/01-CreatingFirstLab/media/explorer05.png
new file mode 100644
index 00000000..e2f3149f
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/explorer05.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/explorer06.png b/HandsOnLabs/01-CreatingFirstLab/media/explorer06.png
new file mode 100644
index 00000000..eb15c5e9
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/explorer06.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/explorer07.png b/HandsOnLabs/01-CreatingFirstLab/media/explorer07.png
new file mode 100644
index 00000000..f894a09a
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/explorer07.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/hvmanager01.png b/HandsOnLabs/01-CreatingFirstLab/media/hvmanager01.png
new file mode 100644
index 00000000..4a7d6138
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/hvmanager01.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/notepad01.png b/HandsOnLabs/01-CreatingFirstLab/media/notepad01.png
new file mode 100644
index 00000000..57f7ece6
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/notepad01.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/powershell01.png b/HandsOnLabs/01-CreatingFirstLab/media/powershell01.png
new file mode 100644
index 00000000..f23acc6c
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/powershell01.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/powershell02.png b/HandsOnLabs/01-CreatingFirstLab/media/powershell02.png
new file mode 100644
index 00000000..a7dfcc9e
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/powershell02.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/powershell03.png b/HandsOnLabs/01-CreatingFirstLab/media/powershell03.png
new file mode 100644
index 00000000..0d7b4cc8
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/powershell03.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/powershell04.png b/HandsOnLabs/01-CreatingFirstLab/media/powershell04.png
new file mode 100644
index 00000000..745f746d
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/powershell04.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/powershell05.png b/HandsOnLabs/01-CreatingFirstLab/media/powershell05.png
new file mode 100644
index 00000000..a448b90c
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/powershell05.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/powershell06.png b/HandsOnLabs/01-CreatingFirstLab/media/powershell06.png
new file mode 100644
index 00000000..d93bf3c7
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/powershell06.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/powershell07.png b/HandsOnLabs/01-CreatingFirstLab/media/powershell07.png
new file mode 100644
index 00000000..8d05515d
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/powershell07.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/media/powershell08.png b/HandsOnLabs/01-CreatingFirstLab/media/powershell08.png
new file mode 100644
index 00000000..150d975d
Binary files /dev/null and b/HandsOnLabs/01-CreatingFirstLab/media/powershell08.png differ
diff --git a/HandsOnLabs/01-CreatingFirstLab/readme.md b/HandsOnLabs/01-CreatingFirstLab/readme.md
new file mode 100644
index 00000000..54bd705e
--- /dev/null
+++ b/HandsOnLabs/01-CreatingFirstLab/readme.md
@@ -0,0 +1,198 @@
+# Creating first Lab
+
+
+
+- [Creating first Lab](#creating-first-lab)
+ - [Task 01: Prepare files](#task-01-prepare-files)
+ - [Step 01: Download MSLab files](#step-01-download-mslab-files)
+ - [Step 02: Unblock files and set execution policy](#step-02-unblock-files-and-set-execution-policy)
+ - [Task 02: Download Required files](#task-02-download-required-files)
+ - [Task 03: Build the lab Prerequisites](#task-03-build-the-lab-prerequisites)
+ - [Step 01: Run first MSLab script](#step-01-run-first-mslab-script)
+ - [Step 02: Create Parent Disks - second MSLab script](#step-02-create-parent-disks---second-mslab-script)
+ - [Step 03: Create Azure Local Parent Disk](#step-03-create-azure-local-parent-disk)
+ - [Task 03 - Deploy Azure Local Lab](#task-03---deploy-azure-local-lab)
+ - [Step 01: Modify LabConfig.ps1](#step-01-modify-labconfigps1)
+ - [Step 02: Deploy](#step-02-deploy)
+ - [Step 03: Cleanup Optional](#step-03-cleanup-optional)
+ - [Tips & Tricks](#tips--tricks)
+ - [Add "Default" virtual switch in Windows Server 2025](#add-default-virtual-switch-in-windows-server-2025)
+
+
+
+
+## Task 01: Prepare files
+
+### Step 01: Download MSLab files
+
+Note: MSLab files are available at https://aka.ms/mslab/download
+
+Note: Since we want you to learn PowerShell a bit, steps - if possible - will be in PowerShell.
+
+```PowerShell
+#download mslab to Downloads
+Invoke-WebRequest -Uri https://aka.ms/mslab/download -OutFile $env:UserProfile\Downloads\mslab.zip
+
+#extract to c:\MSLab
+Expand-Archive -Path $env:UserProfile\Downloads\mslab.zip -DestinationPath c:\MSLab\
+
+```
+
+### Step 02: Unblock files and set execution policy
+
+Note: Since PowerShell scripts are no longer signed (you can push @vladimir machv@microsoft.com to request that internally again, and yes, it's a lot of "paperwork"), files needs to be ublocked and execution policy configured
+
+```PowerShell
+$MSLabPath="C:\MSLab\"
+
+#grab extracted files and unblock
+Get-ChildItem -Path $MSLabPath | Unblock-File
+
+#set execution policy to remote signed
+Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
+
+```
+
+## Task 02: Download Required files
+
+Note: Personally I prefer MSDN images (or VL images if you have it available), but [eval images](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2025) are also fine. Why downloading ISO and not VHD? Because ISO contains all 4 versions (core/GUI,Standard and Datacenter) and VHD is only one. Plus VHD is sized 30GB (last time I checked). So it's more convenient to create image using MSLab scripts
+
+Note: Start-BitsTransfer is used because invoke-webrequest is slow for big files (unless you configure different progresspreference)
+
+```PowerShell
+#ISO
+$WindowsServerEvalURL="https://software-static.download.prss.microsoft.com/dbazure/888969d5-f34g-4e03-ac9d-1f9786c66749/26100.1742.240906-0331.ge_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso"
+#you should download latest image from Azure Portal, this is just an example
+$AzureLocal2509URL="http://aka.ms/hcireleaseimage/2509"
+
+#download
+Start-BitsTransfer -Source $WindowsServerEvalURL -Destination $env:userprofile\Downloads\
+Start-BitsTransfer -Source $AzureLocal2509URL -Destination $env:userprofile\Downloads\AzureLocal2509.iso
+
+```
+
+## Task 03: Build the lab Prerequisites
+
+In your MSLab folder you should be able to see following files
+
+
+
+and ISO files should be downloaded in downloads folder
+
+
+
+
+### Step 01: Run first MSLab script
+
+In your MSLab folder right-click at 1_Prereq.ps1 and run with PowerShell.
+
+
+
+Note: this script will create folders, scripts. Will download all additional files from internet.
+
+Tip: this is the only step that is needed to download files. After this step, you can distribute MSLab folder to offline environment and run 1_prereqs again.
+
+Expected output:
+
+
+
+
+
+
+### Step 02: Create Parent Disks - second MSLab script
+
+In your MSLab folder right-click at 2_CreateParentDisks.ps1 and run with PowerShell.
+
+
+
+The script will ask you about telemetry level and will ask you for Windows Server 2025 ISO. Once asked for MSU, hit cancel.
+
+
+
+
+
+Note: this script will create Windows Server 2025 VHDs (GUI and Core) and will create domain controller. Once all is created, VMs are removed and files cleaned
+
+Result:
+
+
+
+
+
+### Step 03: Create Azure Local Parent Disk
+
+In your MSLab folder navigate to ParentDisks folder. In ParentDisks folder right-click at CreateParentDisk.ps1 and select run with PowerShell
+
+
+
+Script will ask you for ISO, so point it to downloaded Azure Local ISO. Once asked for MSU, click cancel.
+
+
+
+Note: this way you can create any VHD (Windows Server 2016-2025, Windows 11...)
+
+You'll be asked for name and size, simply hit enter to accept default values.
+
+Result:
+
+
+
+## Task 03 - Deploy Azure Local Lab
+
+### Step 01: Modify LabConfig.ps1
+
+In your MSLab folder open LabConfig.ps1 and replace it with following text
+
+Note: following config will use folder name as prefix (MSLab in this case). It will create 2 Azure Local VMs with maximum CPUs available and each with 24GB RAM.
+
+```PowerShell
+$LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPassword='LS1setup!' ; DCEdition='4'; Internet=$true; AdditionalNetworksConfig=@(); VMs=@()}
+
+#Azure Local 24H2
+#labconfig will not domain join VMs
+1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName = "ALNode$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI24H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 1TB ; MemoryStartupBytes= 24GB; VMProcessorCount="MAX" ; vTPM=$true ; Unattend="NoDjoin" ; NestedVirt=$true }}
+
+#VM for Windows Admin Center (optional)
+#$LabConfig.VMs += @{ VMName = 'WACGW' ; ParentVHD = 'Win2025Core_G2.vhdx'; MGMTNICs=1}
+
+#Management machine (Windows Server 2025 GUI)
+$LabConfig.VMs += @{ VMName = 'Management' ; ParentVHD = 'Win2025_G2.vhdx'; MGMTNICs=1 ; AddToolsVHD=$True }
+
+```
+
+Result:
+
+
+
+### Step 02: Deploy
+
+In your MSLab folder right-click at Deploy.ps1 and run with PowerShell. In ~5 minutes you should have your lab
+
+Result:
+
+
+
+### Step 03: Cleanup (Optional)
+
+In your MSLab folder right-click at Cleanup.ps1 and run with PowerShell. In ~10 seconds your lab is gone and ready to deploy again.
+
+## Tips & Tricks
+
+### Add "Default" virtual switch in Windows Server 2025
+
+In Windows 11 there's already "Default virtual switch" that provides internet connectivity to your lab. This script will add it to Windows Server. It's useful if your network does not have DHCP, so you dont have to assign DC IP manually.
+
+```PowerShell
+#install Containers feature to be able manage HNS networks (needs reboot)
+Install-WindowsFeature -Name Containers
+
+#install HNS powershell module
+Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
+Install-Module -Name HNS -Force -AllowClobber
+
+#add default switch
+New-HnsDefaultSwitchNetwork
+```
+
+
+
\ No newline at end of file
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/cluadmin01.png b/HandsOnLabs/02-DeployingAzureLocal/media/cluadmin01.png
new file mode 100644
index 00000000..ce1f6874
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/cluadmin01.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/edge01.png b/HandsOnLabs/02-DeployingAzureLocal/media/edge01.png
new file mode 100644
index 00000000..be884c1b
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/edge01.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/edge02.png b/HandsOnLabs/02-DeployingAzureLocal/media/edge02.png
new file mode 100644
index 00000000..31dd86e7
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/edge02.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/edge03.png b/HandsOnLabs/02-DeployingAzureLocal/media/edge03.png
new file mode 100644
index 00000000..0048b00c
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/edge03.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/edge04.png b/HandsOnLabs/02-DeployingAzureLocal/media/edge04.png
new file mode 100644
index 00000000..b4525129
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/edge04.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/edge05.png b/HandsOnLabs/02-DeployingAzureLocal/media/edge05.png
new file mode 100644
index 00000000..ff19b6b9
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/edge05.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/hypervmanager01.png b/HandsOnLabs/02-DeployingAzureLocal/media/hypervmanager01.png
new file mode 100644
index 00000000..73294e75
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/hypervmanager01.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/hypervmanager02.png b/HandsOnLabs/02-DeployingAzureLocal/media/hypervmanager02.png
new file mode 100644
index 00000000..b2bdc74d
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/hypervmanager02.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/powershell01.png b/HandsOnLabs/02-DeployingAzureLocal/media/powershell01.png
new file mode 100644
index 00000000..4c54ab1e
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/powershell01.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/powershell02.png b/HandsOnLabs/02-DeployingAzureLocal/media/powershell02.png
new file mode 100644
index 00000000..b48bd209
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/powershell02.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/powershell03.png b/HandsOnLabs/02-DeployingAzureLocal/media/powershell03.png
new file mode 100644
index 00000000..e85541cc
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/powershell03.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/powershell04.png b/HandsOnLabs/02-DeployingAzureLocal/media/powershell04.png
new file mode 100644
index 00000000..51851a66
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/powershell04.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/media/powershell05.png b/HandsOnLabs/02-DeployingAzureLocal/media/powershell05.png
new file mode 100644
index 00000000..b0807106
Binary files /dev/null and b/HandsOnLabs/02-DeployingAzureLocal/media/powershell05.png differ
diff --git a/HandsOnLabs/02-DeployingAzureLocal/readme.md b/HandsOnLabs/02-DeployingAzureLocal/readme.md
new file mode 100644
index 00000000..0ea9019a
--- /dev/null
+++ b/HandsOnLabs/02-DeployingAzureLocal/readme.md
@@ -0,0 +1,414 @@
+# Deploying Azure Local
+
+
+
+- [Deploying Azure Local](#deploying-azure-local)
+ - [About the lab](#about-the-lab)
+ - [LabConfig](#labconfig)
+ - [NTP Prerequisite](#ntp-prerequisite)
+ - [The Lab](#the-lab)
+ - [Task 01 - Connect to servers](#task-01---connect-to-servers)
+ - [Step 01 Login to Management VM](#step-01-login-to-management-vm)
+ - [Step 02 Connect to Servers using PowerShell Remoting](#step-02-connect-to-servers-using-powershell-remoting)
+ - [Task 02 - Validate environment using Environment Checker tool](#task-02---validate-environment-using-environment-checker-tool)
+ - [Task 03 - Create AD Prerequisites](#task-03---create-ad-prerequisites)
+ - [Task 04 - Create Azure Resources](#task-04---create-azure-resources)
+ - [Task 05 - Register with Arc](#task-05---register-with-arc)
+ - [Task 06 - Validation prerequisites](#task-06---validation-prerequisites)
+ - [Task 07 - Deploy via Azure Portal](#task-07---deploy-via-azure-portal)
+ - [Result](#result)
+
+
+
+## About the lab
+
+In this lab you will deploy 2 node Azure Local into virtual environment using [cloud deployment](https://learn.microsoft.com/en-us/azure-stack/hci/whats-new#cloud-based-deployment).
+
+To setup lab, follow [01-Creating First Lab](../../HandsOnLabs/01-CreatingFirstLab/readme.md)
+
+Lab consumes ~50GB RAM. You can modify number of VMs and get down to ~40GB. Deployment works with just 16GB RAM per node
+
+## LabConfig
+
+```PowerShell
+$LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPassword='LS1setup!' ; DCEdition='4'; Internet=$true; AdditionalNetworksConfig=@(); VMs=@()}
+
+#Azure Local 24H2
+#labconfig will not domain join VMs
+1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName = "ALNode$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI24H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 1TB ; MemoryStartupBytes= 20GB; VMProcessorCount="MAX" ; vTPM=$true ; Unattend="NoDjoin" ; NestedVirt=$true }}
+
+#VM for Windows Admin Center (optional)
+#$LabConfig.VMs += @{ VMName = 'WACGW' ; ParentVHD = 'Win2025Core_G2.vhdx'; MGMTNICs=1}
+
+#Management machine (Windows Server 2025 GUI)
+$LabConfig.VMs += @{ VMName = 'Management' ; ParentVHD = 'Win2025_G2.vhdx'; MGMTNICs=1 ; AddToolsVHD=$True }
+
+```
+
+## NTP Prerequisite
+
+To successfully configure NTP server it's necessary to disable time synchronization from Hyper-V host.
+
+Run following code **from hyper-v host** to disable time sync
+
+```PowerShell
+Get-VM *ALNode* | Disable-VMIntegrationService -Name "Time Synchronization"
+
+```
+
+## The Lab
+
+### Task 01 - Connect to servers
+
+#### Step 01 Login to Management VM
+
+Provide following credentials:
+
+* Username: LabAdmin
+* Password: LS1setup!
+
+
+
+
+#### Step 02 Connect to Servers using PowerShell Remoting
+
+In Management machine run following PowerShell command to configure Trusted Hosts and invoke command remotely
+
+```PowerShell
+$Servers="ALNode1","ALNode2"
+$UserName="Administrator"
+$Password="LS1setup!"
+$SecuredPassword = ConvertTo-SecureString $password -AsPlainText -Force
+$Credentials= New-Object System.Management.Automation.PSCredential ($UserName,$SecuredPassword)
+
+#configure trusted hosts to be able to communicate with servers
+$TrustedHosts=@()
+$TrustedHosts+=$Servers
+Set-Item WSMan:\localhost\Client\TrustedHosts -Value $($TrustedHosts -join ',') -Force
+
+#Send some command to servers
+Invoke-Command -ComputerName $Servers -ScriptBlock {
+ Get-NetAdapter
+} -Credential $Credentials
+
+```
+
+
+
+### Task 02 - Validate environment using Environment Checker tool
+
+* https://learn.microsoft.com/en-in/azure/azure-local/manage/use-environment-checker?tabs=connectivity
+
+
+```PowerShell
+#Install modules
+<# No longer needed
+Invoke-Command -ComputerName $Servers -Scriptblock {
+ Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
+ Install-Module PowerShellGet -AllowClobber -Force
+ Install-Module -Name AzStackHci.EnvironmentChecker -Force
+} -Credential $Credentials
+#>
+
+#validate environment
+$result=Invoke-Command -ComputerName $Servers -Scriptblock {
+ Invoke-AzStackHciConnectivityValidation -PassThru
+} -Credential $Credentials
+$result | Out-GridView
+
+```
+
+
+
+### Task 03 - Create AD Prerequisites
+
+* https://learn.microsoft.com/en-us/azure/azure-local/deploy/deployment-prep-active-directory
+
+```PowerShell
+$AsHCIOUName="OU=ALClus01,DC=Corp,DC=contoso,DC=com"
+$LCMUserName="ALClus01-LCMUser"
+$LCMPassword="LS1setup!LS1setup!"
+#Create LCM credentials
+$SecuredPassword = ConvertTo-SecureString $LCMPassword -AsPlainText -Force
+$LCMCredentials= New-Object System.Management.Automation.PSCredential ($LCMUserName,$SecuredPassword)
+
+#create objects in Active Directory
+ #install posh module for prestaging Active Directory
+ Install-PackageProvider -Name NuGet -Force
+ Install-Module AsHciADArtifactsPreCreationTool -Repository PSGallery -Force
+
+ #make sure active directory module and GPMC is installed
+ Install-WindowsFeature -Name RSAT-AD-PowerShell,GPMC
+
+ #populate objects
+ New-HciAdObjectsPreCreation -AzureStackLCMUserCredential $LCMCredentials -AsHciOUName $AsHCIOUName
+
+ #to check OU (and future cluster) in GUI install management tools
+ Install-WindowsFeature -Name "RSAT-ADDS","RSAT-Clustering"
+
+```
+
+
+
+### Task 04 - Create Azure Resources
+
+As you can see, there's an optional code block that can create Arc Gateway. Simply uncomment if you want to deploy Azure Local with Arc Gw.
+
+```PowerShell
+$GatewayName="ALClus01-ArcGW"
+$ResourceGroupName="ALClus01-RG"
+$Location="eastus"
+
+#login to azure
+ #download Azure module
+ Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
+ if (!(Get-InstalledModule -Name az.accounts -ErrorAction Ignore)){
+ Install-Module -Name Az.Accounts -Force
+ }
+ #login using device authentication
+ Connect-AzAccount -UseDeviceAuthentication
+
+ #assuming new az.accounts module was used and it asked you what subscription to use - then correct subscription is selected for context
+ $Subscription=(Get-AzContext).Subscription
+
+ #install az resources module
+ if (!(Get-InstalledModule -Name az.resources -ErrorAction Ignore)){
+ Install-Module -Name az.resources -Force
+ }
+
+ #create resource group
+ if (-not(Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction Ignore)){
+ New-AzResourceGroup -Name $ResourceGroupName -Location $location
+ }
+#region (Optional) configure Arc Gateway
+<#
+ #install az.arcgateway module
+ if (!(Get-InstalledModule -Name az.arcgateway -ErrorAction Ignore)){
+ Install-Module -Name az.arcgateway -Force
+ }
+ #make sure "Microsoft.HybridCompute" is registered (and possibly other RPs)
+ Register-AzResourceProvider -ProviderNamespace "Microsoft.HybridCompute"
+ Register-AzResourceProvider -ProviderNamespace "Microsoft.GuestConfiguration"
+ Register-AzResourceProvider -ProviderNamespace "Microsoft.HybridConnectivity"
+ Register-AzResourceProvider -ProviderNamespace "Microsoft.AzureStackHCI"
+
+ #create GW
+ if (Get-AzArcGateway -Name $gatewayname -ResourceGroupName $ResourceGroupName -ErrorAction Ignore){
+ $ArcGWInfo=Get-AzArcGateway -Name $gatewayname -ResourceGroupName $ResourceGroupName
+ }else{
+ $ArcGWInfo=New-AzArcGateway -Name $GatewayName -ResourceGroupName $ResourceGroupName -Location $Location -SubscriptionID $Subscription.ID
+ }
+#>
+#endregion
+
+#generate variables for use in this window
+$SubscriptionID=$Subscription.ID
+$Region=$Location
+$TenantID=$Subscription.TenantID
+$ArcGatewayID=$ArcGWInfo.ID
+
+#output variables (so you can just copy it and have powershell code to create variables in another session or you can copy it to WebUI deployment)
+Write-Host -ForegroundColor Cyan @"
+ #Variables to copy
+ `$SubscriptionID=`"$($Subscription.ID)`"
+ `$ResourceGroupName=`"$ResourceGroupName`"
+ `$Region=`"$Location`"
+ `$TenantID=`"$($subscription.tenantID)`"
+ `$ArcGatewayID=`"$(($ArcGWInfo).ID)`"
+"@
+
+```
+
+Script outputs code that you can copy and hand over to another window if needed to provide variables.
+
+
+
+
+
+### Task 05 - Register with Arc
+
+* https://learn.microsoft.com/en-us/azure/azure-local/deploy/deployment-arc-register-server-permissions?view=azloc-24112&tabs=powershell
+
+As you can see, code contains few fixes that will be addressed in future releases (converting token to plaintext, starting scheduled task that fails in virtual environments...)
+
+```PowerShell
+#Make sure resource providers are registered
+Register-AzResourceProvider -ProviderNamespace "Microsoft.AzureArcData"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.HybridCompute"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.GuestConfiguration"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.HybridConnectivity"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.AzureStackHCI"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.Kubernetes"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.KubernetesConfiguration"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.ExtendedLocation"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.ResourceConnector"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.HybridContainerService"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.Attestation"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.Storage"
+Register-AzResourceProvider -ProviderNamespace "Microsoft.Insights"
+
+#deploy ARC Agent (with Arc Gateway, without proxy. For more examples visit https://learn.microsoft.com/en-us/azure/azure-local/deploy/deployment-arc-register-server-permissions?tabs=powershell)
+ $armtoken = (Get-AzAccessToken).Token
+ $id = (Get-AzContext).Account.Id
+ $Cloud="AzureCloud"
+
+ #check if token is plaintext (older module version outputs plaintext, version 5 outputs secure string)
+ # Check if the token is a SecureString
+ if ($armtoken -is [System.Security.SecureString]) {
+ # Convert SecureString to plaintext
+ $armtoken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($armtoken))
+ Write-Output "Token converted to plaintext."
+ }else {
+ Write-Output "Token is already plaintext."
+ }
+
+<# no longer needed
+ #check if ImageCustomizationScheduledTask is not in disabled state (if it's "ready", run it) - will be fixed in 2506
+ Invoke-Command -ComputerName $Servers -ScriptBlock {
+ $task=Get-ScheduledTask -TaskName ImageCustomizationScheduledTask
+ if ($task.State -ne "Disabled" -and $task.State -ne "Running"){
+ Write-Output "Starting Scheduled task ImageCustomizationScheduledTask on $env:ComputerName"
+ $task | Start-ScheduledTask
+ }
+ } -Credential $Credentials
+ #wait until it's disabled
+ Invoke-Command -ComputerName $Servers -ScriptBlock {
+ $task=Get-ScheduledTask -TaskName ImageCustomizationScheduledTask
+ if ($task.state -eq "running"){
+ do {
+ Write-Output "Waiting for ImageCustomizationScheduledTask on $env:computerName to finish"
+ Start-Sleep 1
+ $task=Get-ScheduledTask -TaskName ImageCustomizationScheduledTask
+ } while ($task.state -ne "Disabled")
+ }
+ } -Credential $Credentials
+#>
+ #register servers
+ Invoke-Command -ComputerName $Servers -ScriptBlock {
+ Invoke-AzStackHciArcInitialization -SubscriptionID $using:SubscriptionID -ResourceGroup $using:ResourceGroupName -TenantID $using:TenantID -Cloud $using:Cloud -Region $Using:Location -ArmAccessToken $using:ARMtoken -AccountID $using:id #-ArcGatewayID $using:ArcGatewayID
+ } -Credential $Credentials
+
+```
+
+Result:
+
+
+
+
+
+### Task 06 - Validation prerequisites
+
+```PowerShell
+#region to successfully validate you need make sure there's just one GW
+ #make sure there is only one management NIC with IP address (setup is complaining about multiple gateways)
+ Invoke-Command -ComputerName $servers -ScriptBlock {
+ Get-NetIPConfiguration | Where-Object IPV4defaultGateway | Get-NetAdapter | Sort-Object Name | Select-Object -Skip 1 | Set-NetIPInterface -Dhcp Disabled
+ } -Credential $Credentials
+#endregion
+
+#region Convert DHCP address to Static (since 2411 there's a check for static IP)
+ Invoke-Command -ComputerName $Servers -ScriptBlock {
+ $InterfaceAlias=(Get-NetIPAddress -AddressFamily IPv4 | Where-Object {$_.IPAddress -NotLike "169*" -and $_.PrefixOrigin -eq "DHCP"}).InterfaceAlias
+ $IPConf=Get-NetIPConfiguration -InterfaceAlias $InterfaceAlias
+ $IPAddress=Get-NetIPAddress -AddressFamily IPv4 -InterfaceAlias $InterfaceAlias
+ $IP=$IPAddress.IPAddress
+ $Index=$IPAddress.InterfaceIndex
+ $GW=$IPConf.IPv4DefaultGateway.NextHop
+ $Prefix=$IPAddress.PrefixLength
+ $DNSServers=@()
+ $ipconf.dnsserver | ForEach-Object {if ($_.addressfamily -eq 2){$DNSServers+=$_.ServerAddresses}}
+ Set-NetIPInterface -InterfaceIndex $Index -Dhcp Disabled
+ New-NetIPAddress -InterfaceIndex $Index -AddressFamily IPv4 -IPAddress $IP -PrefixLength $Prefix -DefaultGateway $GW -ErrorAction SilentlyContinue
+ Set-DnsClientServerAddress -InterfaceIndex $index -ServerAddresses $DNSServers
+ } -Credential $Credentials
+#endregion
+
+#region and make sure password is complex and long enough (12chars at least)
+ $NewPassword="LS1setup!LS1setup!"
+ Invoke-Command -ComputerName $servers -ScriptBlock {
+ Set-LocalUser -Name Administrator -AccountNeverExpires -Password (ConvertTo-SecureString $Using:NewPassword -AsPlainText -Force)
+ } -Credential $Credentials
+ #create new credentials
+ $UserName="Administrator"
+ $SecuredPassword = ConvertTo-SecureString $NewPassword -AsPlainText -Force
+ $Credentials= New-Object System.Management.Automation.PSCredential ($UserName,$SecuredPassword)
+#endregion
+
+```
+
+### Task 07 - Deploy via Azure Portal
+
+* https://learn.microsoft.com/en-us/azure/azure-local/deploy/deploy-via-portal
+
+In Azure Portal navigate to Azure Arc and into Azure Local under Host environments. In Get started with Azure Local, click on **Create instance** under **Deploy Azure Local**.
+
+
+
+Use values below for virtual cluster
+
+```
+Basics:
+ Resource Group: ALClus01-RG
+ ClusterName: ALClus01
+ Keyvaultname:
+
+Configuration:
+ New Configuration
+
+Networking
+ Network Switch for storage
+ Group All traffic
+
+ Network adapter 1: Ethernet
+ Network adapter 1 VLAN ID: 711 (default)
+ Network adapter 2: Ethernet 2
+ Network adapter 2 VLAN ID: 712 (default)
+
+ RDMA Protocol: Disabled (in case you are running lab in VMs)
+ Jumbo Frames: 1514 (in case you are running lab in VMs as hyper-v does not by default support Jumbo Frames)
+
+ Starting IP: 10.0.0.111
+ ENding IP: 10.0.0.116
+ Subnet mask: 255.255.255.0
+ Default Gateway: 10.0.0.1
+ DNS Server: 10.0.0.1
+
+Management
+ Custom location name: ALClus01
+
+ Azure storage account name:
+
+ Domain: corp.contoso.com
+ Computer name prefix: ALClus01
+ OU: OU=ALClus01,DC=Corp,DC=contoso,DC=com
+
+ Deployment account:
+ Username: ALClus01-LCMUser
+ Password: LS1setup!LS1setup!
+
+ Local Administrator
+ Username: Administrator
+ Password: LS1setup!LS1setup!
+
+Security:
+ Customized security settings
+ Unselect Bitlocker for data volumes (would consume too much space)
+
+Advanced:
+ Create workload volumes (Default)
+
+ **Please understand that the workload volumes will use thin provisioning, but their default max size is about 2x larger than the pool capacity. This is by-design.**
+
+Tags:
+
+```
+
+## Result
+
+
+
+
+
+
+
+
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin01.png b/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin01.png
new file mode 100644
index 00000000..164bf531
Binary files /dev/null and b/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin01.png differ
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin02.png b/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin02.png
new file mode 100644
index 00000000..9eff29cb
Binary files /dev/null and b/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin02.png differ
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin03.png b/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin03.png
new file mode 100644
index 00000000..b6a4a006
Binary files /dev/null and b/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin03.png differ
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin04.png b/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin04.png
new file mode 100644
index 00000000..1d919983
Binary files /dev/null and b/HandsOnLabs/03-RackLevelNestedMirror/media/cluadmin04.png differ
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/media/powershell01.png b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell01.png
new file mode 100644
index 00000000..6e101d8a
Binary files /dev/null and b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell01.png differ
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/media/powershell02.png b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell02.png
new file mode 100644
index 00000000..3c0e4c24
Binary files /dev/null and b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell02.png differ
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/media/powershell03.png b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell03.png
new file mode 100644
index 00000000..aad98bb7
Binary files /dev/null and b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell03.png differ
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/media/powershell04.png b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell04.png
new file mode 100644
index 00000000..f9bba1c4
Binary files /dev/null and b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell04.png differ
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/media/powershell05.png b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell05.png
new file mode 100644
index 00000000..e5d985ad
Binary files /dev/null and b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell05.png differ
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/media/powershell06.png b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell06.png
new file mode 100644
index 00000000..bde7e805
Binary files /dev/null and b/HandsOnLabs/03-RackLevelNestedMirror/media/powershell06.png differ
diff --git a/HandsOnLabs/03-RackLevelNestedMirror/readme.md b/HandsOnLabs/03-RackLevelNestedMirror/readme.md
new file mode 100644
index 00000000..bbb080a0
--- /dev/null
+++ b/HandsOnLabs/03-RackLevelNestedMirror/readme.md
@@ -0,0 +1,357 @@
+# Deploy S2D Campus Cluster
+
+
+
+- [Deploy S2D Campus Cluster](#deploy-s2d-campus-cluster)
+ - [About the lab](#about-the-lab)
+ - [LabConfig](#labconfig)
+ - [Task 1 - Configure all prerequisites](#task-1---configure-all-prerequisites)
+ - [Task 2 - Configure fault domains and create cluster](#task-2---configure-fault-domains-and-create-cluster)
+ - [Task 3 - Configure file share or Cloud Witness](#task-3---configure-file-share-or-cloud-witness)
+ - [Task 4 - Configure networking](#task-4---configure-networking)
+ - [Task 5 - Configure advanced settings in networking](#task-5---configure-advanced-settings-in-networking)
+ - [Task 6 - Enable ClusterS2D and create Volumes](#task-6---enable-clusters2d-and-create-volumes)
+ - [Step 1 - Enable Cluster S2D](#step-1---enable-cluster-s2d)
+ - [Step 2 - Check Storage Pool](#step-2---check-storage-pool)
+ - [Step 3 - Create Volumes](#step-3---create-volumes)
+
+
+
+## About the lab
+
+In this lab you will deploy 4 node Windows Server Insider cluster.
+
+To create Windows Server Insider parent disk, you can also use [CreateParentDisk.ps1](https://github.com/microsoft/MSLab/blob/master/Tools/CreateParentDisk.ps1) that you can find in ParentDisks folder in MSLab. It's easier to create your own image using ISO file as you are in control of what edition to create and how big vhd you want to create.
+
+To setup lab, follow [01-Creating First Lab](../../HandsOnLabs/01-CreatingFirstLab/readme.md). If you will create lab from the scratch, DC will be also created with Windows Server Insider.
+
+Rack Level Nested Mirror documentation: https://techcommunity.microsoft.com/discussions/windowsserverinsiders/announcing-windows-server-vnext-preview-build-26445/4432171
+
+## LabConfig
+
+```PowerShell
+$LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPassword='LS1setup!' ; DCEdition='4'; Internet=$true; AdditionalNetworksConfig=@(); VMs=@()}
+
+#S2D Nodes
+1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName="SiteA_$_" ; Configuration='S2D' ; ParentVHD='WinSrvInsiderCore_26445.vhdx' ; HDDNumber=4 ; HDDSize=2TB ; MemoryStartupBytes=1GB; VMProcessorCount=4 ; vTPM=$true}}
+1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName="SiteB_$_" ; Configuration='S2D' ; ParentVHD='WinSrvInsiderCore_26445.vhdx' ; HDDNumber=4 ; HDDSize=2TB ; MemoryStartupBytes=1GB; VMProcessorCount=4 ; vTPM=$true}}
+
+#S2D Nodes (nested virt)
+#1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName="SiteA_$_" ; Configuration='S2D' ; ParentVHD='WinSrvInsiderCore_26445.vhdx' ; HDDNumber=4 ; HDDSize=2TB ; MemoryStartupBytes=8GB ; VMProcessorCount=4 ; vTPM=$true ; NestedVirt=$true}}
+#1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName="SiteB_$_" ; Configuration='S2D' ; ParentVHD='WinSrvInsiderCore_26445.vhdx' ; HDDNumber=4 ; HDDSize=2TB ; MemoryStartupBytes=8GB ; VMProcessorCount=4 ; vTPM=$true ; NestedVirt=$true}}
+
+#Management machine
+$LabConfig.VMs += @{ VMName = 'Management' ; ParentVHD = 'WinSrvInsider_26445.vhdx'; MGMTNICs=1 ; AddToolsVHD=$True }
+
+#Windows Admin Center in GW mode
+#$LabConfig.VMs += @{ VMName = 'WACGW' ; ParentVHD = 'WinSrvInsiderCore_26445.vhdx'; MGMTNICs=1}
+
+```
+
+
+## Task 1 - Configure all prerequisites
+
+Following config is the same as you would configure physical servers.
+
+Run the code from Management machine
+
+```PowerShell
+#region install features
+ #install features for management (assuming you are running these commands on Windows Server with GUI)
+ Install-WindowsFeature -Name NetworkATC,RSAT-Clustering,RSAT-Clustering-Mgmt,RSAT-Clustering-PowerShell,RSAT-Hyper-V-Tools,RSAT-Feature-Tools-BitLocker-BdeAducExt,RSAT-AD-PowerShell,RSAT-AD-AdminCenter,RSAT-DHCP,RSAT-DNS-Server
+
+
+ #servers list
+ $Servers="SiteA_1","SiteA_2","SiteB_1","SiteB_2"
+
+ #install roles and features on servers
+ #install Hyper-V using DISM if Install-WindowsFeature fails (if nested virtualization is not enabled install-windowsfeature fails)
+ Invoke-Command -ComputerName $servers -ScriptBlock {
+ $Result=Install-WindowsFeature -Name "Hyper-V" -ErrorAction SilentlyContinue
+ if ($result.ExitCode -eq "failed"){
+ Enable-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V -Online -NoRestart
+ }
+ }
+ #define and install other features
+ $features="Failover-Clustering","RSAT-Clustering-PowerShell","Hyper-V-PowerShell","NetworkATC","Data-Center-Bridging","RSAT-DataCenterBridging-LLDP-Tools","FS-SMBBW","System-Insights","RSAT-System-Insights"
+ #optional - affects perf even if not enabled on volumes as filter driver is attached (SR,Dedup-the "old one") and also Bitlocker, that affects a little bit.
+ #$features+="Storage-Replica","RSAT-Storage-Replica","BitLocker","RSAT-Feature-Tools-BitLocker"
+ Invoke-Command -ComputerName $servers -ScriptBlock {Install-WindowsFeature -Name $using:features}
+#endregion
+
+#region configure OS settings
+ #Configure Active memory dump https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/varieties-of-kernel-mode-dump-files
+ Invoke-Command -ComputerName $servers -ScriptBlock {
+ Set-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\CrashControl -Name CrashDumpEnabled -value 1
+ Set-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\CrashControl -Name FilterPages -value 1
+ }
+
+ #Configure high performance power plan
+ #set high performance if not VM
+ Invoke-Command -ComputerName $servers -ScriptBlock {
+ if ((Get-ComputerInfo).CsSystemFamily -ne "Virtual Machine"){
+ powercfg /SetActive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
+ }
+ }
+ #check settings
+ Invoke-Command -ComputerName $servers -ScriptBlock {powercfg /list}
+
+#endregion
+
+Restart-Computer $servers -Protocol WSMan -Wait -For PowerShell -Force
+Start-Sleep 20 #Failsafe as Hyper-V needs 2 reboots and sometimes it happens, that during the first reboot the restart-computer evaluates the machine is up
+#make sure computers are restarted
+Foreach ($Server in $Servers){
+ do{$Test= Test-NetConnection -ComputerName $Server -CommonTCPPort WINRM}while ($test.TcpTestSucceeded -eq $False)
+}
+
+```
+
+## Task 2 - Configure fault domains and create cluster
+
+```PowerShell
+#region Create Fault Domains (just an example) https://docs.microsoft.com/en-us/windows-server/failover-clustering/fault-domains
+
+$ClusterName="RA_Cluster"
+$Servers="SiteA_1","SiteA_2","SiteB_1","SiteB_2"
+
+#Describe fault domain
+$xml = @"
+
+
+
+
+
+
+
+
+
+
+
+
+"@
+
+#Create Cluster
+New-Cluster -Name $ClusterName -Node $Servers -NoStorage
+
+#add Fault Domain
+Set-ClusterFaultDomainXML -XML $xml -CimSession $ClusterName
+
+#check fault domains
+Get-ClusterFaultDomainXML
+Get-ClusterFaultDomain
+
+```
+
+
+
+## Task 3 - Configure file share or Cloud Witness
+
+```PowerShell
+ $WitnessType="FileShare" #or Cloud
+ $WitnessServer="DC" #name of server where witness will be configured
+ #if cloud then configure following (use your own, these are just examples)
+ <#
+ $CloudWitnessStorageAccountName="MyStorageAccountName"
+ $CloudWitnessStorageKey="qi8QB/VSHHiA9lSvz1kEIEt0JxIucPL3l99nRHhkp+n1Lpabu4Ydi7Ih192A4VW42vccIgUnrXxxxxxxxxxxxx=="
+ $CloudWitnessEndpoint="core.windows.net"
+ #>
+
+ #ConfigureWitness
+ if ($WitnessType -eq "FileShare"){
+ ##Configure Witness on WitnessServer
+ #Create new directory
+ $WitnessName=$Clustername+"Witness"
+ Invoke-Command -ComputerName $WitnessServer -ScriptBlock {new-item -Path c:\Shares -Name $using:WitnessName -ItemType Directory -ErrorAction Ignore}
+ $accounts=@()
+ $accounts+="$env:userdomain\$ClusterName$"
+ $accounts+="$env:userdomain\$env:USERNAME"
+ #$accounts+="$env:userdomain\Domain Admins"
+ New-SmbShare -Name $WitnessName -Path "c:\Shares\$WitnessName" -FullAccess $accounts -CimSession $WitnessServer
+ #Set NTFS permissions
+ Invoke-Command -ComputerName $WitnessServer -ScriptBlock {(Get-SmbShare $using:WitnessName).PresetPathAcl | Set-Acl}
+ #Set Quorum
+ Set-ClusterQuorum -Cluster $ClusterName -FileShareWitness "\\$WitnessServer\$WitnessName"
+ }elseif($WitnessType -eq $Cloud){
+ Set-ClusterQuorum -Cluster $ClusterName -CloudWitness -AccountName $CloudWitnessStorageAccountName -AccessKey $CloudWitnessStorageKey -Endpoint $CloudWitnessEndpoint
+ }
+#endregion
+
+```
+
+
+
+## Task 4 - Configure networking
+
+Once Cluster is created, Network Intent (NetATC) can be created. Let's create it. If you want to learn more, there is a lab in Dell GEOS GitHub that goes deeper in NetATC https://github.com/DellGEOS/AzureLocalHOLs/tree/main/lab-guides/09-NetworkATCDeepDive.
+
+```PowerShell
+
+$ClusterName="RA_Cluster"
+$Servers="SiteA_1","SiteA_2","SiteB_1","SiteB_2"
+
+#make sure NetATC,FS-SMBBW and other required features are installed on servers
+Invoke-Command -ComputerName $Servers -ScriptBlock {
+ Install-WindowsFeature -Name NetworkATC,Data-Center-Bridging,RSAT-Clustering-PowerShell,RSAT-Hyper-V-Tools,FS-SMBBW
+}
+#make sure NetATC (and other management tools) is installed on Management Machine (note: DCB does not have separate RSAT, therefore it needs to be installed to have posh module)
+Install-WindowsFeature -Name NetworkATC,Data-Center-Bridging,RSAT-Clustering-PowerShell,RSAT-Hyper-V-Tools,FS-SMBBW
+
+#if virtual environment, then skip RDMA config
+if ((Get-CimInstance -ClassName win32_computersystem -CimSession $servers[0]).Model -eq "Virtual Machine"){
+ #virtual environment (skipping RDMA config)
+ $AdapterOverride = New-NetIntentAdapterPropertyOverrides
+ $AdapterOverride.NetworkDirect = 0
+ Add-NetIntent -ClusterName $ClusterName -Name ConvergedIntent -Management -Compute -Storage -AdapterName "Ethernet","Ethernet 2" -AdapterPropertyOverrides $AdapterOverride -Verbose #-StorageVlans 1,2
+}else{
+#on real hardware you can configure RDMA
+ #grab fastest adapters names (assuming that we are deploying converged intent with just Mellanox or Intel E810)
+ $FastestLinkSpeed=(get-netadapter -CimSession $Servers | Where-Object {$_.Status -eq "up" -and $_.HardwareInterface -eq $True}).Speed | Sort-Object -Descending | Select-Object -First 1
+ #grab adapters
+ $AdapterNames=(Get-NetAdapter -CimSession $Servers[0] | Where-Object {$_.Status -eq "up" -and $_.HardwareInterface -eq $True} | where-object Speed -eq $FastestLinkSpeed | Sort-Object Name).Name
+ #$AdapterNames="SLOT 3 Port 1","SLOT 3 Port 2"
+ Add-NetIntent -ClusterName $ClusterName -Name ConvergedIntent -Management -Compute -Storage -AdapterName $AdapterNames -Verbose #-StorageVlans 1,2
+}
+
+ #wait until finished
+ Start-Sleep 20 #let intent propagate a bit
+ Write-Output "applying intent"
+ do {
+ $status=Get-NetIntentStatus -ClusterName $ClusterName
+ Write-Host "." -NoNewline
+ Start-Sleep 5
+ } while ($status.ConfigurationStatus -contains "Provisioning" -or $status.ConfigurationStatus -contains "Retrying")
+
+ #check intent status
+ Get-NetIntentStatus -ClusterName $ClusterName
+
+```
+
+
+
+
+Note: it might happen, that one node will end up in Quarantine (as network was flapping). Simply remove with following PowerShell
+
+
+
+```PowerShell
+Get-ClusterNode -Cluster $ClusterName | Where-Object StatusInformation -eq "Quarantined" | Start-ClusterNode -ClearQuarantine
+
+```
+
+
+
+
+Once all is up, networks should look like this:
+
+
+
+
+## Task 5 - Configure advanced settings in networking
+
+Following code explains what other values can be adjusted to fine tune NetATC global intent settings. It might be valuable increasing number of live migrations (default 1), allow Management network for Live Migration (to allow cluster to cluster LM)...
+
+```PowerShell
+ #region Check settings before applying NetATC
+ #Check what networks were excluded from Live Migration
+ $Networks=(Get-ClusterResourceType -Cluster $clustername -Name "Virtual Machine" | Get-ClusterParameter -Name MigrationExcludeNetworks).Value -split ";"
+ foreach ($Network in $Networks){Get-ClusterNetwork -Cluster $ClusterName | Where-Object ID -Match $Network}
+
+ #check Live Migration option (probably bug, because it should default to SMB - version tested 1366)
+ Get-VMHost -CimSession $Servers | Select-Object *Migration*
+
+ #Check smbbandwith limit cluster settings (notice for some reason is SetSMBBandwidthLimit=1)
+ Get-Cluster -Name $ClusterName | Select-Object *SMB*
+
+ #check SMBBandwidthLimit settings (should be pouplated already with defaults on physical cluster - it calculated 1562500000 bytes per second on 2x25Gbps NICs)
+ Get-SmbBandwidthLimit -CimSession $Servers
+
+ #check VLAN settings (notice it's using Adapter Isolation, not VLAN)
+ Get-VMNetworkAdapterIsolation -CimSession $Servers -ManagementOS
+
+ #check number of live migrations (default is 1)
+ get-vmhost -CimSession $Servers | Select-Object Name,MaximumVirtualMachineMigrations
+ #endregion
+
+ #region Adjust NetATC global overrides (assuming there is one vSwitch) (just an example, default settings are fine)
+ $vSwitchNics=(Get-VMSwitch -CimSession $Servers[0]).NetAdapterInterfaceDescriptions
+ $LinkCapacityInGbps=(Get-NetAdapter -CimSession $Servers[0] -InterfaceDescription $vSwitchNics | Measure-Object Speed -Sum).sum/1000000000
+
+ $overrides=New-NetIntentGlobalClusterOverrides
+ $overrides.MaximumVirtualMachineMigrations=4
+ $overrides.MaximumSMBMigrationBandwidthInGbps=$LinkCapacityInGbps*0.4 #40%, if one switch is down, LM will not saturate bandwidth
+ $overrides.VirtualMachineMigrationPerformanceOption="SMB" #in VMs is Compression selected.
+ Set-NetIntent -GlobalClusterOverrides $overrides -Cluster $CLusterName
+
+ Start-Sleep 20 #let intent propagate a bit
+ Write-Output "applying overrides intent"
+ do {
+ $status=Get-NetIntentStatus -Globaloverrides -Cluster $CLusterName
+ Write-Host "." -NoNewline
+ Start-Sleep 5
+ } while ($status.ConfigurationStatus -contains "Provisioning" -or $status.ConfigurationStatus -contains "Retrying")
+ #endregion
+
+ #region verify settings again
+ #Check Cluster Global overrides
+ $GlobalOverrides=Get-Netintent -GlobalOverrides -Cluster $CLusterName
+ $GlobalOverrides.ClusterOverride
+
+ #check Live Migration option
+ Get-VMHost -CimSession $Servers | Select-Object *Migration*
+
+ #Check LiveMigrationPerf option and Limit (SetSMBBandwidthLimit was 1, now is 0)
+ Get-Cluster -Name $ClusterName | Select-Object *SMB*
+
+ #check SMBBandwidthLimit settings
+ Get-SmbBandwidthLimit -CimSession $Servers
+
+ #check number of live migrations
+ get-vmhost -CimSession $Servers | Select-Object Name,MaximumVirtualMachineMigrations
+
+ #check it in cluster (is only 1 - expected)
+ get-cluster -Name $ClusterName | Select-Object Name,MaximumParallelMigrations
+
+ #endregion
+
+```
+
+## Task 6 - Enable ClusterS2D and create Volumes
+
+
+### Step 1 - Enable Cluster S2D
+
+```PowerShell
+ #Enable-ClusterS2D
+ Enable-ClusterS2D -CimSession $ClusterName -confirm:0 -Verbose
+
+```
+
+Note: During Enable-ClusterS2D you can notice that multiple racks were detected and rack fault domain was created. We can check it in the pool
+
+
+
+### Step 2 - Check Storage Pool
+
+```PowerShell
+Get-StoragePool -CimSession $ClusterName
+
+```
+
+Note: you can see that FaultDomainAwarenessDefault is StorageRack (as we configured multiple racks)
+
+
+
+### Step 3 - Create Volumes
+
+```PowerShell
+#Fixed Volume
+New-Volume -FriendlyName "FourCopyVolumeFixed1" -StoragePoolFriendlyName S2D* -FileSystem CSVFS_ReFS –Size 1TB -PhysicalDiskRedundancy 3 -ProvisioningType Fixed -NumberOfDataCopies 4 –NumberOfColumns 3 -CimSession $ClusterName
+#thin provisioned volume
+New-Volume -FriendlyName "FourCopyVolumeThin1" -StoragePoolFriendlyName S2D* -FileSystem CSVFS_ReFS –Size 1TB -PhysicalDiskRedundancy 3 -ProvisioningType Fixed -NumberOfDataCopies 4 –NumberOfColumns 3 -CimSession $ClusterName
+
+```
+
+
+
+
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/cluadmin01.png b/HandsOnLabs/04-VolumesDeepDive/media/cluadmin01.png
new file mode 100644
index 00000000..5a459902
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/cluadmin01.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/cluadmin02.png b/HandsOnLabs/04-VolumesDeepDive/media/cluadmin02.png
new file mode 100644
index 00000000..ec37aff5
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/cluadmin02.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/edge01.png b/HandsOnLabs/04-VolumesDeepDive/media/edge01.png
new file mode 100644
index 00000000..b3a11f84
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/edge01.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/edge02.png b/HandsOnLabs/04-VolumesDeepDive/media/edge02.png
new file mode 100644
index 00000000..ad1960e4
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/edge02.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/edge03.png b/HandsOnLabs/04-VolumesDeepDive/media/edge03.png
new file mode 100644
index 00000000..8519411e
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/edge03.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/powershell01.png b/HandsOnLabs/04-VolumesDeepDive/media/powershell01.png
new file mode 100644
index 00000000..8a90aeed
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/powershell01.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/powershell02.png b/HandsOnLabs/04-VolumesDeepDive/media/powershell02.png
new file mode 100644
index 00000000..108a7444
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/powershell02.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/powershell03.png b/HandsOnLabs/04-VolumesDeepDive/media/powershell03.png
new file mode 100644
index 00000000..0437d56f
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/powershell03.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/powershell04.png b/HandsOnLabs/04-VolumesDeepDive/media/powershell04.png
new file mode 100644
index 00000000..9eb47f8a
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/powershell04.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/powershell05.png b/HandsOnLabs/04-VolumesDeepDive/media/powershell05.png
new file mode 100644
index 00000000..28b079e3
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/powershell05.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/powershell06.png b/HandsOnLabs/04-VolumesDeepDive/media/powershell06.png
new file mode 100644
index 00000000..55adcba5
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/powershell06.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/powershell07.png b/HandsOnLabs/04-VolumesDeepDive/media/powershell07.png
new file mode 100644
index 00000000..059cec1f
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/powershell07.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/powershell08.png b/HandsOnLabs/04-VolumesDeepDive/media/powershell08.png
new file mode 100644
index 00000000..4ac27079
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/powershell08.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/slab01.png b/HandsOnLabs/04-VolumesDeepDive/media/slab01.png
new file mode 100644
index 00000000..ff7a5467
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/slab01.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/slab02.png b/HandsOnLabs/04-VolumesDeepDive/media/slab02.png
new file mode 100644
index 00000000..55fa5b95
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/slab02.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/slab03.png b/HandsOnLabs/04-VolumesDeepDive/media/slab03.png
new file mode 100644
index 00000000..92da3656
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/slab03.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/slab04.png b/HandsOnLabs/04-VolumesDeepDive/media/slab04.png
new file mode 100644
index 00000000..694fac0f
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/slab04.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/slab05.png b/HandsOnLabs/04-VolumesDeepDive/media/slab05.png
new file mode 100644
index 00000000..89368857
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/slab05.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/slab06.png b/HandsOnLabs/04-VolumesDeepDive/media/slab06.png
new file mode 100644
index 00000000..af5137a0
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/slab06.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/wac01.png b/HandsOnLabs/04-VolumesDeepDive/media/wac01.png
new file mode 100644
index 00000000..21df0b61
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/wac01.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/media/wac02.png b/HandsOnLabs/04-VolumesDeepDive/media/wac02.png
new file mode 100644
index 00000000..0e0b9163
Binary files /dev/null and b/HandsOnLabs/04-VolumesDeepDive/media/wac02.png differ
diff --git a/HandsOnLabs/04-VolumesDeepDive/readme.md b/HandsOnLabs/04-VolumesDeepDive/readme.md
new file mode 100644
index 00000000..7b00d21a
--- /dev/null
+++ b/HandsOnLabs/04-VolumesDeepDive/readme.md
@@ -0,0 +1,319 @@
+# Volumes Deep Dive
+
+
+
+- [Volumes Deep Dive](#volumes-deep-dive)
+ - [About the lab](#about-the-lab)
+ - [Theory: Default Workload volumes Size](#theory-default-workload-volumes-size)
+ - [Theory: Column size, Extent and Slabs](#theory-column-size-extent-and-slabs)
+ - [Column/Extent/Slab deep dive](#columnextentslab-deep-dive)
+ - [one column, 2-way mirror, 2 nodes](#one-column-2-way-mirror-2-nodes)
+ - [two columns, 2-way mirror, 2 nodes](#two-columns-2-way-mirror-2-nodes)
+ - [four columns, 2-way mirror, 2 nodes](#four-columns-2-way-mirror-2-nodes)
+ - [Are default volumes "correct"?](#are-default-volumes-correct)
+ - [Delete Default Volumes](#delete-default-volumes)
+ - [Create Custom Volumes with reserve](#create-custom-volumes-with-reserve)
+
+
+
+## About the lab
+
+In this lab you will learn everything about Storage Spaces virtual disks (Volumes in Azure Local).
+
+You can create Azure Local cluster as a prerequisite [02-DeployingAzureLocal](../../HandsOnLabs/02-DeployingAzureLocal/readme.md)
+
+## Theory: Default Workload volumes Size
+
+If you let the deployment wizard create volumes, it will create Workload volumes (one per server). In MSLab default configuration (4x1TB disk per node) it created 2x 2.5TB virtual disks.
+
+
+
+With this configuration, if you would try to fill the virtual disks, you would need 10TB capacity (two 2.5TB volumes with 2-way mirror = 2 x 2.5 x 2 = 10). But in current configuration Pool capacity is just 8TB.
+
+
+
+Interestingly in Portal you can see 3 TB available size (as Azure Portal rounds up)
+
+
+
+And cluster performance metrics numbers are weird as well
+
+
+
+Let's explore volumes with PowerShell to see exact numbers
+
+```PowerShell
+$ClusterName="ALClus01"
+Get-VirtualDisk -CimSession $ClusterName | Sort-Object FriendlyName
+
+```
+
+
+
+
+Resume:
+
+As you can see, numbers in portal are way off, and cannot be trusted.
+
+To control volume size (and consumption) it's worth calculating size "manually" and creating your own volumes.
+
+## Theory: Column size, Extent and Slabs
+
+```PowerShell
+#Exploring columns, extents and slabs
+
+$ClusterName="ALClus01"
+#let's create fixed volume with 1 and 2 columns
+
+Enter-PSSession $ClusterName #as we would need to increase MaxEvenlopeSize to transfer extents in variable...
+
+New-VirtualDisk -FriendlyName 1ColumnFixed -StoragePoolFriendlyName SU* -ResiliencySettingName Mirror -Size 1TB -ProvisioningType Fixed -NumberOfColumns 1 -NumberOfDataCopies 2
+New-VirtualDisk -FriendlyName 2ColumnsFixed -StoragePoolFriendlyName SU* -ResiliencySettingName Mirror -Size 1TB -ProvisioningType Fixed -NumberOfColumns 2 -NumberOfDataCopies 2
+New-VirtualDisk -FriendlyName 4ColumnsFixed -StoragePoolFriendlyName SU* -ResiliencySettingName Mirror -Size 1TB -ProvisioningType Fixed -NumberOfColumns 4 -NumberOfDataCopies 2
+
+#explore extents
+$VirtualDisk = Get-VirtualDisk -FriendlyName 1ColumnFixed
+$Extents=$VirtualDisk | Get-PhysicalExtent -CimSession $ClusterName | Where-Object VirtualDiskUniqueID -eq $VirtualDisk.UniqueId
+
+#explore extent properties
+$extents[0]
+
+#extent size is 1GB
+$extents[0].Size/1GB
+
+#group different copies (2 copies, 1024*1GB*2=2TB consumed)
+$Extents | group Copynumber -NoElement
+
+#explore first few extents
+$Extents[0..11] | Ft ColumnNumber,CopyNumber,PhysicalDiskUniqueID,VirtualDiskOffset
+
+#let's explore extents on volume with 2 columns
+$VirtualDisk = Get-VirtualDisk -FriendlyName 2ColumnsFixed
+$Extents=$VirtualDisk | Get-PhysicalExtent -CimSession $ClusterName | Where-Object VirtualDiskUniqueID -eq $VirtualDisk.UniqueId
+$Extents[0..11] | Ft ColumnNumber,CopyNumber,PhysicalDiskUniqueID,VirtualDiskOffset
+
+#let's explore extents on volume with 4 columns
+$VirtualDisk = Get-VirtualDisk -FriendlyName 4ColumnsFixed
+$Extents=$VirtualDisk | Get-PhysicalExtent -CimSession $ClusterName | Where-Object VirtualDiskUniqueID -eq $VirtualDisk.UniqueId
+$Extents[0..11] | Ft ColumnNumber,CopyNumber,PhysicalDiskUniqueID,VirtualDiskOffset
+
+Exit-PSSession
+```
+
+Let's summarize what you can learn from above:
+
+
+
+* Extent size is 1GB (Size)
+* Each extent has ColumnNumber and CopyNumber
+* You can learn which slab is it by looking at VirtualDiskOffset
+
+## Column/Extent/Slab deep dive
+
+### one column, 2-way mirror, 2 nodes
+
+
+```PowerShell
+$ClusterName="ALClus01"
+
+Invoke-Command -ComputerName $ClusterName {
+ $VirtualDisk = Get-VirtualDisk -FriendlyName 1ColumnFixed
+ $Extents=$VirtualDisk | Get-PhysicalExtent -CimSession $ClusterName | Where-Object VirtualDiskUniqueID -eq $VirtualDisk.UniqueId
+ $Extents[0..5]| Ft ColumnNumber,CopyNumber,PhysicalDiskUniqueID,VirtualDiskOffset
+}
+
+```
+
+
+
+### two columns, 2-way mirror, 2 nodes
+
+
+
+```PowerShell
+$ClusterName="ALClus01"
+
+Invoke-Command -ComputerName $ClusterName {
+ $VirtualDisk = Get-VirtualDisk -FriendlyName 2ColumnsFixed
+ $Extents=$VirtualDisk | Get-PhysicalExtent -CimSession $ClusterName | Where-Object VirtualDiskUniqueID -eq $VirtualDisk.UniqueId
+ $Extents[0..11]| Ft ColumnNumber,CopyNumber,PhysicalDiskUniqueID,VirtualDiskOffset
+}
+
+```
+
+
+
+### four columns, 2-way mirror, 2 nodes
+
+
+
+
+
+```PowerShell
+$ClusterName="ALClus01"
+
+Invoke-Command -ComputerName $ClusterName {
+ $VirtualDisk = Get-VirtualDisk -FriendlyName 4ColumnsFixed
+ $Extents=$VirtualDisk | Get-PhysicalExtent -CimSession $ClusterName | Where-Object VirtualDiskUniqueID -eq $VirtualDisk.UniqueId
+ $Extents[0..15]| Ft ColumnNumber,CopyNumber,PhysicalDiskUniqueID,VirtualDiskOffset
+}
+
+```
+
+
+## Are default volumes "correct"?
+
+Well, this is great question.
+
+* is the size right? Maybe. But if workload expands beyond some threshold, you will have an issue as you will run out of space.
+
+* is the column size right? Maybe. But if you loose one disk, volumes will not repair until you replace the failed disk as column isolation is configured to PhysicalDisk (so you cant have columns with different column number and same offset on the same physical disk).
+
+* Windows Admin Center recommends (in this case) to keep capacity of 2 disks (in this case 2TB) as a recommended reserve, but you cant rebuild anyway as in this case is default volume NumberOfColumns4 and it won't rebuild. So you can either ignore recommended capacity, or plan a volume that will fit available space and will be able to rebuild (column size 3 and lower)
+
+
+
+
+
+
+
+## Delete Default Volumes
+
+```PowerShell
+$ClusterName="ALClus01"
+
+#install az cli
+ Start-BitsTransfer -Source https://aka.ms/installazurecliwindows -Destination $env:userprofile\Downloads\AzureCLI.msi
+ Start-Process msiexec.exe -Wait -ArgumentList "/I $env:userprofile\Downloads\AzureCLI.msi /quiet"
+ # add az to enviromental variables so no posh restart is needed
+ [System.Environment]::SetEnvironmentVariable('PATH',$Env:PATH+';C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin')
+
+#login
+ az login --use-device-code
+
+# add Az extension https://learn.microsoft.com/en-us/cli/azure/stack-hci-vm?view=azure-cli-latest
+ az extension add --name stack-hci-vm
+
+#delete storage paths
+$StoragePaths=Invoke-Command -ComputerName $CLusterName -ScriptBlock {
+ az stack-hci-vm storagepath list | ConvertFrom-Json
+}
+foreach ($StoragePath in $StoragePaths){
+ az stack-hci-vm storagepath delete --resource-group $StoragePath.ResourceGroup --name $StoragePath.Name --yes
+}
+#delete UserStorage virtual disks
+Get-VirtualDisk -CimSession $ClusterName -FriendlyName UserStorage* | Remove-VirtualDisk #-Confirm:0
+#and the disks we created
+Get-VirtualDisk -CimSession $ClusterName -FriendlyName *Column** | Remove-VirtualDisk #-Confirm:0
+
+```
+
+## Create Custom Volumes (with reserve)
+
+```PowerShell
+$ClusterName="ALClus01"
+#region create volumes
+ #calculate size
+ #calculate reserve
+ $pool=Get-StoragePool -CimSession $ClusterName | Where-Object OtherUsageDescription -eq "Reserved for S2D"
+ $HDDCapacity= ($pool |Get-PhysicalDisk -CimSession $clustername | where mediatype -eq HDD | Measure-Object -Property Size -Sum).Sum
+ $HDDMaxSize= ($pool |Get-PhysicalDisk -CimSession $clustername | where mediatype -eq HDD | Measure-Object -Property Size -Maximum).Maximum
+ $SSDCapacity= ($pool |Get-PhysicalDisk -CimSession $clustername | where mediatype -eq SSD | where usage -ne journal | Measure-Object -Property Size -Sum).Sum
+ $SSDMaxSize= ($pool |Get-PhysicalDisk -CimSession $clustername | where mediatype -eq SSD | where usage -ne journal | Measure-Object -Property Size -Maximum).Maximum
+
+ $numberofNodes=(Get-ClusterNode -Cluster $clustername).count
+ if ($numberofNodes -le 2){
+ if ($SSDCapacity){
+ $SSDCapacityToUse=$SSDCapacity-($numberofNodes*$SSDMaxSize)-700GB #700GB just some reserve (25*2 = perfhistory + 252*2 Infrastructure volume)
+ $sizeofvolumeonSSDs=$SSDCapacityToUse/2/$numberofNodes
+ }
+ if ($HDDCapacity){
+ $HDDCapacityToUse=$HDDCapacity-($numberofNodes*$HDDMaxSize)-700GB #700GB just some reserve (25*2 = perfhistory + 252*2 Infrastructure volume)
+ $sizeofvolumeonHDDs=$HDDCapacityToUse/2/$numberofNodes
+ }
+ }else{
+ if ($SSDCapacity){
+ $SSDCapacityToUse=$SSDCapacity-(3*$SSDMaxSize)-1000GB #1000GB just some reserve (25*3 = perfhistory + 252*3 Infrastructure volume)
+ $sizeofvolumeonSSDs=$SSDCapacityToUse/3/$numberofNodes
+ }
+ if ($HDDCapacity){
+ $HDDCapacityToUse=$HDDCapacity-(3*$HDDMaxSize)-1000GB #1000GB just some reserve (25*3 = perfhistory + 252*3 Infrastructure volume)
+ $sizeofvolumeonHDDs=$HDDCapacityToUse/3/$numberofNodes
+ }
+ }
+
+ #calculate column size (just assuming that column size should not be larger than 6. but can be anything between 1-4 for sure)
+ $numberofNodes=(Get-ClusterNode -Cluster $ClusterName).count
+ $pool=Get-StoragePool -CimSession $ClusterName | Where-Object OtherUsageDescription -eq "Reserved for S2D"
+ $ColumnSizeHDD= (($pool |Get-PhysicalDisk -CimSession $ClusterName | where mediatype -eq HDD).count/$numberofnodes)-1
+ if ($ColumnSizeHDD -gt 6){$ColumnSizeHDD=6}
+ $ColumnSizeSSD= (($pool |Get-PhysicalDisk -CimSession $ClusterName | where mediatype -eq HDD).count/$numberofnodes)-1
+ if ($ColumnSizeSSD -gt 6){$ColumnSizeSSD=6}
+ #create volumes
+ #create volumes
+ $Nodes=(Get-ClusterNode -Cluster $ClusterName).Name
+ Foreach ($Node in $Nodes){
+ if ($sizeofvolumeonHDDs){
+ New-Volume -CimSession $ClusterName -FileSystem CSVFS_ReFS -StoragePool $pool -Size $sizeofvolumeonHDDs -FriendlyName "HDD_$Node" -MediaType HDD -NumberOfColumns $ColumnSizeHDD
+ }
+ if ($sizeofvolumeonSSDs){
+ New-Volume -CimSession $ClusterName -FileSystem CSVFS_ReFS -StoragePool $pool -Size $sizeofvolumeonSSDs -FriendlyName "SSD_$Node" -MediaType SSD -NumberOfColumns $ColumnSizeSSD
+ }
+ }
+ #move volumes to owners
+ foreach ($node in $nodes){
+ Get-ClusterSharedVolume -Cluster $ClusterName -Name *$node* | Move-ClusterSharedVolume -Node $node
+ }
+#endregion
+#region create paths
+ <# Already done before
+ #install az cli
+ Start-BitsTransfer -Source https://aka.ms/installazurecliwindows -Destination $env:userprofile\Downloads\AzureCLI.msi
+ Start-Process msiexec.exe -Wait -ArgumentList "/I $env:userprofile\Downloads\AzureCLI.msi /quiet"
+ # add az to enviromental variables so no posh restart is needed
+ [System.Environment]::SetEnvironmentVariable('PATH',$Env:PATH+';C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin')
+
+ #login
+ az login --use-device-code
+
+ # add Az extension https://learn.microsoft.com/en-us/cli/azure/stack-hci-vm?view=azure-cli-latest
+ az extension add --name stack-hci-vm
+ #>
+
+ $AzureStackHCI= Invoke-Command -ComputerName $ClusterName -ScriptBlock {Get-AzureStackHCI}
+ $ResourceGroupName=($AzureStackHCI).AzureResourceUri -split "/" | Select-Object -Index 4
+ $SubscriptionID=($AzureStackHCI).AzureResourceUri -split "/" | Select-Object -Index 2
+ $Region=($AzureStackHCI).Region
+ $CustomLocation="/subscriptions/$SubscriptionID/resourceGroups/$ResourceGroupName/providers/Microsoft.ExtendedLocation/customLocations/$(($AzureStackHCI).AzureResourceName)"
+
+ #create paths
+ Foreach ($Node in $Nodes){
+ if ($sizeofvolumeonHDDs){
+ az stack-hci-vm storagepath create --resource-group $ResourceGroupName --custom-location $CustomLocation --location $region --path "c:\ClusterStorage\HDD_$Node" --name "HDD_$Node"
+ }
+ if ($sizeofvolumeonSSDs){
+ az stack-hci-vm storagepath create --resource-group $ResourceGroupName --custom-location $CustomLocation --location $region --path "c:\ClusterStorage\SSD_$Node" --name "SSD_$Node"
+ }
+ }
+#endregion
+
+#encrypt (BitLocker) TBD https://learn.microsoft.com/en-us/azure/azure-local/manage/manage-bitlocker
+
+```
+
+
+
+Pool has now recommended reserve
+
+
+
+Number of columns is 3, therefore it will rebuild if one disk will fail
+
+
+
+
+
+Storage Paths in Portal
+
+
+
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/configurator01.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/configurator01.png
new file mode 100644
index 00000000..c68649b8
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/configurator01.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/configurator02.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/configurator02.png
new file mode 100644
index 00000000..9d1ce709
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/configurator02.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/configurator03.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/configurator03.png
new file mode 100644
index 00000000..92727c03
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/configurator03.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge01.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge01.png
new file mode 100644
index 00000000..1e4487e5
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge01.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge02.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge02.png
new file mode 100644
index 00000000..01bd918c
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge02.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge03.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge03.png
new file mode 100644
index 00000000..4cc8681a
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge03.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge04.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge04.png
new file mode 100644
index 00000000..97f84470
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge04.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge05.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge05.png
new file mode 100644
index 00000000..fa79b5eb
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge05.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge06.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge06.png
new file mode 100644
index 00000000..c6f7c245
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge06.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge07.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge07.png
new file mode 100644
index 00000000..bf5ebbc7
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge07.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge08.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge08.png
new file mode 100644
index 00000000..e1afe925
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge08.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge09.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge09.png
new file mode 100644
index 00000000..25232664
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge09.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge10.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge10.png
new file mode 100644
index 00000000..3b0f4796
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/edge10.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/explorer01.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/explorer01.png
new file mode 100644
index 00000000..646a1853
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/explorer01.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/explorer02.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/explorer02.png
new file mode 100644
index 00000000..07b6f5bc
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/explorer02.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/hvconnect01.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/hvconnect01.png
new file mode 100644
index 00000000..b6827deb
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/hvconnect01.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/hvconnect02.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/hvconnect02.png
new file mode 100644
index 00000000..bc29fb25
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/hvconnect02.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/hvmanager01.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/hvmanager01.png
new file mode 100644
index 00000000..f321b880
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/hvmanager01.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/powershell01.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/powershell01.png
new file mode 100644
index 00000000..46d5afde
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/powershell01.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/media/powershell02.png b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/powershell02.png
new file mode 100644
index 00000000..7b46a95a
Binary files /dev/null and b/HandsOnLabs/05-SimplifiedMachineProvisioning/media/powershell02.png differ
diff --git a/HandsOnLabs/05-SimplifiedMachineProvisioning/readme.md b/HandsOnLabs/05-SimplifiedMachineProvisioning/readme.md
new file mode 100644
index 00000000..6ba19c94
--- /dev/null
+++ b/HandsOnLabs/05-SimplifiedMachineProvisioning/readme.md
@@ -0,0 +1,146 @@
+# Simplified Machine Provisioning (Preview)
+
+## About the lab
+
+In this lab you will learn about [Simplified Machine Provisioning](https://techcommunity.microsoft.com/blog/azurearcblog/announcing-public-preview-simplified-machine-provisioning-for-azure-local/4496811)
+
+Simplified provisioning creates .pem file (ownership voucher) if booted from USB. In this scenario (just to test it) we will boot from iso and pick up ownership voucher using scp or/and with [Configurator App for Azure Local](https://aka.ms/ConfiguratorAppForHCI)
+
+## Prerequisites
+
+As prerequisite, download provision-os.iso that is part of [provisioning package](https://aka.ms/provision/software/2604). You can download up-to-date version at https://portal.azure.com/#servicemenu/Microsoft_Azure_ArcCenterUX/AzureArcCenterHub/provisioningGetStarted
+
+The ISO needs to be placed in your ParentDisks folder
+
+
+
+## Labconfig
+
+This labconfig will provision two SMP (Simplified Machine Provisioning) servers with memorystartupbytes 4GB (if you plan to deploy Azure Local instance, increase it to at least 16GB)
+
+Notice, that secureboot is disabled
+
+```PowerShell
+$LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; DCEdition='4'; Internet=$true ; AdditionalNetworksConfig=@(); VMs=@()}
+
+#labconfig for nested virtualization (enough RAM to create ARC RB).
+$LABConfig.VMs += @{ VMName = "SMPNode1" ; Configuration = 'S2D' ; AttachISO = 'provision-os.iso' ;SecureBoot="Disabled"; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 4GB; VMProcessorCount="Max" ; vTPM=$true ; NestedVirt=$true }
+$LABConfig.VMs += @{ VMName = "SMPNode2" ; Configuration = 'S2D' ; AttachISO = 'provision-os.iso' ;SecureBoot="Disabled"; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 4GB; VMProcessorCount="Max" ; vTPM=$true ; NestedVirt=$true }
+
+#Management machine
+$LabConfig.VMs += @{ VMName = 'Management' ; ParentVHD = 'Win2025_G2.vhdx'; MGMTNICs=1 ; AddToolsVHD=$True }
+
+```
+
+
+
+## Scenario
+
+### Task01 - collect ownership vouchers
+
+#### Step01 - connect to SMB Nodes console
+
+ Once lab will start, open console to each of the SMP node and note the IP Address
+
+ 
+
+ In this case, Servers have IP addresses 10.0.0.13 and 16. Let's use it in below script to pull ownership vouchers
+
+#### Step02 - connect to Management machine
+
+Log in into management machine (LabAdmin/LS1setup) and run following command from PowerShell to collect ownership vouchers to download folder.
+
+Modify IP addresses as needed.
+
+```PowerShell
+ $Servers="10.0.0.16","10.0.0.18"
+ $username="edgeuser"
+ #password Password1
+ foreach ($Server in $Servers){
+ #create folder to download key
+ new-item -ItemType directory -name $server -Path $env:userprofile\Downloads -ErrorAction Ignore
+ scp -r $username@$server`:/var/staging/export/vouchers/**/*.pem $env:userprofile\Downloads\$server
+ }
+
+```
+
+The shell will ask if you trust the fingerprint. Simply type yes
+
+Once asked for password, type Password1
+
+
+
+
+
+#### Step03 - validate if vouchers were downloaded
+
+If pem files were successfully downloaded, you should be able to see it in Downloads folder
+
+
+
+#### Step04 - download vouchers using Configurator App for Azure Local
+
+In management machine, navigate to https://aka.ms/ConfiguratorAppForHCI and open the package to install Configurator app. It will also ask you to install .Net 9.0 Desktop runtime
+
+In Configurator App, specify the IP Address into the Machine Name.
+
+
+
+Username and password, specify edgeuser/Password1
+
+
+
+Now you can download ownership voucher
+
+
+
+
+
+### Task02 - Add provisioned machines into portal
+
+#### Step01 - Create Site
+
+In Azure Portal, navigate to (Azure Arc Site Manager)[https://portal.azure.com/#servicemenu/Microsoft_Azure_ArcCenterUX/AzureArcCenterHub/sitesOverview]
+
+If you dont have site yet, you'll need to create one per Subscription and one per ResourceGroup as on picture below.
+
+
+
+#### Step02 - Add Arc machines
+
+In Azure Portal, navigate to (Azure Arc Machine provisioning)[https://portal.azure.com/#servicemenu/Microsoft_Azure_ArcCenterUX/AzureArcCenterHub/arcProvisioningDevices] and click on Provision
+
+
+
+In Site select site you created (in this case MSLab) and add provisioned machines (vouchers)
+
+
+
+You can also rename machines (SMPNode1/SMPNode2)
+
+
+
+And don't forget to select Azure Local image
+
+
+
+
+Create Key vault and provide local admin password
+
+
+
+Once deployed, Azure Local OS will be installed
+
+#### Step03 - Monitor Installation
+
+Connect to SMPNode1/SMPNode2 and to Azure Portal (Arc Provisioning)[https://portal.azure.com/#servicemenu/Microsoft_Azure_ArcCenterUX/AzureArcCenterHub/arcProvisioningDevices] to check the status
+
+
+
+
+
+
+
+### Task03 - Create new cluster
+
+You can use steps from [02-Deploying Azure Local](../../HandsOnLabs/02-DeployingAzureLocal/readme.md)
\ No newline at end of file
diff --git a/HandsOnLabs/06-TestingWindowsServerInsider/media/cluadmin01.png b/HandsOnLabs/06-TestingWindowsServerInsider/media/cluadmin01.png
new file mode 100644
index 00000000..0c2ca34b
Binary files /dev/null and b/HandsOnLabs/06-TestingWindowsServerInsider/media/cluadmin01.png differ
diff --git a/HandsOnLabs/06-TestingWindowsServerInsider/media/cluadmin02.png b/HandsOnLabs/06-TestingWindowsServerInsider/media/cluadmin02.png
new file mode 100644
index 00000000..bd2c59d0
Binary files /dev/null and b/HandsOnLabs/06-TestingWindowsServerInsider/media/cluadmin02.png differ
diff --git a/HandsOnLabs/06-TestingWindowsServerInsider/media/hvconnect01.png b/HandsOnLabs/06-TestingWindowsServerInsider/media/hvconnect01.png
new file mode 100644
index 00000000..2867a878
Binary files /dev/null and b/HandsOnLabs/06-TestingWindowsServerInsider/media/hvconnect01.png differ
diff --git a/HandsOnLabs/06-TestingWindowsServerInsider/media/hvconnect02.png b/HandsOnLabs/06-TestingWindowsServerInsider/media/hvconnect02.png
new file mode 100644
index 00000000..9046e505
Binary files /dev/null and b/HandsOnLabs/06-TestingWindowsServerInsider/media/hvconnect02.png differ
diff --git a/HandsOnLabs/06-TestingWindowsServerInsider/media/hvmanager01.png b/HandsOnLabs/06-TestingWindowsServerInsider/media/hvmanager01.png
new file mode 100644
index 00000000..c6cfc4af
Binary files /dev/null and b/HandsOnLabs/06-TestingWindowsServerInsider/media/hvmanager01.png differ
diff --git a/HandsOnLabs/06-TestingWindowsServerInsider/media/powershell01.png b/HandsOnLabs/06-TestingWindowsServerInsider/media/powershell01.png
new file mode 100644
index 00000000..8c03eeca
Binary files /dev/null and b/HandsOnLabs/06-TestingWindowsServerInsider/media/powershell01.png differ
diff --git a/HandsOnLabs/06-TestingWindowsServerInsider/readme.md b/HandsOnLabs/06-TestingWindowsServerInsider/readme.md
new file mode 100644
index 00000000..2d9e105d
--- /dev/null
+++ b/HandsOnLabs/06-TestingWindowsServerInsider/readme.md
@@ -0,0 +1,274 @@
+# Testing Windows Server Insider Preview
+
+## About the lab
+
+In following lab you will test latest Windows Server Insider Preview features announced at following blog: https://techcommunity.microsoft.com/discussions/windowsserverinsiders/announcing-windows-server-vnext-preview-build-29621/4536572
+
+You will create 2 node S2D cluster and inside the cluster you will create single, empty VM to test Trusted Launch for VMs. You can then add real vhd and test thigs such as TPM and live migration across cluster nodes.
+
+## Labconfig
+
+```PowerShell
+$LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPassword='LS1setup!' ; DCEdition='4'; Internet=$true; AdditionalNetworksConfig=@(); VMs=@()}
+
+#Windows Server Insider S2D nodes
+$LABConfig.VMs += @{ VMName = "S2D1" ; Configuration = 'S2D' ; ParentVHD = 'WinSrvInsiderCore_29621.vhdx' ; HDDNumber = 4 ; HDDSize= 1TB ; MemoryStartupBytes= 4GB; VMProcessorCount="MAX" ; vTPM=$true ; NestedVirt=$true }
+$LABConfig.VMs += @{ VMName = "S2D2" ; Configuration = 'S2D' ; ParentVHD = 'WinSrvInsiderCore_29621.vhdx' ; HDDNumber = 4 ; HDDSize= 1TB ; MemoryStartupBytes= 4GB; VMProcessorCount="MAX" ; vTPM=$true ; NestedVirt=$true }
+
+#Management machine
+$LabConfig.VMs += @{ VMName = 'Management' ; ParentVHD = 'WinSrvInsider_29621.vhdx'; MGMTNICs=1 ; AddToolsVHD=$True }
+
+```
+
+
+
+
+
+
+## Prerequisites
+
+### Create Insider Lab Environment
+
+To create insider parent image, download latest iso from [Windows Server Insiders Downloads](https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewserver) and then follow steps in [01-Creating First Lab](../../HandsOnLabs/01-CreatingFirstLab/readme.md) with Labconfig above.
+
+### Build your cluster
+
+Once you're logged in the management machine, simply paste following PowerShell to build simple 2-node cluster
+
+```PowerShell
+#region Variables
+ #servers list
+ $Servers="S2D1","S2D2"
+ #Cluster Name
+ $ClusterName="S2D-Cluster"
+ #Witness Server
+ $WitnessServer="DC"
+#endregion
+
+#region install keys and activate servers
+ $AllServers=@("Management")+$Servers
+ Invoke-Command -ComputerName $AllServers -ScriptBlock {
+ $key = '2KNJJ-33Y9H-2GXGX-KMQWH-G6H67'
+ #install product key
+ $sls = Get-CimInstance -ClassName 'SoftwareLicensingService'
+ $arg = @{ 'ProductKey' = $key }
+ Invoke-CimMethod -InputObject $sls -MethodName 'InstallProductKey' -Arguments $arg
+ #activate
+ $slp = Get-CimInstance -ClassName 'SoftwareLicensingProduct' -Filter 'PartialProductKey <> null and ApplicationId = ''55c92734-d682-4d71-983e-d6ec3f16059f'''
+ Invoke-CimMethod -InputObject $slp -MethodName 'Activate'
+ #refresh license status
+ $sls = Get-CimInstance -ClassName 'SoftwareLicensingService'
+ Invoke-CimMethod -InputObject $sls -MethodName 'RefreshLicenseStatus'
+ }
+
+ #check status
+ Get-CimInstance SoftwareLicensingProduct -CimSession $AllServers |
+ Where-Object { $_.PartialProductKey -and $_.ApplicationID -eq '55c92734-d682-4d71-983e-d6ec3f16059f' } |
+ Select-Object Name, Description, LicenseStatus, PartialProductKey, PSComputerName,
+ @{N='LicenseStatusText';E={
+ switch ($_.LicenseStatus) {
+ 0 {'Unlicensed'}
+ 1 {'Licensed'}
+ 2 {'OOBGrace'}
+ 3 {'OOTGrace'}
+ 4 {'NonGenuineGrace'}
+ 5 {'Notification'}
+ 6 {'ExtendedGrace'}
+ default {'Unknown'}
+ }
+ }}
+#endregion
+
+#region install required features
+ #install features for management (assuming you are running these commands on Windows Server with GUI)
+ Install-WindowsFeature -Name NetworkATC,RSAT-Clustering,RSAT-Clustering-Mgmt,RSAT-Clustering-PowerShell,RSAT-Hyper-V-Tools,RSAT-Feature-Tools-BitLocker-BdeAducExt,RSAT-AD-PowerShell,RSAT-AD-AdminCenter,RSAT-DHCP,RSAT-DNS-Server
+
+ #install roles and features on servers
+ #install Hyper-V using DISM if Install-WindowsFeature fails (if nested virtualization is not enabled install-windowsfeature fails)
+ Invoke-Command -ComputerName $servers -ScriptBlock {
+ $Result=Install-WindowsFeature -Name "Hyper-V" -ErrorAction SilentlyContinue
+ if ($result.ExitCode -eq "failed"){
+ Enable-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V -Online -NoRestart
+ }
+ }
+ #define and install other features
+ $features="Failover-Clustering","RSAT-Clustering-PowerShell","Hyper-V-PowerShell","NetworkATC","Data-Center-Bridging","RSAT-DataCenterBridging-LLDP-Tools","FS-SMBBW","System-Insights","RSAT-System-Insights"
+ Invoke-Command -ComputerName $servers -ScriptBlock {Install-WindowsFeature -Name $using:features}
+#endregion
+
+#region restart servers to apply
+ Restart-Computer $servers -Protocol WSMan -Wait -For PowerShell -Force
+ Start-Sleep 20 #Failsafe as Hyper-V needs 2 reboots and sometimes it happens, that during the first reboot the restart-computer evaluates the machine is up
+ #make sure computers are restarted
+ Foreach ($Server in $Servers){
+ do{$Test= Test-NetConnection -ComputerName $Server -CommonTCPPort WINRM}while ($test.TcpTestSucceeded -eq $False)
+ }
+#endregion
+
+#region Create cluster
+ #Create Cluster
+ New-Cluster -Name $ClusterName -Node $servers
+ Start-Sleep 5
+ Clear-DnsClientCache
+
+ ##Configure Witness on WitnessServer
+ #Create new directory
+ $WitnessName=$Clustername+"Witness"
+ Invoke-Command -ComputerName $WitnessServer -ScriptBlock {new-item -Path c:\Shares -Name $using:WitnessName -ItemType Directory -ErrorAction Ignore}
+ $accounts=@()
+ $accounts+="$env:userdomain\$ClusterName$"
+ $accounts+="$env:userdomain\$env:USERNAME"
+ #$accounts+="$env:userdomain\Domain Admins"
+ New-SmbShare -Name $WitnessName -Path "c:\Shares\$WitnessName" -FullAccess $accounts -CimSession $WitnessServer
+ #Set NTFS permissions
+ Invoke-Command -ComputerName $WitnessServer -ScriptBlock {(Get-SmbShare $using:WitnessName).PresetPathAcl | Set-Acl}
+ #Set Quorum
+ Set-ClusterQuorum -Cluster $ClusterName -FileShareWitness "\\$WitnessServer\$WitnessName"
+#endregion
+
+#region Configure networking with NetATC https://techcommunity.microsoft.com/t5/networking-blog/network-atc-what-s-coming-in-azure-stack-hci-22h2/ba-p/3598442
+ #make sure NetATC,FS-SMBBW and other required features are installed on servers
+ Invoke-Command -ComputerName $Servers -ScriptBlock {
+ Install-WindowsFeature -Name NetworkATC,Data-Center-Bridging,RSAT-Clustering-PowerShell,RSAT-Hyper-V-Tools,FS-SMBBW
+ }
+
+ #in virtual environment, then skip RDMA config
+
+ Import-Module NetworkATC
+ #virtual environment (skipping RDMA config)
+ $AdapterOverride = New-NetIntentAdapterPropertyOverrides
+ $AdapterOverride.NetworkDirect = 0
+ Add-NetIntent -ClusterName $ClusterName -Name ConvergedIntent -Management -Compute -Storage -AdapterName "Ethernet","Ethernet 2" -AdapterPropertyOverrides $AdapterOverride -Verbose #-StorageVlans 1,2
+
+
+ #check
+ Start-Sleep 20 #let intent propagate a bit
+ Write-Output "applying intent"
+ do {
+ $status=Get-NetIntentStatus -ClusterName $ClusterName
+ Write-Host "." -NoNewline
+ Start-Sleep 5
+ } while ($status.ConfigurationStatus -contains "Provisioning" -or $status.ConfigurationStatus -contains "Retrying")
+
+ #remove if necessary
+ <#
+ Invoke-Command -ComputerName $servers[0] -ScriptBlock {
+ $intents = Get-NetIntent
+ foreach ($intent in $intents){
+ Remove-NetIntent -Name $intent.IntentName
+ }
+ }
+ #>
+
+ #if deploying in VMs, some nodes might fail (quarantined state) and even CNO can go to offline ... go to cluadmin and fix
+ #Get-ClusterNode -Cluster $ClusterName | Where-Object State -eq down | Start-ClusterNode -ClearQuarantine
+#endregion
+
+#region Enable S2D
+ #Enable-ClusterS2D
+ Enable-ClusterS2D -CimSession $ClusterName -confirm:0 -Verbose
+
+#endregion
+
+#region create sample volumes
+ #create 1TB volume on each node
+ foreach ($Server in $Servers){
+ New-Volume -StoragePoolFriendlyName "S2D on $ClusterName" -FriendlyName $Server -Size 1TB -CimSession $ClusterName
+ }
+
+ #align volumes ownership to with servers
+ foreach ($Server in $Servers){
+ Move-ClusterSharedVolume -Name "Cluster Virtual Disk ($Server)" -Node $Server -Cluster $ClusterName
+ }
+#endregion
+
+
+
+```
+
+
+
+
+## Trusted Launch for virtual machines (TVMs)
+
+More information about Trusted Launch - https://techcommunity.microsoft.com/blog/windowsservernewsandbestpractices/announcing-trusted-launch-for-virtual-machines-for-windows-server-insiders/4537082
+
+### Enable TVM feature
+
+```PowerShell
+$Servers="S2D1","S2D2"
+
+Invoke-command -ComputerName $Servers -ScriptBlock {
+ #Set registry keys
+ New-Item -Path "HKLM:\SOFTWARE\Microsoft\AszIgvmAgent" -Force
+ New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\AszIgvmAgent" -Name "TvmWinServer" -Value 1 -PropertyType DWord -Force
+ #enable feature
+ Enable-WindowsOptionalFeature -Online -FeatureName "IsolatedGuestVm" -NoRestart
+}
+
+```
+
+### Validate if TVM is running
+
+```PowerShell
+ Get-Service -ComputerName $Servers -Name "IGVmAgent"
+
+```
+
+### Create VM
+
+```PowerShell
+ New-VM -Name "TVMTest01" -Generation 2 -GuestStateIsolationType TrustedLaunch -SwitchName (get-virtualswitch -cimsession $Servers[0]).Name -Path C:\ClusterStorage\S2D1\ -CimSession $Servers[0]
+ #add as Highly Available
+ Add-ClusterVirtualMachineRole -VirtualMachine "TVMTest01" -Cluster $ClusterName
+ #Start
+ Start-ClusterGroup -Name "TVMTest01" -Cluster $ClusterName
+```
+
+
+
+### Test TVM - disable IGVmAgent
+
+```PowerShell
+ Invoke-Command -ComputerName $Servers -ScriptBlock {
+ Stop-Service -Name "IGVmAgent"
+ }
+ #restart VM
+ Stop-ClusterGroup -Name "TVMTest01" -Cluster $ClusterName
+ Start-ClusterGroup -Name "TVMTest01" -Cluster $ClusterName
+
+```
+
+
+
+### Test TVM - enable IGVmAgent Again
+
+```PowerShell
+ Invoke-Command -ComputerName $Servers -ScriptBlock {
+ Start-Service -Name "IGVmAgent"
+ }
+ #restart VMV
+ Start-ClusterGroup -Name "TVMTest01" -Cluster $ClusterName
+
+```
+
+
+
+## Quick Machine recovery
+
+https://learn.microsoft.com/en-us/windows/configuration/quick-machine-recovery/
+
+
+```PowerShell
+#run from management machine
+#Enable test mode
+reagentc.exe /SetRecoveryTestmode
+#Configure Windows to boot to Windows Recovery Environment on the next boot:
+reagentc.exe /BootToRe
+#reboot machine
+Restart-Computer
+
+```
+
+
+
diff --git a/README.md b/README.md
index 2eb544a5..17e162ec 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,7 @@
## tl;dr
-To start using MSLab just download the latest version of the scripts from the [Releases](https://github.com/microsoft/MSLab/releases) section of this repository or follow [Dell GEOS hands-on-labs](https://www.geos.to/azslabs).
-
+To start using MSLab just download the latest version of the scripts from the [Releases](https://github.com/microsoft/MSLab/releases) or start with [Hands-on-Labs](HandsOnLabs/).
💡 Shortcut to the latest version is https://aka.ms/mslab/download
@@ -11,18 +10,31 @@ To start using MSLab just download the latest version of the scripts from the [R
+
+## NEW - Hands-on-Labs
+
+Originally I developed Hands-on Labs when I was working for Dell at https://github.com/DellGEOS/AzureStackHOLs. To continue with just virtual labs, MSLab HOLs are now being added.
+
+* [01 Creating First Lab](HandsOnLabs/01-CreatingFirstLab/)
+* [02 Deploying Azure Local](HandsOnLabs/02-DeployingAzureLocal/)
+* [03 Rack Level Nested Mirror S2D Cluster (Insider Preview)](HandsOnLabs/03-RackLevelNestedMirror/)
+* [04 Volumes Deep Dive](HandsOnLabs/04-VolumesDeepDive/)
+* [05 Simplified Machine Provisioning](HandsOnLabs/05-SimplifiedMachineProvisioning/)
+* [06 Testing Windows Server Insider Preview](HandsOnLabs/06-TestingWindowsServerInsider/)
+
## Introduction
- [MSLab](#mslab)
- [tl;dr](#tldr)
+ - [NEW - Hands-on-Labs](#new---hands-on-labs)
- [Introduction](#introduction)
- [Requirements](#requirements)
- [Scripts](#scripts)
- [Data Collection](#data-collection)
- [How to get the Scripts](#how-to-get-the-scripts)
- - [Scenarios](#scenarios)
+ - [Scenarios Archived](#scenarios-archived)
- [Use cases](#use-cases)
- [Prototyping](#prototyping)
- [Hands on Labs](#hands-on-labs)
@@ -64,9 +76,11 @@ In the past, this ZIP file was stored in the git repository, and recently we swi
This built ZIP file is more optimized, e. g. the file 0_Shared.ps1 is in-lined to the rest of the scripts to keep the number of MSLab files as low as possible. Compared to the git repository where I tend to split those scripts to multiple independent files for a better supportability on our side.
-## Scenarios
+## Scenarios (Archived)
+
+Over the time, we have developed multiple [scenarios](Scenarios/) simulating Azure Stack HCI and even deep dives into other technologies such as [Windows Admin Center](Scenarios/Windows%20Admin%20Center%20and%20Enterprise%20CA), [Certification Authority](Scenarios/Certification%20Authority) or [Just Enough Administration](Scenarios/BitLocker%20with%20JEA). Scenarios can be reused for real environments. For example [S2D Hyperconverged](Scenarios/S2D%20Hyperconverged) can be used to deploy real Azure Stack HCI clusters. It has not been updated lately as all effort went to [Dell Azure Local HOLs](https://github.com/DellGEOS/AzureLocalHOLs).
-Over the time, we have developed multiple [scenarios](Scenarios/) simulating Azure Stack HCI and even deep dives into other technologies such as [Windows Admin Center](Scenarios/Windows%20Admin%20Center%20and%20Enterprise%20CA), [Certification Authority](Scenarios/Certification%20Authority) or [Just Enough Administration](Scenarios/BitLocker%20with%20JEA). Scenarios can be reused for real environments. For example [S2D Hyperconverged](Scenarios/S2D%20Hyperconverged) can be used to deploy real Azure Stack HCI clusters.
+It has not been updated for quite some time, so consider it just a inspiration.
## Use cases
diff --git a/Scripts/3_Deploy.ps1 b/Scripts/3_Deploy.ps1
index 1400849a..a6b8cabd 100644
--- a/Scripts/3_Deploy.ps1
+++ b/Scripts/3_Deploy.ps1
@@ -1,4 +1,4 @@
-# Verify Running as Admin
+# Verify Running as Admin
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
If (-not $isAdmin) {
Write-Host "-- Restarting as Administrator" -ForegroundColor Cyan ; Start-Sleep -Seconds 1
@@ -40,15 +40,6 @@ If (-not $isAdmin) {
$fileContent = @"
-
-
-
-
- $Blob
-
-
-
-
@@ -66,6 +57,18 @@ If (-not $isAdmin) {
+
+
+
+
+ $Blob
+
+
+
$oeminformation
PFE
@@ -434,7 +437,13 @@ If (-not $isAdmin) {
New-VHD -ParentPath $serverparent.fullname -Path $vhdpath
}else{
WriteInfo "`t Creating blank OS VHD"
- New-VHD -Path $vhdpath -SizeBytes 127GB
+ if ($VMConfig.BlankVHDSize){
+ WriteInfo "`t`t Blank OS VHD Size is $($VMConfig.BlankVHDSize)"
+ New-VHD -Path $vhdpath -SizeBytes $VMConfig.BlankVHDSize
+ }else{
+ WriteInfo "`t`t Blank OS VHD Size is default - 250GB"
+ New-VHD -Path $vhdpath -SizeBytes 250GB
+ }
}
if ($VMConfig.VMVersion){
@@ -582,7 +591,13 @@ If (-not $isAdmin) {
WriteInfo "`t VM Version is $($BuildVersion.Build).$($BuildVersion.Revision)"
}else{
WriteInfo "`t Creating blank OS VHD"
- New-VHD -Path $vhdpath -SizeBytes 127GB
+ if ($VMConfig.BlankVHDSize){
+ WriteInfo "`t`t Blank OS VHD Size is $($VMConfig.BlankVHDSize)"
+ New-VHD -Path $vhdpath -SizeBytes $VMConfig.BlankVHDSize
+ }else{
+ WriteInfo "`t`t Blank OS VHD Size is default - 250GB"
+ New-VHD -Path $vhdpath -SizeBytes 250GB
+ }
}
WriteInfo "`t Creating VM"
@@ -821,7 +836,7 @@ If (-not $isAdmin) {
$unattendfile=CreateUnattendFileNoDjoin -ComputerName $Name -AdminPassword $LabConfig.AdminPassword -RunSynchronous $RunSynchronous -TimeZone $TimeZone
}
}elseif($VMConfig.Win2012Djoin -or $VMConfig.Unattend -eq "DjoinCred"){
- WriteInfoHighlighted "`t Creating Unattend with win2012-ish domain join"
+ WriteInfoHighlighted "`t Creating Unattend with credentials domain join"
$unattendfile=CreateUnattendFileWin2012 -ComputerName $Name -AdminPassword $LabConfig.AdminPassword -DomainName $Labconfig.DomainName -RunSynchronous $RunSynchronous -TimeZone $TimeZone
}elseif($VMConfig.Unattend -eq "DjoinBlob" -or -not ($VMConfig.Unattend)){
@@ -839,7 +854,6 @@ If (-not $isAdmin) {
if ($unattendFile){
WriteInfo "`t Adding unattend to VHD"
Mount-WindowsImage -Path $mountdir -ImagePath $VHDPath -Index 1
- Use-WindowsUnattend -Path $mountdir -UnattendPath $unattendFile
#&"$PSScriptRoot\Tools\dism\dism" /mount-image /imagefile:$vhdpath /index:1 /MountDir:$mountdir
#&"$PSScriptRoot\Tools\dism\dism" /image:$mountdir /Apply-Unattend:$unattendfile
New-item -type directory "$mountdir\Windows\Panther" -ErrorAction Ignore
@@ -970,7 +984,7 @@ If (-not $isAdmin) {
$TimeZone=(Get-TimeZone).id
#Grab number of processors
- Get-CimInstance -ClassName "win32_processor" | ForEach-Object { $global:NumberOfLogicalProcessors += $_.NumberOfLogicalProcessors }
+ Get-CimInstance -ClassName "win32_processor" | ForEach-Object { $global:NumberOfLogicalProcessors += $_.NumberOfEnabledCore }
#Calculate highest VLAN (for additional subnets)
[int]$HighestVLAN=$LabConfig.AllowedVLANs -split "," -split "-" | Select-Object -Last 1
diff --git a/Scripts/LabConfig.ps1 b/Scripts/LabConfig.ps1
index 1f696afd..5b8990c3 100644
--- a/Scripts/LabConfig.ps1
+++ b/Scripts/LabConfig.ps1
@@ -1,16 +1,17 @@
-#basic config for Windows Server 2022, that creates VMs for S2D Hyperconverged scenario https://github.com/Microsoft/MSLab/tree/master/Scenarios/S2D%20Hyperconverged
+#basic config for Windows Server 2025 S2D with 4 nodes
$LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'MSLab-' ; DCEdition='4'; Internet=$true ; AdditionalNetworksConfig=@(); VMs=@()}
-# Windows Server 2022
-1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "S2D$_" ; Configuration = 'S2D' ; ParentVHD = 'Win2022Core_G2.vhdx'; SSDNumber = 0; SSDSize=800GB ; HDDNumber = 12; HDDSize= 4TB ; MemoryStartupBytes= 512MB }}
-# Or Azure Stack HCI 23H2 (non-domain joined) https://github.com/DellGEOS/AzureStackHOLs/tree/main/lab-guides/01a-DeployAzureStackHCICluster-CloudBasedDeployment
-#1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName = "ASNode$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI23H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 24GB; VMProcessorCount=16 ; vTPM=$true ; Unattend="NoDjoin" ; NestedVirt=$true }}
-# Or Windows Server 2025 https://github.com/DellGEOS/AzureLocalHOLs/tree/main/lab-guides/03-TestingWindowsServer2025
-#1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName="S2D$_" ; Configuration='S2D' ; ParentVHD='Win2025Core_G2.vhdx' ; HDDNumber=4 ; HDDSize=2TB ; MemoryStartupBytes=1GB; VMProcessorCount=4 ; vTPM=$true}}
+# Windows Server 2025 https://github.com/DellGEOS/AzureLocalHOLs/tree/main/lab-guides/03-TestingWindowsServer2025
+1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "S2D$_" ; Configuration = 'S2D' ; ParentVHD = 'Win2025Core_G2.vhdx'; SSDNumber = 0; SSDSize=800GB ; HDDNumber = 12; HDDSize= 4TB ; MemoryStartupBytes= 512MB }}
+# Or Azure Stack HCI 23H2 (non-domain joined) https://github.com/microsoft/MSLab/tree/master/HandsOnLabs/02-DeployingAzureLocal
+#1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName = "ALNode$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI24H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 1TB ; MemoryStartupBytes= 24GB; VMProcessorCount="MAX" ; vTPM=$true ; Unattend="NoDjoin" ; NestedVirt=$true }}
+# Or Windows Server Insider https://github.com/microsoft/MSLab/tree/master/HandsOnLabs/03-RackLevelNestedMirror
+#1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName="SiteA_$_" ; Configuration='S2D' ; ParentVHD='WinSrvInsiderCore_26445.vhdx' ; HDDNumber=4 ; HDDSize=2TB ; MemoryStartupBytes=1GB; VMProcessorCount=4 ; vTPM=$true}}
+#1..2 | ForEach-Object {$LABConfig.VMs += @{ VMName="SiteB_$_" ; Configuration='S2D' ; ParentVHD='WinSrvInsiderCore_26445.vhdx' ; HDDNumber=4 ; HDDSize=2TB ; MemoryStartupBytes=1GB; VMProcessorCount=4 ; vTPM=$true}}
### HELP ###
-#If you need more help or different configuration options, ping us at jaromir.kaspar@dell.com or vlmach@microsoft.com
+#If you need more help or different configuration options, ping us at v-jkaspar@microsoft.com or vlmach@microsoft.com
#region Same as above, but with more explanation
<#
@@ -357,6 +358,10 @@ $LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPass
enables/disables secure boot for VM
possible values: windows,linux,disabled
Default: windows for windows machines, disabled for linux VMs
+
+ #BlankVHDSize
+ Example BlankVHDSize="250GB"
+ If ParentVHD is not specified, then new blank VHD will be created with this size. Default is 250GB
#>
#endregion
diff --git a/Tools/DownloadLatestCUs.ps1 b/Tools/DownloadLatestCUs.ps1
index fc28bff8..5aaf5dd8 100644
--- a/Tools/DownloadLatestCUs.ps1
+++ b/Tools/DownloadLatestCUs.ps1
@@ -49,6 +49,7 @@ $Products+=@{Product="Windows Server 2016" ;SearchStrin
$Products+=@{Product="Windows 11 24H2" ;SearchString="Cumulative Update for Windows 11 Version 24H2 for x64-based Systems" ;SSUSearchString=$null ; ID="Windows 11"}
$Products+=@{Product="Windows 11 23H2" ;SearchString="Cumulative Update for Windows 11 Version 23H2 for x64-based Systems" ;SSUSearchString=$null ; ID="Windows 11"}
$Products+=@{Product="Windows 11 22H2" ;SearchString="Cumulative Update for Windows 11 Version 22H2 for x64-based Systems" ;SSUSearchString=$null ; ID="Windows 11"}
+$Products+=@{Product="Windows 10 22H2" ;SearchString="Cumulative Update for Windows 10 Version 22H2 for x64-based Systems" ;SSUSearchString="Servicing Stack Update for Windows 10 Version 22H2 for x64-based Systems" ; ID="Windows 10, version 1903 and later, Windows 10 LTSB"} #SSU no longer required but remain for compat reasons
$Products+=@{Product="Windows 10 21H2" ;SearchString="Cumulative Update for Windows 10 Version 21H2 for x64-based Systems" ;SSUSearchString="Servicing Stack Update for Windows 10 Version 21H2 for x64-based Systems" ; ID="Windows 10, version 1903 and later, Windows 10 LTSB"} #SSU no longer required but remain for compat reasons
$Products+=@{Product="Windows 10 20H2" ;SearchString="Cumulative Update for Windows 10 Version 20H2 for x64-based Systems" ;SSUSearchString="Servicing Stack Update for Windows 10 Version 20H2 for x64-based Systems" ; ID="Windows 10, version 1903 and later"} #SSU no longer required but remain for compat reasons
$Products+=@{Product="Windows 10 2004" ;SearchString="Cumulative Update for Windows 10 Version 2004 for x64-based Systems" ;SSUSearchString="Servicing Stack Update for Windows 10 Version 2004 for x64-based Systems" ; ID="Windows 10, version 1903 and later"} #SSU no longer required but remain for compat reasons