Dictionaries/Computer.Dict.Windows/Computer.Dict.Windows.psm1

<#
 
  ###### ####### ## ## ######## ## ## ######## ######## ########
 ## ## ## ## ### ### ## ## ## ## ## ## ## ##
 ## ## ## #### #### ## ## ## ## ## ## ## ##
 ## ## ## ## ### ## ######## ## ## ## ###### ########
 ## ## ## ## ## ## ## ## ## ## ## ##
 ## ## ## ## ## ## ## ## ## ## ## ## ##
  ###### ####### ## ## ## ####### ## ######## ## ##
 
#>


# function Get-MyInvocation {
# [CmdletBinding()]
# [OutputType([String])]
# Param (
# )
# Begin {
# Write-EnterFunction
# }

# Process {
# $MyInvocation
# }

# End {
# Write-LeaveFunction
# }
# }

<#
.SYNOPSIS
Get the computer name
 
.DESCRIPTION
 
.EXAMPLE
$name = Get-ComputerName
 
.NOTES
This function comes from Dict.Windows module.
#>

function Get-ComputerName {
    [CmdletBinding()]Param (
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        return (hostname) -replace "\..*"
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Get the computer DNS domain name
 
.DESCRIPTION
 
.PARAMETER localhost
request localhost's DNS domain
 
.EXAMPLE
$name = Get-ComputerDomain
 
.NOTES
This function comes from Dict.Windows module.
#>

function Get-ComputerDomain {
    [CmdletBinding()][OutputType([String])]Param (
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        if (fileExist "Env:\USERDNSDOMAIN") {
            return $env:USERDNSDOMAIN
        }

        return 'localdomain'
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Get the computer SerialNumber
 
.DESCRIPTION
 
.PARAMETER localhost
request localhost's SerialNumber
 
.EXAMPLE
$name = Get-ComputerSerialNumber
 
.NOTES
This function comes from Dict.Windows module.
#>

function Get-ComputerSerialNumber {
    [CmdletBinding()]Param (
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        return (Get-CimInstance -ClassName Win32_BIOS).SerialNumber
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Get the computer manufacturer
 
.DESCRIPTION
 
.PARAMETER localhost
request localhost's manufacturer
 
.EXAMPLE
$name = Get-ComputerManufacturer
 
.NOTES
This function comes from Dict.Windows module.
#>

function Get-ComputerManufacturer {
    [CmdletBinding()]Param (
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        return (Get-CimInstance -ClassName Win32_BIOS).Manufacturer
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Get the computer Version
 
.DESCRIPTION
 
.PARAMETER localhost
request localhost's Version
 
.EXAMPLE
$name = Get-ComputerVersion
 
.NOTES
This function comes from Dict.Windows module.
#>

function Get-ComputerVersion {
    [CmdletBinding()]Param (
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        return (Get-CimInstance -ClassName Win32_BIOS).SMBIOSBIOSVersion
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Get the computer Model
 
.DESCRIPTION
 
.PARAMETER localhost
request localhost's Model
 
.EXAMPLE
$name = Get-ComputerModel
 
.NOTES
This function comes from Dict.Windows module.
#>

function Get-ComputerModel {
    [CmdletBinding()]Param (
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        # deprecated in Powershell Core
        # return (Get-WmiObject -Class:Win32_ComputerSystem).Model
        return (Get-CimInstance -ClassName Win32_ComputerSystem).Model
    }

    End {
        Write-LeaveFunction
    }
}

function Get-ComputerEthernet {
    [CmdletBinding()]Param (
        [switch]$localhost,
        [ValidateSet('Index', 'Name')]
        [string]$ListAvailable = 'Index'
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($ListAvailable) {
            'Index' {
                (Get-NetAdapter -Physical).ifIndex | Sort-Object
                # (Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration).Index | Sort-Object
            }
            'Name' {
                # (Get-NetAdapter -IncludeHidden).Name | Sort-Object
                (Get-NetAdapter -Physical).Name | Sort-Object
            }
        }
    }

    End {
        Write-LeaveFunction
    }
}

<#
    .Synopsis
    Determines underlying firmware (BIOS) type and returns an integer indicating UEFI, Legacy BIOS or Unknown.
    Supported on Windows 8/Server 2012 or later
 
    .DESCRIPTION
    This function uses a complied Win32 API call to determine the underlying system firmware type.
 
    .EXAMPLE
    If (Get-ComputerFirmwareType -eq "UEFI") { # System is running UEFI firmware... }
 
    .EXAMPLE
    Switch (Get-ComputerFirmwareType) {
        "BIOS" { "Legacy BIOS" }
        "UEFI" { "UEFI" }
        Default { "Unknown" }
    }
 
    .OUTPUTS
    string "BIOS" or "UEFI"
 
    .FUNCTIONALITY
    Determines underlying system firmware type
 
    .LINK
    https://gallery.technet.microsoft.com/scriptcenter/Determine-UEFI-or-Legacy-7dc79488
 
    .NOTES
    Author : Chris J Warwick https://social.technet.microsoft.com/profile/chris%20j%20warwick/
 
#>

function Get-ComputerFirmwareType {
    [CmdletBinding()][OutputType([String])]Param (
        # [switch]$localhost,
        # [string]$string = ""
    )
    Begin {
        # eenter($MyInvocation.MyCommand)
    }

    Process {
        Add-Type -Language CSharp -TypeDefinition @'
 
        using System;
        using System.Runtime.InteropServices;
 
        public class FirmwareType
        {
            [DllImport("kernel32.dll")]
            static extern bool GetFirmwareType(ref uint FirmwareType);
 
            public static uint GetFirmwareType()
            {
                uint firmwaretype = 0;
                if (GetFirmwareType(ref firmwaretype))
                    return firmwaretype;
                else
                    return 0; // API call failed, just return 'unknown'
            }
        }
'@


        $fw = "BIOS"
        $firmwareTypeNum = [FirmwareType]::GetFirmwareType()
        Switch ($firmwareTypeNum) {
            1       { $fw = "BIOS" }
            2       { $fw = "UEFI" }
            Default { $fw = "Unknown ($firmwareTypeNum)" }
        }

        return $fw

    }

    End {
        # eleave($MyInvocation.MyCommand)
    }
}

function Get-ComputerArch {
    [CmdletBinding()][OutputType([String])]Param (
        [switch]$localhost,
        [string]$string = ""
    )
    Begin {
        # eenter($MyInvocation.MyCommand)
    }

    Process {
        # found specs @url https://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
        $value = (Get-CimInstance -ClassName CIM_Processor).DataWidth
        switch ($value) {
            32 {
                return 'x86'
            }
            64 {
                return 'x64'
            }
            else {
                return 'unknown'
            }
        }
    }

    End {
        # eleave($MyInvocation.MyCommand)
    }
}

<#
 
 ######## ######## ## ## ######## ######## ## ## ######## ########
 ## ## ## ## ## ## ## ### ## ## ##
 ## ## ## ## ## ## ## #### ## ## ##
 ###### ## ######### ###### ######## ## ## ## ###### ##
 ## ## ## ## ## ## ## ## #### ## ##
 ## ## ## ## ## ## ## ## ### ## ##
 ######## ## ## ## ######## ## ## ## ## ######## ##
 
#>


# Get-NetAdapter is not available under all versions of Windows

# array of interfaces found
$Script:interfaces = @()

<#
.SYNOPSIS
Gather informations about network interfaces from registry.
 
.DESCRIPTION
Get-NetAdapter is not available under all versions of Windows. That's why we try to
gather informations from the registry.
 
.EXAMPLE
$interfaces = Get-ComputerNetAdapter
 
.NOTES
General notes
#>

function Get-ComputerNetAdapter {
    [CmdletBinding()]
    [OutputType([array])]
    Param (
        # [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$string
        # [switch]$Passthru,

        # Force a refresh, do not blindly return cache
        [Alias('Refresh')]
        [switch]$Force
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        if (($null -eq $Script:interfaces) -or $Force) {
            $Script:interfaces = @()
            $TcpipRegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces"
            $NetsetupRegPath = "HKLM:\SYSTEM\CurrentControlSet\Control\NetworkSetup2\Interfaces"
            foreach ($item in (Get-ChildItem $TcpipRegPath | Where-Object { $_.PSIsContainer -eq $true })) {
                $interface = @{}
                $interface.guid = $item.PSChildName
                foreach ($property in (Get-Item $item).property) {
                    $interface.$property = Get-ItemPropertyValue $item -name $property
                }
                foreach ($property in (Get-Item "$NetsetupRegPath\$($item.PSChildName)\Kernel").property) {
                    $interface.$property = Get-ItemPropertyValue "$NetsetupRegPath\$($item.PSChildName)\Kernel" -name $property
                }
                $Script:interfaces += $interface
            }
        }
        return $Script:interfaces
    }

    End {
        Write-LeaveFunction
    }
}

function Get-EthIndex {
    [CmdletBinding()]Param(
        [Parameter(ValueFromPipeLine = $true)]
        [string]$AdapterName = "",
        [ValidateSet('IPv4', 'IPv6')]
        [string]$Family = 'IPv4'
    )

    Begin {
        Write-EnterFunction
    }

    Process {
        # switch ($Family) {
        # 'IPv4' {
        # $inet = "inet"
        # }
        # 'IPv6' {
        # $inet = "inet6"
        # }
        # }

        $value = (Get-NetAdapter -Name $AdapterName).ifIndex
        return $value
    }

    End {
        Write-LeaveFunction
    }
}

function Get-EthName {
    [CmdletBinding()]Param(
        [Parameter(ValueFromPipeLine = $true)]
        [int]$AdapterIndex = 0,
        [ValidateSet('IPv4', 'IPv6')]
        [string]$Family = 'IPv4'
    )

    Begin {
        Write-EnterFunction
    }

    Process {
        # switch ($Family) {
        # 'IPv4' {
        # $inet = "inet"
        # }
        # 'IPv6' {
        # $inet = "inet6"
        # }
        # }

        $value = (Get-NetAdapter -InterfaceIndex $AdapterIndex).Name
        return $value
    }

    End {
        Write-LeaveFunction
    }
}

function Get-EthLinkStatus {
    [CmdletBinding(
        DefaultParameterSetName="INDEX"
    )][OutputType([Boolean])]Param(
        # Parameter help description
        [Parameter(ParameterSetName="INDEX",ValueFromPipeLine = $true)]
        [int]$AdapterIndex = 1,

        [Parameter(ParameterSetName="NAME",ValueFromPipeLine = $true)]
        [string]$AdapterName = "lo"
    )

    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'INDEX' {
                $AdapterName = Get-EthName -AdapterIndex $AdapterIndex
            }
            'NAME' {
            }
        }

        $status = (Get-NetAdapter -Name $AdapterName).Status
        switch ($status) {
            'Up' {
                return $true
            }
            default {
                return $false
            }
        }
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Get network adapter speed
 
.DESCRIPTION
Ask OS for network adapter speed
 
.PARAMETER AdapterIndex
Adapter index
 
.PARAMETER AdapterName
Adapter name
 
.EXAMPLE
Get-EthSpeed -AdapterIndex 0
 
.EXAMPLE
Get-EthSpeed -AdapterName Ethernet0
 
.OUTPUTS
[uint32] return link speed in bytes per seconds
 
.NOTES
General notes
#>


function Get-EthSpeed {
    [CmdletBinding(
        DefaultParameterSetName="INDEX"
    )]Param(
        # Parameter help description
        [Parameter(ParameterSetName="INDEX",ValueFromPipeLine = $true)]
        [int]$AdapterIndex = 0,

        [Parameter(ParameterSetName="NAME",ValueFromPipeLine = $true)]
        [string]$AdapterName = "lo"
    )

    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'INDEX' {
                $AdapterName = Get-EthName -AdapterIndex $AdapterIndex
            }
            'NAME' {
            }
        }

        [uint32]$value = (Get-NetAdapter -name $AdapterName).Speed

        return $value
    }

    End {
        Write-LeaveFunction
    }
}

function Get-EthMacAddress {
    [CmdletBinding(
        DefaultParameterSetName="INDEX"
    )]Param(
        # Parameter help description
        [Parameter(ParameterSetName="INDEX",ValueFromPipeLine = $true)]
        [int]$AdapterIndex = 0,

        [Parameter(ParameterSetName="NAME",ValueFromPipeLine = $true)]
        [string]$AdapterName = "lo",

        [ValidateSet('IPv4', 'IPv6')]
        [string]$Family = 'IPv4'
    )

    Begin {
        Write-EnterFunction
    }

    Process {
        # switch ($Family) {
        # 'IPv4' {
        # $inet = "inet"
        # }
        # 'IPv6' {
        # $inet = "inet6"
        # }
        # }
        switch ($PSCmdlet.ParameterSetName) {
            'INDEX' {
                $AdapterName = Get-EthName -AdapterIndex $AdapterIndex
            }
            'NAME' {
            }
        }

        $value = (Get-NetAdapter -name $AdapterName).MacAddress.ToLower().Replace('-',':')

        return $value
    }

    End {
        Write-LeaveFunction
    }
}

function Get-EthIPAddress {
    [CmdletBinding(
        DefaultParameterSetName="INDEX"
    )]Param(
        # Parameter help description
        [Parameter(ParameterSetName="INDEX",ValueFromPipeLine = $true)]
        [int]$AdapterIndex = 0,

        [Parameter(ParameterSetName="NAME",ValueFromPipeLine = $true)]
        [string]$AdapterName = "lo",

        [ValidateSet('IPv4', 'IPv6')]
        [string]$Family = 'IPv4'
    )

    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'INDEX' {
            }
            'NAME' {
                $AdapterIndex = Get-EthIndex -AdapterName $AdapterName
            }
        }

        try {
            $value = (Get-NetIPaddress -InterfaceIndex $AdapterIndex -AddressFamily $Family -ErrorAction:SilentlyContinue).IPAddress
        } catch {
            $value = [ipaddress]"0.0.0.0"
        }

        return $value
    }

    End {
        Write-LeaveFunction
    }
}

function Get-EthNetMask {
    [CmdletBinding(
        DefaultParameterSetName="INDEX"
    )]Param(
        # Parameter help description
        [Parameter(ParameterSetName="INDEX",ValueFromPipeLine = $true)]
        [int]$AdapterIndex = 0,

        [Parameter(ParameterSetName="NAME",ValueFromPipeLine = $true)]
        [string]$AdapterName = "lo",

        [ValidateSet('IPv4', 'IPv6')]
        [string]$Family = 'IPv4'
    )

    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'INDEX' {
            }
            'NAME' {
                $AdapterIndex = Get-EthIndex -AdapterName $AdapterName
            }
        }
        switch ($Family) {
            'IPv4' {
                try {
                    $mask = (Get-NetIPConfiguration -InterfaceIndex $AdapterIndex).ipv4address.prefixlength
                    $value = ConvertTo-Mask -MaskLength $mask
                } catch {
                    $value = [ipaddress]"255.255.255.255"
                }
            }
            'IPv6' {
                # $mask = (Get-NetIPConfiguration -InterfaceIndex $AdapterIndex).ipv6address.prefixlength
                ewarn("Module Indented.Net.IP does not provides IPv6 calculator yet !!")
            }
        }

        return $value
    }

    End {
        Write-LeaveFunction
    }
}

function Get-EthBroadcast {
    [CmdletBinding(
        DefaultParameterSetName="INDEX"
    )]Param(
        # Parameter help description
        [Parameter(ParameterSetName="INDEX",ValueFromPipeLine = $true)]
        [int]$AdapterIndex = 0,

        [Parameter(ParameterSetName="NAME",ValueFromPipeLine = $true)]
        [string]$AdapterName = "lo",

        [ValidateSet('IPv4', 'IPv6')]
        [string]$Family = 'IPv4'
    )

    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'INDEX' {
            }
            'NAME' {
                $AdapterIndex = Get-EthIndex -AdapterName $AdapterName
            }
        }
        $ip = Get-EthIPAddress -AdapterIndex $AdapterIndex -Family $Family
        $mask = Get-EthNetMask -AdapterIndex $AdapterIndex -Family $Family

        switch ($Family) {
            'IPv4' {
                $value = Get-BroadcastAddress -IPAddress $ip -SubnetMask $mask
            }
            'IPv6' {
                ewarn("Module Indented.Net.IP does not provides IPv6 calculator yet !!")
            }
        }

        return $value
    }

    End {
        Write-LeaveFunction
    }
}

function Get-EthGateway {
    [CmdletBinding(
        DefaultParameterSetName="INDEX"
    )]Param(
        # Parameter help description
        [Parameter(ParameterSetName="INDEX",ValueFromPipeLine = $true)]
        [int]$AdapterIndex = 0,

        [Parameter(ParameterSetName="NAME",ValueFromPipeLine = $true)]
        [string]$AdapterName = "lo",

        [ValidateSet('IPv4', 'IPv6')]
        [string]$Family = 'IPv4'
    )

    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'INDEX' {
            }
            'NAME' {
                $AdapterIndex = Get-EthIndex -AdapterName $AdapterName
            }
        }

        switch ($Family) {
            'IPv4' {
                try {
                    $value = (get-netroute -ifIndex $AdapterIndex -DestinationPrefix 0.0.0.0/0).NextHop
                } catch {
                    $value = [ipaddress]"0.0.0.0"
                }
            }
            'IPv6' {
                $value = [ipaddress]"0.0.0.0"
                # $value = (get-netroute -ifIndex $AdapterIndex -DestinationPrefix 0.0.0.0/0).NextHop
            }
        }

        return $value
    }

    End {
        Write-LeaveFunction
    }
}


function Get-ComputerDisks {
    [CmdletBinding()]Param (
        [switch]$localhost,
        [ValidateSet('Index', 'Name')]
        [string]$ListAvailable = 'Index'
    )
    Begin {
        Write-EnterFunction
        $precedence = @('Fixed')
    }

    Process {
        if ($Localhost) {
            foreach ($p in $precedence) {
                switch ($ListAvailable) {
                    'Index' {
                        (Get-Disk | Where-Object { $_.ProvisioningType -eq $p }).Number
                    }
                    'Name' {
                        (Get-Disk | Where-Object { $_.ProvisioningType -eq $p }).UniqeId
                    }
                }
            }
        }
    }

    End {
        Write-LeaveFunction
    }
}

function Get-ComputerDisks2 {
    [CmdletBinding(
        DefaultParameterSetName="ALL"
    )]Param(
        [switch]$localhost,
        [ValidateSet('Index', 'Name')]
        [string]$ListAvailable = 'Index',
        [Parameter(ParameterSetName="INDEX",ValueFromPipeLine = $true)]
        [int]$Index,

        [Parameter(ParameterSetName="NAME",ValueFromPipeLine = $true)]
        [string]$Name
    )

    Begin {
        $disks = @()
        # precedence tell the order search precedence of the type of disk. It aims to mimic BIOS / UEFI discovery order
        $list = Get-Disk
    }

    Process {
        ForEach ($hash in $list) {
            $disk = $null
            # $disk = [PSCustomObject]@{
            # Index = $i++
            # Name = $d
            # serial = $lsblk.serial
            # uuid = $lsblk.uuid
            # manufacturer = $lsblk.vendor
            # model = $lsblk.model
            # PhysicalSectorSize = $lsblk."phy-sec"
            # size = ($lsblk.size.replace(',','.') + "B")/1 | Convert-Size -From Bytes -To Bytes -Precision 0
            # PartitionStyle = ""
            # DriveType = ""
            # }
            $disk = @{}
            $disk.Index = $hash.Number
            $disk.name = $hash.FriendlyName
            $disk.serial = $hash.SerialNumber
            $disk.uuid = $hash.uuid
            $disk.manufacturer = $hash.Manufacturer
            $disk.model = $hash.Model
            $disk.PhysicalSectorSize = $hash.PhysicalSectorSize
            $disk.size = $hash.size | Convert-Size -From Bytes -To Bytes -Precision 0
            $disk.PartitionStyle = $hash.PartitionStyle
            $disk.DriveType = ""
            switch ($hash.ProvisioningType) {
                'Fixed' {
                    $disk.DriveType = 'Fixed'
                }
                'Removable' {
                    $disk.DriveType = 'Removable'
                }
            }
            $disks += $disk
        }

        switch ($PSCmdlet.ParameterSetName) {
            'INDEX' {
                return $disks[$Index]
            }
            'NAME' {
                $Index = ($disks | Where-Object { $_.Name -eq $Name }).Index
                if ($null -ne $Index) {
                    return $disks[$Index]
                } else {
                    return
                }
            }
            'ALL' {
                return $disks
            }
        }
    }

    End {

    }
}

<#
 
 ######## #### ###### ## ## ######
 ## ## ## ## ## ## ## ## ##
 ## ## ## ## ## ## ##
 ## ## ## ###### ##### ######
 ## ## ## ## ## ## ##
 ## ## ## ## ## ## ## ## ##
 ######## #### ###### ## ## ######
 
#>


function Get-DiskInfos {
    [CmdletBinding(
        DefaultParameterSetName="INDEX"
    )]Param(
        [switch]$localhost,
        [Parameter(ParameterSetName="INDEX",ValueFromPipeLine = $true)]
        [int]$DiskIndex,

        [Parameter(ParameterSetName="NAME",ValueFromPipeLine = $true)]
        [string]$DiskName
    )
    Begin {
        # eenter($MyInvocation.MyCommand)
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'INDEX' {
            }
            'NAME' {
                $DiskIndex = (Get-Disk | Where-Object { $_.UniqueId -eq $DiskName }).Number
            }
        }

        $disk = Get-Disk -Number $DiskIndex
        $obj = [PSCustomObject]@{
            name = $disk.UniquId
            device = $disk.Path
            index = $disk.Number
            serial = $disk.SerialNumber
            uuid = $disk.Guid
            manufacturer = $disk.Manufacturer
            model = $disk.Model
            PhysicalSectorSize = $disk.PhysicalSectorSize
            default = $disk.IsBoot
            size = $disk.size
            PartitionStyle = $disk.PartitionStyle
            DriveType = $disk.ProvisioningType
        }

        return $obj
    }

    End {
        # eleave($MyInvocation.MyCommand)
    }
}

