Teil 1 von 6
Teil 1: Vorbereitung der Nodes
Initiale Konfiguration, Features, Secured Core und Domain Join für alle Cluster-Nodes.
Bevor der Cluster entsteht, müssen alle Nodes auf demselben Stand und identisch konfiguriert sein. Features installieren, Secured Core aktivieren, Domain Join. Alles per PowerShell, auf allen Nodes gleichzeitig. Wer das nicht sicherstellt, hat spätestens beim ersten Update einen Problemkandidaten im Cluster.
Vorbereitung des Management Host
Als Management Host wird ein Windows Server 2025 verwendet. Dieser kann als VM oder kleine physische Maschine laufen. Von hier laufen alle Remote-Befehle gegen die Nodes.
Install-WindowsFeature -Name RSAT-Clustering,RSAT-Clustering-Mgmt,RSAT-Clustering-PowerShell,RSAT-Hyper-V-Tools,NetworkATCAllgemeine Konfiguration
Voraussetzungen
Folgende Punkte müssen auf jedem Node vor dem Start erfüllt sein:
- Windows Server 2022 oder höher als Server Core in der Datacenter-Edition installiert
- Systemsprache: Englisch
- Administratorkennwort gesetzt
- IP-Adresse auf einem Management-Netzwerkadapter konfiguriert:
New-NetIPAddress -InterfaceAlias "SLOT 3 Port 1" -IPAddress XXX.XXX.XXX.XXX -PrefixLength 24 -DefaultGateway XXX.XXX.XXX.XXX
# ===================== ANPASSEN =====================
$TrustedHosts = "192.0.2.*" # IP-Bereich der Nodes (z.B. "192.168.1.*")
$LocalUser = "Administrator" # Lokaler Benutzername auf den Nodes$LocalPassword = "Start123!" # Lokales Passwort auf den Nodes
$Servers = "192.0.2.31","192.0.2.32" # IP-Adressen aller Nodes$DNSServer = "192.0.2.51","192.0.2.52" # DNS-Server$Interface = "SLOT 6 Port 1" # Name des Management-Netzwerkadapters
$RDPEnable = $true # $true = RDP aktivieren | $false = RDP deaktivieren$S2D = $true # $true = Storage Spaces Direct | $false = SAN Only
# =================== NICHT ANPASSEN =================
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $TrustedHosts -Force
$SecureStringPassword = ConvertTo-SecureString $LocalPassword -AsPlainText -Force$localCredentials = New-Object System.Management.Automation.PSCredential ($LocalUser, $SecureStringPassword)
$features = @( "Hyper-V", "Failover-Clustering", "RSAT-Clustering-PowerShell", "Hyper-V-PowerShell", "NetworkATC", "Data-Center-Bridging", "RSAT-DataCenterBridging-LLDP-Tools", "FS-SMBBW", "System-Insights", "RSAT-System-Insights", "BitLocker", "RSAT-Feature-Tools-BitLocker")Invoke-Command -ComputerName $servers -Credential $localCredentials -ScriptBlock {
# DNS Server Set-DnsClientServerAddress -InterfaceAlias $using:Interface -ServerAddresses ($using:DNSServer)
# Features installieren Install-WindowsFeature -Name $using:features
# Active Memory Dump Set-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\CrashControl -Name CrashDumpEnabled -Value 1 Set-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\CrashControl -Name FilterPages -Value 1
# Energiesparmodus: Höchstleistung powercfg /SetActive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
# WinRM Envelope Size Set-Item -Path WSMan:\localhost\MaxEnvelopeSizekb -Value 8192
# Ping erlauben netsh advfirewall firewall add rule name="Allow ICMPv4" protocol=icmpv4:8,any dir=in action=allow netsh advfirewall firewall add rule name="Allow ICMPv6" protocol=icmpv6:8,any dir=in action=allow
# RDP konfigurieren if ($using:RDPEnable) { Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" -Value 0 Enable-NetFirewallRule -DisplayGroup "Remote Desktop" } else { Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" -Value 1 Disable-NetFirewallRule -DisplayGroup "Remote Desktop" }
# Getrennte Netzwerkadapter deaktivieren Get-NetAdapter | Where-Object { $_.Status -eq "Disconnected" } | Disable-NetAdapter -Confirm:$false
}Neustart der Nodes
An dieser Stelle ist ein Neustart der Nodes erforderlich.
Credits: MSLab
Restart-Computer $servers -Protocol WSMan -Wait -For PowerShell -Force -Credential $localCredentialsStart-Sleep 20
foreach ($Server in $Servers) { do { $Test = Test-NetConnection -ComputerName $Server -CommonTCPPort WINRM } while ($Test.TcpTestSucceeded -eq $false)}Clean Disks (nur S2D)
Hierbei werden alle nicht-bootfähigen Datenträger bereinigt, damit sie für S2D bereitstehen. Alle Daten auf den Datenträgern gehen dabei verloren.
if ($S2D) { Invoke-Command -ComputerName $Servers -Credential $localCredentials -ScriptBlock { Get-StoragePool | Where-Object IsPrimordial -eq $false | Set-StoragePool -IsReadOnly:$false -ErrorAction SilentlyContinue Get-StoragePool | Where-Object IsPrimordial -eq $false | Get-VirtualDisk | Remove-VirtualDisk -Confirm:$false -ErrorAction SilentlyContinue Get-StoragePool | Where-Object IsPrimordial -eq $false | Remove-StoragePool -Confirm:$false -ErrorAction SilentlyContinue Get-PhysicalDisk | Reset-PhysicalDisk -ErrorAction SilentlyContinue Get-Disk | Where-Object Number -ne $null | Where-Object IsBoot -ne $true | Where-Object IsSystem -ne $true | Where-Object PartitionStyle -ne RAW | Where-Object BusType -ne USB | ForEach-Object { $_ | Set-Disk -IsOffline:$false $_ | Set-Disk -IsReadOnly:$false $_ | Clear-Disk -RemoveData -RemoveOEM -Confirm:$false $_ | Set-Disk -IsReadOnly:$true $_ | Set-Disk -IsOffline:$true } }}Secured Core
Secured Core schützt die Cluster-Nodes auf Hardware-Ebene. Alle Nodes müssen die Anforderungen erfüllen und identisch konfiguriert sein.
$scriptBlock = { $tpm = Get-Tpm $secureBoot = $false try { $secureBoot = Confirm-SecureBootUEFI } catch {}
[PSCustomObject]@{ TPM_Enabled = if ($tpm -and $tpm.TpmEnabled) { "Yes" } else { "No" } SecureBoot = if ($secureBoot) { "Yes" } else { "No" } }
}
$Info = Invoke-Command -ComputerName $servers -Credential $localCredentials -ScriptBlock $scriptBlock$Info | Select-Object PSComputerName, TPM_Enabled, SecureBoot | Format-Table -AutoSize
# In der Tabelle müssen alle Werte auf "Yes" stehen.Hardware-Voraussetzungen
Die folgenden Registry-Werte setzen voraus, dass die Server-Hardware Secured Core Server vollständig unterstützt. Nicht alle Einstellungen greifen auf jeder Hardware. Bei fehlender Unterstützung können einzelne Werte wirkungslos sein oder im schlimmsten Fall den Bootvorgang beeinträchtigen.
Anforderungen für die nachfolgende Konfiguration:
- TPM 2.0 (nicht TPM 1.2)
- UEFI-Firmware mit aktiviertem Secure Boot
- Intel TXT oder AMD SKINIT für System Guard Secure Launch (DRTM)
- IOMMU (Intel VT-d / AMD-Vi) für Kernel DMA Protection
- Memory Attribute Table (MAT) Unterstützung für HVCI MAT
Die genauen Anforderungen und unterstützte Modelle sind in der Herstellerdokumentation des Servers zu prüfen.
Invoke-Command -ComputerName $servers -Credential $localCredentials -ScriptBlock {
# Device Guard REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard" /v "EnableVirtualizationBasedSecurity" /t REG_DWORD /d 1 /f REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard" /v "RequirePlatformSecurityFeatures" /t REG_DWORD /d 3 /f REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard" /v "RequireMicrosoftSignedBootChain" /t REG_DWORD /d 1 /f REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard" /v "Locked" /t REG_DWORD /d 0 /f
# Credential Guard REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v "LsaCfgFlags" /t REG_DWORD /d 1 /f
# System Guard Secure Launch REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\SystemGuard" /v "Enabled" /t REG_DWORD /d 1 /f
# HVCI REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity" /v "Enabled" /t REG_DWORD /d 1 /f REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity" /v "HVCIMATRequired" /t REG_DWORD /d 1 /f REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity" /v "Locked" /t REG_DWORD /d 0 /f
}Update der Nodes (optional)
Vor der Cluster-Erstellung können alle Nodes auf einen einheitlichen Patchstand gebracht werden. Warum optional? Wir können das anschließend auch über Cluster Aware Update (CAU) erledigen. Falls ein älteres ISO verwendet wurde, empfehle ich diesen Schritt jedoch hier.
Credits: MSLab
# Virtuelles Konto erstellen (umgeht CredSSP-Erfordernis)Invoke-Command -ComputerName $servers -Credential $localCredentials -ScriptBlock { New-PSSessionConfigurationFile -RunAsVirtualAccount -Path $env:TEMP\VirtualAccount.pssc Register-PSSessionConfiguration -Name 'VirtualAccount' -Path $env:TEMP\VirtualAccount.pssc -Force} -ErrorAction Ignore
Start-Sleep 2
# Windows Update ausführen
Invoke-Command -ComputerName $servers -Credential $localCredentials -ConfigurationName 'VirtualAccount' { $Searcher = New-Object -ComObject Microsoft.Update.Searcher $SearchCriteria = "IsInstalled=0 and DeploymentAction='Installation' or " + "IsPresent=1 and DeploymentAction='Uninstallation' or " + "IsInstalled=1 and DeploymentAction='Installation' and RebootRequired=1 or " + "IsInstalled=0 and DeploymentAction='Uninstallation' and RebootRequired=1" $SearchResult = $Searcher.Search($SearchCriteria).Updatesif ($SearchResult.Count -gt 0) {$Session = New-Object -ComObject Microsoft.Update.Session$Downloader = $Session.CreateUpdateDownloader()$Downloader.Updates = $SearchResult$Downloader.Download()$Installer = New-Object -ComObject Microsoft.Update.Installer$Installer.Updates = $SearchResult$Result = $Installer.Install()$Result}}
# Temporäre PSSession-Konfiguration entfernen
Invoke-Command -ComputerName $servers -Credential $localCredentials -ScriptBlock {Unregister-PSSessionConfiguration -Name 'VirtualAccount'Remove-Item -Path $env:TEMP\VirtualAccount.pssc}Domain Join & Umbenennung
Jeder Node bekommt seinen finalen Namen und kommt in die Domäne. Danach ist er auch per Hostname statt IP erreichbar.
# ===================== ANPASSEN =====================
$DomainName = "corp.example.com"$OUPath = "OU=S2D-Cluster-01,OU=Virtualization,OU=Germany,OU=Server,DC=corp,DC=example,DC=com"$NodeName = "HV-S2D-N0" # Namensprefix – Nodes werden durchnummeriert (z.B. AHL-S2D-N01, N02 ...)$StartNode = 1 # Startnummer der Durchnummerierung
# =================== NICHT ANPASSEN =================
$DomainUser = Get-Credential
foreach ($server in $servers) { Invoke-Command -ComputerName $server -Credential $localCredentials -ArgumentList $StartNode -ScriptBlock { param($StartNode) $newName = $using:NodeName + $StartNode.ToString() Add-Computer -DomainName $using:DomainName -NewName $newName -OUPath $using:OUPath -DomainCredential $using:DomainUser -Restart } $StartNode++}Start-Sleep 20
foreach ($Server in $Servers) { do { $Test = Test-NetConnection -ComputerName $Server -CommonTCPPort WINRM } while ($Test.TcpTestSucceeded -eq $false)}Berechtigungsgruppe setzen
Damit nicht mit Domänen Admin Rechten gearbeitet werden muss und der Zugriff granularer gesteuert werden kann, wird eine AD-Gruppe als lokale Administratorgruppe auf allen Nodes hinzugefügt. Alle Mitglieder dieser Gruppe haben dann Admin-Rechte auf allen Nodes und somit dem auch Cluster.
# ===================== ANPASSEN =====================
$AdminADGroup = "CORP\Group_Hyper-V_Admin" # AD-Gruppe der Hyper-V Administratoren (muss bereits existieren)
# =================== NICHT ANPASSEN =================
foreach ($server in $servers) { Invoke-Command -ComputerName $server -Credential $localCredentials -ScriptBlock { Add-LocalGroupMember -Group Administrators -Member $using:AdminADGroup }}