Sonntag, 16. September 2018

Virtuelle Maschinen erzeugen und administrieren mit Powershell

Kennt Ihr das? Hilfe Texte stimmen nicht, Beschreibungen funktionieren nicht? Das Thema Powershell und VM ist auch damit ganz schlimm gebeutelt.

VM anlegen

Man kann eine leere VM anlegen, das ergibt ein relativ mageres Ergebnis:
1GB RAM, Generation 1, keine Disk.
New-VM –Name 'Test1'
Will man in etwa das Ergebnis, was der Hyper-V Manager Wizard nur mit der Eingabe "Name" erzeugt, muss man es so machen:
$vm = 'Test2'
New-VM –Name $vm –NewVHDPath "$vm.vhdx" -NewVHDSizeBytes 127GB
Bisher haben wir VMs der Generation 1 erzeugt.
Viel mehr als im nächsten Befehl, kann man bei New-VM gar nicht angeben.
$vm = 'Test3'
New-VM –Name $vm -Generation 2 –MemoryStartupBytes 4GB –NewVHDPath "$vm.vhdx" -NewVHDSizeBytes 32GB
Alles weitere muss man mit Set-VM* Befehlen machen.

Startmedien - DVD Laufwerk

Das wichtigste dabei (und nicht ganz einfach wie man denkt) wäre ja die Angabe von Start- bzw. Installationsmedien. Kaum noch existieren wirklich optische Laufwerke, das Handling am Hyper-V Server wäre extrem unpraktisch. Man kann ohne weiteres ISO oder VHD Images einbinden, aber nur wenn sie lokal auf dem Hyper-V Server liegen. Microsoft hat eine sehr eigenartige Sicherheit eingebaut, wenn es um den Zugriff auf ein Image geht, welches auf einem Server liegt.
Auch die hier beschriebenen Wege funktionieren bei mir nur in der manuellen Art für VMs der Generation 1!

Mein Resumé: Startmedien gehen nur lokal

Mag sein es funktioniert in AD Umgebungen, ich habe es in einer Workgroup versucht, und wirklich viel experimentiert, ich bekomme es nicht hin! Man kann auf dem Hyper-V Host ein Image mounten und sich einen Überblick von den Laufwerksbuchstaben ausgeben lassen.
Mount-DiskImage -ImagePath \\omv1\shares\ISO\de_windows_7_All_with_sp1_x64_.iso
Get-Volume|? DriveType -eq CD-ROM

Für VMs der Generation 1 kann man nun im Hyper-V Manager das physische Laufwerk auswählen und zuweisen. (Mit Set-VMDvdDrive kann man nur echte DVD Laufwerke zuweisen)

Bei VMs der Generation 2 kann man nur mit lokalen ISO Images arbeiten!
Mann muss zunächst ein DVD Laufwerk hinzufügen.
Add-VMDvdDrive -VMName $vm -Path D:\ISO\Name.iso
Mit Set-VMDvdDrive kann man lokale ISO Images zuweisen und ändern.

Konfiguration Netzwerk, Bootreihenfolge und Prozessor

Damit der Startvorgang nicht am PXE Boot "verhungert" sollte man noch die Boot Reihenfolge festlegen
$VMDVD=Get-VMDvdDrive -VMName $vm
Set-VMFirmware -VMName $vm -FirstBootDevice $VMDVD
Oder man legt die Reihenfolge neu fest, die neue vhdx Datei kann sowieso nicht booten, er sollte in dem Fall beim ersten Mal von der DVD starten
$VMHDD=Get-VMHardDiskDrive -VMName $vm
Set-VMFirmware -VMName $vm -BootOrder $VMHDD, $VMDVD
Will man den (einzigen) Netzwerk Adapter noch mit dem (einzigen) virtuellen, externen Switch verbinden, kann man das so machen
Connect-VMNetworkAdapter -VMName $vm -SwitchName (Get-VMSwitch|? Switchtype -eq External).Name
Während wir den mindest RAM bei der Einrichtung festgelegt haben, hat die VM bisher nur einen Prozessor. Man kann so alle verfügbaren Prozessoren zuweisen
Set-VMProcessor -VMName $vm -Count (Get-VMHost).LogicalProcessorCount