<#
.SYNOPSIS
List local disks
 
.DESCRIPTION
Fetch all informations available about local disks.
 
.EXAMPLE
An example
 
.NOTES
General notes
#>

function Get-ComputerDisk {
    [CmdletBinding(DefaultParameterSetName = "ALL")]
    [OutputType([hashtable])]
    Param (
        # Disk number
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKNUMBER')][string]$DiskNumber,

        # Disk device name (without /dev)
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKNAME')][string]$DiskName,

         # Full device address like in '/dev/sda1'
        [Parameter(Mandatory = $true, ParameterSetName = 'DEVICE')][string]$Device,

        # Partition of the disk given its ID
        [Alias('UniqueId')]
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKID')][string]$DiskId,

        # Return all $disks
        [Parameter(Mandatory = $false, ParameterSetName = 'ALL')][switch]$All
    )
    Begin {
        Write-EnterFunction
        $disks = Get-Disk
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'DISKNUMBER' {
                $disks = $disks | Where-Object { $_.DiskNumber -eq $DiskNumber }
            }
            'DISKNAME' {
                $disks = $disks | Where-Object { $_.FriendlyName -eq $DiskName }
            }
            'DEVICE' {
                $disks = $disks | Where-Object { $_.path -eq $Device }
            }
            'DISKID' {
                $disks = $disks | Where-Object { $_.UniqueId -eq $DiskId }
            }
            default {
            }
        }
        return $disks
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
List local partitions
 
.DESCRIPTION
Fetch all informations available about local partitions. Linux lacks the 'Get-Partition' of the Storage module available on Windows.
 
.EXAMPLE
An example
 
.NOTES
General notes
#>

function Get-ComputerDiskPartition {
    [CmdletBinding(DefaultParameterSetName = "ALL")]
    [OutputType([hashtable])]
    Param (
        # Partition object
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true, ParameterSetName = 'OBJECT')]$InputObject,

        # Partition with UUID
        [Alias('UniqueId')]
        [Parameter(Mandatory = $true, ParameterSetName = 'PARTUUID')][string]$PartUUID,

        # Label of the partition to mount
        [Alias('Label', 'PartitionLabel')]
        [Parameter(Mandatory = $true, ParameterSetName = 'PARTLABEL')][string]$PartLabel,

        # Disk device name of the partition
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKPART')][uint32]$DiskNumber,

        # Partition number (can be several strings char since nvme disks. e.g. 'p1')
        [Alias('PartNum')]
        [Parameter(Mandatory = $false, ParameterSetName = 'DISKPART')][uint32]$PartitionNumber,

        # Full device address like in '/dev/sda1'
        [Alias('Path')]
        [Parameter(Mandatory = $true, ParameterSetName = 'DEVICE')][string]$Device,

        # Mountpoint of a mounted partition
        [Alias('DriveLetter')]
        [Parameter(Mandatory = $true, ParameterSetName = 'MOUNTPOINT')][string]$Mountpoint,

        # Partition of the disk given its ID
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKID')][string]$DiskId,

        # Return all partitions
        [Parameter(Mandatory = $false, ParameterSetName = 'ALL')][switch]$All

    )
    Begin {
        Write-EnterFunction
        # $disks = Get-ComputerDisk
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'OBJECT' {
                $partitions = Get-Partition -DiskNumber $InputObject.DiskNumber -PartitionNumber $InputObject.PartitionNumber
            }
            'PARTUUID' {
                $partitions = Get-Partition -UniqueId "$PartUUID"
            }
            'PARTLABEL' {
                $partitions = Get-Partition -Volume (Get-Volume | Where-Object { $_.FileSystemLabel -eq "$Partlabel" })
            }
            'DISKPART' {
                if ($PartitionNumber) {
                    $partitions = Get-Partition -DiskNumber $DiskNumber -PartitionNumber $PartitionNumber
                } else {
                    $partitions = Get-Partition -DiskNumber $DiskNumber
                }
            }
            # 'DEVICE' {
            # $partitions = ($disks.children | Where-Object { $_.path -like "$Device*" })
            # }
            'MOUNTPOINT' {
                # $partitions = Get-Partition -DriveLetter "$Mountpoint"
                $partitions = (Get-Partition | Where-Object { $Mountpoint -in $_.AccessPaths })
            }
            'DISKID' {
                $partitions = Get-Partition -DiskId "$DiskId"
            }
            default {
                $partitions = Get-Partition
            }
        }
        # comply with linux code
        $parts = @()
        foreach ($p in $partitions) {
            # Write-Devel "Processing partition $($p.DiskNumber):$($p.PartitionNumber)"
            $part = @{}
            # merge volume information to get partition label for example
            $vol = get-volume -partition $p
            # Write-Devel "Found volume $($vol.filesystemlabel)"
            $part = Merge-Hashtables ($p | ConvertTo-Hashtable) ($vol | ConvertTo-Hashtable)
            if ($part.DriveLetter) { $part.Mountpoint = "$($part.DriveLetter):" } else { $part.Mountpoint = $null }
            $part.Mountpoints = $part.AccessPaths
            # if ("Guid" -notin $part.PSObject.Properties.Name) { $part.Guid = $null }
            if ($null -eq $part.Guid) {
                $guid = (New-GUID).Guid
                $part.Guid = $guid
                $part.UUID = $guid
            } else {
                $part.UUID = $part.Guid
            }
            $part.label = $part.FileSystemLabel
            $parts += [PSCustomObject]$part
        }
        # # comply with linux code
        # for ($i = 0; $i -lt $partitions.count; $i++) {
        # # merge volume information to get partition label for example
        # $vol = get-volume -partition $partitions[$i]
        # if ($vol) { $partitions[$i] | Merge-Object $vol }
        # if ($partitions[$i].DriveLetter) { $partitions.Mountpoint = "$($partitions[$i].DriveLetter):" }
        # if ($partitions[$i].AccessPaths) { $partitions.Mountpoints = $partitions[$i].AccessPaths }
        # if ($null -eq $partitions[$i].Guid) { $partitions.Guid = (New-GUID).Guid }
        # }
        return $parts
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Find next available drive DriveLetter
 
