Sample PowerShell Scripts for Storage Spaces, standalone Hyper-V over SMB and SQLIO testing

These are some PowerShell snippets to configure a specific set of systems for Hyper-V over SMB testing.
Posting it here mainly for my own reference, but maybe someone else out there is configuring a server with 48 disks split into 6 pools of 8 disks.
These systems do not support SES (SCSI Enclosure Services) so I could not use slot numbers.
This setup includes only two computers: a file server (ES-FS2) and a Hyper-V host (ES-HV1).
Obviously a standalone setup. I'm using those mainly for some SMB Direct performance testing.

Storage Spaces - Create pools

$s = Get-StorageSubSystem -FriendlyName *Spaces*

$d = Get-PhysicalDisk | ? { ";1;2;3;4;5;6;7;8;".Contains(";"+$_.DeviceID+";") }
New-StoragePool -FriendlyName Pool1 -StorageSubSystemFriendlyName $s.FriendlyName -PhysicalDisks $d

$d = Get-PhysicalDisk | ? { ";9;10;11;12;13;14;15;16;".Contains(";"+$_.DeviceID+";") }
New-StoragePool -FriendlyName Pool2 -StorageSubSystemFriendlyName $s.FriendlyName -PhysicalDisks $d

$d = Get-PhysicalDisk | ? { ";17;18;19;20;21;22;23;24;".Contains(";"+$_.DeviceID+";") }
New-StoragePool -FriendlyName Pool3 -StorageSubSystemFriendlyName $s.FriendlyName -PhysicalDisks $d

$d = Get-PhysicalDisk | ? { ";25;26;27;28;29;30;31;32;".Contains(";"+$_.DeviceID+";") }
New-StoragePool -FriendlyName Pool4 -StorageSubSystemFriendlyName $s.FriendlyName -PhysicalDisks $d

$d = Get-PhysicalDisk | ? { ";33;34;35;36;37;38;39;40;".Contains(";"+$_.DeviceID+";") }
New-StoragePool -FriendlyName Pool5 -StorageSubSystemFriendlyName $s.FriendlyName -PhysicalDisks $d

$d = Get-PhysicalDisk | ? { ";41;42;43;44;45;46;47;48;".Contains(";"+$_.DeviceID+";") }
New-StoragePool -FriendlyName Pool6 -StorageSubSystemFriendlyName $s.FriendlyName -PhysicalDisks $d

Storage Spaces - Create Spaces (Virtual Disks)

