This article is intended to guide you to build a lab environment where you can know almost all Windows Server 2016 new roles and features, and others not so new, but very useful:

  • Nested Virtualization
  • Nano Server
  • Stretch Cluster
  • Storage Replica
  • Storage Spaces Direct
  • PowerShell Direct
  • Deduplication
  • ReFS
  • Thin and Tiers
  • Infrastructure as a Code (IaaC)


At the picture below, we can understand that our lab will start at Level 2 of Nested Virtualization.

The intention is to use an Azure E2s v3 Virtual Machine as a base (AzureVM, Level 2), that supports Nested Virtualization. Install Hyper-V role in it and create three VMs. All O.S. will be Windows Server 2016. One VM will be the Domain Controller (DC, Level 3) and two VMs will be Nano Servers (NanoHost01/NanoHost02, Level 3) with Storage, Hyper-V and Failover Cluster roles.

The Nano Servers will form a Storage Spaces Direct and failover cluster (NanoCluster, Level 3) sharing a CSV to Hyper-V create two new nested VMs (Guest01/Guest02, Level 4) with Storage Replica, Failover Cluster, Deduplication and File Server, creating a Stretch Cluster (GuestCluster, Level 4) with Storage Replica and deduplication.

Let's summarize all necessary to start:

Create Azure VM

Create an Azure Virtual Machine E2s v3 named AzureVM.

Add Data Disk

Add a 200GB (SSD) data disk to AzureVM with Read/Write Cache and format as NTFS and assign the H letter.

Connect  To VM With RDP

At this point, we need to connect at AzureVM through RDP, open PowerShell ISE as Administrator and follow one of the methods. · Install Hyper-V and Deduplication features and restart:

Install-WindowsFeature -Name Hyper-V, FS-Data-Deduplication -IncludeAllSubFeature -IncludeManagementTools -Restart

Turn 200GB disk online, initialize as GPT, create an H volume and format as NTFS:
Get-Disk | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -AssignDriveLetter H -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel "Hyper-V" -Confirm:$false

Configure Data Deduplication

The next step is to configure data deduplication at H: as HyperV:


IaaC Method

At AzureVM, just run the H:\IaaC.ps1

PowerShell Method

A good start is creating a transcript file, logging all PowerShell output for further analysis or deployment documentation.


Start-Transcript -Path C:\Transcript.log

We will need to authenticate created VMs using the password defined in Unattended files. For this lab, we set all VMs passwords as P@ssw0rd. As a security best practice, we didn't hardcode the password, asking for it at runtime.

$SecurePassword = Read-Host -Prompt "Enter password" -AsSecureString

Mount the ISO that we'll use to generate the VHDX

$ISO = 'H:\en_windows_server_2016_x64_dvd_9718492.iso'
Mount-DiskImage -ImagePath $ISO | Out-Null
Set-Variable -Name "Drive" -Scope Global -Value ((Get-DiskImage -ImagePath $ISO | Get-Volume).DriveLetter)

Prepare some AzureVM Hyper-V and network options to provide route and internet (NAT) between levels of nested virtualization.

Set-VMhost -EnableEnhancedSessionMode $true
New-VMSwitch -Name 'BR-SP-PRD' -SwitchType Internal
New-VMSwitch -Name 'Cluster PRD' -SwitchType Internal
New-VMSwitch -Name 'BR-SP-DR' -SwitchType Internal
New-VMSwitch -Name 'Cluster DR' -SwitchType Internal
New-NetIPAddress -IPAddress -PrefixLength 24 -InterfaceIndex (Get-NetAdapter -Name '*BR-SP-PRD*').InterfaceIndex
New-NetIPAddress -IPAddress -PrefixLength 24 -InterfaceIndex (Get-NetAdapter -Name '*Cluster PRD*').InterfaceIndex
New-NetIPAddress -IPAddress -PrefixLength 24 -InterfaceIndex (Get-NetAdapter -Name '*BR-SP-DR*').InterfaceIndex
New-NetIPAddress -IPAddress -PrefixLength 24 -InterfaceIndex (Get-NetAdapter -Name '*Cluster DR*').InterfaceIndex
New-NetRoute  -DestinationPrefix -InterfaceAlias "vEthernet (BR-SP-PRD)"  -AddressFamily IPv4 -NextHop
New-NetRoute -DestinationPrefix  -InterfaceAlias "vEthernet (Cluster PRD)" -AddressFamily IPv4 -NextHop
New-NetRoute -DestinationPrefix  -InterfaceAlias "vEthernet (BR-SP-DR)" -AddressFamily IPv4-NextHop
New-NetRoute -DestinationPrefix  -InterfaceAlias "vEthernet (Cluster DR)" -AddressFamily IPv4 -NextHop
New-NetNat -Name MyNATnetwork -InternalIPInterfaceAddressPrefix
Get-NetAdapter | Set-NetIPInterface -Forwarding Enabled


