DiskManagement.psm1

function Get-DiskFacts {
        <#
        .SYNOPSIS
        Show the attached disks and disk information of the target host.
        requirements:
            - Windows 8.1 / Windows 2012R2 (NT 6.3) / PowerShell 4.0 or newer
        author:
            - Marc Tschapek (c5211773)
        license:
            - Copyright: (c) 2018, Marc Tschapek <marc.tschapek@itelligence.de>
            - BSD-2-Clause (see https://opensource.org/licenses/BSD-2-Clause)
        .DESCRIPTION
            - With this Cmdlet you can retrieve and output detailed information about the attached disks of the target and
              its volumes and partitions if existent.
            - You can use the Trimmed switch parameter in order to get detailed disk information
              but no details about the partitions and volumes of the disk.
        .EXAMPLE
        Get-DiskFacts

        This example would give you the details of the disk and it's partitions and volumes.
        .EXAMPLE
        Get-DiskFacts -Trimmed

        This example would give you the details of the disk but not the details of it's partitions and volumes
        because the Trimmed switch parameter is used.
        .EXAMPLE
        powershell -command "Get-DiskInfo"

        This example would give you the details of the disk and it's partitions and volumes.
        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 Oasis).
        .INPUTS
        None. You cannot pipe objects to Get-DiskFacts.
        .OUTPUTS
        System.String. Get-DiskFacts 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"

        - total_disks:
            - Count of found disks on the target.
            Returned: if disks were found
            Type: int
            Sample: 3
        
        - disks:
            - Detailed information about one particular disk.
            Returned: if disks were found
            Type: list
            Contains:
                number:
                    - Disk number of the particular disk.
                    Returned: always
                    Type: int
                    Sample: 0
                size:
                    - Size in Gibibyte of the particular disk.
                    Returned: always
                    Type: string
                    Sample: "100GiB"
                bus_type:
                    - Bus type of the particular disk.
                    Returned: always
                    Type: string
                    Sample: "SCSI"
                friendly_name:
                    - Friendly name of the particular disk.
                    Returned: always
                    Type: string
                    Sample: "Red Hat VirtIO SCSI Disk Device"
                partition_style:
                    - Partition style of the particular disk.
                    Returned: always
                    Type: string
                    Sample: "MBR"
                partition_count:
                    - Number of partitions on the particular disk.
                    Returned: always
                    Type: int
                    Sample: 4
                operational_status:
                    - Operational status of the particular disk.
                    Returned: always
                    Type: string
                    Sample: "Online"
                sector_size:
                    - Sector size in byte of the particular disk.
                    Returned: always
                    Type: string
                    Sample: "512s/byte/bytes/"
                read_only:
                    - Read only status of the particular disk.
                    Returned: always
                    Type: boolean
                    Sample: True
                bootable:
                    - Information whether the particular disk is a bootable disk.
                    Returned: always
                    Type: boolean
                    Sample: False
                system_disk:
                    - Information whether the particular disk is a system disk.
                    Returned: always
                    Type: boolean
                    Sample: True
                clustered:
                    - Information whether the particular disk is clustered (part of a failover cluster).
                    Returned: always
                    Type: boolean
                    Sample: False
                manufacturer:
                    - Manufacturer of the particular disk.
                    Returned: always
                    Type: string
                    Sample: "Red Hat"
                model:
                    - Model specification of the particular disk.
                    Returned: always
                    Type: string
                    Sample: "VirtIO"
                firmware_version:
                    - Firmware version of the particular disk.
                    Returned: always
                    Type: string
                    Sample: "0001"
                location:
                    - Location of the particular disk on the target.
                    Returned: always
                    Type: string
                    Sample: "PCIROOT(0)#PCI(0400)#SCSI(P00T00L00)"
                serial_number:
                    - Serial number of the particular disk on the target.
                    Returned: always
                    Type: string
                    Sample: "b62beac80c3645e5877f"
                unique_id:
                    - Unique ID of the particular disk on the target.
                    Returned: always
                    Type: string
                    Sample: "3141463431303031"
                guid:
                    - GUID of the particular disk on the target.
                    Returned: if existent
                    Type: string
                    Sample: "{efa5f928-57b9-47fc-ae3e-902e85fbe77f}"
                path:
                    - Path of the particular disk on the target.
                    Returned: always
                    Type: string
                    Sample: "\\\\?\\scsi#disk&ven_red_hat&prod_virtio#4&23208fd0&1&000000#{<id>}"
                partitions:
                    - Detailed information about one particular partition on the specified disk.
                    - The following output is only visisble if no Trimmed switch is passed to the Cmdlet.
                    Returned: if existent
                    Type: list
                    Contains:
                        number:
                            - Number of the particular partition.
                            Returned: always
                            Type: int
                            Sample: 1
                        size:
                            - Size in Gibibyte of the particular partition.
                            - Accurate to three decimal places.
                            Returned: always
                            Type: string
                            Sample: "0.031GiB"
                        type:
                            - Type of the particular partition.
                            Returned: always
                            Type: string
                            Sample: "IFS"
                        gpt_type:
                            - gpt type of the particular partition.
                            Returned: if partition_style property of the particular disk has value "GPT"
                            Type: string
                            Sample: "{e3c9e316-0b5c-4db8-817d-f92df00215ae}"
                        no_default_driveletter:
                            - Information whether the particular partition has a default drive letter or not.
                            Returned: if partition_style property of the particular disk has value "GPT"
                            Type: boolean
                            Sample: True
                        mbr_type:
                            - mbr type of the particular partition.
                            Returned: if partition_style property of the particular disk has value "MBR"
                            Type: int
                            Sample: 7
                        active:
                            - Information whether the particular partition is an active partition or not.
                            Returned: if partition_style property of the particular disk has value "MBR"
                            Type: boolean
                            Sample: True
                        drive_letter:
                            - Drive letter of the particular partition.
                            Returned: if existent
                            Type: string
                            Sample: "C"
                        transition_state:
                            - Transition state of the particular partition.
                            Returned: always
                            Type: int
                            Sample: 1
                        offset:
                            - Offset of the particular partition.
                            Returned: always
                            Type: int
                            Sample: 368050176
                        hidden:
                            - Information whether the particular partition is hidden or not.
                            Returned: always
                            Type: boolean
                            Sample: True
                        shadow_copy:
                            - Information whether the particular partition is a shadow copy of another partition.
                            Returned: always
                            Type: boolean
                            Sample: False
                        guid:
                            - GUID of the particular partition.
                            Returned: if existent
                            Type: string
                            Sample: "{302e475c-6e64-4674-a8e2-2f1c7018bf97}"
                        access_paths:
                            - Access paths of the particular partition.
                            Returned: if existent
                            Type: string
                            Sample: "\\\\?\\Volume{85bdc4a8-f8eb-11e6-80fa-806e6f6e6963}\\"
                        volumes:
                            - Detailed information about one particular volume on the specified partition.
                            - The following output is only visisble if no Trimmed switch is passed to the Cmdlet.
                            Returned: if existent
                            Type: list
                            Contains:
                                size:
                                    - Size in Gibibyte of the particular volume.
                                    - Accurate to three decimal places.
                                    Returned: always
                                    Type: string
                                    Sample: "0,342GiB"
                                size_remaining:
                                    - Remaining size in Gibibyte of the particular volume.
                                    - Accurate to three decimal places.
                                    Returned: always
                                    Type: string
                                    Sample: "0,146GiB"
                                type:
                                    - File system type of the particular volume.
                                    Returned: always
                                    Type: string
                                    Sample: "NTFS"
                                label:
                                    - File system label of the particular volume.
                                    Returned: always
                                    Type: string
                                    Sample: "System Reserved"
                                health_status:
                                    - Health status of the particular volume.
                                    Returned: always
                                    Type: string
                                    Sample: "Healthy"
                                drive_type:
                                    - Drive type of the particular volume.
                                    Returned: always
                                    Type: string
                                    Sample: "Fixed"
                                allocation_unit_size:
                                    - Allocation unit size in kb of the particular volume.
                                    Returned: always
                                    Type: string
                                    Sample: "64kb"
                                object_id:
                                    - Object ID of the particular volume.
                                    Returned: always
                                    Type: string
                                    Sample: "\\\\?\\Volume{85bdc4a9-f8eb-11e6-80fa-806e6f6e6963}\\"
                                path:
                                    - Path of the particular volume.
                                    Returned: always
                                    Type: string
                                    Sample: "\\\\?\\Volume{85bdc4a9-f8eb-11e6-80fa-806e6f6e6963}\\"
                physical_disk:
                    - Detailed information about physical disk properties of the particular disk.
                    - The following output is only visisble if no Trimmed switch is passed to the Cmdlet.
                    Returned: if existent
                    Type: complex
                    Contains:
                        media_type:
                            - Media type of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "UnSpecified"
                        size:
                            - Size in Gibibyte of the particular physical disk.
                            - Accurate to three decimal places.
                            Returned: always
                            Type: string
                            Sample: "200GiB"
                        allocated_size:
                            - Allocated size in Gibibyte of the particular physical disk.
                            - Accurate to three decimal places.
                            Returned: always
                            Type: string
                            Sample: "100GiB"
                        device_id:
                            - Device ID of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "0"
                        friendly_name:
                            - Friendly name of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "PhysicalDisk0"
                        operational_status:
                            - Operational status of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "OK"
                        health_status:
                            - Health status of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "Healthy"
                        bus_type:
                            - Bus type of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "SCSI"
                        usage_type:
                            - Usage type of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "Auto-Select"
                        supported_usages:
                            - Supported usage types of the particular physical disk.
                            Returned: always
                            Type: complex
                            Contains:
                                Count:
                                    - Count of supported usage types.
                                    Returned: always
                                    Type: int
                                    Sample: 5
                                value:
                                    - List of supported usage types.
                                    Returned: always
                                    Type: string
                                    Sample: "Auto-Select, Hot Spare"
                        spindle_speed:
                            - Spindle speed in rpm of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "4294967295rpm"
                        physical_location:
                            - Physical location of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "Integrated : Adapter 3 : Port 0 : Target 0 : LUN 0"
                        manufacturer:
                            - Manufacturer of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "SUSE"
                        model:
                            - Model of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "Xen Block"
                        can_pool:
                            - Information whether the particular physical disk can be added to a storage pool.
                            Returned: always
                            Type: boolean
                            Sample: False
                        cannot_pool_reason:
                            - Information why the particular physical disk can not be added to a storage pool.
                            Returned: if can_pool property has value false
                            Type: string
                            Sample: "Insufficient Capacity"
                        indication_enabled:
                            - Information whether indication is enabled for the particular physical disk.
                            Returned: always
                            Type: boolean
                            Sample: True
                        partial:
                            - Information whether the particular physical disk is partial.
                            Returned: always
                            Type: boolean
                            Sample: False
                        serial_number:
                            - Serial number of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "b62beac80c3645e5877f"
                        object_id:
                            - Object ID of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: '{1}\\\\HOST\\root/Microsoft/Windows/Storage/Providers_v2\\SPACES_PhysicalDisk.ObjectId=\"{<object_id>}:PD:{<pd>}\"'
                        unique_id:
                            - Unique ID of the particular physical disk.
                            Returned: always
                            Type: string
                            Sample: "3141463431303031"
                virtual_disk:
                    - Detailed information about virtual disk properties of the particular disk.
                    - The following output is only visisble if no Trimmed switch is passed to the Cmdlet.
                    Returned: if existent
                    Type: complex
                    Contains:
                        size:
                            - Size in Gibibyte of the particular virtual disk.
                            - Accurate to three decimal places.
                            Returned: always
                            Type: string
                            Sample: "300GiB"
                        allocated_size:
                            - Allocated size in Gibibyte of the particular virtual disk.
                            - Accurate to three decimal places.
                            Returned: always
                            Type: string
                            Sample: "100GiB"
                        footprint_on_pool:
                            - Footprint on pool in Gibibyte of the particular virtual disk.
                            - Accurate to three decimal places.
                            Returned: always
                            Type: string
                            Sample: "100GiB"
                        name:
                            - Name of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "vDisk1"
                        friendly_name:
                            - Friendly name of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "Prod2 Virtual Disk"
                        operational_status:
                            - Operational status of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "OK"
                        health_status:
                            - Health status of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "Healthy"
                        provisioning_type:
                            - Provisioning type of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "Thin"
                        allocation_unit_size:
                            - Allocation unit size in kb of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "4kb"
                        media_type:
                            - Media type of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "Unspecified"
                        parity_layout:
                            - Parity layout of the particular virtual disk.
                            Returned: if existent
                            Type: int
                            Sample: 1
                        access:
                            - Access of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "Read/Write"
                        detached_reason:
                            - Detached reason of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "None"
                        write_cache_size:
                            - Write cache size in byte of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "100s/byte/bytes/"
                        fault_domain_awareness:
                            - Fault domain awareness of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "PhysicalDisk"
                        inter_leave:
                            - Inter leave in kb of the particular virtual disk.
                            - Accurate to three decimal places.
                            Returned: always
                            Type: string
                            Sample: "100kb"
                        deduplication_enabled:
                            - Information whether deduplication is enabled for the particular virtual disk.
                            Returned: always
                            Type: boolean
                            Sample: True
                        enclosure_aware:
                            - Information whether the particular virtual disk is enclosure aware.
                            Returned: always
                            Type: boolean
                            Sample: False
                        manual_attach:
                            - Information whether the particular virtual disk is manual attached.
                            Returned: always
                            Type: boolean
                            Sample: True
                        snapshot:
                            - Information whether the particular virtual disk is a snapshot.
                            Returned: always
                            Type: boolean
                            Sample: False
                        tiered:
                            - Information whether the particular virtual disk is tiered.
                            Returned: always
                            Type: boolean
                            Sample: True
                        physical_sector_size:
                            - Physical sector size in kb of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "4kb"
                        logical_sector_size:
                            - Logical sector size in byte of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "512s/byte/bytes/"
                        available_copies:
                            - Number of the available copies of the particular virtual disk.
                            Returned: if existent
                            Type: int
                            Sample: 1
                        columns:
                            - Number of the columns of the particular virtual disk.
                            Returned: always
                            Type: int
                            Sample: 2
                        groups:
                            - Number of the groups of the particular virtual disk.
                            Returned: always
                            Type: int
                            Sample: 1
                        physical_disk_redundancy:
                            - Type of the physical disk redundancy of the particular virtual disk.
                            Returned: always
                            Type: int
                            Sample: 1
                        read_cache_size:
                            - Read cache size in byte of the particular virtual disk.
                            Returned: always
                            Type: int
                            Sample: 0
                        request_no_spof:
                            - Information whether the particular virtual disk requests no single point of failure.
                            Returned: always
                            Type: boolean
                            Sample: True
                        resiliency_setting_name:
                            - Type of the physical disk redundancy of the particular virtual disk.
                            Returned: always
                            Type: int
                            Sample: 1
                        object_id:
                            - Object ID of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: '{1}\\\\HOST\\root/Microsoft/Windows/Storage/Providers_v2\\SPACES_VirtualDisk.ObjectId=\"{<object_id>}:VD:{<vd>}\"'
                        unique_id:
                            - Unique ID of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "260542E4C6B01D47A8FA7630FD90FFDE"
                        unique_id_format:
                            - Unique ID format of the particular virtual disk.
                            Returned: always
                            Type: string
                            Sample: "Vendor Specific"
        .NOTES
          - If you invoke the Cmdlet all common parameters are available but no parameter will take an affect.
          - Of course you can use the common parameter -OutVariable.
          - In order to understand all the returned properties and values please visit the following site and open the respective MSFT class
            U(https://msdn.microsoft.com/en-us/library/windows/desktop/hh830612.aspx)
        .LINK
        Ansible Version: https://github.com/ansible/ansible/pull/32935
        .LINK
        Get-Disk
        Get-Volume
        Get-Partition
        Get-PhysicalDisk
        Get-VirtualDisk
        #>

        [CmdletBinding()
        ]
        param(
            [switch]$Trimmed
        )

        Process {
            # Create a new result object
            $result = @{
                changed = $false
                total_disks = 0
                disks = @()
            }

            # Search disks
            try {
                $disks = Get-Disk
            } catch {
                $result.message = "Failed to search the disks on the target: $($_.Exception.Message)"
                $result.failed = "true"
                return $result | ConvertTo-Json
            }
            [int32]$diskcount = $disks | Measure-Object | Select-Object  -ExpandProperty Count
            $result.total_disks = $diskcount
            foreach ($disk in $disks) {
                $disk_info = @{}
                $pdisk = Get-PhysicalDisk -ErrorAction SilentlyContinue | Where-Object {
                    $_.DeviceId -eq $disk.Number
                }
                if ($pdisk -and !$Trimmed.IsPresent) {
                    $disk_info["physical_disk"] += @{
                        size = "$($pSize = "{0:N3}" -f ($pdisk.Size / 1GB))$($pSize)GiB"
                        allocated_size = "$($pAllocSize = "{0:N3}" -f ($pdisk.AllocatedSize / 1GB))$($pAllocSize)GiB"
                        device_id = $pdisk.DeviceId
                        friendly_name = $pdisk.FriendlyName
                        operational_status = $pdisk.OperationalStatus
                        health_status = $pdisk.HealthStatus
                        bus_type = $pdisk.BusType
                        usage_type = $pdisk.Usage
                        supported_usages = $pdisk.SupportedUsages
                        spindle_speed = "$($pdisk.SpindleSpeed)rpm"
                        firmware_version = $pdisk.FirmwareVersion
                        physical_location = $pdisk.PhysicalLocation
                        manufacturer = $pdisk.Manufacturer
                        model = $pdisk.Model
                        can_pool = $pdisk.CanPool
                        indication_enabled = $pdisk.IsIndicationEnabled
                        partial = $pdisk.IsPartial
                        serial_number = $pdisk.SerialNumber
                        object_id = $pdisk.ObjectId
                        unique_id = $pdisk.UniqueId
                    }
                    if ([single]"$([System.Environment]::OSVersion.Version.Major).$([System.Environment]::OSVersion.Version.Minor)" -ge 6.3) {
                        $disk_info.physical_disk.media_type = $pdisk.MediaType
                    }
                    if (-not $pdisk.CanPool) {
                        $disk_info.physical_disk.cannot_pool_reason = $pdisk.CannotPoolReason
                    }
                    $vdisk = Get-VirtualDisk -PhysicalDisk $pdisk -ErrorAction SilentlyContinue
                    if ($vdisk) {
                        $disk_info["virtual_disk"] += @{
                            size = "$($vDSize = "{0:N3}" -f ($vdisk.Size / 1GB))$($vDSize)GiB"
                            allocated_size = "$($vDAllocSize = "{0:N3}" -f ($vdisk.AllocatedSize / 1GB))$($vDAllocSize)GiB"
                            footprint_on_pool = "$($vDPrint = "{0:N3}" -f ($vdisk.FootprintOnPool / 1GB))$($vDPrint)GiB"
                            name = $vdisk.name
                            friendly_name = $vdisk.FriendlyName
                            operational_status = $vdisk.OperationalStatus
                            health_status = $vdisk.HealthStatus
                            provisioning_type = $vdisk.ProvisioningType
                            allocation_unit_size = "$($vdisk.AllocationUnitSize / 1KB)kb"
                            media_type = $vdisk.MediaType
                            parity_layout = $vdisk.ParityLayout
                            access = $vdisk.Access
                            detached_reason = $vdisk.DetachedReason
                            write_cache_size = "$($vdisk.WriteCacheSize)s/byte/bytes/"
                            fault_domain_awareness = $vdisk.FaultDomainAwareness
                            inter_leave = "$($vDLeave = "{0:N3}" -f ($vdisk.InterLeave / 1KB))$($vDLeave)kb"
                            deduplication_enabled = $vdisk.IsDeduplicationEnabled
                            enclosure_aware = $vdisk.IsEnclosureAware
                            manual_attach = $vdisk.IsManualAttach
                            snapshot = $vdisk.IsSnapshot
                            tiered = $vdisk.IsTiered
                            physical_sector_size = "$($vdisk.PhysicalSectorSize / 1KB)kb"
                            logical_sector_size = "$($vdisk.LogicalSectorSize)s/byte/bytes/"
                            available_copies = $vdisk.NumberOfAvailableCopies
                            columns = $vdisk.NumberOfColumns
                            groups = $vdisk.NumberOfGroups
                            physical_disk_redundancy = $vdisk.PhysicalDiskRedundancy
                            read_cache_size = $vdisk.ReadCacheSize
                            request_no_spof = $vdisk.RequestNoSinglePointOfFailure
                            resiliency_setting_name = $vdisk.ResiliencySettingName
                            object_id = $vdisk.ObjectId
                            unique_id_format = $vdisk.UniqueIdFormat
                            unique_id = $vdisk.UniqueId
                        }
                    }
                }
                $disk_info.number = $disk.Number
                $disk_info.size = "$($DSize = "{0:N3}" -f ($disk.Size / 1GB))$($DSize)GiB"
                $disk_info.bus_type = $disk.BusType
                $disk_info.friendly_name = $disk.FriendlyName
                $disk_info.partition_style = $disk.PartitionStyle
                $disk_info.partition_count = $disk.NumberOfPartitions
                $disk_info.operational_status = $disk.OperationalStatus
                $disk_info.sector_size = "$($disk.PhysicalSectorSize)s/byte/bytes/"
                $disk_info.read_only = $disk.IsReadOnly
                $disk_info.bootable = $disk.IsBoot
                $disk_info.system_disk = $disk.IsSystem
                $disk_info.clustered = $disk.IsClustered
                $disk_info.manufacturer = $disk.Manufacturer
                $disk_info.model = $disk.Model
                $disk_info.firmware_version = $disk.FirmwareVersion
                $disk_info.location = $disk.Location
                $disk_info.serial_number = $disk.SerialNumber
                $disk_info.unique_id = $disk.UniqueId
                $disk_info.guid = $disk.Guid
                $disk_info.path = $disk.Path
                $parts = Get-Partition -DiskNumber $($disk.Number) -ErrorAction SilentlyContinue
                if ($parts -and !$Trimmed.IsPresent) {
                    $disk_info["partitions"]  += @()
                    foreach ($part in $parts) {
                        $partition_info  = @{
                            number = $part.PartitionNumber
                            size = "$($pSize = "{0:N3}" -f ($part.Size /1GB))$($pSize)GiB"
                            type = $part.Type
                            drive_letter = $part.DriveLetter
                            transition_state = $part.TransitionState
                            offset = $part.Offset
                            hidden = $part.IsHidden
                            shadow_copy = $part.IsShadowCopy
                            guid = $part.Guid
                            access_paths = $part.AccessPaths
                        }
                        if ($disks.PartitionStyle -eq "GPT") {
                            $partition_info.gpt_type = $part.GptType
                            $partition_info.no_default_driveletter = $part.NoDefaultDriveLetter
                        } elseif ($disks.PartitionStyle -eq "MBR") {
                            $partition_info.mbr_type = $part.MbrType
                            $partition_info.active = $part.IsActive
                        }
                        $vols = Get-Volume -Partition $part -ErrorAction SilentlyContinue
                        if ($vols) {
                            $partition_info["volumes"]  += @()
                            foreach ($vol in $vols) {
                                $volume_info  = @{
                                    size = "$($vSize = "{0:N3}" -f ($vol.Size / 1GB))$($vSize)GiB"
                                    size_remaining = "$($vSizeRe = "{0:N3}" -f ($vol.SizeRemaining / 1GB))$($vSizeRe)GiB"
                                    type = $vol.FileSystem
                                    label = $vol.FileSystemLabel
                                    health_status = $vol.HealthStatus
                                    drive_type = $vol.DriveType
                                    object_id = $vol.ObjectId
                                    path = $vol.Path
                                }
                                if ([System.Environment]::OSVersion.Version.Major -ge 10) {
                                    $volume_info.allocation_unit_size = "$($vol.AllocationUnitSize /1KB)kb"
                                } else {
                                    $volPath = ($vol.Path.TrimStart("\\?\")).TrimEnd("\")
                                    $BlockSize = (Get-CimInstance -Query "SELECT BlockSize FROM Win32_Volume WHERE DeviceID like '%$volPath%'" -ErrorAction SilentlyContinue | Select-Object BlockSize).BlockSize
                                    $volume_info.allocation_unit_size = "$($BlockSize / 1KB)kb"
                                }
                                $partition_info.volumes  += $volume_info
                            }
                        }
                    $disk_info.partitions += $partition_info
                    }
                }
                $result.disks += $disk_info
            }

            # Return result
            $result.message = "Cmdlet finished successfully"
            return $result | ConvertTo-Json -Depth 6
        }

}

function Edit-Disk {
        <#
        .SYNOPSIS
        This Cmdlet will avoid the manual setup of a disk which was attached via Tic to a target.
        requirements:
            - Windows 8.1 / Windows 2012R2 (NT 6.3) / PowerShell 4.0 or newer
            - Administrator privileges
        author:
            - Marc Tschapek (c5211773)
        license:
            - Copyright: (c) 2018, 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 1GiB and maximum 18446744073709551615GiB.
        .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 int64 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 256000GiB.
        - Maximum volume size for refs is the maximum GetSize parameter value 18446744073709551615GiB.
        - If the disk size of the selected disk does not match with the passed value
          for parameter SetFileSystem (e.g. "ntfs" over 256000GiB) 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 Oasis).
        .EXAMPLE
        Get-Disk | Edit-Disk -IsNotReadOnly `
        -SetPartitionStyle 'gpt' -SetDriveLetter F -SetFileSystem 'refs' -SetLabel 'application_disk' -SetAllocationUnitSize 64 -SetIntegrityStreams

        In this example Get-Disk will pipe all found disk objects to the Edit-Disk Cmdlet (object per object).
        Not the whole disk object will be piped but the Number, PartitionStyle and OperationalStatus property of each disk object.
        Edit-Disk will use this information in order to find the respective disk and you don't need to pass this mentioned properties additionally as Edit-Disk
        parameters.
        .INPUTS
        You can pipe PowerShell objects to this Cmdlet which contain one or more of the following properties:
        
        - Number
        - PartitionStyle
        - OperationalStatus
        - (Size - not yet available, will be added in next version)
       
        To retrieve and pipe these objects you can use a PowerShell Cmdlet or an object created by your own (PSObject, PSCustomObject).
        Hashtables are not intended to be used for the pipeline.
        .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:

                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
                    - Information whether service could not be stopped or started.
                    Returned: if service could not be stopped
                    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.
          - There is one known bug if you don't set any value for parameter SetFileSystem. The dynamic variables get somehow
          - 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)]
                [Alias('Size')]
                [ValidateRange(1,18446744073709551615)]
                [uint64]$GetSize,
                [Parameter(
                    Position = 1,
                    ValueFromPipelineByPropertyName = $true
                    )
                ]
                [Alias('Number')]
                [ValidateNotNullorEmpty()]
                [int32]$GetNumber,
                [Parameter(
                    Position = 2,
                    ValueFromPipelineByPropertyName = $true
                    )
                ]
                [Alias('PartitionStyle')]
                [ValidateSet("raw", "mbr", "gpt")]
                [string]$GetPartitionStyle = "raw",
                [Parameter(
                    Position = 3,
                    ValueFromPipelineByPropertyName = $true
                    )
                ]
                [Alias('OperationalStatus')]
                [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 ($PSBoundParameters["SetFileSystem"]) {
                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 = [string]::Empty
                    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
                    [hashtable]$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
            [hashtable]$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
                $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
                        }
                        $result.changed = $true
                        $SetOnline = $true
                    }
                }      
                if ($DROState -eq $true) {
                    if (-not $CheckMode) {
                        # Set writeable
                        try {
                            Set-DiskWriteable -Disk $disk
                        } catch {
                            if ($SetOnline) {
                                try {
                                    Set-OperationalStatus -Disk $disk -Deactivate
                                } catch {
                                    $OPStatusFailed = $true
                                } finally {
                                    if (-not $OPStatusFailed) {
                                        $result.changed = $true
                                    }
                                }
                            }
                            $result.message = "Failed to set the disk from read-only to writeable state: $($_.Exception.Message)"
                            $result.failed = "true"
                            return $result | ConvertTo-Json
                        }
                        $result.changed = $true
                        $SetWriteable = $true
                    }
                } 
            }

            # Check volumes and partitions
            $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) {
                                $result.changed = $true
                            }
                        }
                    }
                    if ($SetWriteable) {
                        try {
                            Set-DiskWriteable -Disk $disk -Deactivate
                        } catch {
                            $ROStatusFailed = $true
                        } finally {
                            if (-not $ROStatusFailed) {
                                $result.changed = $true
                            }
                        }
                    }
                    $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) {
                                $result.changed = $true
                            }
                        }
                    }
                    if ($SetWriteable) {
                        try {
                            Set-DiskWriteable -Disk $disk -Deactivate
                        } catch {
                            $ROStatusFailed = $true
                        } finally {
                            if (-not $ROStatusFailed) {
                                $result.changed = $true
                            }
                        }
                    }
                    $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) {
                                $result.changed = $true
                            }
                        }
                    }
                    if ($SetWriteable) {
                        try {
                            Set-DiskWriteable -Disk $disk -Deactivate
                        } catch {
                            $ROStatusFailed = $true
                        } finally {
                            if (-not $ROStatusFailed) {
                                $result.changed = $true
                            }
                        }
                    }
                    $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) {
                                $result.changed = $true
                            }
                        }
                    }
                    if ($SetWriteable) {
                        try {
                            Set-DiskWriteable -Disk $disk -Deactivate
                        } catch {
                            $ROStatusFailed = $true
                        } finally {
                            if (-not $ROStatusFailed) {
                                $result.changed = $true
                            }
                        }
                    }
                    $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) {
                                    $result.changed = $true
                                }
                            }
                        }
                        if ($SetWriteable) {
                            try {
                                Set-DiskWriteable -Disk $disk -Deactivate
                            } catch {
                                $ROStatusFailed = $true
                            } finally {
                                if (-not $ROStatusFailed) {
                                    $result.changed = $true
                                }
                            }
                        }
                        $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) {
                                    $result.changed = $true
                                }
                            }
                        }
                        if ($SetWriteable) {
                            try {
                                Set-DiskWriteable -Disk $disk -Deactivate
                            } catch {
                                $ROStatusFailed = $true
                            } finally {
                                if (-not $ROStatusFailed) {
                                    $result.changed = $true
                                }
                            }
                        }
                        $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) {
                                $result.changed = $true
                            }
                        }
                    }
                    if ($SetWriteable) {
                        try {
                            Set-DiskWriteable -Disk $disk -Deactivate
                        } catch {
                            $ROStatusFailed = $true
                        } finally {
                            if (-not $ROStatusFailed) {
                                $result.changed = $true
                            }
                        }
                    }
                    $result.message = "Parameter size with value $($GetSize)GiB 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  
                }
            }

            # 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) {
                                        $result.changed = $true
                                    }
                                }
                            }
                        }
                        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) {
                                        $result.changed = $true
                                    }
                                }
                            }
                        }
                        $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) {
                                        $result.changed = $true
                                    }
                                }
                            }
                            if ($SetWriteable) {
                                try {
                                    Set-DiskWriteable -Disk $disk -Deactivate
                                } catch {
                                    $ROStatusFailed = $true
                                } finally {
                                    if (-not $ROStatusFailed) {
                                        $result.changed = $true
                                    }
                                }
                            }
                            $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) {
                                $StopSuccess = $true
                                $result.changed = $true
                            }
                        }
                    }
                } 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) {
                                $result.changed = $true
                            }
                        }
                    }
                    if ($SetWriteable) {
                        try {
                            Set-DiskWriteable -Disk $disk -Deactivate
                        } catch {
                            $ROStatusFailed = $true
                        } finally {
                            if (-not $ROStatusFailed) {
                                $result.changed = $true
                            }
                        }
                    }
                    if ($StopSuccess) {
                        try {
                            Manage-ShellHWService -Action "Start"
                        } catch {
                            $StartFailed = $true
                        } finally {
                            if (-not $StartFailed) {
                                $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) {
                                    $result.changed = $true
                                }
                            }
                        }
                        if ($SetWriteable) {
                            try {
                                Set-DiskWriteable -Disk $disk -Deactivate
                            } catch {
                                $ROStatusFailed = $true
                            } finally {
                                if (-not $ROStatusFailed) {
                                    $result.changed = $true
                                }
                            }
                        }
                        if ($StopSuccess) {
                            try {
                                Manage-ShellHWService -Action "Start"
                            } catch {
                                $StartFailed = $true
                            } finally {
                                if (-not $StartFailed) {
                                    $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) {
                [hashtable]$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) {
                                $result.changed = $true
                            }
                        }
                    }
                    if ($SetWriteable) {
                        try {
                            Set-DiskWriteable -Disk $disk -Deactivate
                        } catch {
                            $ROStatusFailed = $true
                        } finally {
                            if (-not $ROStatusFailed) {
                                $result.changed = $true
                            }
                        }
                    }
                    if ($StopSuccess) {
                        try {
                            Manage-ShellHWService -Action "Start"
                        } catch {
                            $StartFailed = $true
                        } finally {
                            if (-not $StartFailed) {
                                $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 Get-DiskInfo Get-DiskFacts
Set-Alias Manage-Disk Edit-Disk
Export-ModuleMember -Function *-Disk* -Alias Manage-Disk, Get-DiskInfo