1..6 | % {
Set-ResiliencySetting -Name Mirror -NumberofColumnsDefault 4 -StoragePool ( Get-StoragePool -FriendlyName Pool$_ )
New-VirtualDisk -FriendlyName Space$_ -StoragePoolFriendlyName Pool$_ -ResiliencySettingName Mirror -UseMaximumSize

Initialize disks, partitions and volumes

1..6 | % {
$c = Get-VirtualDisk -FriendlyName Space$_ | Get-Disk
Set-Disk -Number $c.Number -IsReadOnly 0
Set-Disk -Number $c.Number -IsOffline 0
Initialize-Disk -Number $c.Number -PartitionStyle GPT
$L = “DEFGHI”[$_-1]
New-Partition -DiskNumber $c.Number -DriveLetter $L -UseMaximumSize
Initialize-Volume -DriveLetter $L -FileSystem NTFS -Confirm:$false

Confirm everything is OK

Get-StoragePool Pool* | sort FriendlyName | % { $_ ; ($_ | Get-PhysicalDisk).Count }
Get-VirtualDisk | Sort FriendlyName
Get-VirtualDisk Space* | % { $_ | FT FriendlyName, Size, OperationalStatus, HealthStatus ; $_ | Get-PhysicalDisk | FT DeviceId, Usage, BusType, Model }
Get-Volume | Sort DriveLetter

Verify SMB Multichannel configuration

Get-SmbServerNetworkInterface -CimSession ES-FS2
Get-SmbClientNetworkInterface -CimSession ES-HV1 | ? LinkSpeed -gt 1
Get-SmbMultichannelConnection -CimSession ES-HV1

On the local system, create files and run SQLIO

1..6 | % {
   $d = “EFGHIJ”[$_-1]
   fsutil file createnew $f (256GB)
   fsutil file setvaliddata $f (256GB)

c:sqliosqlio2.exe -s10 -T100 -t4 -o16 -b512 -BN -LS -fsequential -dEFGHIJ testfile.dat
c:sqliosqlio2.exe -s10 -T100 -t16 -o16 -b8 -BN -LS -frandom -dEFGHIJ testfile.dat

Create the SMB Shares for use by ES-HV1

1..6 | % {
   $p = “EFGHIJ”[$_-1]+":"
   $s = "Share"+$_
   New-SmbShare -Name $s -Path $p -FullAccess ESUser, ESES-HV1$
   (Get-SmbShare -Name $s).PresetPathAcl | Set-Acl

On remote system, map the drives and run SQLIO again:

1..6 | % {
   $l = “EFGHIJ”[$_-1]+":"
   $r = "\ES-FS2Share"+$_
   New-SmbMapping -LocalPath $l -RemotePath $r

c:sqliosqlio2.exe -s10 -T100 -t4 -o16 -b512 -BN -LS -fsequential -dEFGHIJ testfile.dat
c:sqliosqlio2.exe -s10 -T100 -t16 -o16 -b32 -BN -LS -frandom -dEFGHIJ testfile.dat

Creating the VM BASE


Set up VMs - Option 1 - from a BASE and empty shares

1..6 | % {
   Copy C:VMSBase.VHDX \ES-FS2Share$_VM$_.VHDX
   New-VHD -Path \ES-FS2Share$_Data$_.VHDX -Fixed -Size 256GB
   New-VM -Name VM$_ -VHDPath \ES-FS2Share$_VM$_.VHDX -Path \ES-FS2Share$_ -Memory 8GB
   Set-VM -Name VM$_ -ProcessorCount 8
   Add-VMHardDiskDrive -VMName VM$_ -Path \ES-FS2Share$_Data$_.VHDX
   Add-VMNetworkAdapter -VMName VM$_ -SwitchName Internal

Set up VMS - Option 2 - when files are already in place

1..6 | % {
   New-VM -Name VM$_ -VHDPath \ES-FS2Share$_VM$_.VHDX -Path \ES-FS2Share$_ -Memory 8GB
   Set-VM -Name VM$_ -ProcessorCount 8
   Add-VMHardDiskDrive -VMName VM$_ -Path \ES-FS2Share$_Data$_.VHDX
   Add-VMNetworkAdapter -VMName VM$_ -SwitchName Internal

Setting up E: data disk inside each VM

Set-Disk -Number 1 -IsReadOnly 0
Set-Disk -Number 1 -IsOffline 0
Initialize-Disk -Number 1 -PartitionStyle GPT
New-Partition -DiskNumber 1 -DriveLetter E -UseMaximumSize
Initialize-Volume -DriveLetter E -FileSystem NTFS -Confirm:$false

fsutil file createnew E:testfile.dat (250GB)
fsutil file setvaliddata E:testfile.dat (250GB)

Script to run inside the VMs

PowerShell script sqlioloop.ps1 on a shared X: drive (mapped SMB share) run from each VM
Node identified by the last byte of its IPv4 address.
Empty file go.go works as a flag to start running the workload on several VMs at once.
Also saves SQLIO output to a text file on the shared X: drive
Using a separate batch files to run SQLIO itself so they can be easily tuned even when the script is running on all VMs

$node = (Get-NetIPAddress | ? IPaddress -like 192.168.99.*).IPAddress.Split(".")[3]
while ($true)
  if ((dir x:sqliogo.go).count -gt 0)
     "Starting large IO..."
     .sqliolarge.bat >large$node.txt
     "Pausing 10 seconds..."
      start-sleep 10
     "Starting small IO..."
      .sqliosmall.bat >small$node.txt
     "Pausing 10 seconds..."
      start-sleep 10
   "node "+$node+" is waiting..."
   start-sleep 1

PowerShell script above uses file sqliolarge.bat

.sqlio2.exe -s20 -T100 -t2 -o16 -b512 -BN -LS -fsequential -dE testfile.dat

Also uses sqliosmall.bat

.sqlio2.exe -s20 -T100 -t4 -o16 -b32 -BN -LS -frandom -dE testfile.dat


P.S.: The results obtained from a configuration similar to this one were published at