Public/Get-sqmDiskBlockSize.ps1
|
<# .SYNOPSIS Prueft die NTFS-Blockgroesse (Cluster-Groesse) von Laufwerken auf 64KB. .DESCRIPTION Liest die NTFS-Allokationseinheit (Blockgroesse) der angegebenen Laufwerke per WMI (Win32_Volume) und prueft ob die fuer SQL Server empfohlenen 64 KB (65536 Bytes) konfiguriert sind. Kann entweder gezielt einzelne Laufwerkbuchstaben pruefen oder automatisch alle Laufwerke ermitteln die von einer SQL Server-Instanz genutzt werden (Data, Log, Backup, TempDB). Rein lesender Zugriff - keine Aenderungen am System. Zum Formatieren: Invoke-sqmFormatDrive64k .PARAMETER Drive Laufwerkbuchstabe(n) ohne Doppelpunkt, z.B. 'F', 'G', 'H'. Pipeline-faehig. Wenn nicht angegeben: -SqlInstance muss gesetzt sein. .PARAMETER SqlInstance SQL Server-Instanz. Wenn angegeben werden automatisch alle von SQL Server genutzten Laufwerke (Data, Log, Backup, TempDB) aus der Registry ermittelt. .PARAMETER ComputerName Zielcomputer fuer die WMI-Abfrage. Standard: lokaler Computer. .PARAMETER RecommendedBlockSize Empfohlene Blockgroesse in Bytes. Standard: 65536 (64 KB). .PARAMETER EnableException Ausnahmen sofort ausloesen statt Write-Error. .OUTPUTS [PSCustomObject] je Laufwerk mit den Feldern: Drive : Laufwerkbuchstabe Path : Vollstaendiger Pfad (z.B. F:\) Label : Volume-Label BlockSize : Aktuelle Blockgroesse in Bytes BlockSizeKB : Aktuelle Blockgroesse in KB RecommendedBlockSize : Empfohlene Blockgroesse in Bytes IsRecommended : $true wenn Blockgroesse korrekt Status : OK | Warning | NotNTFS | Error Message : Detailmeldung .EXAMPLE # Einzelne Laufwerke pruefen Get-sqmDiskBlockSize -Drive 'F', 'G', 'H' .EXAMPLE # Automatisch alle SQL-Laufwerke der Instanz ermitteln und pruefen Get-sqmDiskBlockSize -SqlInstance "SQL01" .EXAMPLE # Pipeline 'F','G' | Get-sqmDiskBlockSize .EXAMPLE # Nur Laufwerke mit falscher Blockgroesse anzeigen Get-sqmDiskBlockSize -SqlInstance "SQL01" | Where-Object { -not $_.IsRecommended } .NOTES SQL Server Empfehlung: NTFS-Allokationseinheit 64 KB fuer alle Datenlaufwerke. Standard-Windows-Format: 4 KB - fuer SQL Server nicht optimal. Gilt nicht fuer System- und OS-Laufwerke (C:\). Zum Formatieren mit 64 KB: Invoke-sqmFormatDrive64k #> function Get-sqmDiskBlockSize { [CmdletBinding(DefaultParameterSetName = 'ByDrive')] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory = $true, ParameterSetName = 'ByDrive', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] [ValidatePattern('^[A-Za-z]$')] [string[]]$Drive, [Parameter(Mandatory = $true, ParameterSetName = 'BySqlInstance')] [string]$SqlInstance, [Parameter(Mandatory = $false)] [string]$ComputerName = $env:COMPUTERNAME, [Parameter(Mandatory = $false)] [ValidateSet(4096, 8192, 16384, 32768, 65536, 131072)] [int]$RecommendedBlockSize = 65536, [Parameter(Mandatory = $false)] [switch]$EnableException ) begin { $functionName = $MyInvocation.MyCommand.Name $drivesToCheck = [System.Collections.Generic.List[string]]::new() # Empfohlene Blockgroesse aus Modulkonfiguration lesen (ueberschreibt Parameter-Default wenn nicht explizit angegeben) if (-not $PSBoundParameters.ContainsKey('RecommendedBlockSize')) { $cfgVal = Get-sqmConfig -Key 'CheckDiskBlockSize' if ($null -ne $cfgVal) { $RecommendedBlockSize = [int]$cfgVal } } Invoke-sqmLogging -Message "Starte $functionName auf $ComputerName (Empfehlung: $([Math]::Round($RecommendedBlockSize/1024)) KB)" -FunctionName $functionName -Level 'INFO' # Hilfsfunktion: WMI-Abfrage fuer ein Laufwerk function _GetVolumeInfo { param ([string]$DriveLetter, [string]$Computer) $path = "$($DriveLetter.ToUpper()):\\" $wql = "SELECT Name, Label, BlockSize FROM Win32_Volume WHERE FileSystem='NTFS' AND Name='$path'" try { Get-WmiObject -Query $wql -ComputerName $Computer -ErrorAction Stop } catch { $null } } # Bei BySqlInstance: Laufwerke aus Registry ermitteln if ($PSCmdlet.ParameterSetName -eq 'BySqlInstance') { try { Invoke-sqmLogging -Message "Ermittle SQL-Laufwerke fuer $SqlInstance aus Registry." -FunctionName $functionName -Level 'INFO' $instances = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' ` -ErrorAction Stop).PSObject.Properties | Where-Object { $_.Name -notmatch '^PS' } foreach ($inst in $instances) { $regKey = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$($inst.Value)\MSSQLServer" ` -ErrorAction SilentlyContinue foreach ($prop in @('BackupDirectory', 'DefaultData', 'DefaultLog')) { $val = ($regKey.PSObject.Properties | Where-Object { $_.Name -eq $prop }).Value if ($val -match '^([A-Za-z]):') { $drivesToCheck.Add($Matches[1].ToUpper()) | Out-Null } } } # TempDB-Laufwerke via SMO [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.Smo') $smo = New-Object Microsoft.SqlServer.Management.Smo.Server($SqlInstance) $smo.Databases['tempdb'].FileGroups | ForEach-Object { $_.Files } | ForEach-Object { if ($_.FileName -match '^([A-Za-z]):') { $drivesToCheck.Add($Matches[1].ToUpper()) | Out-Null } } $drivesToCheck = ($drivesToCheck | Sort-Object -Unique) Invoke-sqmLogging -Message "SQL-Laufwerke ermittelt: $($drivesToCheck -join ', ')" -FunctionName $functionName -Level 'INFO' } catch { $errMsg = "Fehler beim Ermitteln der SQL-Laufwerke: $($_.Exception.Message)" Invoke-sqmLogging -Message $errMsg -FunctionName $functionName -Level 'ERROR' if ($EnableException) { throw } Write-Error $errMsg return } } } process { # ByDrive: Laufwerke aus Pipeline/Parameter sammeln if ($PSCmdlet.ParameterSetName -eq 'ByDrive') { foreach ($d in $Drive) { $drivesToCheck.Add($d.ToUpper()) | Out-Null } } } end { $results = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($d in ($drivesToCheck | Sort-Object -Unique)) { try { $vol = _GetVolumeInfo -DriveLetter $d -Computer $ComputerName if (-not $vol) { $status = 'NotNTFS' $message = "Laufwerk ${d}: nicht gefunden oder kein NTFS-Volume." Write-Host " ---- Laufwerk ${d}: $message" -ForegroundColor DarkGray Invoke-sqmLogging -Message $message -FunctionName $functionName -Level 'WARNING' $results.Add([PSCustomObject]@{ Drive = $d Path = "${d}:\" Label = $null BlockSize = $null BlockSizeKB = $null RecommendedBlockSize = $RecommendedBlockSize IsRecommended = $false Status = $status Message = $message }) continue } $blockSize = [int]$vol.BlockSize $blockSizeKB = [Math]::Round($blockSize / 1024, 0) $isOk = ($blockSize -eq $RecommendedBlockSize) if ($isOk) { $status = 'OK' $message = "Laufwerk ${d}: Blockgroesse $blockSizeKB KB - entspricht Empfehlung ($([Math]::Round($RecommendedBlockSize/1024))KB)." Write-Host " OK $message" -ForegroundColor Green } else { $status = 'Warning' $message = "Laufwerk ${d}: Blockgroesse $blockSizeKB KB - empfohlen sind $([Math]::Round($RecommendedBlockSize/1024)) KB fuer SQL Server." Write-Host " WARN $message" -ForegroundColor Yellow } Invoke-sqmLogging -Message $message -FunctionName $functionName -Level $(if ($isOk) { 'INFO' } else { 'WARNING' }) $results.Add([PSCustomObject]@{ Drive = $d Path = "${d}:\" Label = $vol.Label BlockSize = $blockSize BlockSizeKB = $blockSizeKB RecommendedBlockSize = $RecommendedBlockSize IsRecommended = $isOk Status = $status Message = $message }) } catch { $errMsg = "Fehler bei Laufwerk ${d}: $($_.Exception.Message)" Invoke-sqmLogging -Message $errMsg -FunctionName $functionName -Level 'ERROR' if ($EnableException) { throw } Write-Error $errMsg $results.Add([PSCustomObject]@{ Drive = $d Path = "${d}:\" Label = $null BlockSize = $null BlockSizeKB = $null RecommendedBlockSize = $RecommendedBlockSize IsRecommended = $false Status = 'Error' Message = $errMsg }) } } Invoke-sqmLogging -Message "$functionName abgeschlossen. $($results.Count) Laufwerke geprueft." -FunctionName $functionName -Level 'INFO' return $results } } |