Zusammenfassung

Hier ein Template/Script für eine neue VM, Der VM Name kann als Parameter übergeben werden oder wird abgefragt (Mandatory=$true).
In der ersten und zweiten Zeile werden alle Parameter gesetzt. Ich habe deshalb auf übermäßig viele Variablen verzichtet.
<#
.SYNOPSIS 
    Das Script erstellt eine neue virtuelle Maschine
.DESCRIPTION 
    Das Script erstellt eine neue virtuelle Maschine
.EXAMPLE 
    CreateVM Name 
.NOTES 
    Generation 2, 32 GB HDD, 4 GB RAM, alle Prozessoren
#>
#region Params
param(
    [Parameter(Position=0, Mandatory=$true,HelpMessage="Name der VM",ValueFromPipeline=$false)]
    [System.String]
    $vm=""
)
#endregion 

New-VM –Name $vm -Generation 2 –MemoryStartupBytes 4GB –NewVHDPath "$vm.vhdx" -NewVHDSizeBytes 32GB
Add-VMDvdDrive -VMName $vm 
$VMDVD=Get-VMDvdDrive -VMName $vm
$VMHDD=Get-VMHardDiskDrive -VMName $vm
Set-VMFirmware -VMName $vm -BootOrder $VMHDD, $VMDVD
Connect-VMNetworkAdapter -VMName $vm -SwitchName (Get-VMSwitch|? Switchtype -eq External).Name
Set-VMProcessor -VMName $vm -Count (Get-VMHost).LogicalProcessorCount

Für die DVD habe ich mal noch einen "Würgaround" gefunden.
Dieser Mehrzeiler schaut nach, ob die ISO Datei im lokalen Pfad schon existiert und kopiert diese ansonsten dahin.
$DVD = "name.iso"
$Ziel = 'D:\ISO\'
$Quelle = '\\omv1\shares\iso\'
$Diff = Compare-Object -ReferenceObject (gci -Path $Ziel) -DifferenceObject (gci -Path $Quelle)
Copy-Item -Path (($Diff|? InputObject -like $DVD).InputObject).FullName -Destination $Ziel
# DVD einlegen
Set-VMDvdDrive -VMName $vm -Path $Ziel$DVD

Damit das lokale Verzeichnis nicht vermüllt, kann man ja von Zeit zu Zeit die ISOs entfernen, die nicht mehr eingebunden sind.
$Ziel = 'D:\ISO\'
$InUse = (Get-VMDvdDrive -VMName *|? Path -like $Ziel*).Path
$Exist = (gci -Path $Ziel).Fullname
$Diff = Compare-Object -ReferenceObject $Exist -DifferenceObject $InUse
Remove-Item ($Diff|? SideIndicator -eq '<=').InputObject -confirm

Aufräumen

Dieser Einzeiler löscht alle VMs und deren virtuelle HDDs, deren Name mit Test beginnt.
Jeder Löschvorgang verlangt nach Bestätigung! Bitte genau hinschauen, ich übernehme keine Verantwortung!
Ich hoffe, dass ich in einem Jahr noch weiß, wie dieser Einzeiler funktioniert.
% ist das Kürzel für ForEach, $_ Ist das aktuelle Element der Schleife
-process enthält den Ausführungsteil(, beide Befehlsteile werden in getrennten Schleifen abgearbeitet.)
-InputObject die Eingabe
% -process {Remove-Item (Get-VMHardDiskDrive -VMName $_.name).Path -Confirm ; Remove-VM -VMName $_.name} -inputobject (Get-VM -VMName Test*)

Informationen

Man kann sich relativ schnell einen Überblick über die Cmdlets verschaffen und detaillierte Hilfe abrufen. (Leider stimmt auch in den Hilfetexten nicht alles)
Get-Help Hyper-V
Get-Help Set-VM*

Mittwoch, 5. September 2018

Storage Pool auf dem Hyper-V Server verwalten