The first computer to support our lab is the Domain Controller (DC)

Function CreateDC ($Computer,$Volume){
    # Convert ISO to VHDX
    . ${Drive}:\NanoServer\NanoServerImageGenerator\Convert-WindowsImage.ps1
    Convert-WindowsImage -SourcePath "${Drive}:\sources\install.wim" -Edition Datacenter -VHDPath "${Volume}:\Hyper-V\$Computer\Virtual Hard Disks\$Computer-C.vhdx" -VHDFormat VHDX -DiskLayout UEFI -RemoteDesktopEnable -BCDinVHD VirtualMachine -UnattendPath H:\DC-Unattend.xml
    # Cria, configura e inicia a VM
    New-VM -Name $Computer -Path ${Volume}:\Hyper-V -MemoryStartupBytes 2048MB -Generation 2 -VHDPath "${Volume}:\Hyper-V\$Computer\Virtual Hard Disks\$Computer-C.vhdx" -SwitchName 'BR-SP-PRD'
    Set-VMProcessor –VMName $Computer -Count 2
    Rename-VMNetworkAdapter -VMName $Computer -Name 'Network Adapter' -NewName 'BR-SP-PRD'
    Get-VM -Name DC | Add-VMDvdDrive
    Set-VMDvdDrive -VMName DC -Path $ISO
    Start-VM -Name $Computer
CreateDC -Computer "DC" -Volume "H"


While DC start we can create both NanoHost01 and NanoHost02 with Windows Server 2016 Datacenter Nano that will form the Storage Spaces Direct and Hyper-V Failover Cluster (NanoCluster) with CSV to support the next Level (3) of Nested VM Guests.


Function CreateNano ($Computer,$IPPRD,$IPGW,$Volume,$Enviroment){
    # Convert ISO to VHDX
    Import-Module ${Drive}:\NanoServer\NanoServerImageGenerator
    New-NanoServerImage -Edition Datacenter -DeploymentType Guest -MediaPath "${Drive}:\" -BasePath ${Volume}:\Hyper-V\Base -TargetPath "${Volume}:\Hyper-V\$Computer\Virtual Hard Disks\$Computer-C.vhdx" -ComputerName $Computer -Compute -Clustering -Storage -AdministratorPassword $SecurePassword -InterfaceNameOrIndex 'Ethernet' -Ipv4Address $IPPRD -Ipv4SubnetMask -Ipv4Gateway $IPGW -Ipv4Dns -EnableRemoteManagementPort -UnattendPath 'H:\Nano-Unattend.xml'
    # Create, start and configure the VM
    New-VM -Name $Computer -Path ${Volume}:\Hyper-V -MemoryStartupBytes 4352MB -Generation 2 -VHDPath "${Volume}:\Hyper-V\$Computer\Virtual Hard Disks\$Computer-C.vhdx" -SwitchName "BR-SP-$Enviroment"
    # Enable Nested Virtualization
    Set-VMProcessor -VMName $Computer -ExposeVirtualizationExtensions $true -Count 2
    Set-VMMemory -VMName $Computer -DynamicMemoryEnabled $false
    New-VHD -Path "${Volume}:\Hyper-V\$Computer\Virtual Hard Disks\$Computer-SSD.vhdx" -SizeBytes 10GB -Dynamic
    New-VHD -Path "${Volume}:\Hyper-V\$Computer\Virtual Hard Disks\$Computer-HDD.vhdx" -SizeBytes 60GB -Dynamic
    Add-VMHardDiskDrive -VMName $Computer -ControllerType SCSI -Path "${Volume}:\Hyper-V\$Computer\Virtual Hard Disks\$Computer-SSD.vhdx" -ControllerNumber 0 -ControllerLocation 1
    Add-VMHardDiskDrive -VMName $Computer -ControllerType SCSI -Path "${Volume}:\Hyper-V\$Computer\Virtual Hard Disks\$Computer-HDD.vhdx" -ControllerNumber 0 -ControllerLocation 2
    Enable-VMIntegrationService -Name "Guest Service Interface" -VMName $Computer
    Set-VMFirmware –Vmname $Computer -EnableSecureBoot Off
    Start-VM -Name $Computer
    Start-Sleep -Seconds 20
    ${NICPRD} = (Get-VMNetworkAdapter -VMName $Computer | Where-Object IPAddresses -Match 192.168.).Name
    Rename-VMNetworkAdapter -VMName $Computer -Name ${NICPRD} -NewName "BR-SP-$Enviroment"
    Set-VMNetworkAdapter -VMName $Computer -Name "BR-SP-$Enviroment" -MacAddressSpoofing On
    Add-VMNetworkAdapter -VMName $Computer -Name "Cluster $Enviroment" -SwitchName "Cluster $Enviroment"
    Set-VMNetworkAdapter -VMName $Computer -Name "Cluster $Enviroment" -MacAddressSpoofing On
CreateNano -Computer NanoHost01 -IPPRD -IPGW -Volume "H" -Enviroment 'PRD'
CreateNano -Computer NanoHost02 -IPPRD -IPGW -Volume "H" -Enviroment 'DR'


While NanoHosts do its first start, let’s configure and promote the DC using PowerShell Direct from AzureVM (host) to DC (guest), without the need of network or IP configured at guest NIC at this time.


Function ConfigureDC{
    $cred= New-Object System.Management.Automation.PSCredential ("DC\Administrator",$SecurePassword)
    # PowerShell Direct
    Invoke-Command -VMName DC -Credential $cred {param($SecurePassword)
        # Configure Network
        Get-NetAdapter | Rename-NetAdapter -NewName "BR-SP-PRD"
        New-NetIPAddress -InterfaceAlias "BR-SP-PRD" -IPAddress '' -AddressFamily IPv4 -PrefixLength 24 -DefaultGateway ''
        # As PDC of root forest, it need to sync with external source
        Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" -Name "Type" -Value "NTP" -Force
        # Install Roles and Features
        Add-WindowsFeature AD-Domain-Services,RSAT-ADDS,RSAT-ADDS-Tools,RSAT-AD-PowerShell,DNS,RSAT-DNS-Server,RSAT-Hyper-V-Tools,Hyper-V-Powershell,RSAT-Clustering,RSAT-Clustering-Powershell,FS-Data-Deduplication
        # Configure DNS Server and Client
        $DNS = Get-DnsServerSetting -All ; $DNS.ListeningIPAddress = @("") ; Set-DNSServerSetting -InputObject $DNS
        Set-DnsClientServerAddress -InterfaceAlias "BR-SP-PRD" -ServerAddresses
        # Promote as Domain Controller
        Install-ADDSForest -CreateDnsDelegation:$false -DatabasePath "C:\Windows\NTDS" -DomainMode "WinThreshold" -DomainName "bauab.local" -DomainNetbiosName "BAUAB" -ForestMode "WinThreshold" -InstallDns:$true -LogPath "C:\Windows\NTDS" -SysvolPath "C:\Windows\SYSVOL" -Force:$true -SafeModeAdministratorPassword $SecurePassword
    } -ArgumentList ($SecurePassword)
    Start-Sleep -Seconds 600


Once promoted, we need to keep the domain credential for future logons


$DomainCred = New-Object System.Management.Automation.PSCredential ("Bauab\Administrator",$SecurePassword)

Now we can configure DC Roles, adjusting Sites, Site Links, Subnets, DNS Zones and Scavenging.


Function ConfigureDCRoles{
    Invoke-Command -VMName DC -Credential $DomainCred {
        Get-ADReplicationSite -Filter {Name -eq "Default-First-Site-Name"} | Rename-ADObject -NewName "BR-SP-PRD"
        Get-ADReplicationSite -Filter {Name -eq 'BR-SP-PRD'} | Set-ADReplicationSite -Description 'Production Site'
        New-ADReplicationSite -Name BR-SP-DR -Description 'Disaster Recovery Site'
        Get-ADReplicationSiteLink -Filter {Name -eq "DEFAULTIPSITELINK"} | Rename-ADObject -NewName "BR-SP-PRD<>BR-SP-DR"
        Get-ADReplicationSiteLink -Filter {Name -eq "BR-SP-PRD<>BR-SP-DR"} | Set-ADReplicationSiteLink -SitesIncluded @{Add="BR-SP-DR"}
        New-ADReplicationSubnet -Name -Description "Production" -Location "São Paulo,Brasil" -Site "BR-SP-PRD"
        New-ADReplicationSubnet -Name -Description "Cluster Production" -Location "São Paulo,Brasil" -Site "BR-SP-PRD"
        New-ADReplicationSubnet -Name -Description "Disaster Recovery" -Location "São Paulo,Brasil" -Site "BR-SP-DR"
        New-ADReplicationSubnet -Name -Description "Cluster Disaster Recovery" -Location "São Paulo,Brasil" -Site "BR-SP-DR"
        New-ADReplicationSubnet -Name -Description "Production" -Location "São Paulo,Brasil" -Site "BR-SP-PRD"
        New-ADReplicationSubnet -Name -Description "Cluster Production" -Location "São Paulo,Brasil" -Site "BR-SP-PRD"
        New-ADReplicationSubnet -Name -Description "Disaster Recovery" -Location "São Paulo,Brasil" -Site "BR-SP-DR"
        New-ADReplicationSubnet -Name -Description "Cluster Disaster Recovery" -Location "São Paulo,Brasil" -Site "BR-SP-DR"
        Add-DnsServerPrimaryZone -DynamicUpdate Secure -Name "" -ReplicationScope Domain
        Set-DnsServerScavenging -ScavengingState $true -ApplyOnAllZones -ScavengingInterval 7.00:00:00



Done, we are ready to join the NanoHosts to Bauab.local domain, configure VMSwitches, adapters and routes to support Guests VMs.


Function ConfigureNano ($Computer,$IPProd,$IPCluster,$IPClusterClient,$IPClusterOnly,$DestinationPrefix,$NextHop,$Enviroment){
    $cred = New-Object System.Management.Automation.PSCredential ("$Computer\Administrator",$SecurePassword)
    Invoke-Command -VMName DC -Credential $DomainCred -ScriptBlock {param($Computer,$IPProd,$cred)
        # Set NanoHosts as trusted hosts to stablish a WSMan session to copy the djoin file
        Set-Item WSMan:\localhost\Client\TrustedHosts $Computer -Concatenate -Force
        Add-DnsServerResourceRecordA -IPv4Address $IPProd -Name $Computer -ZoneName bauab.local -AllowUpdateAny -CreatePtr -AgeRecord
        $SessionNano = New-PSSession -ComputerName $Computer -Credential $cred
        Djoin.exe /provision /domain bauab.local /machine $Computer /savefile C:\$Computer.djoin
        Copy-Item C:\$Computer.djoin C:\ -ToSession $SessionNano
        } -ArgumentList ($Computer,$IPProd,$cred)
    Invoke-Command -VMName $Computer -Credential $cred -ScriptBlock {param($Computer,$IPCluster,$IPClusterClient,$IPClusterOnly,$DestinationPrefix,$NextHop,$Enviroment)
        Set-VMhost -EnableEnhancedSessionMode $true
        New-VMSwitch -Name "Cluster and Client" -SwitchType Internal
        New-VMSwitch -Name "Cluster Only" -SwitchType Internal
        Get-NetAdapter -Name "vEthernet (Cluster and Client)" | New-NetIPAddress -IPAddress $IPClusterClient -AddressFamily IPv4 -PrefixLength 24
        Get-NetAdapter -Name "vEthernet (Cluster Only)" | New-NetIPAddress -IPAddress $IPClusterOnly -AddressFamily IPv4 -PrefixLength 24
        Get-NetAdapter | Set-NetIPInterface -Forwarding Enabled
        Get-NetAdapter -Name ((Get-NetIPAddress | Where-Object {$_.IPAddress -Match '169.' -and $_.AddressState -eq 'Preferred' -and $_.AddressFamily -eq 'IPv4'}).InterfaceAlias) | Rename-NetAdapter -NewName "Cluster $Enviroment" | Set-DnsClient -RegisterThisConnectionsAddress $False
        New-NetIPAddress -InterfaceAlias "Cluster $Enviroment" -IPAddress $IPCluster -AddressFamily IPv4 -PrefixLength 24
        Get-NetAdapter -Name ((Get-NetIPAddress | Where-Object {$_.IPAddress -eq $IPProd -and $_.AddressState -eq 'Preferred'}).InterfaceAlias) | Rename-NetAdapter -NewName "BR-SP-$Enviroment"
        If ($Computer -eq 'NanoHost01'){
            New-NetRoute -DestinationPrefix -InterfaceAlias "vEthernet (Cluster Only)" -AddressFamily IPv4 -NextHop
            New-NetRoute -DestinationPrefix -InterfaceAlias "Cluster PRD" -AddressFamily IPv4 -NextHop
            New-NetRoute -DestinationPrefix -InterfaceAlias "Cluster PRD" -AddressFamily IPv4 -NextHop
        Else {
            New-NetRoute -DestinationPrefix -InterfaceAlias "vEthernet (Cluster Only)" -AddressFamily IPv4 -NextHop
            New-NetRoute -DestinationPrefix -InterfaceAlias "Cluster DR" -AddressFamily IPv4 -NextHop
            New-NetRoute -DestinationPrefix -InterfaceAlias "Cluster DR" -AddressFamily IPv4 -NextHop
        # You can adjust to reflect yours TZ
        Set-TimeZone "E. South America Standard Time"
        Djoin.exe /RequestODJ /loadfile C:\$Computer.djoin /windowspath c:\windows /localos
        } -ArgumentList ($Computer,$IPCluster,$IPClusterClient,$IPClusterOnly,$DestinationPrefix,$NextHop,$Enviroment)
    Stop-VM $Computer ; Start-VM $Computer
ConfigureNano -Computer "NanoHost01" -IPProd -IPCluster -IPClusterClient -IPClusterOnly -DestinationPrefix -NextHop -Enviroment 'PRD'
ConfigureNano -Computer "NanoHost02" -IPProd -IPCluster -IPClusterClient -IPClusterOnly -DestinationPrefix -NextHop -Enviroment 'DR'
Start-Sleep -Seconds 30




Now that we have both NanoHosts joined in domain, we can form the Hyper-V Failover Cluster (NanoCluster) using Storage Pool Direct, to provide the CSV for guest nested computers (Guest01\Guest02). We will set some VHDX as SSD and others as HDD just to try Tiers.

# Form Hyper-V Failover Cluster with Storage Spaces Direct with CSV and Tiers
Invoke-Command -VMName DC -Credential $DomainCred {
    New-Cluster -Name NanoCluster -Node NanoHost01,NanoHost02 -StaticAddress, -NoStorage
    New-Item C:\Quorum -ItemType Directory
    $acl = Get-Acl -Path C:\Quorum
    $perm = "bauab\NanoCluster$", 'Read,Modify', 'ContainerInherit, ObjectInherit', 'None', 'Allow'
    $rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $perm
    $acl | Set-Acl -Path C:\Quorum
    New-SmbShare -Name "Quorum" -Path "C:\Quorum" -FullAccess "bauab\NanoCluster$"
    Start-Sleep 60
    Set-ClusterQuorum -FileShareWitness \\dc\Quorum -Cluster NanoCluster.bauab.local
    (Get-ClusterNetwork -Cluster NanoCluster.bauab.local | Where-Object {$_.Address -eq ""}).Name = "BR-SP-PRD"
    (Get-ClusterNetwork -Cluster NanoCluster.bauab.local | Where-Object {$_.Address -eq ""}).Name = "Cluster PRD"
    (Get-ClusterNetwork -Cluster NanoCluster.bauab.local | Where-Object {$_.Address -eq ""}).Name = "BR-SP-DR"
    (Get-ClusterNetwork -Cluster NanoCluster.bauab.local | Where-Object {$_.Address -eq ""}).Name = "Cluster DR"
    (Get-ClusterNetwork -Cluster NanoCluster.bauab.local | Where-Object {$_.Address -eq ""}).Name = "Cluster and Client"
    (Get-ClusterNetwork -Cluster NanoCluster.bauab.local | Where-Object {$_.Address -eq ""}).Name = "Cluster Only"
    (Get-ClusterNetwork -Cluster NanoCluster.bauab.local | Where-Object {$_.Address -eq ""}).Name = "Cluster and Client DR"
    (Get-ClusterNetwork -Cluster NanoCluster.bauab.local | Where-Object {$_.Address -eq ""}).Name = "Cluster Only DR"
    # Enable Storage Pool Direct at cluster
    $ClusterSession = New-CimSession -ComputerName NanoCluster.bauab.local
    Enable-ClusterStorageSpacesDirect –CimSession $ClusterSession -CacheState Disabled -SkipEligibilityChecks -confirm:$false
    # Set SSD and HDD
    Get-PhysicalDisk -CimSession $ClusterSession | Where Size -EQ 10GB | Set-PhysicalDisk -CimSession $ClusterSession -MediaType SSD
    Get-PhysicalDisk -CimSession $ClusterSession | Where Size -EQ 60GB | Set-PhysicalDisk -CimSession $ClusterSession -MediaType HDD
    Get-PhysicalDisk -CimSession $ClusterSession | select Size,mediatype
    # Create Storage Tiers
    Get-StoragePool -CimSession $ClusterSession -FriendlyName "S2D*" | New-StorageTier –FriendlyName Performance –MediaType SSD
    Get-StorageTier -CimSession $ClusterSession | FT FriendlyName,Size
    Get-StoragePool -CimSession $ClusterSession -FriendlyName "S2D*" | FL Size, AllocatedSize
    Get-StorageTierSupportedSize -CimSession $ClusterSession Performance | FT -AutoSize
    Get-StorageTierSupportedSize -CimSession $ClusterSession Capacity | FT -AutoSize
    # Create disk and CSV volume with tiers SSD and HDD
    $Volume = New-Volume -CimSession $ClusterSession -StoragePoolFriendlyName "S2D*" -FriendlyName VMs -FileSystem CSVFS_NTFS -StorageTierFriendlyNames Performance,Capacity -StorageTierSizes 7GB,56GB -ResiliencySettingName Mirror -WriteCacheSize 1GB -ProvisioningType Thin



With NanoCluster ready, we proceed to deploy and start two Windows 2016 Datacenter VMs (Guest01 and Guest02) with Failover Cluster, Storage Replica and Deduplication as nested virtual machines. (Level 4)
Function CreateGuest ($Computer,$Enviroment){
    # Generate Windows Server 2016 Datacenter VHDX
    $NanoHost = "NanoHost0$($Computer.Substring($Computer.Length -1))"
    . ${Drive}:\NanoServer\NanoServerImageGenerator\Convert-WindowsImage.ps1
    Convert-WindowsImage -SourcePath "${Drive}:\sources\install.wim" -Edition Datacenter -VHDPath "H:\Hyper-V\$Computer-C.vhdx" -VHDFormat VHDX -DiskLayout UEFI -RemoteDesktopEnable -BCDinVHD VirtualMachine -UnattendPath "H:\Guest0$($Computer.Substring($Computer.Length -1))-Unattend.xml"
    Copy-VMFile -FileSource Host -SourcePath "H:\Hyper-V\$Computer-C.vhdx" -Name NanoHost01 -DestinationPath "C:\ClusterStorage\Volume1\Hyper-V\$Computer\Virtual Hard Disks\$Computer-C.vhdx" -CreateFullPath
    Invoke-Command -VMName DC -Credential $DomainCred {Param($Computer,$NanoHost,$Enviroment)
        # Create and configure the VM at NanoHost
        New-VM -ComputerName $NanoHost -Name $Computer -Path C:\ClusterStorage\Volume1\Hyper-V -MemoryStartupBytes 1792MB -Generation 2 -VHDPath "C:\ClusterStorage\Volume1\Hyper-V\$Computer\Virtual Hard Disks\$Computer-C.vhdx" -SwitchName "Cluster and Client"
        Set-VMProcessor -ComputerName $NanoHost -VMName $Computer -Count 2
        Set-VMMemory -ComputerName $NanoHost -VMName $Computer -DynamicMemoryEnabled $false
        Rename-VMNetworkAdapter -ComputerName $NanoHost -VMName $Computer -Name "Network Adapter" -NewName "BR-SP-$Enviroment"
        # Add two Thin disks for storage replica, one for
        New-VHD -ComputerName $NanoHost -Path "C:\ClusterStorage\Volume1\Hyper-V\$Computer\Virtual Hard Disks\$Computer-Log.vhdx" -SizeBytes 9GB -Dynamic
        New-VHD -ComputerName $NanoHost -Path "C:\ClusterStorage\Volume1\Hyper-V\$Computer\Virtual Hard Disks\$Computer-Data.vhdx" -SizeBytes 10GB -Dynamic
        Add-VMHardDiskDrive -ComputerName $NanoHost -VMName $Computer -ControllerType SCSI -Path "C:\ClusterStorage\Volume1\Hyper-V\$Computer\Virtual Hard Disks\$Computer-Log.vhdx" -ControllerNumber 0 -ControllerLocation 1
        Add-VMHardDiskDrive -ComputerName $NanoHost -VMName $Computer -ControllerType SCSI -Path "C:\ClusterStorage\Volume1\Hyper-V\$Computer\Virtual Hard Disks\$Computer-Data.vhdx" -ControllerNumber 0 -ControllerLocation 2
        # Add VM to Cluster and start
        Add-ClusterVirtualMachineRole -Cluster NanoCluster -VirtualMachine $Computer
        Set-ClusterOwnerNode -Cluster NanoCluster -Group $Computer -Owners $NanoHost
        Start-VM -ComputerName NanoCluster -Name $Computer
    } -ArgumentList($Computer,$NanoHost,$Enviroment)
CreateGuest -Computer "Guest01" -Enviroment 'PRD'
CreateGuest -Computer "Guest02" -Enviroment 'DR'
Start-Sleep -Seconds 600

After first start of Guests, lets configure its network, roles, features, disks and join to domain.
Function ConfigureGuest($NanoHost,$Guest,$IPPRD,$IPClus,$IPGW,$Enviroment){
    $credGuest= New-Object System.Management.Automation.PSCredential ("$Guest\Administrator",$SecurePassword)
    Invoke-Command -VMName $NanoHost -Credential $DomainCred {Param($Guest,$IPPRD,$IPClus,$IPGW,$Enviroment,$credGuest)
        While ((Get-VMNetworkAdapter -VMName $Guest | Where-Object IPAddresses -Match 169.) -eq $null){Start-Sleep 5}
        Invoke-Command -VMName $Guest -Credential $credGuest {Param($IPPRD,$IPGW,$Enviroment)
            # Configure Client and Cluster Network
            Get-NetAdapter | Rename-NetAdapter -NewName "BR-SP-$Enviroment"
            New-NetIPAddress -InterfaceAlias "BR-SP-$Enviroment" -IPAddress $IPPRD -AddressFamily IPv4 -PrefixLength 24 -DefaultGateway $IPGW
            Set-DnsClientServerAddress -InterfaceAlias "BR-SP-$Enviroment" -ServerAddresses
            # Install Roles and Features
            Add-WindowsFeature Failover-Clustering,FS-Data-Deduplication,Storage-Replica,FS-FileServer -IncludeManagementTools -Restart
        } -ArgumentList($IPPRD,$IPGW,$Enviroment)
        While ((Get-VM -VMName $Guest).Uptime.Minutes -gt 1) {Start-Sleep 5}
        Add-VMNetworkAdapter -VMName $Guest -Name "Cluster $Enviroment" -SwitchName "Cluster Only"
        While ((Get-VMNetworkAdapter -VMName $Guest | Where-Object IPAddresses -Match 169.) -eq $null){Start-Sleep 5}
        Invoke-Command -VMName $Guest -Credential $credGuest {Param($Guest,$IPClus,$Enviroment)
            # Configure cluster network
            Get-NetAdapter -Name ((Get-NetIPAddress | Where-Object {$_.IPAddress -Match '169.' -and $_.AddressState -eq 'Preferred' -and $_.AddressFamily -eq 'IPv4'}).InterfaceAlias) | Rename-NetAdapter -NewName "Cluster $Enviroment"
            Get-NetAdapter -Name "Cluster $Enviroment" | Set-DnsClient -RegisterThisConnectionsAddress $False
            New-NetIPAddress -InterfaceAlias "Cluster $Enviroment" -IPAddress $IPClus -AddressFamily IPv4 -PrefixLength 24
            If ($Guest -eq 'Guest01'){
                New-NetRoute -DestinationPrefix -InterfaceAlias "Cluster PRD" -AddressFamily IPv4 -NextHop
                New-NetRoute -DestinationPrefix -InterfaceAlias "Cluster PRD" -AddressFamily IPv4 -NextHop
                New-NetRoute -DestinationPrefix -InterfaceAlias "Cluster DR" -AddressFamily IPv4 -NextHop
                New-NetRoute -DestinationPrefix -InterfaceAlias "Cluster DR" -AddressFamily IPv4 -NextHop
            # Initialize disks, create partitions, NTFS volumes and enable Deduplication.
            Get-Disk | Where size -eq 10GB | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -DriveLetter D  -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel "Dados" -Confirm:$false
            Get-Disk | Where size -eq 9GB | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -DriveLetter L -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel "Log" -Confirm:$false
            Get-Volume | Where size -LE 10GB | Enable-DedupVolume -UsageType Default
        } -ArgumentList($Guest,$IPClus,$Enviroment)
    } -ArgumentList($Guest,$IPPRD,$IPClus,$IPGW,$Enviroment,$credGuest)
    Invoke-Command -VMName DC -Credential $DomainCred {param($Guest,$IPPRD,$credGuest)
        # Configure Guest as trusted for WSMan
        Set-Item WSMan:\localhost\Client\TrustedHosts $Guest -Concatenate -Force
        Add-DnsServerResourceRecordA -IPv4Address $IPPRD -Name $Guest -ZoneName bauab.local -AllowUpdateAny -CreatePtr -AgeRecord
        If (Test-Connection -ComputerName $Guest -Quiet)
            $SessionGuest = New-PSSession -ComputerName $Guest -Credential $credGuest
            Djoin.exe /provision /domain bauab.local /machine $Guest /savefile C:\$Guest.djoin
            Copy-Item C:\$Guest.djoin C:\ -ToSession $SessionGuest
        } -ArgumentList ($Guest,$IPPRD,$credGuest)
    Invoke-Command -VMName $NanoHost -Credential $DomainCred {Param($Guest,$credGuest)
        While ((Get-VMNetworkAdapter -VMName $Guest | Where-Object IPAddresses -Match 192.) -eq $null){Start-Sleep 5}
        Invoke-Command -VMName $Guest -Credential $credGuest {Param($Guest)
            # Join to bauab.local domain
            Djoin.exe /RequestODJ /loadfile C:\$Guest.djoin /windowspath c:\windows /localos
        } -ArgumentList($Guest)
        Stop-VM $Guest ; Start-VM $Guest
    } -ArgumentList($Guest,$credGuest)
ConfigureGuest -NanoHost NanoHost01 -Guest Guest01 -IPPRD -IPClus -IPGW -Enviroment PRD
ConfigureGuest -NanoHost NanoHost02 -Guest Guest02 -IPPRD -IPClus -IPGW -Enviroment DR
Finally the last step! At this point we will create the GuestCluster, a Stretch Cluster with File Share Role, appropriate for Disaster Recovery sites, using Storage Replica.
Function ConfigureGuestCluster{
    Invoke-Command -VMName NanoHost01 -Credential $DomainCred -ScriptBlock {Param($DomainCred)
        While ((Get-VMNetworkAdapter -VMName Guest01 | Where-Object IPAddresses -Match 192.) -eq $null){Start-Sleep 5}
        While ((Get-VMNetworkAdapter -VMName Guest02 | Where-Object IPAddresses -Match 192.) -eq $null){Start-Sleep 5}
        Invoke-Command -VMName Guest01 -Credential $DomainCred -ScriptBlock {
            # Create Stretch Cluster
            New-Cluster GuestCluster –Node Guest01,Guest02 –StaticAddress, -NoStorage
    } -ArgumentList($DomainCred)
    If (Test-Connection -ComputerName GuestCluster -Quiet)
        Invoke-Command -VMName DC -Credential $DomainCred -ScriptBlock {
            # Create share for GuestCluster
            New-Item C:\GuestQuorum -ItemType Directory
            $acl = Get-Acl -Path C:\GuestQuorum
            $perm = "bauab\GuestCluster$", 'Read,Modify', 'ContainerInherit, ObjectInherit', 'None', 'Allow'
            $rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $perm
            $acl | Set-Acl -Path C:\GuestQuorum
            New-SmbShare -Name "GuestQuorum" -Path "C:\GuestQuorum" -FullAccess "bauab\GuestCluster$"
        Invoke-Command -VMName NanoHost01 -Credential $DomainCred -ScriptBlock {Param($DomainCred)
            Invoke-Command -VMName Guest01 -Credential $DomainCred -ScriptBlock {
                Set-ClusterQuorum -FileShareWitness \\dc\GuestQuorum -Cluster GuestCluster.bauab.local
                (Get-ClusterNetwork -Cluster GuestCluster | Where-Object {$_.Address -eq ""}).Name = "BR-SP-PRD"
                (Get-ClusterNetwork -Cluster GuestCluster | Where-Object {$_.Address -eq ""}).Name = "Cluster PRD"
                (Get-ClusterNetwork -Cluster GuestCluster | Where-Object {$_.Address -eq ""}).Name = "BR-SP-DR"
                (Get-ClusterNetwork -Cluster GuestCluster | Where-Object {$_.Address -eq ""}).Name = "Cluster DR"
                Get-ClusterResource -Cluster GuestCluster -Name "Cluster Name" | Set-ClusterParameter HostRecordTTL 300
                # Configure Stretch Cluster site awareness
                New-ClusterFaultDomain -Name BR-SP-PRD -Type Site -Description “Production" -Location “BR-SP-PRD"
                New-ClusterFaultDomain -Name BR-SP-DR -Type Site -Description “DR" -Location “BR-SP-DR"
                Set-ClusterFaultDomain -Name Guest01 -Parent BR-SP-PRD
                Set-ClusterFaultDomain -Name Guest02 -Parent BR-SP-DR
                Get-ClusterAvailableDisk -All -Cluster GuestCluster | Add-ClusterDisk
                # Create File Share Role
                Add-ClusterFileServerRole -Cluster GuestCluster -Name FS -Storage "Cluster Disk 3" -StaticAddress,
                MD D:\Shares\Public
                $acl = Get-Acl -Path D:\Shares\Public
                $perm = "bauab\Administrator", 'Read,Modify', 'ContainerInherit, ObjectInherit', 'None', 'Allow'
                $rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $perm
                $acl | Set-Acl -Path D:\Shares\Public
                New-SmbShare -Name Public -Path D:\Shares\Public -ContinuouslyAvailable $false -FullAccess "bauab\Administrator"
                Get-ClusterResource -Cluster GuestCluster -Name FS | Set-ClusterParameter HostRecordTTL 300
                # Enable Storage Replica
                New-SRPartnership -SourceComputerName Guest01.bauab.local -SourceRGName rg01 -SourceVolumeName D: -SourceLogVolumeName L: -DestinationComputerName Guest02.bauab.local -DestinationRGName rg02 -DestinationVolumeName D: -DestinationLogVolumeName L:
        } -ArgumentList($DomainCred)


We can stop the transcript and retain it as deploy documentation (C:\Transcript.log)