.DESCRIPTION
List all drive letter already used and return the next one.
It does not fill holes, it return the next letter of the far most one used.
So if C: D: and G: are used, it will return H:, not E:
 
.EXAMPLE
Get-ComputerDiskNextAvailableDriveLetter
 
.NOTES
General notes
#>

function Get-ComputerDiskNextAvailableDriveLetter {
    [CmdletBinding()]
    [OutputType([char])]
    Param (
        # [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$string
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        # one-liner
        return [char](([byte]((Get-Volume).DriveLetter | Sort-Object)[-1])+1)
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Mount a partition into running operating system
 
.DESCRIPTION
Mount specified partition
 
.EXAMPLE
An example
 
.NOTES
General notes
 
.OUTPUTS
[string] mountpoint
#>

function Mount-ComputerDiskPartition {
    [CmdletBinding()]
    [OutputType([String])]
    Param (
        # Partition object
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true, ParameterSetName = 'OBJECT')]$InputObject,

        # UUID of the partition to mount
        [Parameter(Mandatory = $true, ParameterSetName = 'PARTUUID')][string]$PartUuid,

        # Label of the partition to mount
        [Alias('Label', 'PartitionLabel')]
        [Parameter(Mandatory = $true, ParameterSetName = 'PARTLABEL')][string]$PartLabel,

        # Disk device name of the partition
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKPART')][uint32]$DiskNumber,

        # Partition number (can be several strings char since nvme disks. e.g. 'p1')
        [Alias('PartNum')]
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKPART')][uint32]$PartitionNumber,

        # Full device address like in '/dev/sda1'
        [Parameter(Mandatory = $true, ParameterSetName = 'DEVICE')][string]$Device,

        # options to pass
        [Parameter(Mandatory = $false)][string]$Options,

        # Unused here. For linux comatibility of Mount-ComputerDiskPartition
        [Parameter(Mandatory = $false)][switch]$Recurse,

        # force mounting to the mountpoint
        [Parameter(Mandatory = $false)][string]$Mountpoint,

        # Force mounting
        # If partition is already mounted, it will mount it to another mountpoint
        [Parameter(Mandatory = $false)][switch]$Force,

        # if specified, return the whole partition object,
        # else return only mountpoint
        [switch]$Passthru
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'OBJECT' {
                $partition = $InputObject
            }
            'PARTUUID' {
                $partition = Get-ComputerDiskPartition -PartUUID $PartUuid
            }
            'PARTLABEL' {
                $partition = Get-ComputerDiskPartition -PartLabel $PartLabel
            }
            'DISKPART' {
                $partition = Get-ComputerDiskPartition -DiskNumber $DiskNumber -PartitionNumber $PartitionNumber
            }
            'DEVICE' {
                $partition = Get-ComputerDiskPartition -Device $Device
            }
        }
        if ($null -eq $partition) {
            return $null
        }
        # handle -Bind first because if it already mounted we do not have to remount it twice
        if ($Force) {
            $Mountpoint = "$($env:ProgramData)\PwSh.Fw.Computer\Mountpoints\$($partition.Guid)\"
            $partition = Mount-ComputerDiskPartition -InputObject $partition -Mountpoint $Mountpoint -Passthru
        }
        # partition is already mounted ? return mountpoint
        # 1st case -> $Mountpoint not specified and Partition already mounted -> return
        # 2nd case -> $Mountpoint specified and partition already mounted -> check if partition is mounted on this specific mountpoint
        if ((!$Mountpoint) -and $partition.mountpoint) {
            Write-Debug "Partition $($partition.name) already mounted at '$($partition.mountpoint)'"
            if ($Passthru) {
                return Get-ComputerDiskPartition -InputObject $partition
            } else {
                return $Mountpoint
            }
        }
        if ($Mountpoint -in $partition.mountpoints) {
            Write-Debug "Partition at Disk $($partition.DiskNumber) Partition $($partition.PartitionNumber) already mounted at '$Mountpoint'"
            if ($Passthru) {
                return Get-ComputerDiskPartition -InputObject $partition
            } else {
                return $Mountpoint
            }
        }
        if (!$Mountpoint) {
            $Mountpoint = "$($env:ProgramData)\PwSh.Fw.Computer\Mountpoints\$($partition.Guid)"
        }
        # auto add ":" char to single drive letter
        if ($Mountpoint.length -eq 1) { $Mountpoint += ":" }
        try {
            # NOTES on Add-PartitionAccessPath @https://learn.microsoft.com/en-us/powershell/module/storage/add-partitionaccesspath?view=windowsserver2022-ps#parameters
            # . Mounted folders are supported only on NTFS-formatted partitions.
            # . You can only assign a single drive letter to a partition. To change the drive letter, use the Set-Partition cmdlet with the NewDriveLetter parameter.
            if (${partition}?.FileSystem -eq 'NTFS') {
                $null = New-Item $Mountpoint -ItemType Directory -Force -ErrorAction SilentlyContinue
                Write-Devel "Add-PartitionAccessPath -DiskNumber $($partition.DiskNumber) -PartitionNumber $($partition.PartitionNumber) -AccessPath $Mountpoint"
                # $null = Add-PartitionAccessPath -DiskNumber $partition.DiskNumber -PartitionNumber $partition.PartitionNumber -AccessPath "$Mountpoint"
                $null = Add-PartitionAccessPath -DiskNumber $partition.DiskNumber -PartitionNumber $partition.PartitionNumber -AccessPath "$Mountpoint"
            } else {
                # If partition already mounted on a drive letter, return it
                # return $partition.Mountpoint to return letter with colon
                if ($partition.DriveLetter) {
                    if ($Passthru) {
                        return Get-ComputerDiskPartition -InputObject $partition
                    } else {
                        return $partition.MountPoint
                    }
                } else {
                    Write-Devel "Add-PartitionAccessPath -DiskNumber $($partition.DiskNumber) -PartitionNumber $($partition.PartitionNumber) -AssignDriveLetter"
                    $Mountpoint = Add-PartitionAccessPath -DiskNumber $partition.DiskNumber -PartitionNumber $partition.PartitionNumber -AssignDriveLetter
                }
            }
        } catch {
            Write-Error $_
            return $null
        }

        if ($Passthru) {
            return Get-ComputerDiskPartition -InputObject $partition
        } else {
            return $Mountpoint
        }
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Mount a partition into running operating system
 
.DESCRIPTION
Mount specified partition
 
.EXAMPLE
An example
 
.NOTES
General notes
 
.OUTPUTS
[string] mountpoint
#>

function DisMount-ComputerDiskPartition {
    [CmdletBinding(DefaultParameterSetName = 'OBJECT')]
    [OutputType([System.Boolean])]
    Param (
        # Partition object
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true, ParameterSetName = 'OBJECT')]
        [object]$InputObject,

        # UUID of the partition to mount
        [Parameter(Mandatory = $true, ParameterSetName = 'PARTUUID')][string]$PartUuid,

        # Label of the partition to mount
        [Alias('Label', 'PartitionLabel')]
        [Parameter(Mandatory = $true, ParameterSetName = 'PARTLABEL')][string]$PartLabel,

        # Disk device name of the partition
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKPART')][uint32]$DiskNumber,

        # Partition number (can be several strings char since nvme disks. e.g. 'p1')
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKPART')][uint32]$PartitionNumber,

        # Full device address like in '/dev/sda1'
        [Parameter(Mandatory = $true, ParameterSetName = 'DEVICE')][string]$Device,

        # Mountpoint of a mounted partition
        # A partition can have multiple mountpoints.
        # With a combination of -InputObject and -Mountpoint we can decide wich one to unmount
        [Alias('DriveLetter')]
        [Parameter(Mandatory = $false, ParameterSetName = 'OBJECT')]
        [Parameter(Mandatory = $true, ParameterSetName = 'MOUNTPOINT')]
        [string]$Mountpoint,

        # if specified, return the whole partition object,
        # else return only mountpoint
        [switch]$Passthru
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'OBJECT' {
                $partition = $InputObject
            }
            'PARTUUID' {
                $partition = Get-ComputerDiskPartition -PartUUID $PartUuid
            }
            'PARTLABEL' {
                $partition = Get-ComputerDiskPartition -PartLabel $PartLabel
            }
            'DISKPART' {
                $partition = Get-ComputerDiskPartition -Disk $Disk -PartNum $PartNum
            }
            'MOUNTPOINT' {
                $partition = Get-ComputerDiskPartition -Mountpoint "$Mountpoint"
            }
           'DEVICE' {
                $partition = Get-ComputerDiskPartition -Device $Device
            }
        }
        try {
            if (!$Mountpoint) {
                $Mountpoint = $partition.AccessPaths[-1]
            }
            $p = Remove-PartitionAccessPath -DiskNumber $partition.DiskNumber -PartitionNumber $partition.PartitionNumber -AccessPath "$Mountpoint"
            # if successful, remove folder
            $folder = Get-Item $Mountpoint -ErrorAction SilentlyContinue
            if ($folder.LinkType) {
                # folder is not a regular directory. Do not delete it
            } else {
                $null = Remove-Item $folder -Force
            }
        } catch {
            Write-Error $_
        }
        $rc = $?
        if ($Passthru) {
            return $p
        } else {
            return $rc
        }
    }

    End {
        Write-LeaveFunction
    }
}