DiskManagement.psm1
function Edit-Disk { <# .SYNOPSIS This Cmdlet will avoid the manual setup of a disk which was attached to a target. requirements: - Windows 8.1 / Windows 2012R2 (NT 6.3) or newer - Administrator privileges author: - Marc Tschapek license: - Copyright: (c) 2017, Marc Tschapek <marc.tschapek@itelligence.de> - BSD-2-Clause (see https://opensource.org/licenses/BSD-2-Clause) .DESCRIPTION - Select and manage a disk, its partitions and file systems - The alias for Edit-Disk which you can also use is Manage-Disk DYNAMIC PARAMETERS - SetDriveLetter - Manage disk parameter. - Drive letter which will be set for the partition on selected disk. - This dynamic parameter let's you browse through all available drive letters on the target directly (with Tab key) if you set -SetDriveLetter while invoking the Cmdlet. - If you pass a drive letter to the Cmdlet which was chosen by your own (without Tab key to get the free drive letters automatically) the Cmdlet will use this drive letter if it is a free drive letter on the target. If the passed drive letter is not a free drive letter on the target, the Cmdlet will be canceled. - If no SetDriveLetter parameter value was passed but a valid value for parameter SetPartitionAccessPath was passed to the Cmdlet, the Cmdlet will create this partition access path and no drive letter for the partition on selected disk. - If no SetDriveLetter parameter value and no value for parameter SetPartitionAccessPath was passed to the Cmdlet, the Cmdlet will set a free drive letter for the partition randomly and no partition access path on selected disk. If in this case no free drive lettter is left on the target the Cmdlet will be canceled. - If a valid value for SetDriveLetter and for SetPartitionAccessPath parameter was passed to the Cmdlet, the Cmdlet will setup this partition drive letter and access path on selected disk. - SetLargerFRS - Manage disk parameter. - Switch to set Large FRS parameter for file system on selected disk, solely settable for ntfs file system. - This switch depends on the SetFileSystem parameter and can only passed to the Cmdlet if SetFileSystem parameter has value "ntfs". - SetShortNames - Manage disk parameter. - Switch to set Short Names parameter for file system on selected disk, solely settable for ntfs file system. - This switch depends on the SetFileSystem parameter and can only passed to the Cmdlet if SetFileSystem parameter has value "ntfs". - SetIntegrityStreams - Manage disk parameter. - Switch to set Integrity Streams parameter for file system on selected disk, solely settable for refs file system. - This switch depends on the SetFileSystem parameter and can only passed to the Cmdlet if SetFileSystem parameter has value "refs". .PARAMETER GetSize - Select disk parameter. - Size of the disk in gigabyte which will be selected. - If a size is passed the Cmdlet will try to select the disk with this size. - GetSize value must be equal or greater than 1gb and maximum 18446744073709551615gb. .PARAMETER GetPartitionStyle - Select disk parameter. - Partition style of the disk which will be selected. .PARAMETER GetOperationalStatus - Select disk parameter. - Operational Status of the disk which will be selected. .PARAMETER IsNotReadOnly - Select disk parameter. - Default behavior of the Cmdlet is, that the disk which will be selected has to be in read-only status. - With this switch you can specify that the disk which will be selected is not in read-only status (disk is writeable). .PARAMETER GetNumber - Select disk parameter. - Number of the disk which will be selected. - If a number is passed the Cmdlet will try to select the disk with this number. - Passed value will be checked in the beginning of the Cmdlet whether it is an int32 value. - If it is of type in64 the Cmdlet will be canceled. .PARAMETER SetPartitionStyle - Manage disk parameter. - Partition style which will be set on selected disk. .PARAMETER SetPartitionAccessPath - Manage disk parameter. - Access path which will be set on partition of selected disk. - The Cmdlet validates whether the passed value is already in use as access path by another disk, is a proper path/directory/folder on the target, is already in use as a link and whether it is empty (no files or folders inside). - This parameter has some dependencies with the dynamic parameter SetDriveLetter. For more information read the DYNAMIC PARAMETERS part of the help section. .PARAMETER SetFileSystem - Manage disk parameter. - File system which will be set on selected disk. - Maximum volume size for ntfs is 256000gb. - Maximum volume size for refs is the maximum GetSize parameter value 18446744073709551615gb. - If the disk size of the selected disk does not match with the passed value for parameter SetFileSystem (e.g. "ntfs" over 256000gb) the Cmdlet will be canceled. - Also consider the dynamic parameters SetLargerFRS, SetShortNames and SetIntegrityStreams which are switches who can be set in addition to the FileSystem parameter but they depend on the passed file system value. For more information read the DYNAMIC PARAMETERS part of the help section. .PARAMETER SetLabel - Manage disk parameter. - File system label which should be set for the file system on selected disk. .PARAMETER SetAllocationUnitSize - Manage disk parameter. - Allocation unit size which will be set for the file system on selected disk (possible values for file system ntfs 4,8,16,32,64kb;refs 64kb). - If parameter SetFileSystem is set to "refs" the allocation unit size will be automatically adjusted to "64" (kb). .EXAMPLE Manage-Disk -GetNumber 1 -GetPartitionStyle 'raw' -GetOperationalStatus 'offline' ` -SetPartitionStyle 'mbr' -SetPartitionAccessPath C:\Test -SetFileSystem 'ntfs' -SetLabel 'database_disk' -SetLargeFRS -SetShortNames -WhatIf This example would try to select and set the disk as specified and shows verbose logging information but would not apply any changes to the target because of the passed common parameter WhatIf. .EXAMPLE powershell -command "Edit-Disk -GetSize 50 -GetPartitionStyle 'mbr' -GetOperationalStatus 'online' -IsNotReadOnly ` -SetPartitionStyle 'gpt' -SetDriveLetter F -SetFileSystem 'refs' -SetLabel 'application_disk' -SetAllocationUnitSize 64 -SetIntegrityStreams -Verbose" This example would try to select and set the disk as specified. Furthermore it will show what was changed on the target caused by the common parameter -Verbose. In this example the Cmdlet is started in a new PowerShell session. This is useful if you want to invoke the Cmdlet from within cmd or another tool which is not a PowerShell host (for instance Cmd). .INPUTS None. You cannot pipe objects to Edit-Disk. .OUTPUTS System.String. Edit-Disk returns a string with a in JSON converted hashtable. The hashtable contains the following items and sub-items: - changed - Whether anything was changed. Returned: always Type: boolean Sample: true - message - Possible error message on failure. Returned: failed Type: string Sample: "No free drive letter left on the target" - change_log - Dictionary containing all the detailed information about changes on the selected disk. Returned: if -WhatIf or/and -Verbose option was passed to the Cmdlet Type: complex Contains: operational_status - Detailed information about setting operational status of the disk. Returned: success or failed Type: string Sample: "Disk set online" writeable_status - Detailed information whether disk was set from read-only to writeable. and if not why it was not set to it. Returned: success or failed Type: string Sample: "Disk set from read-only to writeable" allocation_unit - Information whether allocation_unit_size value was automatically adjusted. Returned: if file_system option value was refs and allocation_unit_size value was not 64 Type: string Sample: "Size was automatically adjusted to 64kb due to file_system option value refs" initializing - Detailed information about initializing the disk. Returned: success or failed Type: string Sample: "Disk initialization successful - Partition style raw (GetPartitionStyle) was initalized to gpt (SetPartitionStyle)" converting - Detailed information about converting the partition style. of the disk (in case of converting no initalization of disk). Returned: success or failed Type: string Sample: "Partition style mbr (GetPartitionStyle) was converted to gpt (SetPartitionStyle)" partitioning - Detailed information about partition creation on the selected disk. Returned: success or failed Type: string Sample: "Initial partition Basic was created successfully on partition style gpt" access_path - Detailed information about access path creation on the partition of selected disk. Returned: success or failed Type: string Sample: "Partition access path C:\\Test was created successfully for partition Basic" formatting - Detailed information about volume creation on partitoned disk. Returned: success or failed Type: string Sample: "Volume ReFS was created successfully on partition Basic" shellhw_service_state - Detailed information about executed ShellHWService action (start, stop). Returned: success or failed Type: string Sample: "Could not be set from 'Stopped' to 'Running' again" .NOTES - To select the disk and to manage it you have several parameters which are all described in the documentation. - If you invoke the Cmdlet without any parameter the default parameter values will be used. - If you pass a decimal value for any of the int parameters it will be rounded to an even number. - To identify the parameters which are used to select a disk consider the "Get" verb in front of the parameter (except for -IsNotReadOnly which is also a a select parameter) and the "Select disk parameter" hint in the parameter description. - To identify the parameters which are used to manage a disk consider the "Set" verb in front of the parameter and the "Manage disk parameter" hint in the parameter description. - In order to find only one disk on the target you can use the GetSize and/or GetNumber parameter for the search of the disk. - If no GetSize and GetNumber parameter value was defined and multiple disks were found on the target based on the passed parameter values the Cmdlet will select the first disk found. - The Cmdlet detects any existing volume and/or partition on the selected disk and will cancel the Cmdlet in this case. - If the disk is not yet initialized the Cmdlet will initialize the disk (set partition style, online and writeable). - If the disk is initialized already the Cmdlet will try to set the disk to "online" and "writeable" (read-only eq. false) if it's not the state of the disk already. - Further in this case it will convert the partition style of the disk to the selected partition style if needed. - The Cmdlet will stop and start the service "ShellHWService" again in order to avoid disk management GUI messages. - If the Cmdlet fails with an error and the operational status was set from "offline" to "online" before the Cmdlet will try to set the disk to operational status "offline" again but will not be canceled if set "offline" again fails. - If the Cmdlet fails with an error and the writeable status was set from "read-only" to "writeable" before the Cmdlet will try to set the disk to writeable status "read-only" again but will not be canceled if set "read-only" again fails. - If you use the common parameter -Verbose you will get detailed information about the changes on the target. - If you use the common parameter -WhatIf nothing will be changed on the target but you will get the information what would be changed (contains -Verbose). - If you invoke the Cmdlet all common parameters are available but only -Verbose, -Whatif and -Confirm will take an affect. - Please note that in this Cmdlet -Verbose and -WhatIf does act in a different way as usual because of the hashtable output (manual created Verbose and WhatIf output, not the standard Cmdlets output). - The Confirm parameter acts as per PowerShell standard behavior. - Of course you can use the common parameter -OutVariable. - The Cmdlet needs to be invoked with administrator privileges otherwise the Cmdlet will be canceled. .LINK Ansible Version: https://github.com/ansible/ansible/pull/27634 .LINK Get-Disk Get-Volume Set-Disk Initialize-Disk New-Partition Format-Volume #> [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = "High" ) ] param( [Parameter(Position = 0)] [ValidateRange(1,18446744073709551615)] [uint64]$GetSize, [Parameter(Position = 1)] [ValidateNotNullorEmpty()] [int32]$GetNumber, [Parameter(Position = 2)] [ValidateSet("raw", "mbr", "gpt")] [string]$GetPartitionStyle = "raw", [Parameter(Position = 3)] [ValidateSet("offline", "online")] [string]$GetOperationalStatus = "offline", [Parameter(Position = 4)] [switch]$IsNotReadOnly, [Parameter( Position = 5, Mandatory = $true, HelpMessage = "Please enter a valid partition style (gpt or mbr) which should be set on the selected disk" ) ] [ValidateSet("gpt", "mbr")] [string]$SetPartitionStyle, [Parameter(Position = 7)] [ValidateScript( { if ((-not ((Get-Partition).AccessPaths -like "$_*")) -and (Test-Path $_ -PathType Container) -and ((Get-Item $_).LinkType -eq $null)) { if (-not (Get-ChildItem $_)) { $true } else { throw "$_ is not an empty folder (contains files and/or folders)" } } elseif ((Get-Partition).AccessPaths -like "$_*") { throw "$_ is already in use as access path by another disk" } elseif (-not (Test-Path $_ -PathType Container)) { throw "$_ is not a valid path/directory/folder on the target for parameter SetPartitionStyle" } elseif((Get-Item $_).LinkType -ne $null) { throw "$_ is already in use as a link of type $((Get-Item $_).LinkType)" } } ) ] [string]$SetPartitionAccessPath, [Parameter( Position = 8, Mandatory = $true, HelpMessage = "Please enter a valid file system (ntfs or refs) which should be set on the selected disk" ) ] [ValidateNotNullorEmpty()] [ValidateSet("ntfs", "refs")] [string]$SetFileSystem, [Parameter(Position = 9)] [ValidateNotNullorEmpty()] [string]$SetLabel = "additional_disk", [Parameter( Position = 10, Mandatory = $true, HelpMessage = "Please enter a valid allocation unit size (4, 8, 16, 32 or 64) which should be set for the file system on the selected disk" ) ] [ValidateSet(4,8,16,32,64)] [int32]$SetAllocationUnitSize ) DynamicParam { $DriveLetterAttribute = New-Object System.Management.Automation.ParameterAttribute $DriveLetterAttribute.Position = 6 $DriveLetterAttribute.ParameterSetName = '__AllParameterSets' $attributeCollectionDriveLetter = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollectionDriveLetter.Add($DriveLetterAttribute) $Letters = try { (Get-ChildItem Function:[a-z]: -Name | Where-Object { -not (Test-Path -Path $_) }).TrimEnd(":") } catch { throw "Gather all free drive letters on the target failed" } $attributeCollectionDriveLetter.Add((New-Object System.Management.Automation.ValidateSetAttribute($Letters))) $DriveLetterParam = New-Object System.Management.Automation.RuntimeDefinedParameter('SetDriveLetter', [char], $attributeCollectionDriveLetter) $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $paramDictionary.Add('SetDriveLetter', $DriveLetterParam) if ($SetFileSystem -eq "ntfs") { $LargeFRSAttribute = New-Object System.Management.Automation.ParameterAttribute $LargeFRSAttribute.Position = 11 $LargeFRSAttribute.ParameterSetName = '__AllParameterSets' $attributeCollectionFRS = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollectionFRS.Add($LargeFRSAttribute) $LargeFRSParam = New-Object System.Management.Automation.RuntimeDefinedParameter('SetLargeFRS', [switch], $attributeCollectionFRS) $paramDictionary.Add('SetLargeFRS', $LargeFRSParam) $ShortNamesAttribute = New-Object System.Management.Automation.ParameterAttribute $ShortNamesAttribute.Position = 12 $ShortNamesAttribute.ParameterSetName = '__AllParameterSets' $attributeCollectionShort = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollectionShort.Add($ShortNamesAttribute) $ShortNamesParam = New-Object System.Management.Automation.RuntimeDefinedParameter('SetShortNames', [switch], $attributeCollectionShort) $paramDictionary.Add('SetShortNames', $ShortNamesParam) } elseif ($SetFileSystem -eq "refs") { $IntegrityStreamsAttribute = New-Object System.Management.Automation.ParameterAttribute $IntegrityStreamsAttribute.Position = 13 $IntegrityStreamsAttribute.ParameterSetName = '__AllParameterSets' $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollection.Add($IntegrityStreamsAttribute) $IntegrityStreamsParam = New-Object System.Management.Automation.RuntimeDefinedParameter('SetIntegrityStreams', [switch], $attributeCollection) $paramDictionary.Add('SetIntegrityStreams', $IntegrityStreamsParam) } return $paramDictionary } Process { # Check WhatIf and Verbose parameter [bool]$CheckMode = $WhatIfPreference [bool]$Verbose = if ($PSBoundParameters["Verbose"]) { $true } else { $false } # Create a new result object [hashtable]$result = @{ message = $null failed = $false changed = $false } # Functions function Test-Admin { $CurrentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent()) $IsAdmin = $CurrentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) return $IsAdmin } function Search-Disk { param( $DiskSize, $PartitionStyle, $OperationalStatus, $ReadOnly, $Number ) if ($DiskSize -ne $null) { $DiskSize = $DiskSize *1GB } if ($DiskSize -and ($Number -ne $null)) { $disk = Get-Disk | Where-Object { ($_.PartitionStyle -eq $PartitionStyle) -and ($_.OperationalStatus -eq $OperationalStatus) -and ($_.IsReadOnly -eq $ReadOnly) -and ($_.Size -eq $DiskSize) -and ($_.Number -eq $Number) } } elseif ($DiskSize) { $disk = Get-Disk | Where-Object { ($_.PartitionStyle -eq $PartitionStyle) -and ($_.OperationalStatus -eq $OperationalStatus) -and ($_.IsReadOnly -eq $ReadOnly) -and ($_.Size -eq $DiskSize) } } elseif ($Number -ne $null) { $disk = Get-Disk | Where-Object { ($_.PartitionStyle -eq $PartitionStyle) -and ($_.OperationalStatus -eq $OperationalStatus) -and ($_.IsReadOnly -eq $ReadOnly) -and ($_.Number -eq $Number) } } else { $disk = Get-Disk | Where-Object { ($_.PartitionStyle -eq $PartitionStyle) -and ($_.OperationalStatus -eq $OperationalStatus) -and ($_.IsReadOnly -eq $ReadOnly) } } return $disk } function Set-OperationalStatus { param( $Disk, [switch]$Deactivate ) $null = Set-Disk -Number ($Disk.Number) -IsOffline $Deactivate.IsPresent } function Set-DiskWriteable { param( $Disk, [switch]$Deactivate ) $null = Set-Disk -Number ($Disk.Number) -IsReadonly $Deactivate.IsPresent } function Search-Volume { param( $Partition ) $FoundVolume = Get-Volume | Where-Object { $Partition.AccessPaths -like $_.ObjectId } if ($FoundVolume -eq $null) { return $false } return $FoundVolume } function Set-Initialized { param( $Disk, $PartitionStyle ) $null = $Disk| Initialize-Disk -PartitionStyle $PartitionStyle -Confirm:$false } function Convert-PartitionStyle { param( $Disk, $PartitionStyle ) $null = Invoke-Expression "'Select Disk $($Disk.Number)','Convert $($PartitionStyle)' | diskpart" } function Manage-ShellHWService { param( $Action ) switch ($Action) { Stop { $null = Stop-Service -Name ShellHWDetection } Start { $null = Start-Service -Name ShellHWDetection } Check { $CheckService = (Get-Service ShellHWDetection).Status -eq "Running" return $CheckService } } } function Create-Partition { param( $Disk, $SetDriveLetter ) if (-not $SetDriveLetter -eq [string]::Empty) { $Partition = $Disk | New-Partition -UseMaximumSize -DriveLetter $SetDriveLetter } else { $Partition = $Disk | New-Partition -UseMaximumSize } return $Partition } function Add-AccessPath { param( $Partition, $Path ) $null = $Partition | Add-PartitionAccessPath -AccessPath $Path } function Create-Volume { param( $Volume, $FileSystem, $FileSystemLabel, $FileSystemAllocUnitSize, $FileSystemLargeFRS, $FileSystemShortNames, $FileSystemIntegrityStreams ) $Alloc = $FileSystemAllocUnitSize *1KB $ParaVol = @{ FileSystem = $FileSystem NewFileSystemLabel = $FileSystemLabel AllocationUnitSize = $Alloc } switch ($FileSystem) { ntfs { $ParaVol += @{ShortFileNameSupport = $FileSystemShortNames; UseLargeFRS = $FileSystemLargeFRS} } refs { $ParaVol['SetIntegrityStreams'] = $FileSystemIntegrityStreams } } $CreatedVolume = $Volume | Format-Volume @ParaVol -Force -Confirm:$false return $CreatedVolume } # Check admin rights if (-not (Test-Admin)) { $result.message = "Cmdlet was not started with elevated rights" $result.failed = "true" return $result | ConvertTo-Json } # Rescan disks $null = Invoke-Expression '"rescan" | diskpart' # Search disk $ParamsDisk = @{ DiskSize = if ($PSBoundParameters["GetSize"]) { $GetSize } else { $null } PartitionStyle = $GetPartitionStyle OperationalStatus = $GetOperationalStatus ReadOnly = if ($IsNotReadOnly.IsPresent) { $false } else { $true } Number = if ($PSBoundParameters["GetNumber"]) { $GetNumber } else { $null } } try { $disk = Search-Disk @ParamsDisk } catch { $result.message = "Failed to search and/or select the disk with the specified parameter values: $($_.Exception.Message)" $result.failed = "true" return $result | ConvertTo-Json } if ($disk) { $diskcount = $disk | Measure-Object | Select-Object -ExpandProperty Count if ($diskcount -ge 2) { $disk = $disk[0] } [string]$DOperSt = $disk.OperationalStatus [string]$DPartStyle = $disk.PartitionStyle [string]$DROState = $disk.IsReadOnly } else { $result.message = "No disk could be found and selected with the passed parameter values" $result.failed = "true" return $result | ConvertTo-Json } # Create change log if ($CheckMode -or $Verbose) { $result += @{ change_log = @{ } } } # Check and set operational status and read-only state $SetOnline = $false $SetWriteable = $false $OPStatusFailed = $false $ROStatusFailed = $false if ($DPartStyle -ne "RAW") { if ($DOperSt -ne "Online") { if (-not $CheckMode) { # Set online try { Set-OperationalStatus -Disk $disk } catch { $result.message = "Failed to set the disk online: $($_.Exception.Message)" $result.failed = "true" if ($Verbose) { $result.Remove("change_log") } return $result | ConvertTo-Json } if ($Verbose) { $result.change_log.operational_status = "Disk set online" } $result.changed = $true $SetOnline = $true } else { $result.change_log.operational_status = "Disk is offline but was not set online due to passed -WhatIf switch" } } if ($DROState -eq "True") { if (-not $CheckMode) { # Set writeable try { Set-DiskWriteable -Disk $disk } catch { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set from read-only to writeable" } if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } $result.message = "Failed to set the disk from read-only to writeable state: $($_.Exception.Message)" $result.failed = "true" return $result | ConvertTo-Json } if ($Verbose) { $result.change_log.writeable_status = "Disk set from read-only to writeable" } $result.changed = $true $SetWriteable = $true } else { $result.change_log.writeable_status = "Disk is read-only but was not set writeable due to passed -WhatIf switch" } } } # Check volumes and partitions [string]$PartNumber = $disk.NumberOfPartitions # Verify partitons and volumes if ($PartNumber -ge 1) { # Collect partitions try { $partition = Get-Partition -DiskNumber $disk.Number } catch { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } $result.message = "General error while searching for partitions on the selected disk: $($_.Exception.Message)" $result.failed = "true" return $result | ConvertTo-Json } # Collect volumes try { $volume = Search-Volume -Partition $partition } catch { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } $result.message = "General error while searching for volumes on the selected disk: $($_.Exception.Message)" $result.failed = "true" return $result | ConvertTo-Json } # Existent volumes and partitions if (-not $volume) { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } $result.message = "Existing partitions found on the selected disk" $result.failed = "true" return $result | ConvertTo-Json } else { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } $result.message = "Existing volumes found on the selected disk" $result.failed = "true" return $result | ConvertTo-Json } } # Check set parameter values # Check drive letter and access path if ((-not $PSBoundParameters["SetDriveLetter"]) -and ($SetPartitionAccessPath -eq [String]::Empty)) { # Use random drive letter try { $DriveLetter = Get-ChildItem Function:[a-z]: -Name | Where-Object { -not (Test-Path -Path $_) } | Get-Random } catch { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } $result.message = "The check to get free drive letters on the target failed" $result.failed = "true" return $result | ConvertTo-Json } if ($DriveLetter) { $SetDriveLetter = $DriveLetter.TrimEnd(":") } else { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } $result.message = "No free drive letter left on the target" $result.failed = "true" return $result | ConvertTo-Json } } elseif (-not $PSBoundParameters["SetDriveLetter"]) { [string]$SetDriveLetter = [string]::Empty } else { $SetDriveLetter = $PSBoundParameters.SetDriveLetter } # Check file system if ($SetFileSystem -eq "ntfs") { if ($GetSize -le 256000) { } else { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } $result.message = "Parameter size with value $($GetSize)gb is not a valid size for ntfs hence the disk can not be formatted with this file system" $result.failed = "true" return $result | ConvertTo-Json } } elseif ($SetFileSystem -eq "refs") { if ($SetAllocationUnitSize -ne 64) { $SetAllocationUnitSize = 64 if ($Verbose) { $result.change_log.allocation_unit = "Size was automatically adjusted to 64kb due to FileSystem parameter value refs" } } } # Initialize / convert disk if ($DPartStyle -eq "RAW") { if (-not $CheckMode) { if ($DOperSt -eq "Offline") { $SetOnline = $true } if ($DROState -eq "True") { $SetWriteable = $true } # Initialize disk try { Set-Initialized -Disk $disk -PartitionStyle $SetPartitionStyle } catch { $GetDiskFailed = $false $FailDisk = $null if ($SetOnline) { try { $FailDisk = Get-Disk -Number $disk.Number } catch { $GetDiskFailed = $true } finally { if (-not $GetDiskFailed) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk was tried to set online during failed disk initalization and set now to it's initial state offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk was tried to set online during failed disk initalization, set it to it's inital state offline again failed also now" } } } else { if ($Verbose) { $result.change_log.operational_status = "Disk was tried to set online during failed disk initalization and was now tried to set offline again but disk could not be found anymore" } } } } if ($SetWriteable) { if (-not $FailDisk) { try { $FailDisk = Get-Disk -Number $disk.Number } catch { $GetDiskFailed = $true } } if (-not $GetDiskFailed) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk was tried to set writeable during failed disk initalization and set now to it's initial state read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk was tried to set writeable during failed disk initalization, set it to it's inital state read-only again failed also now" } } } } else { if ($Verbose) { $result.change_log.writeable_status = "Disk was tried to set writeable during failed disk initalization and was now tried to set read-only again but disk could not be found anymore" } } } $result.message = "Failed to initialize the disk: $($_.Exception.Message)" $result.failed = "true" return $result | ConvertTo-Json } if ($Verbose) { $result.change_log.initializing = "Disk initialization successful - Partition style $GetPartitionStyle (partition_style_select) was initalized to $SetPartitionStyle (partition_style_set)" } $result.changed = $true } else { $result.change_log.initializing = "Disk with partition style $GetPartitionStyle (partition_style_select) will not be initialized to $SetPartitionStyle (partition_style_set) due to passed -WhatIf switch" } } else { if ($DPartStyle -ne $SetPartitionStyle) { if (-not $CheckMode) { # Convert disk try { Convert-PartitionStyle -Disk $disk -PartitionStyle $SetPartitionStyle } catch { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } $result.message = "Failed to convert the disk: $($_.Exception.Message)" $result.failed = "true" return $result | ConvertTo-Json } if ($Verbose) { $result.change_log.converting = "Partition style $GetPartitionStyle (partition_style_select) was converted to $SetPartitionStyle (partition_style_set)" } $result.changed = $true } else { $result.change_log.converting = "Disk will not be converted from partition style $GetPartitionStyle (partition_style_select) to $SetPartitionStyle (partition_style_set) due to passed -WhatIf switch" } } } # Maintain ShellHWService (not Cmdlet terminating) $StopSuccess = $false $StopFailed = $false $StartFailed = $false $CheckFailed = $false # Check ShellHWService try { $Check = Manage-ShellHWService -Action "Check" } catch { $CheckFailed = $true } finally { if ($Check) { if (-not $CheckMode) { # Stop ShellHWService try { Manage-ShellHWService -Action "Stop" } catch { $StopFailed = $true } finally { if (-not $StopFailed) { if ($Verbose) { $result.change_log.shellhw_service_state = "Set from 'Running' to 'Stopped'" } $StopSuccess = $true $result.changed = $true } else { if ($Verbose) { $result.change_log.shellhw_service_state = "Could not be set from 'Running' to 'Stopped'" } } } } else { $result.change_log.shellhw_service_state = "Service will not be set from 'Running' to 'Stopped' due to passed -WhatIf switch" } } elseif ($CheckFailed) { if ($Verbose) { $result.change_log.shellhw_service_state = "Service will not be changed because the check has failed" } } } # Part disk if (-not $CheckMode) { try { $CPartition = Create-Partition -Disk $disk -SetDriveLetter $SetDriveLetter } catch { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } if ($StopSuccess) { try { Manage-ShellHWService -Action "Start" } catch { $StartFailed = $true } finally { if (-not $StartFailed) { if ($Verbose) { $result.change_log.shellhw_service_state = "Set from 'Stopped' to 'Running' again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.shellhw_service_state = "Could not be set from 'Stopped' to 'Running' again" } } } } $result.message = "Failed to create the partition on the disk: $($_.Exception.Message)" $result.failed = "true" return $result | ConvertTo-Json } if ($Verbose) { if ((-not $PSBoundParameters["SetDriveLetter"]) -and ($SetPartitionAccessPath -eq [String]::Empty)) { $result.change_log.partitioning = "Initial partition $($CPartition.Type) with random drive letter $SetDriveLetter was created successfully on partition style $SetPartitionStyle" } elseif (-not $PSBoundParameters["SetDriveLetter"]) { $result.change_log.partitioning = "Initial partition $($CPartition.Type) with no drive letter was created successfully on partition style $SetPartitionStyle" } else { $result.change_log.partitioning = "Initial partition $($CPartition.Type) with passed drive letter $SetDriveLetter was created successfully on partition style $SetPartitionStyle" } } $result.changed = $true } else { $result.change_log.partitioning = "Disk will not be partitioned due to passed -WhatIf switch" } # Add partition access path if (-not $SetPartitionAccessPath -eq [String]::Empty) { if (-not $CheckMode) { try { Add-AccessPath -Partition $CPartition -Path $SetPartitionAccessPath } catch { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } if ($StopSuccess) { try { Manage-ShellHWService -Action "Start" } catch { $StartFailed = $true } finally { if (-not $StartFailed) { if ($Verbose) { $result.change_log.shellhw_service_state = "Set from 'Stopped' to 'Running' again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.shellhw_service_state = "Could not be set from 'Stopped' to 'Running' again" } } } } $result.message = "Failed to create partition access path: $($_.Exception.Message)" $result.failed = "true" return $result | ConvertTo-Json } if ($Verbose) { $result.change_log.access_path = "Partition access path $SetPartitionAccessPath was created successfully for partition $($CPartition.Type)" } $result.changed = $true } else { $result.change_log.access_path = "Partition access path will not be added to partition due to passed -WhatIf switch" } } # Create volume if (-not $CheckMode) { $ParamsVol = @{ Volume = $CPartition FileSystem = $SetFileSystem FileSystemLabel = $SetLabel FileSystemAllocUnitSize = $SetAllocationUnitSize FileSystemLargeFRS = if ($PSBoundParameters["SetLargeFRS"]) { $PSBoundParameters.SetLargeFRS.IsPresent } else { $false } FileSystemShortNames = if ($PSBoundParameters["SetShortNames"]) { $PSBoundParameters.SetShortNames.IsPresent } else { $false } FileSystemIntegrityStreams = if ($PSBoundParameters["SetIntegrityStreams"]) { $PSBoundParameters.SetIntegrityStreams.IsPresent } else { $false } } try { $CVolume = Create-Volume @ParamsVol } catch { if ($SetOnline) { try { Set-OperationalStatus -Disk $disk -Deactivate } catch { $OPStatusFailed = $true } finally { if (-not $OPStatusFailed) { if ($Verbose) { $result.change_log.operational_status = "Disk set online and now offline again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.operational_status = "Disk failed to set offline again" } } } } if ($SetWriteable) { try { Set-DiskWriteable -Disk $disk -Deactivate } catch { $ROStatusFailed = $true } finally { if (-not $ROStatusFailed) { if ($Verbose) { $result.change_log.writeable_status = "Disk set writeable and now read-only again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.writeable_status = "Disk failed to set read-only again" } } } } if ($StopSuccess) { try { Manage-ShellHWService -Action "Start" } catch { $StartFailed = $true } finally { if (-not $StartFailed) { if ($Verbose) { $result.change_log.shellhw_service_state = "Set from 'Stopped' to 'Running' again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.shellhw_service_state = "Could not be set from 'Stopped' to 'Running' again" } } } } $result.message = "Failed to create the volume on the disk: $($_.Exception.Message)" $result.failed = "true" return $result | ConvertTo-Json } if ($Verbose) { $result.change_log.formatting = "Volume $($CVolume.FileSystem) with allocation unit size $SetAllocationUnitSize and label $SetLabel was created successfully on partition $($CPartition.Type)" } $result.changed = $true } else { $result.change_log.formatting = "Disk will not be formatted due to passed -WhatIf switch" } # Finally check if ShellHWService needs to be started again if (-not $CheckMode) { if ($StopSuccess) { # Start ShellHWService try { Manage-ShellHWService -Action "Start" } catch { $StartFailed = $true } finally { if (-not $StartFailed) { if ($Verbose) { $result.change_log.shellhw_service_state = "Set from 'Stopped' to 'Running' again" } $result.changed = $true } else { if ($Verbose) { $result.change_log.shellhw_service_state = "Could not be set from 'Stopped' to 'Running' again" } } } } } # Return result $result.message = "Cmdlet finished successfully" return $result | ConvertTo-Json } } # Define module environment settings $ErrorActionPreference = "Stop" Set-StrictMode -Version 2.0 # Export functions and aliases Set-Alias Manage-Disk Edit-Disk Export-ModuleMember -Function *-Disk -Alias Manage-Disk |