Wie bekommt man einen gespiegeltes HDD System, welches unter Windows Server 2012 als Storage Pool eingerichtet war, wieder online?

Ein paar Befehle für die  Analyse des Storage mit Powershell 

So bekommt man ein Bild der vorhandenen Disks:
get-disk

Number Friendly Name Serial Number                    HealthStatus         OperationalStatus      Total Size Partition
                                                                                                             Style
------ ------------- -------------                    ------------         -----------------      ---------- ----------
1      Samsung SS... S1DBNSAF878015M                  Healthy              Online                  232.89 GB GPT
0      Samsung SS... S21PNSAG155972R                  Healthy              Online                  232.89 GB GPT
6      VD1           {05bf1a4e-44d2-11e5-80d3-7824... Healthy              Offline                   1.82 TB GPT
5      VD2           {dcdf1498-41bd-11e5-80cf-7824... Healthy              Offline                    930 GB GPT
7      TOSHIBA Ex...            92N5P3KXT             Healthy              Online                  931.51 GB MBR
Zwei Platten sind Offline, die Disks 2,3 und 4 "fehlen".

Die Abfrage der Virtuellen Disk ergibt folgendes Bild:
get-virtualdisk

FriendlyName ResiliencySettingName OperationalStatus HealthStatus IsManualAttach    Size
------------ --------------------- ----------------- ------------ --------------    ----
VD1          Mirror                OK                Healthy      False          1.82 TB
VD2          Simple                OK                Healthy      False           930 GB
Obwohl die eigentlichen Laufwerke im Storage Pool ausgeblendet werden, kann man sich die echten Laufwerke anzeigen lassen:
Get-physicaldisk

FriendlyName              SerialNumber    CanPool OperationalStatus HealthStatus Usage            Size
------------              ------------    ------- ----------------- ------------ -----            ----
Samsung SSD 840 EVO 250GB S1DBNSAF878015M True    OK                Healthy      Auto-Select 232.89 GB
WDC WD2003FYPS-27Y2B0     WD-WCAVY7115101 False   OK                Healthy      Auto-Select   1.82 TB
TOSHIBA External USB 3.0  92N5P3KXT       False   OK                Healthy      Auto-Select 931.51 GB
Samsung SSD 850 EVO 250GB S21PNSAG155972R False   OK                Healthy      Auto-Select 232.89 GB
ST31000333AS              9TE24JDS        False   OK                Healthy      Auto-Select  931.5 GB
WDC WD2003FYPS-27Y2B0     WD-WCAVY5334150 False   OK                Healthy      Auto-Select   1.82 TB

Get_PhysicalDisk kann aber mehr, mit Hilfe von Format-List habe ich mal eine Ausgabe der Zuordnung zu den Hardware Ports gebastelt
Get-PhysicalDisk| Select-Object -Property DeviceId,PhysicalLocation,BusType,MediaType,FriendlyName,SerialNumber| Sort-Object -Property DeviceId |Format-Table

DeviceId PhysicalLocation                BusType MediaType   FriendlyName              SerialNumber
-------- ----------------                ------- ---------   ------------              ------------
0        Integrated : Adapter 0 : Port 1 SATA    SSD         Samsung SSD 850 EVO 250GB S21PNSAG155972R
1        Integrated : Adapter 1 : Port 0 SATA    SSD         Samsung SSD 840 EVO 250GB S1DBNSAF878015M
2        Integrated : Adapter 1 : Port 1 SATA    HDD         ST31000333AS              9TE24JDS
3        Integrated : Adapter 1 : Port 4 SATA    HDD         WDC WD2003FYPS-27Y2B0     WD-WCAVY5334150
4        Integrated : Adapter 1 : Port 5 SATA    HDD         WDC WD2003FYPS-27Y2B0     WD-WCAVY7115101
6        Integrated : Adapter 0 : Port 0 USB     Unspecified TOSHIBA External USB 3.0  92N5P3KXT

Den Zusammenhang zwischen Storage Pool, VirtualDisk und PhysicalDisk kann man auch anzeigen lassen. Dazu braucht man zunächst ein Object auf den Storage Pool
$stpool = (Get-StoragePool -FriendlyName "SP1")
Get-VirtualDisk -StoragePool $stpool

FriendlyName ResiliencySettingName OperationalStatus HealthStatus IsManualAttach    Size
------------ --------------------- ----------------- ------------ --------------    ----
VD1          Mirror                Degraded          Warning      False          1.82 TB

Get-PhysicalDisk -StoragePool $stpool

FriendlyName          SerialNumber    CanPool OperationalStatus HealthStatus Usage          Size
------------          ------------    ------- ----------------- ------------ -----          ----
WDC WD2003FYPS-27Y2B0 WD-WCAVY7115101 False   OK                Healthy      Retired     1.82 TB
WDC WD2003FYPS-27Y2B0 WD-WCAVY5334150 False   OK                Healthy      Auto-Select 1.82 TB
Für die Information auf welcher Disk das Volume des Systemlaufwerkes ist, habe ich diese Befehlskette gefunden.
Get-Disk (Get-Partition | ? isboot).DiskNumber

Storage Pool Disk wieder aktivieren

Ich möchte das VD1 wieder online ist, dies wird nämlich nicht automatisch getan, wenn der Storagepool an einen andere Maschine gehangen wird. Da VD1 eine (virtuelle) Disk ist, geht das mit dem set-disk Cmdlet
Set-Disk -Number 6 -IsOffline $False
Es gab bei mir die Situation, dass eine Disk nach dem Import von einer Physical Disk in eine vhdx Datei mit dem Hyper-V Manager sowohl offline als auch readonly war. Auch readonly lässt sich mit Set-Disk beheben.
Set-Disk -Number 5 -IsReadonly $False

Storage Pool Disk Fehler behandeln

Versucht man in einem StoragePool, der 2 HDD enthält und darin eine Mirror VirtualDisk - eine HDD geordnet zu entfernen und diese danach durch eine Andere zu ersetzen, funktioniert das praktisch nicht. Obwohl man eine Platte als Retired markiert, lässt sie sich nicht entfernen. Man muss erst einen neue HDD zum StoragePool hinzufügen. Aber selbst das ist mir nicht richtig gelungen.
Dies habe ich versucht:
Set-PhysicalDisk -FriendlyName "WDC WD2003FYPS-27Y2B0" -SerialNumber WD-WCAVY7115101 -Usage Retired
Repair-VirtualDisk -FriendlyName "VD1"
# Zwei Varianten um das Objekt der richtigen PhysicalDisk zu bekommen
$PDToRemove = Get-PhysicalDisk | Where-Object { $_.Usage -eq 'Retired'}
$PDToRemove = Get-PhysicalDisk -FriendlyName "WDC WD2003FYPS-27Y2B0" -SerialNumber WD-WCAVY7115101
Remove-PhysicalDisk -PhysicalDisks $PDToRemove -StoragePoolFriendlyName "SP1"
Aber diese Aktion endet mit einem Fehler:
StorageWMI 48011,Remove-PhysicalDisk

Storage Pool entfernen

Erkenntnis: Storage Pool sieht gut aus, ist mir aber so undurchsichtig! Nach der kompletten Sicherung der Daten, habe ich einfach alles entfernt:
Set-Disk -Number 6 -IsOffline $true
Remove-VirtualDisk -FriendlyName "VD1"
Remove-StoragePool -FriendlyName "SP1"
Jetzt tauchen die Platten 3 + 4 wieder normal in der Ansicht auf (get-disk) und können normal verwendet werden. Um sie völlig leer zu machen verwendet man Clear-Disk
Clear-Disk -Number 3
Clear-Disk -Number 4
Get-Disk |Where-Object PartitionStyle –Eq "RAW" |Initialize-Disk 

Um eine neue Partition anzulegen, einen bestimmten Laufwerksbuchstaben zu vergeben und es gleich zu formatieren, kann man so vorgehen.
New-Partition -DiskNumber 3 -UseMaximumSize -DriveLetter S |Format-Volume

Dienstag, 4. September 2018

Partitionen und virtuelle Disk

In Hyper-V per default eine neue virtuelle Platte (VHDX) erstellt - und dann...

  • stellt man fest, dass Platten mit dynamischer Größe mit der Zeit viel langsamer sind als virtuelle Platten mit fester Größe. 
  • Das virtuelle Platten auf einer SSD viel schneller sind als auf einer herkömmlichen HDD
  • Das es jetzt ziemlich verschwenderisch ist, 127 GB auf der knappen SSD zu belegen, um eine virtuelle Platte zu hosten, die mit weniger als der Hälfte üppig ausgestattet wäre.

Dafür ein kurzes HowTo, wie man von dieser Ausgangssituation zum gewünschten Ziel kommt. Für einen Trockentest, kann man so eine virtuelle Disk zum Test anlegen. Die verwendeten Größen sind nur als Beispiel zu sehen.

Test Datei erstellen

Die typische System Platte sieht in etwa so aus.
PartitionNumber  DriveLetter Offset                                        Size Type
---------------  ----------- ------                                        ---- ----
1                            1048576                                     450 MB Recovery
2                            472907776                                    99 MB System
3                            576716800                                    16 MB Reserved
4                C           593494016                                126.45 GB Basic
Die folgende Zeilen erstellen eine vhdx Datei mit genau dieser Struktur. Das Windows Upgrade baut manchmal noch eine Partition hinten dran.
Dummerweise gibt es die x-VHD Powershell-Cmdlets nur, wenn die Hyper-V Rolle komplett installiert ist. Alternativ kann man dafür diskpart verwenden.
$VDisk = "D:\VHD\TestBase.vhdx"
New-VHD -Path $VDisk -SizeBytes 127GB
$Disk = Mount-VHD -Path $VDisk -Passthru|Get-Disk
$Disk
$Disk|Initialize-Disk -Passthru|Remove-Partition -PartitionNumber 1
$Disk|New-Partition -Size 450MB -GptType '{de94bba4-06d1-4d40-a16a-bfd50179d6ac}'
$Disk|New-Partition -Size 99MB -GptType '{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}'
$Disk|New-Partition -Size 16MB -GptType '{e3c9e316-0b5c-4db8-817d-f92df00215ae}'
$Disk|New-Partition -Size ((get-disk $Disk.Number).LargestFreeExtent-450MB) -AssignDriveLetter|Format-Volume
$Disk|New-Partition -UseMaximumSize -GptType '{de94bba4-06d1-4d40-a16a-bfd50179d6ac}'
$DriveLetter = ($Disk|Get-Partition|Get-Volume|? Driveletter -ne $null).DriveLetter
$path = ($DriveLetter+":\Test.tmp");$file = [io.file]::Create($path);$file.SetLength(10GB);$file.Close()
Dismount-VHD -Path $VDisk
Initialize-Disk erzeugt per default eine "reserved" Partition. Da ich die Original Struktur haben wollte, wird diese zunächst gelöscht und an dritter Stelle wieder erzeugt. Für diese Test vhdx ist das unerheblich, im produktiven Umfeld würde ich das nicht machen.
Die vorletzte Zeile enthält keine Pipe sondern mehrere Befehle um einfach einen große Datei in dem Volume zu erzeugen.

Minimierung der Ausgangsdatei

Die folgende Befehlsfolge reduziert die Partition in der Datei auf ein Minimum und reduziert die vhdx Datei auf ein Minimum. Zunächst mounten wir die vhdx Datei und erhalten als Ausgabe die Tabelle der Volumes.
Mount-VHD -Path $VDisk -Passthru| Get-Disk | Get-Partition | Get-Volume
Die folgende Aktion kann eine Partition auf minimale Größe bringen. Den richtigen Laufwerksbuchstaben einsetzen:
$DriveLetter = "E"
$size = Get-PartitionSupportedSize -DriveLetter $DriveLetter
Resize-Partition -DriveLetter $DriveLetter -Size $size.sizeMin
Manchmal baut Windows Update an das Ende der Systemplatte eine Wiederherstellungspartition, diese ist eigentlich unnütz und verhindert die Verkleinerung der gesamten Disk.
So kann man Partitionen entfernen. Vorsicht! Dieser Befehl löscht Daten, fragt aber vorher nach. Bitte genau lesen! 
Der zweite Befehl listet uns den Inhalt, im dritten Befehl muss die richtige PartitionNumber nn gesetzt werden.
$Disk = Get-Disk (Get-Partition -DriveLetter $DriveLetter).DiskNumber|Get-Partition
$Disk
$Disk |? PartitionNumber -eq nn|Remove-Partition
Hat man die letzte Partition verkleinert, kann man auch die gesamte Disk verkleinern:
Dismount-VHD -Path $VDisk
Resize-VHD -path $VDisk -ToMinimumSize
Zum Schluss kann man noch die Dateigröße der vhdx Datei verringern.
Optimize-VHD -path $VDisk -mode full
Jetzt befindet sich die vhdx in minimaler Größe und kann z.B. schneller kopiert werden.

In Form bringen

Anschließend soll die Datei wieder in eine produktive Form gebracht werden. Zunächst die komplette Disk auf die gewünschte Größe bringen.
Resize-VHD -path $VDisk -SizeBytes 500MB
Danach Mounten und die Struktur anschauen, das letzte Volume kann vergrößert werden.
In der zweiten Zeile den richtigen Laufwerksbuchstaben einsetzen!
Mount-VHD -Path $VDisk -Passthru| Get-Disk | Get-Partition | Get-Volume
$DriveLetter = "E"
$size = Get-PartitionSupportedSize -DriveLetter $DriveLetter
Resize-Partition -DriveLetter $DriveLetter -Size $size.sizeMax
Jetzt noch die virtuelle Disk vom dynamischen in ein festes Format wandeln. Dabei wird eine neue Datei erzeugt.
Dismount-VHD -Path $VDisk
Convert-VHD -path $VDisk –DestinationPath V:\NeuerName.vhdx –VHDType Fixed

Nützlich

Zwischen den einzelnen Schritten, kann immer mal der momentane Zustand der vhdx Datei und der Partition überprüft werden.
Get-VHD $VDisk
Get-PartitionSupportedSize -DriveLetter $DriveLetter
So ermittelt man die Werte für Disk und Partition.
Get-Disk
Get-Partition -DiskNumber 8
$size = (Get-PartitionSupportedSize -DiskNumber 8 -PartitionNumber 2)
Resize-Partition -DiskNumber 8 -PartitionNumber 2 -Size $size.SizeMax
Die x-VHD Powershell Cmdlets lassen sich etwas umständlicher durch diskpart Befehle ersetzen
create vdisk file="c:\ttt\testBase.vhdx" maximum=1024
sel vdisk file="c:\ttt\testBase.vhdx"
attach vdisk
detach vdisk
expand vdisk maximum=20000
Anbinden und auswerfen der vhdx Datei geht auch mit dem Windows Explorer.
Die Konvertierung kann man auch mit dem Hyper-V Manager durch Import der vhdx Datei bewerkstelligen.

Will man mehr als die Standardausgabe von einem Cmdlet sehen, hilft das Format-List Cmdlet. Es gibt alle Elemente in einzelnen Zeilen aus.
Get-Disk |Format-List -Property *
Dadurch erhält man z.B. die Information über Elemente nach denen man auch Filtern kann.
Get-Disk |Where-Object {$_.Bustype -Eq "USB"}|

Ist die Zielfestplatte beim Konvertieren in den Type Fixed zu klein, ist die Fehlermeldung ziemlich unspezifisch:
Convert-VHD : Fehler beim Konvertieren des virtuellen Datenträgers.
Fehler beim Konvertieren von "d:\vhd\hyper-v-2016.vhdx".
In Zeile:1 Zeichen:1
+ Convert-VHD -path $VDisk –DestinationPath V:\Hyper-V2016-C.vhdx –VHDT ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Convert-VHD], VirtualizationException
    + FullyQualifiedErrorId : OperationFailed,Microsoft.Vhd.PowerShell.Cmdlets.ConvertVhd