nxtools.psm1

#Region './prefix.ps1' 0
Import-Module -Name $PSScriptRoot\Modules\PSNativeCmdDevKit -ErrorAction Stop
Import-Module -Name $PSScriptRoot\Modules\DscResource.Common -ErrorAction Stop
#EndRegion './prefix.ps1' 3
#Region './Enum/1.Ensure.ps1' 0
enum Ensure
{
    Absent
    Present
}
#EndRegion './Enum/1.Ensure.ps1' 6
#Region './Enum/nxArchiveAlgorithm.ps1' 0
enum nxArchiveAlgorithm
{
    auto
    bzip2
    xz
    lzma
    gzip
}
#EndRegion './Enum/nxArchiveAlgorithm.ps1' 9
#Region './Enum/nxFileSystemAccessRight.ps1' 0

[Flags()]
enum nxFileSystemAccessRight
{
    Read    = 4
    Write   = 2
    Execute = 1
    None    = 0
}
#EndRegion './Enum/nxFileSystemAccessRight.ps1' 10
#Region './Enum/nxFileSystemItemType.ps1' 0
enum nxFileSystemItemType
{
    File
    Directory
    Link
    Pipe
    Socket
}
#EndRegion './Enum/nxFileSystemItemType.ps1' 9
#Region './Enum/nxFileSystemSpecialMode.ps1' 0

[Flags()]
enum nxFileSystemSpecialMode
{
    SetUserId  = 4 # S_ISUID: Set user ID on execution
    SetGroupId = 2 # S_ISVTX: Set group ID on execution
    StickyBit  = 1 # S_ISVTX: Sticky bit
    None       = 0
}
#EndRegion './Enum/nxFileSystemSpecialMode.ps1' 10
#Region './Enum/nxFileSystemUserClass.ps1' 0
[Flags()]
enum nxFileSystemUserClass
{
    User   = 4 # u
    Group  = 2 # g
    Others = 1 # o
}
#EndRegion './Enum/nxFileSystemUserClass.ps1' 8
#Region './Enum/nxSupportedPackageType.ps1' 0
enum nxSupportedPackageType
{
    dpkg
    yum
    # dnf
    # rpm
    # apt
    # zypper
    # snap
}
#EndRegion './Enum/nxSupportedPackageType.ps1' 11
#Region './Classes/nxEtcShadowEntry.ps1' 0
class nxEtcShadowEntry
{
    hidden static [regex] $EtcShadowLineParser = @(
        '^(?<username>[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$))'
        '(?<password>[^:]*)'
        '(?<lastchanged>[^:]*)'
        '(?<min>[^:]*)'
        '(?<max>[^:]*)'
        '(?<warn>[^:]*)'
        '(?<inactive>[^:]*)'
        '(?<expire>[^:]*)'
        '(?<other>[^:]*)'
    ) -join ':'

    hidden [string] $ShadowEntry
    [string] $Username
    [string] $EncryptedPassword # as in the Shadow file
    [datetime] $PasswordLastChanged
    [int] $MinimumPasswordAgeInDays
    [int] $MaximumPasswordAgeInDays
    [int] $PasswordAgeWarningPeriodInDays
    [int] $PasswordInactivityPeriodInDays
    [System.Nullable[datetime]] $AccountExipreOn
    [string] $ReservedField

    nxEtcShadowEntry()
    {
        # default ctor
    }

    nxEtcShadowEntry([string] $EtcShadowEntry)
    {
        Write-Debug -Message "[nxEtcShadowEntry] Parsing '$_'."
        if ($EtcShadowEntry -notmatch [nxEtcShadowEntry]::EtcShadowLineParser)
        {
            throw "Unrecognised passwd entry: '$EtcShadowEntry'."
        }

        $this.ShadowEntry = $EtcShadowEntry
        $this.Username = $Matches.username
        $this.EncryptedPassword = $Matches.password
        $this.PasswordLastChanged = ([datetime]'1/1/1970').AddDays($Matches.lastchanged)
        $this.MinimumPasswordAgeInDays = $Matches.min
        $this.MaximumPasswordAgeInDays = $Matches.max
        $this.PasswordAgeWarningPeriodInDays = $Matches.warn
        $this.PasswordInactivityPeriodInDays = $Matches.inactive
        if ($Matches.expire)
        {
            $this.AccountExipreOn = ([datetime]'1/1/1970').AddDays($Matches.expire)
        }

        $this.ReservedField = $Matches.other

        $this | Add-Member -MemberType ScriptProperty -Name 'PasswordLocked' -Value {
            $this.IsPasswordLocked()
        }
    }

    [System.String] ToString()
    {
        return ($this.ShadowEntry)
    }

    [bool] IsPasswordLocked()
    {
        if ($this.EncryptedPassword -match '^!')
        {
            return $true
        }
        else
        {
            return $false
        }
    }
}
#EndRegion './Classes/nxEtcShadowEntry.ps1' 76
#Region './Classes/nxFileSystemInfo.ps1' 0

class nxFileSystemInfo : System.IO.FileSystemInfo
{
    [nxFileSystemMode] $Mode
    [nxFileSystemItemType] $nxFileSystemItemType
    [int] $nxLinkCount
    [System.String] $nxOwner
    [System.String] $nxGroup
    [long] $Length

    [string] $Name
    [datetime] $LastWriteTime

    nxFileSystemInfo ([System.Collections.IDictionary]$properties)
    {
        Write-Verbose -Message "Creating [nxFileSystemInfo] with path '$($properties.FullPath)'."
        $this.OriginalPath = $properties.FullPath
        $this.FullPath = $properties.FullPath
        $this.SetPropertiesFromIDictionary($properties)
        $this.Name = [System.Io.Path]::GetFileName($this.FullPath)
    }

    hidden [void] SetPropertiesFromIDictionary ([System.Collections.IDictionary]$properties)
    {
        Write-Verbose -Message "Setting Propeties from Dictionary."

        $properties.keys.Foreach{
            if ($this.psobject.Properties.name -contains $_)
            {
                try
                {
                    Write-Debug -Message "`tAdding '$_' with value '$($properties[$_])'."
                    $this.($_) = $properties[$_]
                }
                catch
                {
                    Write-Warning -Message $_.Exception.Message
                }
            }
            else
            {
                Write-Verbose -Message "The key '$_' is not a property."
            }
        }
    }

    nxFileSystemInfo([string]$Path)
    {
        # ctor
        $this.OriginalPath = $Path
        $this.FullPath = [System.IO.Path]::GetFullPath($Path)
        $this.Name = [System.Io.Path]::GetFileName($this.FullPath)
    }

    [void] Delete()
    {
        Remove-Item -Path $this.FullName -ErrorAction Stop
        $this.Dispose()
    }

    hidden [string] GetModeWithItemType()
    {
        $modeSymbol = $this.Mode.ToString()
        $typeSymbol = switch ($this.nxFileSystemItemType)
        {
            ([nxFileSystemItemType]::File) { '-' }
            ([nxFileSystemItemType]::Directory) { 'd' }
            ([nxFileSystemItemType]::Link) { 'l' }
            ([nxFileSystemItemType]::Socket) { 's' }
            ([nxFileSystemItemType]::Pipe) { 'p' }
        }

        return ('{0}{1}' -f $typeSymbol,$modeSymbol)
    }
}
#EndRegion './Classes/nxFileSystemInfo.ps1' 76
#Region './Classes/nxFileSystemMode.ps1' 0
class nxFileSystemMode
{
    hidden static [string] $SymbolicTriadParser = '^[-dlsp]?(?<User>[-wrxsStT]{3})(?<Group>[-wrxsStT]{3})(?<Others>[-wrxsStT]{3})$'
    hidden static [string] $SymbolicOperationParser = '^(?<userClass>[ugoa]{1,3})(?<operator>[\-\+\=]{1})(?<permissions>[wrxTtSs-]{1,3})$'
    [nxFileSystemSpecialMode]  $SpecialModeFlags
    [nxFileSystemAccessRight]  $OwnerMode
    [nxFileSystemAccessRight]  $GroupMode
    [nxFileSystemAccessRight]  $OthersMode

    nxFileSystemMode()
    {
        # default ctor, can be used like this:
        <#
            [nxFileSystemMode]@{
                SpecialModeFlags = 'None'
                OwnerMode = 'Read, Write, Execute'
                GroupMode = 'Read, Execute'
                OthersMode = 7
            }
        #>

    }

    nxFileSystemMode([String]$Modes)
    {
        if ($Modes -match '^\d{3,4}$')
        {
            # Convert from Int to nxFileSystemAccessRight
            $this.setNxFileSystemModeFromInt([int]::Parse($Modes))
        }
        elseif ($Modes -cmatch [nxFileSystemMode]::SymbolicTriadParser)
        {
            $this.setNxFileSystemModeFromSymbolicTriadNotation($Modes)
        }
        elseif (-not ($Modes -split '\s+').Where{$_ -cnotmatch [nxFileSystemMode]::SymbolicOperationParser})
        {
            # All items of the space delimited Symbolic operations have been checked.
            $this.DoSymbolicChmodOperation($Modes)
        }
        else
        {
            throw "The symbolic string '$Modes' is invalid."
        }
    }

    nxFileSystemMode([int]$Modes)
    {
        $this.setNxFileSystemModeFromInt($Modes)
    }

    hidden [void] setNxFileSystemModeFromSymbolicTriadNotation([string]$SymbolicTriad)
    {
        $null = $SymbolicTriad -cmatch [nxFileSystemMode]::SymbolicTriadParser

        $this.DoSymbolicChmodOperation(@(
            ('u=' + $Matches['User'])
            ('g=' + $Matches['Group'])
            ('o=' + $Matches['Others'])
        ) -join ' ')
    }

    hidden [void] setNxFileSystemModeFromInt([Int]$Modes)
    {
        # Adding leading 0s to ensure we have a 0 for the special flags i.e. 777 -> 0777
        $StringMode = "{0:0000}" -f $Modes
        Write-Debug -Message "Trying to parse the permission set expressed by '$($Modes)'."

        if ($StringMode.Length -gt 4)
        {
            throw "Mode set should be expressed with 4 or 3 digits (you can omit the one on the left): setuid(4)/setgid(2)/sticky bit(1)|Owner|Group|Others). '$($StringMode)'"
        }

        Write-Debug -Message "Parsing Special Mode Flags: $([int]::Parse($StringMode[0]))"
        $this.SpecialModeFlags = [int]::Parse($StringMode[0])
        $this.OwnerMode  = [int]::Parse($StringMode[1])
        $this.GroupMode  = [int]::Parse($StringMode[2])
        $this.OthersMode = [int]::Parse($StringMode[3])
    }

    [void] DoChmodOperation ([nxFileSystemUserClass]$UserClass, [char]$Operator, [nxFileSystemAccessRight]$AccessRights, [nxFileSystemSpecialMode]$SpecialMode)
    {
        switch ($operator)
        {
            '=' {
                $this.SetMode($userClass, $accessRights, $specialMode)
            }

            '+'
            {
                $this.AddMode($userClass, $accessRights, $specialMode)
            }

            '-'
            {
                $this.RemoveMode($userClass, $accessRights, $specialMode)
            }

            default
            {
                throw "Operator not recognised '$operator'."
            }
        }
    }

    [void] DoSymbolicChmodOperation ([string]$SymbolicChmodString)
    {
        $symbolicChmodList = $SymbolicChmodString -split '\s+'

        foreach ($symbolicChmodStringItem in $symbolicChmodList)
        {
            Write-Debug -Message "Doing Symbolic Operation '$symbolicChmodStringItem'."
            if ($symbolicChmodStringItem -match [nxFileSystemMode]::SymbolicOperationParser)
            {
                $userClassChars = $Matches['userClass']
                $operator       = $Matches['operator']
                $permissions    = $Matches['permissions']
                $userClass      = [nxFileSystemUserClass](Convert-nxSymbolToFileSystemUserClass -Char $userClassChars)
                Write-Debug -Message "Parsing $permissions"
                $specialMode    = [nxFileSystemSpecialMode](Convert-nxSymbolToFileSystemSpecialMode -SpecialModeSymbol $permissions -UserClass $UserClass)
                $accessRights   = [nxFileSystemAccessRight](Convert-nxSymbolToFileSystemAccessRight -AccessRightSymbol $permissions)

                $this.DoChmodOperation($userClass, $operator, $accessRights, $specialMode)
            }
        }
    }

    [void] SetMode ([nxFileSystemUserClass]$UserClass, [nxFileSystemAccessRight]$AccessRights, [nxFileSystemSpecialMode]$SpecialMode)
    {
        Write-Debug -Message "Setting rights '$($AccessRights)' and special flag '$($SpecialMode)' to '$($UserClass)'"
        switch ($UserClass)
        {
            { $_ -band [nxFileSystemUserClass]::User } {
                $this.OwnerMode = $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Group } {
                $this.GroupMode = $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Others } {
                $this.OthersMode = $AccessRights
            }

            default {
                throw "Error with unrecognized User Class '$UserClass'."
            }
        }

        $this.SpecialModeFlags = $SpecialMode
    }

    [void] AddMode ([nxFileSystemUserClass]$UserClass, [nxFileSystemAccessRight]$AccessRights, [nxFileSystemSpecialMode]$SpecialMode)
    {
        Write-Debug -Message "Adding rights '$($AccessRights)' and special flag '$($SpecialMode)' to '$($UserClass)'"
        switch ($UserClass)
        {
            { $_ -band [nxFileSystemUserClass]::User } {
                $this.OwnerMode = $this.OwnerMode -bor $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Group } {
                $this.GroupMode = $this.GroupMode -bor $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Others } {
                $this.OthersMode = $this.OthersMode -bor $AccessRights
            }

            default {
                throw "Error with unrecognized User Class '$UserClass'."
            }
        }

        $this.SpecialModeFlags = $this.SpecialModeFlags -bor $SpecialMode
    }

    [void] RemoveMode ([nxFileSystemUserClass]$UserClass, [nxFileSystemAccessRight]$AccessRights, [nxFileSystemSpecialMode]$SpecialMode)
    {
        Write-Debug -Message "Removing rights '$($AccessRights)' and special flag '$($SpecialMode)' to '$($UserClass)'"
        switch ($UserClass)
        {
            { $_ -band [nxFileSystemUserClass]::User } {
                $this.OwnerMode = $this.OwnerMode -band -bnot $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Group } {
                $this.GroupMode = $this.GroupMode -band -bnot $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Others } {
                $this.OthersMode = $this.OthersMode -band -bnot $AccessRights
            }

            default {
                throw "Error with unrecognized User Class '$UserClass'."
            }
        }

        $this.SpecialModeFlags = $this.SpecialModeFlags -band -bnot $SpecialMode
    }

    [string] ToString()
    {
        Write-Verbose -Message "$($this.OwnerMode)"
        Write-Verbose -Message "$(@($this.OthersMode, $this.SpecialModeFlags) -join '|')"

        $SymbolNotation = [PSCustomObject]@{
            UserClass         = [nxFileSystemUserClass]::User
            AccessRight       = $this.OwnerMode
            UseDashWhenAbsent = $true
        },
        [PSCustomObject]@{
            UserClass         = [nxFileSystemUserClass]::Group
            AccessRight       = $this.GroupMode
            UseDashWhenAbsent = $true
        },
        [PSCustomObject]@{
            UserClass         = [nxFileSystemUserClass]::User
            AccessRight       = $this.OthersMode
            UseDashWhenAbsent = $true
        } | Convert-nxFileSystemAccessRightToSymbol

        Write-Verbose -Message "SymbolNotation: $SymbolNotation"
        return ($SymbolNotation -join '')
    }

    [string] ToOctal()
    {
        return ('{0}{1}{2}{3}' -f (
            ([int]$this.SpecialModeFlags),
            ([int]$this.OwnerMode),
            ([int]$this.GroupMode),
            ([int]$this.OthersMode)
        ))
    }
}
#EndRegion './Classes/nxFileSystemMode.ps1' 236
#Region './Classes/nxLocalGroup.ps1' 0
class nxLocalGroup
{
    hidden static $GroupEntryParser = '^(?<groupname>[^:]+):(?<pwd>[^:]*):(?<gid>[\d]+):(?<members>.*)$'
    [string]    $GroupName
    [string]    $Password
    [int]       $GroupId
    [string[]]  $GroupMember

    nxLocalGroup()
    {
        # default ctor
    }

    nxLocalGroup([string]$GroupEntry)
    {
        Write-Debug -Message "[nxLocalGroup] Parsing '$_'."
        if ($groupEntry -notmatch [nxLocalGroup]::GroupEntryParser)
        {
            throw "Unrecognized Group entry from /etc/group with '($GroupEntry)'."
        }
        else
        {
            $this.GroupName = $Matches.groupname
            $this.Password  = $Matches.pwd
            $this.GroupId   = [int]::Parse($Matches.gid)
            $this.GroupMember = $Matches.members -split ','
        }
    }

    [System.String] ToString()
    {
        return $this.GroupName
    }

    static [bool] Exists([string]$GroupName)
    {
        if (Get-nxLocalGroup -GroupName $GroupName -ErrorAction 'SilentlyContinue')
        {
            return $true
        }
        else
        {
            return $false
        }
    }
}
#EndRegion './Classes/nxLocalGroup.ps1' 47
#Region './Classes/nxLocalUser.ps1' 0
class nxLocalUser
{
    # gcolas:x:1000:1000:,,,:/home/gcolas:/bin/bash
    static [regex] $PasswordLineParser = @(
        '^(?<username>[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$))'
        '(?<password>[^:]+)'
        '(?<userid>[\d]+)'
        '(?<groupid>[\d]+)'
        '(?<userinfo>[^:]*)'
        '(?<homedir>[^:]*)'
        '(?<shellcmd>[^:]*)'
    ) -join ':'

    hidden [bool] $HasChanged = $false

    [string] $UserName
    [string] $Password
    [int]    $UserId
    [int]    $GroupId

    hidden [string] $UserInfo # GECOS field
    [string] $FullName
    [string] $Office
    [string] $OfficePhone
    [string] $HomePhone
    [string] $Description

    [string] $HomeDirectory
    [string] $ShellCommand

    nxLocalUser()
    {
        # default ctor
    }

    nxLocalUser([System.String]$passwdEntry)
    {
        Write-Debug -Message "[nxLocalUser] Parsing '$_'."
        if ($passwdEntry -notmatch [nxLocalUser]::PasswordLineParser)
        {
            throw "Unrecognised passwd entry: '$passwdEntry'."
        }

        $this.UserName = $Matches.username
        $this.Password = $Matches.password
        $this.UserId = [int]::Parse($Matches.userid)
        $this.GroupId = [int]::Parse($Matches.groupid)
        $this.UserInfo = $Matches.userinfo

        if (-not [string]::IsNullOrEmpty($this.UserInfo))
        {
            $this.LoadGecosFields()
        }

        $this.HomeDirectory = $Matches.homedir
        $this.ShellCommand = $Matches.shellcmd

        # the below script properties should probably go in the type format
        $this |
            Add-Member -PassThru -MemberType ScriptProperty -Name 'MemberOf' -Value {
                # only calling the method when needed to avoid unecessary calls
                $this.GetMemberOf()
            }|
            Add-Member -PassThru -MemberType ScriptProperty -Name 'EtcShadow' -Value {
                $this.GetEtcShadow()
            }
    }

    [void] LoadGecosFields()
    {
        $gecosFields = [nxLocalUser]::GetGecosFieldsFromUserInfo($this.UserInfo)
        $this.FullName      = $gecosFields['FullName']
        $this.Office        = $gecosFields['Office']
        $this.OfficePhone   = $gecosFields['OfficePhone']
        $this.HomePhone     = $gecosFields['HomePhone']
        $this.Description   = $gecosFields['Description']
    }

    static [hashtable] GetGecosFieldsFromUserInfo([String] $UserInfoString)
    {
        $gecosFields = $UserInfoString -split ',',5

        return @{
            FullName    = $gecosFields[0]
            Office      = $gecosFields[1]
            OfficePhone = $gecosFields[2]
            HomePhone   = $gecosFields[3]
            Description = $gecosFields[4]
        }
    }

    static [bool] Exists([string]$UserName)
    {
        try
        {
            $result = Invoke-NativeCommand -Executable 'id' -Parameters @('-u', $UserName) -ErrorAction 'Stop'
        }
        catch
        {
            Write-Debug -Message "The command 'id' returned '$_'."
            $result = $false
        }

        [int]$ParsedUserID = -1

        if ([int]::TryParse($result, [ref]$ParsedUserID))
        {
            Write-Debug -Message "User id for '$UserName' is '$result'."
            return $true
        }
        else
        {
            return $false
        }
    }

    [string] ToString()
    {
        return $this.UserName
    }

    [string] ToPasswdString()
    {
        return ('{0}:{1}:{2}:{3}:{4}:{5}:{6}:{7}' -f
            $this.UserName,
            $this.Password,
            $this.UserId,
            $this.GroupId,
            $this.UserInfo,
            $this.HomeDirectory,
            $this.ShellCommand
        )
    }

    [void] Save()
    {
        if ([nxLocalUser]::Exists($this.Username))
        {
            $this.Update()
        }
        else
        {
            $this.SaveAsNewNxLocalAccount()
        }
    }

    [void] Update()
    {
        $this | Set-nxLocalUser
    }

    [void] SaveAsNewNxLocalAccount()
    {
        $null = $this | Add-nxLocalUser -ErrorAction 'Stop'
    }

    [nxLocalGroup[]] GetMemberOf()
    {
        return (Get-nxLocalUserMemberOf -User $this.UserName).MemberOf
    }

    [nxEtcShadowEntry] GetEtcShadow()
    {
        return (Get-nxEtcShadow -UserName $this.UserName)
    }

    [bool] IsDisabled()
    {
        $shadowEntry = $this.GetEtcShadow()
        return ($shadowEntry.IsPasswordLocked() -and $shadowEntry.AccountExipreOn -le [dateTime]::Now)
    }
}
#EndRegion './Classes/nxLocalUser.ps1' 173
#Region './Classes/ValidShell.ps1' 0
class ValidShell : System.Management.Automation.IValidateSetValuesGenerator
{
    [String[]] GetValidValues()
    {
        return (Get-Content -Path '/etc/shells' -ErrorAction 'Stop' | Where-Object -FilterScript {$_ -notmatch '^#'})
    }
}
#EndRegion './Classes/ValidShell.ps1' 8
#Region './Classes/1.DscResources/01.nxFile.ps1' 0
$script:localizedDataNxFile = Get-LocalizedData -DefaultUICulture en-US -FileName 'nxFile.strings.psd1'


[DscResource()]
class nxFile
{
    [DscProperty()]
    [Ensure] $Ensure

    [DscProperty(key)]
    [System.String] $DestinationPath

    [DscProperty()]
    [System.String] $SourcePath # Write Only

    [DscProperty()]
    [System.String] $Type = 'File' # directory | file | link

    [DscProperty()]
    [System.String] $Contents

    [DscProperty()]
    [System.String] $Checksum # ctime | mtime | md5 | Value

    [DscProperty()]
    [System.string] $Mode

    [DscProperty()]
    [bool] $Force   # Write Only

    [DscProperty()]
    [bool] $Recurse # Write Only

    [DscProperty()]
    [System.String] $Owner

    [DscProperty()]
    [System.String] $Group

    #Links (follow | manage | ignore)

    [DscProperty()]
    [Reason[]] $Reasons

    [nxFile] Get()
    {
        Write-Verbose -Message (
            $script:localizedDataNxFile.RetrieveFile -f $this.DestinationPath
        )

        $nxFileSystemInfo = Get-nxItem -Path $this.DestinationPath -ErrorAction SilentlyContinue
        $currentState = [nxFile]::new()
        $currentState.DestinationPath = $this.DestinationPath

        if ($nxFileSystemInfo) # The file/folder/link exists
        {
            $currentState.Ensure = [Ensure]::Present
            $currentState.Owner = $nxFileSystemInfo.nxOwner
            $currentState.Group = $nxFileSystemInfo.nxGroup
            $currentState.Type  = $nxFileSystemInfo.nxFileSystemItemType

            if ($this.Mode -match '^\d+$') # using octal notation (i.e. 0777)
            {
                $currentState.Mode = $nxFileSystemInfo.Mode.ToOctal()

                if ($this.Mode.Length -eq 3)
                {
                    # if the desired value omits special flags digit (assuming 0), re-add for comparison
                    $this.Mode = '0' + $this.Mode
                }
            }
            else    # Using Symbolic notation (i.e. rwxrwxrwx)
            {
                $currentState.Mode  = $nxFileSystemInfo.Mode.ToString()
            }

            $isSameFile = $false
            if ($this.Checksum -and $this.Type -eq 'File') # checksum checks has precedence over contents check
            {
                switch ($this.Checksum)
                {
                    'MD5'
                    {
                        # Compare Destination with source using MD5
                        if ($this.SourcePath -and (Test-Path -Path $this.SourcePath))
                        {
                            $sourceHash = (Get-FileHash -Path $this.SourcePath -Algorithm 'MD5').Hash
                            $destinationHash = (Get-FileHash -Path $currentState.DestinationPath -Algorithm 'MD5').Hash
                            $isSameFile = $sourceHash -eq $destinationHash

                            if ($this.Contents)
                            {
                                # Do not compare contents if the comparison is done by checksum
                                $currentState.Contents = $this.Contents
                            }
                        }
                        elseif (-not (Test-Path -Path $this.SourcePath))
                        {
                            throw ($script:localizedDataNxFile.SourcePathNotFound -f $this.SourcePath)
                        }
                    }

                    'ctime' # change time (metadata)
                    {
                        # Compare Destination with source using ctime
                        if ($this.SourcePath -and (Test-Path -Path $this.SourcePath))
                        {
                            $sourceCtime = (Get-nxItem -Path $this.SourcePath).CreationTimeUtc
                            $destinationCtime = $nxFileSystemInfo.CreationTimeUtc
                            $isSameFile = $sourceCtime -eq $destinationCtime
                            Write-Verbose -Message (
                                $script:localizedDataNxFile.CompareCtime -f $this.DestinationPath, $destinationCtime, $sourceCtime
                            )

                            if ($this.Contents)
                            {
                                # Do not compare contents if the comparison is done by checksum
                                $currentState.Contents = $this.Contents
                            }
                        }
                        elseif (-not (Test-Path -Path $this.SourcePath))
                        {
                            throw ($script:localizedDataNxFile.SourcePathNotFound -f $this.SourcePath)
                        }
                    }

                    'mtime' # Modify time (data)
                    {
                        # Compare Destination with Source using mtime
                        if ($this.SourcePath -and (Test-Path -Path $this.SourcePath))
                        {
                            $sourceMtime = (Get-nxItem -Path $this.SourcePath).LastWriteTimeUtc
                            $destinationMtime = $nxFileSystemInfo.LastWriteTimeUtc
                            $isSameFile = $sourceMtime -eq $destinationMtime

                            Write-Verbose -Message (
                                $script:localizedDataNxFile.CompareCtime -f $this.DestinationPath, $destinationMtime, $sourceMtime
                            )

                            if ($this.Contents)
                            {
                                # Do not compare contents if the comparison is done by checksum
                                $currentState.Contents = $this.Contents
                            }
                        }
                        elseif (-not (Test-Path -Path $this.SourcePath))
                        {
                            throw ($script:localizedDataNxFile.SourcePathNotFound -f $this.SourcePath)
                        }
                    }

                    default
                    {
                        # Compare Destination with the provided checksum (ignore source file for comparison)
                        $checksumHashAlgorithm = Get-FileHashAlgorithmFromHash -FileHash $this.Checksum -ErrorAction Stop
                        $currentDestinationFileChecksum = (Get-FileHash -Algorithm $checksumHashAlgorithm -Path $currentState.DestinationPath).Hash
                        Write-Verbose -Message (
                            $script:localizedDataNxFile.CompareChecksum -f $this.Checksum, $currentDestinationFileChecksum
                        )

                        $currentState.Checksum = $currentDestinationFileChecksum
                        $isSameFile = $currentDestinationFileChecksum -eq $this.Checksum

                        if ($this.Contents)
                        {
                            # Do not compare contents if the comparison is done by checksum
                            $currentState.Contents = $this.Contents
                        }
                    }
                }
            }
            elseif ($this.Contents) # no checksum but contents is set. use for comparison
            {

                if ($this.Type -eq 'File')
                {
                    Write-Verbose -Message (
                        $script:localizedDataNxFile.GetFileContent -f $currentState.DestinationPath
                    )

                    $currentState.Contents = Get-Content -Raw -Path $currentState.DestinationPath
                }
                else
                {
                    $currentState.Contents = $this.Contents # to make sure it does not flag in the comparison
                }

            }
            else
            {
                # if we don't check against the source, against a provided checksum, or against the provided content
                # assume it's the same file because the file already exists ([ensure]::Present)
                $isSameFile = $true
            }

            if ($isSameFile)
            {
                $currentState.Checksum = $this.Checksum
            }

            $valuesToCheck = @(
                # DestinationPath can be skipped because it's determined with Ensure absent/present
                # SourcePath is write-only property
                'Ensure'
                'Type'
                'Contents'
                'Checksum'
                'Mode'
                # Force is write-only property
                # Recurse is write-only property
                'Owner'
                'Group'

            ).Where({ $null -ne $this.$_ }) #remove properties not set from comparison

            $compareStateParams = @{
                CurrentValues = ($currentState | Convert-ObjectToHashtable)
                DesiredValues = ($this | Convert-ObjectToHashtable)
                ValuesToCheck = $valuesToCheck
                IncludeValue  = $true
            }

            $comparedState = Compare-DscParameterState @compareStateParams

            $currentState.reasons = switch ($comparedState.Property)
            {
                'Ensure'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Ensure' -f $this.GetType()
                        Phrase = $script:localizedDataNxFile.nxFileShouldBeAbsent -f $this.DestinationPath
                    }
                }

                'Type'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Type' -f $this.GetType()
                        Phrase = $script:localizedDataNxFile.TypeMismatch -f $this.DestinationPath, $this.Type, $currentState.Type
                    }
                    break # If the type is wrong, we can't recover from this.
                }

                'Contents'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Contents' -f $this.GetType()
                        Phrase = $script:localizedDataNxFile.ContentsMismatch -f $this.DestinationPath, $this.Contents, $currentState.Contents
                    }
                }

                'Checksum'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Checksum' -f $this.GetType()
                        Phrase = $script:localizedDataNxFile.ChecksumMismatch -f $this.DestinationPath, $this.Checksum, $currentState.Checksum
                    }
                }

                'Mode'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Mode' -f $this.GetType()
                        Phrase = $script:localizedDataNxFile.ModeMismatch -f $this.DestinationPath, $this.Mode, $currentState.Mode
                    }
                }

                'Owner'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Owner' -f $this.GetType()
                        Phrase = $script:localizedDataNxFile.OwnerMismatch -f $this.DestinationPath, $this.Owner, $currentState.Owner
                    }
                }

                'Group'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Group' -f $this.GetType()
                        Phrase = $script:localizedDataNxFile.GroupMismatch -f $this.DestinationPath, $this.Group, $currentState.Group
                    }
                }
            }
        }
        else
        {
            # No item found for this Destination path
            $currentState.Ensure = [Ensure]::Absent

            if ($this.Ensure -ne $currentState.Ensure)
            {
                # We expected the file to be Present
                $currentState.Reasons = [Reason]@{
                    Code = '{0}:{0}:Ensure' -f $this.GetType()
                    Phrase = $script:localizedDataNxFile.nxItemNotFound -f $this.DestinationPath
                }
            }
            else
            {
                Write-Verbose -Message ($script:localizedDataNxFile.nxFileInDesiredState -f  $this.DestinationPath)
            }
        }

        return $currentState
    }

    [bool] Test()
    {
        $currentState = $this.Get()
        $testTargetResourceResult = $currentState.Reasons -eq 0

        return $testTargetResourceResult
    }

    [void] Set()
    {
        $currentState = $this.Get()

        if ($this.Ensure -eq [Ensure]::Present) # Desired State: Ensure present
        {
            if ($currentState.Ensure -ne $this.Ensure) # but is absent
            {
                Write-Verbose -Message (
                    $script:localizedDataNxFile.CreateFile -f $this.DestinationPath
                )

                # Copy from source or
                # Create new file [with content]
                New-Item -ItemType $this.Type -Path $this.DestinationPath -Value $this.Contents -Force:($this.Force)
                Set-nxMode -Path $this.DestinationPath -Mode $this.Mode
                Set-nxGroupOwnership -Path $this.DestinationPath -Group $this.Group
                Set-nxOwner -Path $this.DestinationPath -Owner $this.Owner

            }
            elseif ($currentState.Reasons.Count -gt 0)
            {
                # The file exists but is not properly configured
                Write-Verbose -Message (
                    $script:localizedDataNxFile.SetFile -f $this.DestinationPath
                )

                switch -Regex ($currentState.Reasons.Code)
                {
                    # DestinationPath can be skipped because it's determined with Ensure absent/present
                    # SourcePath is write-only property
                    # 'Ensure' is managed by the file being present or not (already covered)
                    'Type' # if an item of different type, throw... (we can't delete the item to create a new one)
                    {
                        throw ($script:localizedDataNxFile.SetTypeError -f $this.DestinationPath, $this.Type, $currentState.Type)
                    }

                    'Contents'
                    {
                        Write-Verbose -Message (
                            $script:localizedDataNxFile.SetFileContent -f $this.DestinationPath
                        )

                        [System.IO.File]::WriteAllText($currentState.DestinationPath, $this.Contents) # Set content adds a new line
                    }

                    'Checksum'
                    {
                        # either copy from source
                        if ($this.SourcePath -and (Test-Path -Path $this.SourcePath))
                        {
                            Write-Verbose -Message (
                                $script:localizedDataNxFile.CopySourceToDestination -f $this.SourcePath, $this.DestinationPath
                            )

                            Copy-Item -Confirm:$false -Path $this.SourcePath -Destination $this.DestinationPath -Force -Recurse:($this.Recurse)
                        }
                        elseif ($this.Contents -and $this.Type -eq 'File')
                        {
                            Write-Verbose -Message (
                                $script:localizedDataNxFile.SetFileContent -f $this.SourcePath
                            )

                            # or set content from $this.Contents
                            Set-Content -Path $this.DestinationPath -Value $this.Contents -Confirm:$false -Force
                        }
                    }

                    'Mode'
                    {
                        Set-nxMode -Path $this.DestinationPath -Mode $this.Mode -Recurse:($this.Force) -Confirm:$false -Force:($this.Force)
                    }

                    # Force is write-only property
                    # Recurse is write-only property
                    'Owner'
                    {
                        Set-nxOwner -Path $this.DestinationPath -Owner $this.Owner -Recurse:($this.Recurse) -Force:($this.Force) -Confirm:$false
                    }

                    'Group'
                    {
                        Set-nxGroupOwnership -Path $this.DestinationPath -Group $this.Group -Recurse:($this.Recurse) -Force:($this.Force) -Confirm:$false
                    }
                }
            }
            else
            {
                # Set has been invoked but the file is compliant with the desired state (no reasons found).
            }
        }
        else # Desired to be Absent
        {
            $nxFileSystemInfo = Get-nxItem -Path $this.DestinationPath -ErrorAction Stop | Where-Object -FilterScript { $this.Type -eq $_.nxFileSystemItemType}
            if ($nxFileSystemInfo -and $currentState.Ensure -eq [Ensure]::Present)
            {
                Remove-Item -Path $nxFileSystemInfo.DestinationPath -Force:($this.Force) -Recurse:($this.Recurse) -Confirm:$false
            }
        }
    }
}
#EndRegion './Classes/1.DscResources/01.nxFile.ps1' 416
#Region './Classes/1.DscResources/02.nxGroup.ps1' 0
$script:localizedDataNxGroup = Get-LocalizedData -DefaultUICulture en-US -FileName 'nxGroup.strings.psd1'

[DscResource()]
class nxGroup
{
    [DscProperty()]
    [Ensure] $Ensure = [Ensure]::Present

    [DscProperty(Key)]
    [System.String] $GroupName

    [DscProperty()]
    [System.String[]] $Members

    [DscProperty()]
    [System.String[]] $MembersToInclude

    [DscProperty()]
    [System.String[]] $MembersToExclude

    [DscProperty()]
    [System.String] $PreferredGroupID

    [DscProperty(NotConfigurable)]
    [Reason[]] $Reasons

    [nxGroup] Get()
    {
        Write-Verbose -Message (
            $script:localizedDataNxGroup.RetrieveGroup -f $this.GroupName
        )

        $nxLocalGroup = Get-nxLocalGroup -GroupName $this.GroupName
        $currentState = [nxGroup]::new()
        $currentState.GroupName = $this.GroupName

        if ($nxLocalGroup) # The group with this name exists
        {
            Write-Verbose -Message ($script:localizedDataNxGroup.nxGroupFound -f $this.GroupName)
            $currentState.Ensure = [Ensure]::Present
            $currentState.GroupName = $nxLocalGroup.GroupName # Make sure we get exactly what's in /etc/passwd
            $currentState.Members = $nxLocalGroup.GroupMember # Only compare during Exact match

            if ($this.MembersToInclude -and -not $this.Members) # Contains
            {
                $currentState.MembersToInclude = $nxLocalGroup.GroupMember.Where({$_ -in $this.MembersToInclude})
            }

            if ($this.MembersToExclude -and -not ($this.Members)) # Not Contains
            {
                # If it should be excluded but is present, remove it so the compare picks the difference on the right group.
                $currentState.MembersToExclude = $this.MembersToExclude.Where({$_ -notin $nxLocalGroup.GroupMember})
            }

            $currentState.PreferredGroupID = $nxLocalGroup.GroupID

            $valuesToCheck = @(
                # GroupName can be skipped because it's determined with Ensure absent/present
                'Ensure'
                'Members'
                'MembersToInclude'
                'MembersToExclude'
                'PreferredGroupID'
            ).Where({ $null -ne $this.$_ }) #remove properties not set from comparison

            $compareStateParams = @{
                CurrentValues   = ($currentState | Convert-ObjectToHashtable)
                DesiredValues   = ($this | Convert-ObjectToHashtable)
                ValuesToCheck   = $valuesToCheck
                IncludeValue    = $true
                SortArrayValues = $true
            }

            $comparedState = Compare-DscParameterState @compareStateParams

            $currentState.reasons = switch ($comparedState.Property)
            {
                'Ensure'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Ensure' -f $this.GetType()
                        Phrase = $script:localizedDataNxGroup.nxLocalGroupShouldBeAbsent -f $this.GroupName
                    }
                }

                'Members'
                {
                    $Property = $comparedState.Where({$_.Property -eq 'Members'})
                    $missingMembers = $Property.ExpectedValue.Where({$_ -notin $Property.ActualValue})
                    $ExtraMembers = $Property.ActualValue.Where({$_ -notin $Property.ExpectedValue})

                    [Reason]@{
                        Code = '{0}:{0}:Members' -f $this.GetType()
                        Phrase = $script:localizedDataNxGroup.MembersMismatch -f $this.GroupName, ($missingMembers -join ', '), ($ExtraMembers -join ', ')
                    }
                }

                'MembersToInclude'
                {
                    $Property = $comparedState.Where({$_.Property -eq 'MembersToInclude'})
                    $missingMembers = $Property.ExpectedValue.Where({$_ -notin $Property.ActualValue})

                    [Reason]@{
                        Code = '{0}:{0}:MembersToInclude' -f $this.GetType()
                        Phrase = $script:localizedDataNxGroup.MembersToIncludeMismatch -f $this.GroupName, ($missingMembers -join ', '), ($missingMembers -join ',')
                    }
                }

                'MembersToExclude'
                {
                    $Property = $comparedState.Where({$_.Property -eq 'MembersToExclude'})
                    $UndesiredMembers = $this.MembersToExclude.Where({$_ -notin $Property.ActualValue})

                    [Reason]@{
                        Code = '{0}:{0}:MembersToExclude' -f $this.GetType()
                        Phrase = $script:localizedDataNxGroup.MembersToExcludeMismatch -f $this.GroupName,  ($UndesiredMembers -join ', ')
                    }
                }

                'PreferredGroupID'
                {
                    [Reason]@{
                        Code = '{0}:{0}:PreferredGroupID' -f $this.GetType()
                        Phrase = $script:localizedDataNxGroup.PreferredGroupIDMismatch -f $this.GroupName,  $this.PreferredGroupID, $currentState.PreferredGroupID
                    }
                }
            }
        }
        else # no matching group for 'Name'
        {
            $currentState.Ensure = [Ensure]::Absent
            $currentState.GroupName = $this.GroupName
            Write-Verbose -Message ($script:localizedDataNxGroup.nxLocalGroupNotFound -f $this.GroupName)
            if ($this.Ensure -ne $currentState.Ensure)
            {
                $currentState.reasons = [Reason]@{
                    Code = '{0}:{0}:Ensure' -f $this.GetType()
                    Phrase = $script:localizedDataNxGroup.nxLocalGroupNotFound -f $this.GroupName
                }
            }
            else
            {
                Write-Verbose -Message ('The group ''{0}'' is in the desired state' -f $this.GroupName)
            }
        }

        return $currentState
    }

    [bool] Test()
    {
        $currentState = $this.Get()
        $testTargetResourceResult = $currentState.Reasons.Where({$_.Code -notmatch ':PreferredGroupID$'}).count -eq 0

        return $testTargetResourceResult
    }

    [void] Set()
    {
        # Not implemented yet
        # throw 'Set not implemented yet'

        $currentState = $this.Get()

        if ($this.Ensure -eq [Ensure]::Present) # Desired State: Ensure present
        {
            if ($currentState.Ensure -eq [Ensure]::Absent) # but is absent
            {
                Write-Verbose -Message (
                    $script:localizedDataNxUser.CreateGroup -f $this.GroupName
                )

                $newNxLocalGroupParams = @{
                    GroupName = $this.GroupName
                    PassThru = $true
                }

                if ($this.PreferredGroupID)
                {
                    $newNxLocalGroupParams['GroupID'] = $this.PreferredGroupID
                }

                $nxLocalGroup = New-nxLocalGroup @newNxLocalGroupParams

                if ($this.Members)
                {
                    Set-nxLocalGroup -GroupName $this.GroupName -Member $this.Members
                }
                else
                {
                    if ($this.MembersToExclude)
                    {
                        $this.MembersToExclude.Where({
                            $_ -in $nxLocalGroup.GroupMember
                        }) | Remove-nxLocalGroupMember -UserName $_ -GroupName $this.GroupName -Confirm:$false
                    }

                    if ($this.MembersToInclude)
                    {
                        $this.MembersToInclude.Where({
                            $_ -notin $nxLocalGroup.GroupMember
                        }) | Add-nxLocalGroupMember -GroupName $this.GroupName -Confirm:$false
                    }
                }
            }
            elseif ($this.Reasons.Count -gt 0)
            {
                $nxLocalGroup = Get-nxLocalGroup -GroupName $this.GroupName
                # The Group exists but is not set properly
                switch -Regex ($currentState.Reasons.Code)
                {
                    ':PreferredGroupID$'
                    {
                        Set-nxLocalGroupGID -GroupName $nxLocalGroup.GroupName -GroupID $this.PreferredGroupID -Confirm:$false
                    }

                    ':Members$'
                    {
                        Set-nxLocalGroup -GroupName $nxLocalGroup.GroupName -Member $this.Members -Confirm:$false
                    }

                    ':MembersToInclude$'
                    {
                        if (-not $this.Members)
                        {
                            $this.MembersToInclude.Where({
                                $_ -notin $nxLocalGroup.GroupMember
                            }) | Add-nxLocalGroupMember -GroupName $this.GroupName -Confirm:$false
                        }
                    }

                    ':MembersToExclude$'
                    {
                        if (-not $this.Members)
                        {
                            $this.MembersToExclude.Where({
                                $_ -in $nxLocalGroup.GroupMember
                            }) | Remove-nxLocalGroupMember -GroupName $this.GroupName -Confirm:$false
                        }
                    }
                }
            }
            else
            {
                # Set() invoked but no change needed.
            }
        }
        else
        {
            # Desired state: Ensure Absent
            if ($currentState.Ensure -eq [Ensure]::Present)
            {
                Remove-nxLocalGroup -GroupName $this.GroupName -Force -Confirm:$false
            }
        }
    }
}
#EndRegion './Classes/1.DscResources/02.nxGroup.ps1' 258
#Region './Classes/1.DscResources/03.nxUser.ps1' 0

$script:localizedDataNxUser = Get-LocalizedData -DefaultUICulture en-US -FileName 'nxUser.strings.psd1'

[DscResource()]
class nxUser
{
    [DscProperty()]
    [Ensure] $Ensure = [Ensure]::Present

    [DscProperty(Key)]
    [string] $UserName

    [DscProperty()]
    [string] $FullName

    [DscProperty()]
    [string] $Description

    [DscProperty()]
    [string] $Password

    [DscProperty()]
    [System.Nullable[bool]] $Disabled

    [DscProperty()]
    [string] $PasswordChangeRequired

    [DscProperty()]
    [string] $HomeDirectory

    [DscProperty()]
    [string] $GroupID

    [DscProperty(NotConfigurable)]
    [Reason[]] $Reasons

    [nxUser] Get()
    {
        Write-Verbose -Message (
            $script:localizedDataNxUser.RetrieveUser -f $this.UserName
        )

        $nxLocalUser = Get-nxLocalUser -UserName $this.UserName
        $currentState = [nxUser]::new()

        if ($nxLocalUser)
        {
            Write-Verbose -Message ($script:localizedDataNxUser.nxUserFound -f $this.UserName)

            $currentState.Ensure        = [Ensure]::Present
            $currentState.UserName      = $nxLocalUser.UserName
            $currentState.FullName      = $nxLocalUser.FullName
            $currentState.Description   = $nxLocalUser.Description
            $currentState.Password      = $nxLocalUser.EtcShadow.EncryptedPassword
            $currentState.Disabled      = $nxLocalUser.isDisabled()
            # $currentState.PasswordChangeRequired --> this is a WriteNoRead
            $currentState.HomeDirectory = $nxLocalUser.HomeDirectory

            # get the current primary group as an ID or a string based on what is Desired
            if ($this.GroupID -as [int])
            {
                $currentState.GroupId = $nxLocalUser.GroupID
            }
            else
            {
                # Expected GroupID is a string, resolve the Current GroupId's name.
                $currentState.GroupId = (Get-nxLocalGroup).Where({ $_.GroupID -eq $nxLocalUser.GroupID }).GroupName | Select-Object -First 1
            }

            $valuesToCheck = @(
                # UserName can be skipped because it's determined with Ensure absent/present
                'Ensure'
                'FullName'
                'Description'
                'Password'
                'Disabled'
                'HomeDirectory'
                'GroupID'
            ).Where({ $null -ne $this.$_ }) #remove properties not set from comparison

            $compareStateParams = @{
                CurrentValues = ($currentState | Convert-ObjectToHashtable)
                DesiredValues = ($this | Convert-ObjectToHashtable)
                ValuesToCheck = $valuesToCheck
            }

            $compareState = Compare-DscParameterState @compareStateParams

            $currentState.reasons = switch ($compareState.Property)
            {
                'Ensure'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Ensure' -f $this.GetType()
                        Phrase = $script:localizedDataNxUser.nxLocalUserShouldBeAbsent -f $this.UserName
                    }
                }

                'FullName'
                {
                    [Reason]@{
                        Code = '{0}:{0}:FullName' -f $this.GetType()
                        Phrase = $script:localizedDataNxUser.FullNameMismatch -f $this.FullName, $currentState.FullName
                    }
                }

                'Description'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Description' -f $this.GetType()
                        Phrase = $script:localizedDataNxUser.DescriptionMismatch -f $this.Description, $currentState.Description
                    }
                }

                'Password'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Password' -f $this.GetType()
                        Phrase = $script:localizedDataNxUser.PasswordMismatch -f $this.Password, $currentState.Password
                    }
                }

                'Disabled'
                {
                    [Reason]@{
                        Code = '{0}:{0}:Disabled' -f $this.GetType()
                        Phrase = $script:localizedDataNxUser.DisabledMismatch -f $this.Disabled, $currentState.Disabled
                    }
                }

                'PasswordChangeRequired'
                {
                    [Reason]@{
                        Code = '{0}:{0}:PasswordChangeRequired' -f $this.GetType()
                        Phrase = $script:localizedDataNxUser.PasswordChangeRequiredMismatch -f $this.PasswordChangeRequired, $currentState.PasswordChangeRequired
                    }
                }

                'HomeDirectory'
                {
                    [Reason]@{
                        Code = '{0}:{0}:HomeDirectory' -f $this.GetType()
                        Phrase = $script:localizedDataNxUser.HomeDirectoryMismatch -f $this.HomeDirectory, $currentState.HomeDirectory
                    }
                }

                'GroupID'
                {
                    [Reason]@{
                        Code = '{0}:{0}:GroupID' -f $this.GetType()
                        Phrase = $script:localizedDataNxUser.GroupIDMismatch -f $this.GroupID, $currentState.GroupID
                    }
                }
            }
        }
        else
        {
            $currentState.Ensure = [Ensure]::Absent
            $currentState.UserName = $this.UserName
            Write-Verbose -Message ($script:localizedDataNxUser.nxLocalUserNotFound -f $this.UserName)
            if ($this.Ensure -ne $currentState.Ensure)
            {
                $currentState.reasons = [Reason]@{
                    Code = '{0}:{0}:Ensure' -f $this.GetType()
                    Phrase = $script:localizedDataNxUser.nxLocalUserNotFound -f $this.UserName
                }
            }
            else
            {
                Write-Verbose -Message ('The user ''{0}'' is in the desired state' -f $this.UserName)
            }
        }

        return $currentState
    }

    [void] Set()
    {
        $currentState = $this.Get()

        if ($this.Ensure -eq [Ensure]::Present) # must be present
        {
            if ($currentState.Ensure -eq [Ensure]::Absent) # but is absent
            {
                Write-Verbose -Message (
                    $script:localizedDataNxUser.CreateUser -f $this.UserName
                )

                $newNxLocalUserParam = @{
                    Username    = $this.UserName
                    Passthru    = $true
                    ErrorAction = 'Stop'
                    Confirm     = $false
                }

                if ($this.GroupID)
                {
                    $newNxLocalUserParam.Add(
                        'PrimaryGroup', $this.GroupID
                    )
                }

                if ($this.Password)
                {
                    $newNxLocalUserParam.Add(
                        'EncryptedPassword', $this.Password
                    )
                }

                if ($this.HomeDirectory)
                {
                    $newNxLocalUserParam.Add(
                        'HomeDirectory', $this.HomeDirectory
                    )
                }

                if ($this.FullName -or $this.Description)
                {
                    $newNxLocalUserParam.Add(
                        'UserInfo',
                        ('{0},,,,{1},' -f $this.FullName, $this.Description)
                    )
                }

                if ($this.PasswordChangeRequired)
                {
                    # Make it expired yesterday
                    $newNxLocalUserParam.Add(
                        'ExpireOn',
                        (Get-Date).AddDays(-1)
                    )
                }

                $localUser = New-nxLocalUser @newNxLocalUserParam
            }
            else
            {
                # The user exists but has some non-compliant settings (found in the reasons)

                # Get user so we can set other properties
                $localUser = Get-nxLocalUser -UserName $this.UserName -ErrorAction Stop
                $setUserParams = @{}

                switch -Regex ($currentState.Reasons.Code)
                {
                    ':FullName$'
                    {
                        $setUserParams['FullName'] = $this.FullName
                    }

                    ':Description$'
                    {
                        $setUserParams['Description'] = $this.Description
                    }

                    ':Password$'
                    {
                        $setUserParams['EncryptedPassword'] = $this.Password
                    }

                    ':HomeDirectory$'
                    {
                        $setUserParams['HomeDirectory'] = $this.HomeDirectory
                    }

                    ':GroupID$'
                    {
                        Write-Verbose -Message ('Forcing the PrimaryGroup to be ID {0}' -f  $this.GroupID)
                        $setUserParams['GroupID'] = $this.GroupID
                    }
                }

                if ($setUserParams.Keys.Count -gt 0)
                {
                    Set-nxLocalUser @setUserParams
                }

                if ('nxUser:nxUser:Disabled' -in $currentState.Reasons.Code)
                {
                    if ($true -eq $this.Disabled)
                    {
                        Disable-nxLocalUser -UserName $this.UserName
                    }
                    elseif ($false -eq $this.Disabled)
                    {
                        Enable-nxLocalUser -UserName $this.UserName
                    }
                }
            }

            # Set other properties if needed
            if ($this.Disabled -and -not $localUser.IsDisabled())
            {
                Write-Verbose -Message "Disabling user account '$($this.UserName)'."
                Disable-nxLocalUser -UserName $localUser.UserName
            }
            elseif ($false -eq $this.Disabled -and $localUser.IsDisabled())
            {
                Write-Verbose -Message "Enabling user account '$($this.UserName)'."
                Enable-nxLocalUser -UserName $localUser.UserName
            }

            Write-Verbose -Message (
                $script:localizedDataNxUser.SettingProperties -f $this.UserName
            )
        }
        else
        {
            # The user must not exist
            if ($currentState.Ensure -eq 'Present')
            {
                # But it does, remove it
                Write-Verbose -Message (
                    $script:localizedDataNxUser.RemoveNxLocalUser -f $this.Path
                )

                Remove-nxLocalUser -UserName $this.Username -Confirm:$false
            }
        }
    }

    [bool] Test()
    {
        $currentState = $this.Get()
        $testTargetResourceResult = $currentState.Reasons.count -eq 0

        return $testTargetResourceResult
    }
}
#EndRegion './Classes/1.DscResources/03.nxUser.ps1' 330
#Region './Classes/1.DscResources/04.nxPackage.ps1' 0

# This will be a DSC Resource, eventually
class nxPackage
{
    [Ensure] $Ensure
    [String] $Name
    [String] $Version
    [String] $PackageType

    [bool] IsInstalled()
    {
        return $false
    }
}
#EndRegion './Classes/1.DscResources/04.nxPackage.ps1' 15
#Region './Classes/1.DscResources/Reason.ps1' 0

class Reason
{
    [DscProperty()]
    [string] $Phrase

    [DscProperty()]
    [string] $Code
}
#EndRegion './Classes/1.DscResources/Reason.ps1' 10
#Region './Classes/2.GCResources/01.GC_LinuxGroup.ps1' 0
[DscResource()]
class GC_LinuxGroup : nxGroup
{
    [DscProperty()]
    [System.String] $MembersAsString

    [DscProperty()]
    [System.String] $MembersToIncludeAsString

    [DscProperty()]
    [System.String] $MembersToExcludeAsString

    GC_LinuxGroup()
    {
        # default ctor
    }

    GC_LinuxGroup ([nxGroup] $nxGroup)
    {
        $this.GroupName = $nxGroup.GroupName
        $this.Members = $nxGroup.Members
        $this.MembersToInclude = $nxGroup.MembersToInclude
        $this.MembersToExclude = $nxGroup.MembersToExclude
        $this.Reasons = $nxGroup.Reasons
        $this.Ensure = $nxGroup.Ensure
        $this.PreferredGroupID = $nxGroup.PreferredGroupID

        $this.MembersAsString = $nxGroup.Members -join ';'
        $this.MembersToExcludeAsString = $nxGroup.MembersToExclude -join ';'
        $this.MembersToIncludeAsString = $nxGroup.MembersToInclude -join ';'
    }

    [void] ConvertAsStringToBaseClass()
    {
        if ($null -ne $this.MembersToIncludeAsString)
        {
            $this.MembersToInclude = $this.MembersToIncludeAsString -split ';'
        }

        if ($null -ne $this.MembersToExcludeAsString)
        {
            $this.MembersToExclude = $this.MembersToExcludeAsString -split ';'
        }

        if ($null -ne $this.MembersAsString)
        {
            $this.Members = $this.MembersAsString -split ';'
        }
    }

    [GC_LinuxGroup] Get()
    {
        $this.ConvertAsStringToBaseClass()

        return ([GC_LinuxGroup]([nxGroup]$this).Get())
    }

    [bool] Test()
    {
        $currentState = $this.Get()
        $testTargetResourceResult = $currentState.Reasons.Where({$_.Code -notmatch ':PreferredGroupID$'}).count -eq 0

        return $testTargetResourceResult
    }

    [void] Set()
    {
        $this.ConvertAsStringToBaseClass()
        ([nxGroup]$this).Set()
    }
}
#EndRegion './Classes/2.GCResources/01.GC_LinuxGroup.ps1' 72
#Region './Classes/2.GCResources/02.GC_msid110.ps1' 0

# author: Michael Greene

# control 'msid110' do
# impact 1.0
# title 'Remote connections from accounts with empty passwords should be disabled.'
# desc 'An attacker could gain access through password guessing'

# describe file('/etc/ssh/sshd_config') do
# its('content') { should match "^[\s\t]*PermitEmptyPasswords\s+no" }
# end
# end

# instance of MSFT_ChefInSpecResource as $MSFT_ChefInSpecResource2ref
# {
# ResourceID = "[ChefInSpec]MSID110";
# SourceInfo = "::11::5::ChefInSpec";
# Name = "PasswordPolicy_msid110";
# ModuleName = "ChefInSpec";
# ModuleVersion = "1.0";
# GithubPath = "PasswordPolicy_msid110/Modules/PasswordPolicy_msid110_inspec_controls/";
# ConfigurationName = "chefInSpec";
# };



[DscResource()]
class GC_msid110
{
    [DscProperty(Key)]
    [String] $Name

    [DscProperty(NotConfigurable)]
    [Reason[]] $Reasons

    [GC_msid110] Get()
    {
        $sshdContentMatch = Get-Content -Path '/etc/ssh/sshd_config' -ErrorAction SilentlyContinue | Where-Object -FilterScript {
            $_ -match '^[\s\t]*PermitEmptyPasswords\s+no'
        }

        $result = [GC_msid110]::new()
        $result.Name = $this.Name

        if (-not $sshdContentMatch)
        {
            $result.Reasons += [Reason]@{
                Code = '{0}:{0}:sshdPermitEmptyPasswords' -f $this.GetType()
                Phrase = 'Remote connections from accounts with empty passwords is not disabled.'
            }
        }

        return $result
    }

    [bool] Test()
    {
        $getResult = $this.Get()
        if ($getResult.Reasons -is [Reason[]] -and $getResult.Count -ge 1)
        {
            return $false
        }
        else
        {
            return $true
        }
    }

    [void] Set()
    {
        throw 'The Set method is not implemented for this Audit resource.'
    }
}
#EndRegion './Classes/2.GCResources/02.GC_msid110.ps1' 74
#Region './Classes/2.GCResources/03.GC_msid121.ps1' 0
# author: Michael Greene

# control 'msid12.1' do
# impact 1.0
# title '/etc/passwd file permissions should be 0644'
# desc 'An attacker could modify userIDs and login shells'

# describe file('/etc/passwd') do
# its('mode') { should cmp '0644' }
# end
# end

# instance of MSFT_ChefInSpecResource as $MSFT_ChefInSpecResource2ref
# {
# ResourceID = "[ChefInSpec]MSID121";
# SourceInfo = "::11::5::ChefInSpec";
# Name = "PasswordPolicy_msid121";
# ModuleName = "ChefInSpec";
# ModuleVersion = "1.0";
# GithubPath = "PasswordPolicy_msid121/Modules/PasswordPolicy_msid121_inspec_controls/";
# ConfigurationName = "chefInSpec";
# };


[DscResource()]
class GC_msid121
{
    [DscProperty(Key)]
    [String] $Name

    [DscProperty(NotConfigurable)]
    [Reason[]] $Reasons

    [GC_msid121] Get()
    {
        $etcPasswdFile = Get-nxItem -Path '/etc/passwd'
        $modeDifference = Compare-nxMode -ReferenceMode 0644 -DifferenceMode $etcPasswdFile.Mode

        $result = [GC_msid121]::new()
        $result.Name = $this.Name

        if ($null -ne $modeDifference)
        {
            $result.Reasons += [Reason]@{
                Code = '{0}:{0}:passwd' -f $this.GetType()
                Phrase = 'The file ''/etc/passwd'' has a mode of ''{0}''.' -f $etcPasswdFile.Mode.ToOctal()
            }
        }

        return $result
    }

    [bool] Test()
    {
        $getResult = $this.Get()
        if ($getResult.Reasons -is [Reason[]] -and $getResult.Count -ge 1)
        {
            return $false
        }
        else
        {
            return $true
        }
    }

    [void] Set()
    {
        throw 'The Set method is not implemented for this Audit resource.'
    }
}
#EndRegion './Classes/2.GCResources/03.GC_msid121.ps1' 71
#Region './Classes/2.GCResources/04.GC_msid232.ps1' 0

# author: Michael Greene

# control 'msid23.2' do
# impact 1.0
# title 'There are no accounts without passwords'
# desc 'An attacker could modify userIDs and login shells'

# describe file('/etc/shadow') do
# its('content') { should_not match "^[^:]+::" }
# end
# end

# instance of MSFT_ChefInSpecResource as $MSFT_ChefInSpecResource2ref
# {
# ResourceID = "[ChefInSpec]MSID232";
# SourceInfo = "::11::5::ChefInSpec";
# Name = "PasswordPolicy_msid232";
# ModuleName = "ChefInSpec";
# ModuleVersion = "1.0";
# GithubPath = "PasswordPolicy_msid232/Modules/PasswordPolicy_msid232_inspec_controls/";
# ConfigurationName = "chefInSpec";
# };

[DscResource()]
class GC_msid232
{
    [DscProperty(Key)]
    [String] $Name

    [DscProperty(NotConfigurable)]
    [Reason[]] $Reasons

    [GC_msid232] Get()
    {
        $userAccountWithoutPassword = Get-nxLocalUser | Where-Object -FilterScript {
            [string]::IsNullOrEmpty($_.etcShadow.Encryptedpassword)
        } #| select username,password,@{N='pass';E={$_.etcShadow.EncryptedPassword}}

        $result = [GC_msid232]::new()
        $result.Name = $this.Name

        foreach ($item in $userAccountWithoutPassword)
        {
            $result.Reasons += [Reason]@{
                Code = '{0}:{0}:{1}' -f $this.GetType(),$item.UserName
                Phrase = 'Username ''{0}'' has an empty password.' -f $item.UserName
            }
        }

        return $result
    }

    [bool] Test()
    {
        $getResult = $this.Get()
        if ($getResult.Reasons -is [Reason[]] -and $getResult.Count -ge 1)
        {
            return $false
        }
        else
        {
            return $true
        }
    }

    [void] Set()
    {
        throw 'The Set method is not implemented for this Audit resource.'
    }
}
#EndRegion './Classes/2.GCResources/04.GC_msid232.ps1' 72
#Region './Classes/2.GCResources/05.GC_InstalledApplicationLinux.ps1' 0
# val_packages = attribute('packages', description: 'The names of the packages that should be installed.')

# control 'Installed Application Packages' do
# impact 1.0
# title 'Verify installed applications'
# desc 'Validates that application packages are installed'

# val_packages.each do |val_package|
# describe package(val_package) do
# it { should be_installed }
# end
# end
# end

# instance of MSFT_ChefInSpecResource as $MSFT_ChefInSpecResource1ref
# {
# ResourceID = "[ChefInSpec]InstalledApplicationLinuxResource1";
# SourceInfo = "::11::5::ChefInSpec";
# Name = "installed_application_linux";
# ModuleName = "ChefInSpec";
# ModuleVersion = "1.0";
# ConfigurationName = "InstalledApplicationLinux";
# GithubPath = "installed_application_linux/Modules/installed_application_linux_inspec_controls/";
# AttributesYmlContent = "packages: [Unknown Application]";
# };

[DscResource()]
class GC_InstalledApplicationLinux
{
    [DscProperty(Key)]
    [String] $Name

    [DscProperty()]
    [String] $AttributesYmlContent = "packages: [Unknown Application]"

    [DscProperty(NotConfigurable)]
    [string[]] $PackageShouldBeInstalled = @()

    [DscProperty(NotConfigurable)]
    [Reason[]] $Reasons

    [GC_InstalledApplicationLinux] Get()
    {
        $this.ConvertAttributesYmlContentToStringArray()

        $getResult = [GC_InstalledApplicationLinux]@{
            Name = $this.Name
        }

        $getResult.PackageShouldBeInstalled = (Get-nxPackageInstalled -Name $this.PackageShouldBeInstalled -ErrorAction Ignore).Name
        $this.PackageShouldBeInstalled.Where({$_ -notin $getResult.PackageShouldBeInstalled}).Foreach({
            $getResult.Reasons += [Reason]@{
                code = '{0}:{0}:Ensure' -f $this.GetType()
                phrase = 'The package ''{0}'' is expected to be installed but could not be found on the local system' -f $_
            }
        })

        $getResult.ConvertStringArrayToAttributeYmlContent()

        return $getResult
    }

    [bool] Test()
    {
        $getResult = $this.Get()
        if ($getResult.Reasons -is [Reason[]] -and $getResult.Count -ge 1)
        {
            return $false
        }
        else
        {
            return $true
        }
    }

    [void] Set()
    {
        throw "Remediation (Set) is not implemented yet."
    }

    [void] ConvertAttributesYmlContentToStringArray()
    {
        # remove 'packages:' from the string
        # split what's in [] with ; separator
        # update $this.PackageShouldBeInstalled
        $stringList = $this.AttributesYmlContent -replace '^packages:\s*\[|^\[|\]$'
        $this.PackageShouldBeInstalled = $stringList -split '\s*;\s*'
    }

    [void] ConvertStringArrayToAttributeYmlContent()
    {
        $this.AttributesYmlContent = $this.PackageShouldBeInstalled -join ';'
    }
}
#EndRegion './Classes/2.GCResources/05.GC_InstalledApplicationLinux.ps1' 95
#Region './Classes/2.GCResources/06.GC_NotInstalledApplicationLinux.ps1' 0
# val_packages = attribute('packages', description: 'The names of the packages that should not be installed.')

# control 'Not Installed Application Packages' do
# impact 1.0
# title 'Verify not installed applications'
# desc 'Validates that application packages are not installed'

# val_packages.each do |val_package|
# describe package(val_package) do
# it { should_not be_installed }
# end
# end
# end


# instance of MSFT_ChefInSpecResource as $MSFT_ChefInSpecResource1ref
# {
# ResourceID = "[ChefInSpec]NotInstalledApplicationLinuxResource1";
# SourceInfo = "::11::5::ChefInSpec";
# Name = "not_installed_application_linux";
# ModuleName = "ChefInSpec";
# ModuleVersion = "1.0";
# ConfigurationName = "NotInstalledApplicationLinux";
# GithubPath = "not_installed_application_linux/Modules/not_installed_application_linux_inspec_controls/";
# AttributesYmlContent = "packages: [Unknown Application]";
# };

[DscResource()]
class GC_NotInstalledApplicationLinux
{
    [DscProperty(Key)]
    [String] $Name

    [DscProperty()]
    [String] $AttributesYmlContent = "packages: [Unknown Application]"

    [DscProperty(NotConfigurable)]
    [string[]] $PackageShouldNotBeInstalled = @()

    [DscProperty(NotConfigurable)]
    [Reason[]] $Reasons

    [GC_NotInstalledApplicationLinux] Get()
    {
        $this.ConvertAttributesYmlContentToStringArray()

        $getResult = [GC_NotInstalledApplicationLinux]@{
            Name = $this.Name
        }

        $getResult.PackageShouldNotBeInstalled = (Get-nxPackageInstalled -Name $this.PackageShouldNotBeInstalled).Name
        $this.PackageShouldNotBeInstalled.Where({$_ -in $getResult.PackageShouldNotBeInstalled}).Foreach({
            $getResult.Reasons += [Reason]@{
                code = '{0}:{0}:Ensure' -f $this.GetType()
                phrase = 'The package ''{0}'' is expected to not be installed but was found on the local system' -f $_
            }
        })

        $getResult.ConvertStringArrayToAttributeYmlContent()
        return $getResult
    }

    [bool] Test()
    {
        $getResult = $this.Get()
        if ($getResult.Reasons -is [Reason[]] -and $getResult.Count -ge 1)
        {
            return $false
        }
        else
        {
            return $true
        }
    }

    [void] Set()
    {
        throw "Remediation (Set) is not implemented yet."
    }

    [void] ConvertAttributesYmlContentToStringArray()
    {
        # remove 'packages:' from the string
        # split what's in [] with ; separator
        # update $this.PackageShouldNotBeInstalled
        $stringList = $this.AttributesYmlContent -replace '^packages:\s*\[|^\[|\]$'
        $this.PackageShouldNotBeInstalled = $stringList -split '\s*;\s*'
    }

    [void] ConvertStringArrayToAttributeYmlContent()
    {
        $this.AttributesYmlContent = $this.PackageShouldNotBeInstalled -join ';'
    }
}
#EndRegion './Classes/2.GCResources/06.GC_NotInstalledApplicationLinux.ps1' 95
#Region './Classes/3.Packages/01.nxDpkgPackage.ps1' 0
#using module Package
class nxDpkgPackage : nxPackage
{
    # https://www.debian.org/doc/debian-policy/ch-controlfields.html

    # This field identifies the source package name.
    $Source

    # The package maintainer’s name and email address.
    # The name must come first, then the email address inside angle brackets <> (in RFC822 format).
    $Maintainer

    # List of the names and email addresses of co-maintainers of the package, if any.
    $Uploaders

    # The name and email address of the person who prepared this version of the package,
    # usually a maintainer. The syntax is the same as for the Maintainer field.
    $ChangedBy

    # This field specifies an application area into which the package has been classified.
    # See Sections.
    $Section

    # This field represents how important it is that the user have the package installed.
    # See Priorities.
    $Priority

    # The name of the binary package.
    # Binary package names must follow the same syntax and restrictions as source package names.
    # See Source for the details.
    # This also populates the Name property of the [Package] parent class
    $Package

    # Depending on context and the control file used, the Architecture field can include the following sets of values:
    # - A unique single word identifying a Debian machine architecture as described in Architecture specification strings.
    # (https://www.debian.org/doc/debian-policy/ch-customized-programs.html#s-arch-spec)
    # - An architecture wildcard identifying a set of Debian machine architectures, see Architecture wildcards.
    # (https://www.debian.org/doc/debian-policy/ch-customized-programs.html#s-arch-wildcard-spec)
    # `any` matches all Debian machine architectures and is the most frequently used.
    # - all, which indicates an architecture-independent package.
    # - source, which indicates a source package.
    $Architecture

    # This is a boolean field which may occur only in the control file of a binary package or
    # in a per-package fields paragraph of a source package control file.
    # If set to yes then the package management system will refuse to remove the package
    # (upgrading and replacing it is still possible).
    # The other possible value is no, which is the same as not having the field at all.
    $Essential

    #region Package interrelationship fields
    # These fields describe the package’s relationships with other packages.
    # Their syntax and semantics are described in Declaring relationships between packages.
    # (https://www.debian.org/doc/debian-policy/ch-relationships.html)

    $Depends
    $PreDepends
    $Recommends
    $Suggests
    $Breaks
    $Conflicts
    $Provides
    $Replaces
    $Enhances

    #endregion Package interrelationship fields

    $StandardsVersion

    # $Version # defined in Parent Class [Package]
    $Description

    $Distribution
    $Date
    $Format
    $Urgency
    $Changes
    $Binary
    $InstalledSize
    $Files
    $Closes
    $Homepage
    $ChecksumsSha1
    $ChecksumsSha256
    $DMUploadAllowed # obsolete
    $PackageList
    $PackageType
    $Dgit
    $TestSuite
    $RulesRequiresRoot

    # Additional Fields
    $Status
    $OriginalMaintainer
    $MultiArch
    $Conffiles
    $AdditionalFields = @{}

    $Vendor
}
#EndRegion './Classes/3.Packages/01.nxDpkgPackage.ps1' 101
#Region './Classes/3.Packages/02.nxYumPackage.ps1' 0

class nxYumPackage : nxPackage
{
    $Arch
    $Release
    $Size
    $Repo
    $FromRepo
    $Summary
    $Url
    $License
    $Description

    $AdditionalFields = @{}
}
#EndRegion './Classes/3.Packages/02.nxYumPackage.ps1' 16
#Region './Private/Convert-ObjectToHashtable.ps1' 0

function Convert-ObjectToHashtable
{
    [CmdletBinding()]
    [OutputType([Hashtable])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object]
        [Alias('Object')]
        $InputObject
    )

    process
    {

        $hashResult = @{}

        $InputObject.psobject.Properties | Foreach-Object {
            $hashResult[$_.Name] = $_.Value
        }

        return $hashResult
    }
}
#EndRegion './Private/Convert-ObjectToHashtable.ps1' 26
#Region './Private/Get-nxEscapedPath.ps1' 0
function Get-nxEscapedPath
{
    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(ValueFromPipeline = $true)]
        [System.String]
        $Path
    )

    process
    {
        return ('"{0}"' -f $Path)
    }
}
#EndRegion './Private/Get-nxEscapedPath.ps1' 17
#Region './Private/Get-nxEscapedString.ps1' 0
function Get-nxEscapedString
{
    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(ValueFromPipeline = $true)]
        [System.String]
        $String
    )

    process
    {
        return ('''{0}''' -f ($String -replace "\'","''"))
    }
}
#EndRegion './Private/Get-nxEscapedString.ps1' 17
#Region './Private/Get-nxSourceFile.ps1' 0
function Get-nxSourceFile
{
    [CmdletBinding()]
    [OutputType([void])]
    param (
        [Parameter(Mandatory = $true)]
        [System.String]
        [ValidateScript({$null -ne ($_ -as [uri]).Scheme -or (Test-Path -Path $_ -PathType Leaf)})]
        [Alias('Uri')]
        $Path,

        [Parameter()]
        [System.String]
        $DestinationFile,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $Force
    )

    if (-not $PSBoundParameters.ContainsKey('DestinationFile'))
    {
        $fileName = [System.Io.FileInfo](Split-Path -Leaf $Path)
        if ($null -ne ($Path -as [uri]).Scheme -and -not [string]::IsNullOrEmpty($fileName.Extension))
        {
            $DestinationFile = $fileName
        }
    }

    if (Test-Path -Path $DestinationFile)
    {
        if ($Force.IsPresent)
        {
            Remove-Item -Force -Recurse -Path $DestinationFile
        }
        else
        {
            throw ('File ''{0}'' already exists.' -f $DestinationFile)
        }
    }

    if ($Path -as [uri] -and ([uri]$Path).Scheme -match '^http|^ftp')
    {
        $null = Invoke-WebRequest -Uri $Path -OutFile $DestinationFile -ErrorAction 'Stop'
    }
    else
    {
        Copy-Item -Path $Path -Destination $DestinationFile -ErrorAction Stop -Force:$Force
    }
}
#EndRegion './Private/Get-nxSourceFile.ps1' 51
#Region './Private/nxFileSystem/Convert-nxFileSystemAccessRightToSymbol.ps1' 0
function Convert-nxFileSystemAccessRightToSymbol
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String[]]
        [ValidateScript({$_ -as [nxFileSystemAccessRight] -or $_ -as [nxFileSystemSpecialMode]})]
        $AccessRight,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [nxFileSystemUserClass]
        [Alias('Class')]
        $UserClass,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [System.Management.Automation.SwitchParameter]
        $UseDashWhenAbsent
    )

    process {

        Write-Verbose "Access Right: '$($AccessRight -join "', '")'"

        [nxFileSystemAccessRight]$AccessRightEntry = 'none'
        [nxFileSystemSpecialMode]$SpecialModeEntry = 'none'

        $AccessRight.ForEach({
            if ($_ -as [nxFileSystemAccessRight])
            {
                $AccessRightEntry = $AccessRightEntry -bor [nxFileSystemAccessRight]$_
            }
            elseif ($_ -as [nxFileSystemSpecialMode])
            {
                $SpecialModeEntry = $SpecialModeEntry -bor [nxFileSystemSpecialMode]$_
            }
        })

        Write-Debug -Message "AccessRight: '$AccessRightEntry', SpecialMode: $SpecialModeEntry"

        $Symbols = @(
            $AccessRightEntry -band [nxFileSystemAccessRight]::Read ? 'r' : ($UseDashWhenAbsent ? '-':'')
            $AccessRightEntry -band [nxFileSystemAccessRight]::Write ? 'w' : ($UseDashWhenAbsent ? '-':'')

            if (
                $UserClass -band [nxFileSystemUserClass]::Group -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::SetGroupId -and
                $AccessRightEntry -band [nxFileSystemAccessRight]::Execute
            )
            {
                's'
            }
            elseif (
                $UserClass -band [nxFileSystemUserClass]::Group -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::SetGroupId
            )
            {
                'S'
            }
            elseif (
                $UserClass -band [nxFileSystemUserClass]::User -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::SetUserId -and
                $AccessRightEntry -band [nxFileSystemAccessRight]::Execute
            )
            {
                's'
            }
            elseif (
                $UserClass -band [nxFileSystemUserClass]::User -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::SetUserId
            )
            {
                'S'
            }
            elseif (
                $UserClass -band [nxFileSystemUserClass]::Others -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::StickyBit -and
                $AccessRightEntry -band [nxFileSystemAccessRight]::Execute
            )
            {
                't'
            }
            elseif (
                $UserClass -band [nxFileSystemUserClass]::Others -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::StickyBit
            )
            {
                'T'
            }
            elseif ($AccessRightEntry -band [nxFileSystemAccessRight]::Execute)
            {
                'x'
            }
            elseif ($UseDashWhenAbsent)
            {
                '-'
            }
        )

        Write-Verbose -Message "Symbols: '$($Symbols -join '')'."
        ($Symbols -join '')
    }
}
#EndRegion './Private/nxFileSystem/Convert-nxFileSystemAccessRightToSymbol.ps1' 104
#Region './Private/nxFileSystem/Convert-nxFileSystemModeComparisonToSymbolicOperation.ps1' 0
function Convert-nxFileSystemModeComparisonToSymbolicOperation
{
    [CmdletBinding()]
    [OutputType([string])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Class')]
        [nxFileSystemUserClass]
        $UserClass,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String]
        [ValidateScript({$_ -as [nxFileSystemAccessRight] -or $_ -as [nxFileSystemSpecialMode]})]
        [Alias('InputObject')]
        $EnumValue,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string]
        $SideIndicator
    )

    process {
        # FTR the side indicator points where the EnumValue is found: REFERENCE <=> DIFFERENCE
        # The SympolicOperation generated aims to make the DifferenceMode compliante with the reference.

        Write-Debug "[$UserClass] [$EnumValue] [$SideIndicator]"

        if ($SideIndicator -eq '<=')
        {
            # Need to add something that is not in the reference
            $operator = '+'
        }
        else
        {
            # Need to remove something that is not in the reference
            $operator = '-'
        }

        $UserClassSymbol = Convert-nxFileSystemUserClassToSymbol -UserClass $UserClass
        $ModeSymbol = Convert-nxFileSystemAccessRightToSymbol -AccessRight $EnumValue -UserClass $UserClass

        return ('{0}{1}{2}' -f $UserClassSymbol, $operator, $ModeSymbol)
    }
}
#EndRegion './Private/nxFileSystem/Convert-nxFileSystemModeComparisonToSymbolicOperation.ps1' 46
#Region './Private/nxFileSystem/Convert-nxFileSystemUserClassToSymbol.ps1' 0
function Convert-nxFileSystemUserClassToSymbol
{
    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [nxFileSystemUserClass]
        [Alias('Class')]
        $UserClass
    )

    $symbols = switch ($UserClass)
    {
        ([nxFileSystemUserClass]::User)   { 'u' }
        ([nxFileSystemUserClass]::Group)  { 'g' }
        ([nxFileSystemUserClass]::Others) { 'o' }
    }

    return ($symbols -join '')
}
#EndRegion './Private/nxFileSystem/Convert-nxFileSystemUserClassToSymbol.ps1' 22
#Region './Private/nxFileSystem/Convert-nxLsEntryToFileSystemInfo.ps1' 0
function Convert-nxLsEntryToFileSystemInfo
{
    [CmdletBinding()]
    [OutputType([nxFileSystemInfo])]
    param
    (
        [Parameter(ValueFromPipeline = $true)]
        [System.String]
        $lsLine,

        [Parameter()]
        [System.String]
        $InitialPath = '.',

        [Parameter()]
        [scriptblock]
        $ErrorHandler = {
            switch -Regex ($_)
            {
                default { Write-Error -Message $_ }
            }
        }
    )

    begin {
        $lastParent = $null
    }

    process {
        foreach ($lineToParse in $lsLine.Where{$_})
        {
            Write-Verbose -Message "Parsing ls line output: '$lineToParse'."

            if ($lineToParse -is [System.Management.Automation.ErrorRecord])
            {
                Write-Debug -Message 'Dispatching to ErrorHandler...'
                $lineToParse | &$ErrorHandler
            }
            elseif ($lineToParse -match '^/.*ls:\s(?<message>.*)')
            {
                Write-Error -Message $Matches.message
            }
            elseif ($lineToParse -match '^\s*total')
            {
                Write-Verbose -Message $lineToParse
            }
            elseif ($lineToParse -match '^(?<parent>/.*):$')
            {
                $lastParent = $Matches.parent
            }
            else
            {
                $Mode, $nxLinkCount, $nxOwner, $nxGroup, $Length, $lastModifyDate, $lastModifyTime, $lastModifyTimezone, $fileName = $lineToParse -split '\s+',9
                $nxFileSystemItemType = switch ($Mode[0])
                {
                    '-' { 'File' }
                    'd' { 'Directory' }
                    'l' { 'Link' }
                    'p' { 'Pipe' }
                    's' { 'socket' }
                }

                $lastWriteTime = Get-Date -Date ($lastModifyDate + " " + $lastModifyTime + $lastModifyTimezone)

                # Maybe there's no $lastParent yet (top folder from search Path)
                if ($null -eq $lastParent)
                {
                    if ($InitialPath -eq [io.Path]::GetFullPath($fileName, $PWD.Path))
                    {
                        Write-Debug -Message "No `$lastParent and Initial path is '$InitialPath' same as file name is '$fileName'."
                        # no Last parent and the InitialPath is the same as the file Name. (i.e. ./CHANGELOG.md or CHANGELOG.md)
                        $lastParent = [io.path]::GetFullPath("$InitialPath/..")
                    }
                    else
                    {
                        $lastParent = [io.Path]::GetFullPath($InitialPath)
                    }

                    $fullPath = [io.Path]::GetFullPath($fileName, $lastParent)
                }
                else
                {
                    Write-Debug -Message "`$lastParent is '$lastParent', Initial Path is '$InitialPath' and file name is '$fileName'."
                    $fullPath = [io.path]::GetFullPath($fileName, $lastParent)
                }

                [nxFileSystemInfo]::new(
                    @{
                        FullPath                = $fullPath
                        LastWriteTime           = $lastWriteTime
                        nxFileSystemItemType    = $nxFileSystemItemType
                        nxOwner                 = $nxOwner
                        nxGroup                 = $nxGroup
                        Length                  = [long]::Parse($Length)
                        nxLinkCount             = $nxLinkCount
                        Mode                    = $Mode
                    }
                )
            }
        }
    }
}
#EndRegion './Private/nxFileSystem/Convert-nxLsEntryToFileSystemInfo.ps1' 103
#Region './Private/nxFileSystem/Convert-nxSymbolToFileSystemAccessRight.ps1' 0
function Convert-nxSymbolToFileSystemAccessRight
{
    [CmdletBinding()]
    [OutputType([nxFileSystemAccessRight])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Char[]]
        [Alias('Char')]
        $AccessRightSymbol
    )

    process {
        foreach ($charItem in $AccessRightSymbol)
        {
            switch -CaseSensitive ($charItem)
            {
                'w'
                {
                    [nxFileSystemAccessRight]::Write
                }

                'r'
                {
                    [nxFileSystemAccessRight]::Read
                }

                'x'
                {
                    [nxFileSystemAccessRight]::Execute
                }

                '-'
                {
                    [nxFileSystemAccessRight]::None
                }

                'T'
                {
                    Write-Debug -Message "The UpperCase 'T' means there's no Execute right."
                    [nxFileSystemAccessRight]::None
                }

                't'
                {
                    [nxFileSystemAccessRight]::Execute
                }

                'S'
                {
                    Write-Debug -Message "The UpperCase 'S' means there's no Execute right."
                    [nxFileSystemAccessRight]::None
                }

                's'
                {
                    [nxFileSystemAccessRight]::Execute
                }
            }
        }
    }
}
#EndRegion './Private/nxFileSystem/Convert-nxSymbolToFileSystemAccessRight.ps1' 63
#Region './Private/nxFileSystem/Convert-nxSymbolToFileSystemSpecialMode.ps1' 0
function Convert-nxSymbolToFileSystemSpecialMode
{
    [CmdletBinding()]
    [OutputType([nxFileSystemSpecialMode])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Char[]]
        [Alias('Char')]
        # the possible char are [sStT], but if other values sucha as [rwx-] are passed, we should just ignore them (no special permission).
        $SpecialModeSymbol,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [nxFileSystemUserClass]
        $UserClass
    )

    process {
        foreach ($charItem in $SpecialModeSymbol)
        {
            Write-Debug -Message "Converting '$charItem' to [nxFileSystemSpecialMode]."
            switch ($charItem)
            {
                't'
                {
                    Write-Debug -Message "Adding StickyBit."
                    [nxFileSystemSpecialMode]::StickyBit
                }

                's'
                {
                    if ($UserClass -eq [nxFileSystemUserClass]::User)
                    {
                        Write-Debug -Message "Adding SetUserId."
                        [nxFileSystemSpecialMode]::SetUserId
                    }

                    if ($UserClass -band [nxFileSystemUserClass]::Group)
                    {
                        Write-Debug -Message "Adding SetGroupId."
                        [nxFileSystemSpecialMode]::SetGroupId
                    }

                    if ((-not $UserClass -band [nxFileSystemUserClass]::Group) -and (-not $UserClass -eq [nxFileSystemUserClass]::User))
                    {
                        Write-Warning -Message "Cannot determine whether to set the SUID or SGID because the User class is invalid: '$UserClass'"
                    }
                }

                default {
                    Write-Debug -Message "Nothing to return for char '$charItem'."
                }
            }
        }
    }
}
#EndRegion './Private/nxFileSystem/Convert-nxSymbolToFileSystemSpecialMode.ps1' 57
#Region './Private/nxFileSystem/Convert-nxSymbolToFileSystemUserClass.ps1' 0
function Convert-nxSymbolToFileSystemUserClass
{
    [CmdletBinding()]
    [OutputType([nxFileSystemUserClass])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [char[]]
        $Char
    )

    process {
        foreach ($charItem in $char)
        {
            switch ($charItem)
            {
                'u' { [nxFileSystemUserClass]'User'   }
                'g' { [nxFileSystemUserClass]'Group'  }
                'o' { [nxFileSystemUserClass]'Others' }
                'a' { [nxFileSystemUserClass]'User, Group, Others' }
                default { throw "Unexpected char '$CharItem'" }
            }
        }
    }
}
#EndRegion './Private/nxFileSystem/Convert-nxSymbolToFileSystemUserClass.ps1' 26
#Region './Private/nxFileSystem/Get-FileHashAlgorithmFromHash.ps1' 0
function Get-FileHashAlgorithmFromHash
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String]
        [Alias('Hash')]
        $FileHash
    )

    switch ($FileHash.Length)
    {
        32
        {
            'MD5'
        }

        40
        {
            'SHA1'
        }

        64
        {
            'SHA256'
        }

        128
        {
            'SHA512'
        }

        default
        {
            throw ('Could not resolve the Algorith used for hash ''{0}''' -f $FileHash)
        }
    }
}
#EndRegion './Private/nxFileSystem/Get-FileHashAlgorithmFromHash.ps1' 41
#Region './Public/FileSystem/Compare-nxMode.ps1' 0
function Compare-nxMode
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [nxFileSystemMode]
        $ReferenceMode,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [nxFileSystemMode[]]
        [Alias('Mode')]
        $DifferenceMode,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]
        [Alias('FullName', 'Path')]
        $DifferencePath,

        [Parameter()]
        [Switch]
        $IncludeEqual
    )

    process {
        foreach ($ModeItem in $DifferenceMode)
        {
            Write-Verbose -Message "Comparing '$ReferenceMode' with '$ModeItem'"

            $diffOwner = $ReferenceMode.OwnerMode -bxor $ModeItem.OwnerMode
            $diffGroup = $ReferenceMode.GroupMode -bxor $ModeItem.GroupMode
            $diffOthers = $ReferenceMode.OthersMode -bxor $ModeItem.OthersMode
            $diffSpecialModeFlags = $ReferenceMode.SpecialModeFlags -bxor $ModeItem.SpecialModeFlags

            foreach ($enumValue in ([Enum]::GetValues([nxFileSystemAccessRight]).Where({$_ -ne [nxFileSystemAccessRight]::None})))
            {
                if ($diffOwner -band $enumValue)
                {
                    $sideIndicator = $ReferenceMode.OwnerMode -band $enumValue ? '<=' : '=>'
                    Write-Verbose -Message "[$([nxFileSystemUserClass]::User)]'$enumValue' is only on this side [REF '$sideIndicator' DIFF]."
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::User
                        InputObject          = $enumValue
                        SideIndicator        = $sideIndicator
                        DifferencePath       = $DifferencePath
                    } | Add-Member -PassThru -Name RemediationOperation -MemberType ScriptProperty -Value {$this | Convert-nxFileSystemModeComparisonToSymbolicOperation}
                }
                elseif ($IncludeEqual)
                {
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::User
                        InputObject          = $enumValue
                        SideIndicator        = '='
                        RemediationOperation = ''
                        DifferencePath       = $DifferencePath
                    }
                }

                if ($diffGroup -band $enumValue)
                {
                    $sideIndicator = $ReferenceMode.GroupMode -band $enumValue ? '<=' : '=>'
                    Write-Verbose -Message "[$([nxFileSystemUserClass]::Group)]'$enumValue' is only on this side [REF '$sideIndicator' DIFF]."
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::Group
                        InputObject          = $enumValue
                        SideIndicator        = $sideIndicator
                        DifferencePath       = $DifferencePath
                    } | Add-Member -PassThru -Name RemediationOperation -MemberType ScriptProperty -Value {$this | Convert-nxFileSystemModeComparisonToSymbolicOperation}
                }
                elseif ($IncludeEqual)
                {
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::Group
                        InputObject          = $enumValue
                        SideIndicator        = '='
                        RemediationOperation = ''
                        DifferencePath       = $DifferencePath
                    }
                }

                if ($diffOthers -band $enumValue)
                {
                    $sideIndicator = $ReferenceMode.OthersMode -band $enumValue ? '<=' : '=>'
                    Write-Verbose -Message "[$([nxFileSystemUserClass]::Others)]'$enumValue' is only on this side [REF '$sideIndicator' DIFF]."
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::Others
                        InputObject          = $enumValue
                        SideIndicator        = $sideIndicator
                        DifferencePath       = $DifferencePath
                    } | Add-Member -PassThru -Name RemediationOperation -MemberType ScriptProperty -Value {$this | Convert-nxFileSystemModeComparisonToSymbolicOperation}
                }
                elseif ($IncludeEqual)
                {
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::Others
                        InputObject          = $enumValue
                        SideIndicator        = '='
                        RemediationOperation = ''
                        DifferencePath       = $DifferencePath
                    }
                }
            }

            foreach ($enumValue in ([Enum]::GetValues([nxFileSystemSpecialMode])))
            {
                if ($diffSpecialModeFlags -band $enumValue)
                {
                    $sideIndicator = $ReferenceMode.SpecialModeFlags -band $enumValue ? '<=' : '=>'
                    Write-Verbose -Message "[$([nxFileSystemUserClass]::None)]'$enumValue' is only on this side [REF '$sideIndicator' DIFF]."
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::None
                        InputObject          = $enumValue
                        SideIndicator        = $sideIndicator
                        DifferencePath       = $DifferencePath
                    } | Add-Member -PassThru -Name RemediationOperation -MemberType ScriptProperty -Value {$this | Convert-nxFileSystemModeComparisonToSymbolicOperation}
                }
                elseif ($IncludeEqual)
                {
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::None
                        InputObject          = $enumValue
                        SideIndicator        = '='
                        RemediationOperation = ''
                        DifferencePath       = $DifferencePath
                    }
                }
            }
        }
    }
}
#EndRegion './Public/FileSystem/Compare-nxMode.ps1' 132
#Region './Public/FileSystem/Get-nxChildItem.ps1' 0
function Get-nxChildItem
{
    [CmdletBinding(DefaultParameterSetName = 'default')]
    [OutputType([nxFileSystemInfo[]])]
    param
    (
        [Parameter(ParameterSetName = 'default'         , Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(ParameterSetName = 'FilterDirectory' , Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(ParameterSetName = 'FilterFile'      , Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String[]]
        $Path = '.',

        [Parameter(ParameterSetName = 'default'         , Position = 1, ValueFromPipelineByPropertyName = $true)]
        [Parameter(ParameterSetName = 'FilterDirectory' , Position = 1, ValueFromPipelineByPropertyName = $true)]
        [Parameter(ParameterSetName = 'FilterFile'      , Position = 1, ValueFromPipelineByPropertyName = $true)]
        [Switch]
        $Recurse,

        [Parameter(ParameterSetName = 'FilterDirectory' , ValueFromPipelineByPropertyName = $true, Position = 2)]
        [Switch]
        $Directory,

        [Parameter(ParameterSetName = 'FilterFile'      , ValueFromPipelineByPropertyName = $true, Position = 2)]
        [Switch]
        $File
    )

    begin
    {
        $verbose   = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters.Verbose) -or $VerbosePreference -ne 'SilentlyContinue'
        $debug     = ($PSBoundParameters.ContainsKey('Debug') -and $PSBoundParameters['Debug']) -or $DebugPreference -ne 'SilentlyContinue'
        $lsParams  = @('-Al','--full-time','--group-directories-first')

        if ($PSBoundParameters.ContainsKey('Recurse') -and $PSboundParameters['Recurse'])
        {
            $lsParams += '-R' # Alpine linux does not support --recursive
        }
    }

    process
    {
        foreach ($pathItem in $Path.Where{$_})
        {
            $pathItem = [System.IO.Path]::GetFullPath($pathItem, $PWD.Path)
            $unfilteredListCommand = {
                Invoke-NativeCommand -Executable 'ls' -Parameters ($lsParams + @($pathItem)) -Verbose:($verbose -or $debug) |
                    Convert-nxLsEntryToFileSystemInfo -InitialPath $pathItem -Verbose:$debug
            }

            if ($PSCmdlet.ParameterSetName -eq 'FilterFile' -and $PSBoundParameters['File'])
            {
                &$unfilteredListCommand | Where-Object -FilterScript { $_.nxFileSystemItemType -eq [nxFileSystemItemType]::File }
            }
            elseif ($PSCmdlet.ParameterSetName -eq 'FilterDirectory' -and $PSBoundParameters['Directory'])
            {
                &$unfilteredListCommand | Where-Object -FilterScript { $_.nxFileSystemItemType -eq [nxFileSystemItemType]::Directory }
            }
            else
            {
                &$unfilteredListCommand
            }
        }
    }
}
#EndRegion './Public/FileSystem/Get-nxChildItem.ps1' 65
#Region './Public/FileSystem/Get-nxItem.ps1' 0
function Get-nxItem
{
    [CmdletBinding()]
    [OutputType([nxFileSystemInfo])]
    param
    (
        [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        [System.String[]]
        $Path = '.'
    )

    begin
    {
        $verbose   = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose']) -or $VerbosePreference -ne 'SilentlyContinue'
        $debug     = ($PSBoundParameters.ContainsKey('Debug') -and $PSBoundParameters['Debug']) -or $DebugPreference -ne 'SilentlyContinue'
        $lsParams  = @('-Al','--full-time','--group-directories-first','-d')
    }

    process
    {
        foreach ($pathItem in $Path.Where{$_})
        {
            $pathItem = [System.IO.Path]::GetFullPath($pathItem, $PWD.Path)
            Invoke-NativeCommand -Executable 'ls' -Parameters ($lsParams + @($pathItem)) -Verbose:($verbose -or $debug) |
                Convert-nxLsEntryToFileSystemInfo -InitialPath $pathItem -Verbose:$debug
        }
    }
}
#EndRegion './Public/FileSystem/Get-nxItem.ps1' 29
#Region './Public/FileSystem/Set-nxGroupOwnership.ps1' 0
function Set-nxGroupOwnership
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Default')]
    [OutputType([void])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default', Position = 0)]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll', Position = 0)]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath', Position = 0)]
        [System.String[]]
        $Path,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default', Position = 1)]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll', Position = 1)]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath', Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Group,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # affect each symbolic link instead of any referenced file (useful only on systems that can change the ownership of a symlink)
        # -h
        $NoDereference,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # Do not traverse any symbolic links by default
        $Recurse,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [System.Management.Automation.SwitchParameter]
        # Traverse every symbolic link to a directory encountered
        # -L
        $RecursivelyTraverseSymLink,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # If $Path is a symbolic link to a directory, traverse it.
        # -H
        $OnlyTraversePathIfSymLink,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # Disable root preservation security.
        $Force
    )

    begin {
        $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters.Verbose) -or $VerbosePreference -ne 'SilentlyContinue'
    }

    process {
        foreach ($pathItem in $Path)
        {
            $pathItem = [System.Io.Path]::GetFullPath($pathItem, $PWD.Path)

            $chgrpParams = @()

            if ($PSBoundParameters.ContainsKey('NoDereference') -and $PSBoundParameters['NoDereference'])
            {
                $chgrpParams += '-h'
            }

            if ($PSBoundParameters.ContainsKey('RecursivelyTraverseSymLink') -and $PSBoundParameters['RecursivelyTraverseSymLink'])
            {
                $chgrpParams += '-L'
            }

            if ($PSBoundParameters.ContainsKey('OnlyTraversePathIfSymLink') -and $PSBoundParameters['OnlyTraversePathIfSymLink'])
            {
                $chgrpParams += '-H'
            }

            if ($PSBoundParameters.ContainsKey('Recurse') -and $PSBoundParameters['Recurse'])
            {
                $chgrpParams += '-R'
            }

            $chgrpParams = ($chgrpParams + @($Group, $pathItem))

            if (
                $PSCmdlet.ShouldProcess("Performing the unix command 'chgrp $($chgrpParams -join ' ')'.", $PathItem, "chgrp $($chgrpParams -join ' ')")
            )
            {
                if ($pathItem -eq '/' -and -not ($PSBoundParameters.ContainsKey('Force') -and $Force))
                {
                    # can't use the built-in --preserve-root because it's not available on Alpine linux
                    Write-Warning "You are about to chgrp your root. Please use -Force."
                    return
                }

                Write-Verbose -Message ('chgrp {0}' -f ($chgrpParams -join ' '))
                Invoke-NativeCommand -Executable 'chgrp' -Parameters $chgrpParams -Verbose:$verbose -ErrorAction 'Stop' | Foreach-Object -Process {
                    Write-Error -Message $_
                }
            }
        }
    }
}
#EndRegion './Public/FileSystem/Set-nxGroupOwnership.ps1' 107
#Region './Public/FileSystem/Set-nxMode.ps1' 0
function Set-nxMode
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Default')]
    [OutputType([void])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default', Position = 0)]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll', Position = 0)]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath', Position = 0)]
        [System.String[]]
        $Path,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default', Position = 1)]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll', Position = 1)]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath', Position = 1)]
        [nxFileSystemMode]
        $Mode,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # affect each symbolic link instead of any referenced file (useful only on systems that can change the ownership of a symlink)
        # -h
        $NoDereference,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # Do not traverse any symbolic links by default
        $Recurse,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [System.Management.Automation.SwitchParameter]
        # Traverse every symbolic link to a directory encountered
        # -L
        $RecursivelyTraverseSymLink,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # If $Path is a symbolic link to a directory, traverse it.
        # -H
        $OnlyTraversePathIfSymLink,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # Disable root preservation security.
        $Force
    )

    begin {
        $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters.Verbose) -or $VerbosePreference -ne 'SilentlyContinue'
    }

    process {
        foreach ($pathItem in $Path)
        {
            $pathItem = [System.Io.Path]::GetFullPath($pathItem, $PWD.Path)

            $chmodParams = @()

            if ($PSBoundParameters.ContainsKey('NoDereference') -and $PSBoundParameters['NoDereference'])
            {
                $chmodParams += '-h'
            }

            if ($PSBoundParameters.ContainsKey('RecursivelyTraverseSymLink') -and $PSBoundParameters['RecursivelyTraverseSymLink'])
            {
                $chmodParams += '-L'
            }

            if ($PSBoundParameters.ContainsKey('OnlyTraversePathIfSymLink') -and $PSBoundParameters['OnlyTraversePathIfSymLink'])
            {
                $chmodParams += '-H'
            }

            if ($PSBoundParameters.ContainsKey('Recurse') -and $PSBoundParameters['Recurse'])
            {
                $chmodParams += '-R'
            }

            $OctalMode = $Mode.ToOctal()
            $chmodParams = ($chmodParams + @($OctalMode, $pathItem))

            Write-Debug "Parameter Set Name: '$($PSCmdlet.ParameterSetName)'."

            if (
                $PSCmdlet.ShouldProcess("Performing the unix command 'chmod $($chmodParams -join ' ')'.", $PathItem, "chmod $($chmodParams -join ' ')")
            )
            {
                if ($pathItem -eq '/' -and -not ($PSBoundParameters.ContainsKey('Force') -and $Force))
                {
                    # can't use the built-in --preserve-root because it's not available on Alpine linux
                    Write-Warning "You are about to chmod your root. Please use -Force."
                    return
                }

                Write-Verbose -Message ('chmod {0}' -f ($chmodParams -join ' '))
                Invoke-NativeCommand -Executable 'chmod' -Parameters $chmodParams -Verbose:$verbose -ErrorAction 'Stop'  | Foreach-Object -Process {
                    Write-Error -Message $_
                }
            }
        }
    }
}
#EndRegion './Public/FileSystem/Set-nxMode.ps1' 109
#Region './Public/FileSystem/Set-nxOwner.ps1' 0
function Set-nxOwner
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Default')]
    [OutputType([void])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.String[]]
        $Path,

        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [ValidateNotNullOrEmpty()]
        [System.String]
        [Alias('UserName')]
        $Owner,

        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [ValidateNotNullOrEmpty()]
        [string]
        $Group,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # affect each symbolic link instead of any referenced file (useful only on systems that can change the ownership of a symlink)
        # -h
        $NoDereference,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # Do not traverse any symbolic links by default
        $Recurse,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [System.Management.Automation.SwitchParameter]
        # Traverse every symbolic link to a directory encountered
        # -L
        $RecursivelyTraverseSymLink,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # If $Path is a symbolic link to a directory, traverse it.
        # -H
        $OnlyTraversePathIfSymLink,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursiveAll')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'RecursivePath')]
        [System.Management.Automation.SwitchParameter]
        # Disable root preservation security.
        $Force
    )

    begin {
        $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters.Verbose) -or $VerbosePreference -ne 'SilentlyContinue'
    }

    process {
        foreach ($pathItem in $Path)
        {
            $pathItem = [System.Io.Path]::GetFullPath($pathItem, $PWD.Path)

            $chownParams = @()

            if ($PSBoundParameters.ContainsKey('NoDereference') -and $PSBoundParameters['NoDereference'])
            {
                $chownParams += '-h'
            }

            if ($PSBoundParameters.ContainsKey('RecursivelyTraverseSymLink') -and $PSBoundParameters['RecursivelyTraverseSymLink'])
            {
                $chownParams += '-L'
            }

            if ($PSBoundParameters.ContainsKey('OnlyTraversePathIfSymLink') -and $PSBoundParameters['OnlyTraversePathIfSymLink'])
            {
                $chownParams += '-H'
            }

            if ($PSBoundParameters.ContainsKey('Recurse') -and $PSBoundParameters['Recurse'])
            {
                $chownParams += '-R'
            }

            if ($PSBoundParameters.ContainsKey('Group'))
            {
                $Owner = '{0}:{1}' -f $Owner,$Group
            }

            $chownParams = ($chownParams + @($Owner, $pathItem))

            if (
                $PSCmdlet.ShouldProcess("Performing the unix command 'chown $($chownParams -join ' ')'.", $PathItem, "chown $($chownParams -join ' ')")
            )
            {
                if ($pathItem -eq '/' -and -not ($PSBoundParameters.ContainsKey('Force') -and $Force))
                {
                    # can't use the built-in --preserve-root because it's not available on Alpine linux
                    Write-Warning "You are about to chown your root. Please use -Force."
                    return
                }

                Write-Verbose -Message ('chown {0}' -f ($chownParams -join ' '))
                Invoke-NativeCommand -Executable 'chown' -Parameters $chownParams -Verbose:$verbose -ErrorAction 'Stop' | Foreach-Object -Process {
                    Write-Error -Message $_
                }
            }
        }
    }
}
#EndRegion './Public/FileSystem/Set-nxOwner.ps1' 120
#Region './Public/Archive/Compress-nxArchive.ps1' 0
function Compress-nxArchive
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String[]]
        [Alias('FullName')]
        $Path,

        [Parameter(Mandatory = $true)]
        [String]
        $Destination,

        [Parameter()]
        [nxArchiveAlgorithm[]]
        $Compression = 'Auto',

        [Parameter()]
        [String[]]
        $Exclude,

        [Parameter()]
        [switch]
        $FollowSymLinks,

        [Parameter()]
        [Switch]
        $Force
    )

    begin
    {
        $verbose = $VerbosePreference -ne 'SilentlyContinue' -or ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose'])

        $tarVerbose = ''
        if ($verbose)
        {
            $tarVerbose = 'v'
        }

        $tarParams = @('-c{0}' -f $tarVerbose)

        $compressWith = @()

        switch ($Compression)
        {
            'auto'
            {
                Write-Debug -Message 'Skipping algo. Letting Tar discover using the file extension.'
                break
            }

            'bzip2'
            {
                $compressWith += @('j')
            }

            'xz'
            {
                $compressWith += @('J')
            }

            'lzma'
            {
                $compressWith += @('a')
            }

            'gzip'
            {
                $compressWith += @('z')
            }
        }

        if ($compressWith.Count -gt 0)
        {
            $tarParams += @(('-{0}' -f ($compressWith -join '')))
        }
        else
        {
            Write-Debug -Message "Auto compression detection for '$Destination'."
        }

        if ($PSBoundParameters.ContainsKey('Destination'))
        {
            $tarParams += @('-f', (Get-nxEscapedString -String $Destination))
            $destinationParent = Split-Path -Parent -Path ([io.Path]::GetFullPath($Destination))
            if ($Force.IsPresent -and -not (Test-Path -Path $destinationParent))
            {
                $null = New-Item -Path $destinationParent -Force
            }
        }

        if ($FollowSymLinks.IsPresent)
        {
            $tarParams += @('-h')
        }

        foreach ($excludePattern in $Exclude)
        {
            $tarParams += @('--exclude', (Get-nxEscapedString -String $excludePattern))
        }
    }

    process
    {
        foreach ($PathItem in $Path)
        {
            Write-Debug -Message "Preparing to compress $PathItem..."
            $tarParams += @($PathItem)
        }
    }

    end
    {
        if ($PSCmdlet.ShouldProcess(
            "Compressing using the unix command 'tar $($tarParams -join ' ')'.",
            $UserNameItem,
            "Compressing [$($Path -join ',')] to '$Destination'.")
        )
        {
            Invoke-NativeCommand -Executable 'tar' -Parameters $tarParams -Verbose:$verbose |
                ForEach-Object -Process {
                    if ($_ -match '^tar:')
                    {
                        Write-Error $_
                    }
                    else
                    {
                        Write-Verbose -Message $_
                    }
                }

            $destinationFullName = [io.Path]::GetFullPath($Destination)
            if (Test-Path -Path $destinationFullName)
            {
                $destinationFullName
            }
        }
    }
}
#EndRegion './Public/Archive/Compress-nxArchive.ps1' 143
#Region './Public/Archive/Expand-nxArchive.ps1' 0
function Expand-nxArchive
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String[]]
        $Path,

        [Parameter()]
        [String]
        [ValidateNotNullOrEmpty()]
        [Alias('ExtractTo')]
        $Destination,

        [Parameter()]
        [nxArchiveAlgorithm[]]
        $Compression = 'Auto',

        [Parameter()]
        [Switch]
        $ListOnly,

        [Parameter()]
        [Switch]
        $Force
    )

    begin
    {
        $verbose = $VerbosePreference -ne 'SilentlyContinue' -or ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose'])
        $tarParams = @()

        $tarVerbose = ''
        if ($verbose)
        {
            $tarVerbose = 'v'
        }

        if ($ListOnly.IsPresent)
        {
            $tarParams += @('-t{0}' -f $tarVerbose)
        }
        else
        {
            $tarParams += @('-x{0}' -f $tarVerbose)
        }

        switch ($Compression)
        {
            'auto'
            {
                Write-Debug -Message 'Skipping algo. Letting Tar discover using the file extension.'
                break
            }

            'bzip2'
            {
                $compressWith += @('j')
            }

            'xz'
            {
                $compressWith += @('J')
            }

            'lzma'
            {
                $compressWith += @('a')
            }

            'gzip'
            {
                $compressWith += @('z')
            }
        }

        if ($decompressWith.Count -gt 0)
        {
            $tarParams += @(('-{0}' -f ($decompressWith -join '')))
        }
        else
        {
            Write-Debug -Message "Auto compression detection for $Destination."
        }

        if ($PSBoundParameters.ContainsKey('Destination'))
        {
            $tarParams += @('-C', $Destination)
            if ($Force.IsPresent -and -not (Test-Path -Path $Destination))
            {
                $null = New-Item -Path $Destination -ItemType Directory -Force
            }
        }
    }

    process
    {

        foreach ($pathItem in $Path)
        {
            $tarParams += @('-f', (Get-nxEscapedPath -Path $pathItem))

            if ($PSCmdlet.ShouldProcess(
                "Extracting using the unix command 'tar $($tarParams -join ' ')'.",
                $pathItem,
                "Extracting '$pathItem' to '$Destination'.")
            )
            {
                Invoke-NativeCommand -Executable 'tar' -Parameters $tarParams -Verbose:$verbose |
                    ForEach-Object -Process {
                        if ($_ -match '^tar:')
                        {
                            Write-Error $_
                        }
                        else
                        {
                            if ($_ -is [String] -and $ListOnly.IsPresent)
                            {
                                $_
                            }
                            else
                            {
                                Write-Verbose -Message $_
                            }
                        }
                    }

                $destinationFullName = [io.Path]::GetFullPath($Destination)
                if (Test-Path -Path $destinationFullName)
                {
                    $destinationFullName
                }
            }
        }
    }
}
#EndRegion './Public/Archive/Expand-nxArchive.ps1' 139
#Region './Public/Packages/Get-nxPackage.ps1' 0

function Get-nxPackage
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [String[]]
        $Name,

        [Parameter()]
        [nxSupportedPackageType[]]
        $PackageType = (Get-nxSupportedPackageType)
    )

    # Work out the $PackageType priority
    # for GET prefer in order: dpkg, dnf, yum, apt, zapper, snap
    $PackageTypeStrings = [string[]]($PackageType.Foreach({$_.ToString()}))
    $packageTypeToUseInPriority = @('dpkg', 'dnf', 'yum', 'apt', 'zapper', 'snap').Where{$_ -in $PackageTypeStrings} | Select-Object -First 1
    Write-Debug -Message "The package type to use in priority to list packages is '$packageTypeToUseInPriority'."

    switch ($packageTypeToUseInPriority)
    {
        'dpkg' { Get-nxDpkgPackage -Name $Name -ErrorAction Ignore }
        'yum'  { Get-nxYumPackage -Name $Name -ErrorAction Ignore }

        default
        {
            throw ('The Package type {0} is not yet supported with ''Get-nxPackage''.' -f $packageTypeToUseInPriority)
        }
    }
}
#EndRegion './Public/Packages/Get-nxPackage.ps1' 33
#Region './Public/Packages/Get-nxPackageInstalled.ps1' 0

function Get-nxPackageInstalled
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [String[]]
        $Name,

        [Parameter()]
        [nxSupportedPackageType[]]
        $PackageType = (Get-nxSupportedPackageType)
    )

    # Work out the $PackageType priority
    # for GET prefer in order: dpkg, dnf, yum, apt, zapper, snap
    $PackageTypeStrings = [string[]]($PackageType.Foreach({$_.ToString()}))
    $packageTypeToUseInPriority = @('dpkg', 'dnf', 'yum', 'apt', 'zapper', 'snap').Where{$_ -in $PackageTypeStrings} | Select-Object -First 1
    Write-Debug -Message "The package type to use in priority to list packages is '$packageTypeToUseInPriority'."

    switch ($packageTypeToUseInPriority)
    {
        'dpkg' { Get-nxDpkgPackageInstalled -Name $Name -ErrorAction Ignore }
        'yum'  { Get-nxYumPackageInstalled -Name $Name -ErrorAction Ignore }

        default
        {
            throw ('The Package type {0} is not yet supported with ''Get-nxPackage''.' -f $packageTypeToUseInPriority)
        }
    }
}
#EndRegion './Public/Packages/Get-nxPackageInstalled.ps1' 33
#Region './Public/Packages/Get-nxSupportedPackageType.ps1' 0

function Get-nxSupportedPackageType
{
    [CmdletBinding()]
    [OutputType([string[]])]
    param
    (
        [Parameter()]
        [nxSupportedPackageType[]]
        $PackageType = [Enum]::GetNames([nxSupportedPackageType])
    )

    $packageUtilFound = Get-Command -Name @($PackageType.Foreach({$_.ToString()})) -ErrorAction Ignore

    return $packageUtilFound.Name
}
#EndRegion './Public/Packages/Get-nxSupportedPackageType.ps1' 17
#Region './Public/Packages/dpkg/Get-nxDpkgPackage.ps1' 0

function Get-nxDpkgPackage
{
    [CmdletBinding(DefaultParameterSetName = 'dpkgInstalledPackage')]
    param
    (
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'dpkgInstalledPackage', Position = 0)]
        [Alias('Package')]
        [string[]]
        $Name,

        [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'dpkgFile', Position = 0)]
        $Path
    )

    process
    {

        switch ($PSCmdlet.ParameterSetName)
        {
            'dpkgInstalledPackage' {
                $PackageToParse = { Get-nxDpkgPackageInstalled -Name $Name | Where-Object {$null -ne $_.Version} }
            }

            'dpkgFile'  {
                $PackageToParse = { Get-Item -Path $Path }
            }
        }

        &$PackageToParse | ForEach-Object {
            if ($_ -is [System.IO.FileInfo])
            {
                # dpkg --info ./localpackage.deb for getting info of non-installed package
                $dpkgParams = @('--info', $_.FullName)
            }
            else
            {
                # dpkg --status packageName for having details of the installed package
                $dpkgParams = @('--status',$_.Name)
            }

            Write-Verbose "Fetching details for '$($_.Name)'"
            $getPropertyHashFromListOutputParams = @{
                AllowedPropertyName     = ([nxDpkgPackage].GetProperties().Name)
                AddExtraPropertiesAsKey = 'AdditionalFields'
                ErrorVariable           = 'packageError'
            }

            $properties = Invoke-NativeCommand -Executable 'dpkg' -Parameters $dpkgParams |
                Get-PropertyHashFromListOutput @getPropertyHashFromListOutputParams

            # Making sure we replicate the package property to Name property
            # To correctly make the Base object (Package class)
            #TODO: This should probably go in the nxDpkgPackage class constructors
            $properties['PackageType'] = 'dpkg'
            $properties.add('Name', $properties['Package'])

            if (-not $packageError)
            {
                [nxDpkgPackage]$properties
            }
        }
    }
}
#EndRegion './Public/Packages/dpkg/Get-nxDpkgPackage.ps1' 65
#Region './Public/Packages/dpkg/Get-nxDpkgPackageInstalled.ps1' 0
function Get-nxDpkgPackageInstalled
{
    [CmdletBinding()]
    param
    (
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('Package')]
        [string[]]
        $Name
    )

    process
    {
        # Debian policy says Package name must be lowercase, making the user a service by forcing ToLower()
        # https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-source
        $Name = $Name.ForEach({$_.ToLower()})

        Invoke-NativeCommand -Executable 'dpkg-query' -Parameters @('-W',($Name -join ' ')) |
            ForEach-Object -Process {
                if ($_ -is [System.Management.Automation.ErrorRecord])
                {
                    switch -Regex ($_)
                    {
                        # this Adds a way to process the error stream in a customized way.
                        # 'no\spackages\sfound' { throw "Package $($Name) not found." } # Use this if you wan to throw when this error is raised
                        default { Write-Error "$_." }
                    }
                }
                else
                {
                    $dpkgPackage = $_ -split "`t"
                    [PSCustomObject]@{
                        PSTypeName  = 'nxDpkgPackage.Installed'
                        Name        = $dpkgPackage[0]
                        Version     = $dpkgPackage[1]
                    }
                }
            }
    }
}
#EndRegion './Public/Packages/dpkg/Get-nxDpkgPackageInstalled.ps1' 41
#Region './Public/Packages/yum/Get-nxYumPackage.ps1' 0
function Get-nxYumPackage
{
    [CmdletBinding()]
    param
    (
        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [String[]]
        $Name
    )

    process
    {
        $getNxYumPackageInstalledParams = @{ }

        if ($PSBoundParameters.ContainsKey('Name'))
        {
            $getNxYumPackageInstalledParams['Name'] = $Name
            ErrorAction = 'Ignore'
        }

        Get-nxYumPackageInstalled @getNxYumPackageInstalledParams | ForEach-Object -Process {
            $yumInfoParams = @('info','-q', $_.Name)
            $oneObjectOutput = [System.Collections.ArrayList]::new()
            Invoke-NativeCommand -Executable 'yum' -Parameters $yumInfoParams -ErrorAction Ignore |
                Foreach-Object -Process {
                    switch -Regex ($_)
                    {
                        '^Available\sPackages'
                        {
                            Write-Verbose -Message $_
                            break
                        }

                        '^Installed\sPackages'
                        {
                            Write-Verbose -Message $_
                            break
                        }

                        '^$'
                        {
                            Write-Debug -Message "Empty line reached."
                            if ($oneObjectOutput.count -gt 0)
                            {
                                ,$oneObjectOutput.Clone()
                                $oneObjectOutput.Clear()
                            }
                        }

                        default
                        {
                            Write-Debug -Message "Adding line to object: $($_)"
                            $null = $oneObjectOutput.Add($_)
                        }
                    }
                } | ForEach-Object -Process {
                    $getPropertyHashFromListOutputParams = @{
                        AllowedPropertyName     = ([nxYumPackage].GetProperties().Name)
                        # AddExtraPropertiesAsKey = 'AdditionalFields'
                        ErrorVariable           = 'packageError'
                        Regex                   = '^(?<property>[\w][\w-\s]*):\s*(?<val>.*)'
                        DiscardExtraProperties = $true
                    }

                    $properties = $_.GetEnumerator() | Get-PropertyHashFromListOutput @getPropertyHashFromListOutputParams
                    $properties['PackageType'] = 'yum'

                    $properties['Description'] = ($properties['Description'] -split '\n').Foreach({
                        $_ -replace '^\s+\:'
                    }) -join "`n"

                    if (-not $packageError)
                    {
                        [nxYumPackage]$properties
                    }
                }
        }
    }
}
#EndRegion './Public/Packages/yum/Get-nxYumPackage.ps1' 81
#Region './Public/Packages/yum/Get-nxYumPackageInstalled.ps1' 0
function Get-nxYumPackageInstalled
{
    [CmdletBinding()]
    param
    (
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)]
        [Alias('Package')]
        [string[]]
        $Name
    )

    process
    {
        $Name = $Name.ForEach({$_.ToLower()})
        $yumParams = @('list','installed',($Name -join ' '),'--quiet')
        Write-Debug -Message "Running shell command: yum $($yumParams -join ' ')"

        Invoke-NativeCommand -Executable 'yum' -Parameters $yumParams -ErrorAction SilentlyContinue |
            ForEach-Object -Process {
                if ($_ -is [System.Management.Automation.ErrorRecord])
                {
                    switch -Regex ($_)
                    {
                        # this Adds a way to process the error stream in a customized way.
                        # 'no\spackages\sfound' { throw "Package $($Name) not found." } # Use this if you wan to throw when this error is raised
                        default { Write-Error "$_." }
                    }
                }
                else
                {
                    switch -Regex ($_)
                    {
                        '^Installed\sPackages'
                        {
                            Write-Verbose -Message $_
                            break
                        }

                        default
                        {
                            $yumPackage = $_ -split "\s+"
                            $packageName, $packageArch = $yumPackage[0] -split '\.'

                            [PSCustomObject]@{
                                PSTypeName  = 'nxYumPackage.Installed'
                                Name        = $packageName
                                Arch        = $packageArch
                                Version     = $yumPackage[1]
                                Vendor      = $yumPackage[2]
                            }
                        }
                    }
                }
            }
    }
}
#EndRegion './Public/Packages/yum/Get-nxYumPackageInstalled.ps1' 57
#Region './Public/System/Get-nxDistributionInfo.ps1' 0
function Get-nxDistributionInfo
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [System.String[]]
        $InfoFilePath = '/etc/*-release'
    )

    $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters.Verbose) -or $VerbosePreference -ne 'SilentlyContinue'

    $InfoFilePath = [string[]](Get-Item $InfoFilePath -ErrorAction Stop -Verbose:$Verbose)
    Write-Verbose -Message "Extracting distro info from '$($InfoFilePath -join "', '")'"

    $properties = Get-Content -Path $InfoFilePath |
        Get-PropertyHashFromListOutput -Regex '^\s*(?<property>[\w-\s]*)=\s*"?(?<val>.*)\b'

    [PSCustomObject]$properties | Add-Member -TypeName 'nx.DistributionInfo' -PassThru
}
#EndRegion './Public/System/Get-nxDistributionInfo.ps1' 21
#Region './Public/System/Get-nxKernelInfo.ps1' 0
function Get-nxKernelInfo
{
    [CmdletBinding()]
    param
    (

    )

    $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters.Verbose) -or $VerbosePreference -ne 'SilentlyContinue'

    $unameOutput = Invoke-NativeCommand -Executable 'uname' -Parameters @(
        # MacOS does not support long arguments
        '-s' # '--kernel-name',
        '-n' # '--nodename',
        '-r' # '--kernel-release',
        '-m' # '--machine',
        '-p' # '--processor',
        '-i' # '--hardware-platform',
        '-o' # '--operating-system'
    ) -Verbose:$verbose -ErrorAction 'Stop'

    if ($unameOutput -match '^\/.*uname:\s+')
    {
        throw $unameOutput
    }

    $kernelName, $ComputerName, $kernelRelease, $machineHardware, $processor, $hardwarePlatform, $OS = $unameOutput -split '\s'

    # uname --kernel-version
    $kernelVersion = Invoke-NativeCommand -Executable 'uname' -Parameters '-v' -Verbose:$verbose -ErrorAction 'Stop'

    [PSCustomObject]@{
        kernelName       = $kernelName
        ComputerName     = $ComputerName
        KernelRelease    = $kernelRelease
        KernelVersion    = $kernelVersion
        MachineHardware  = $machineHardware
        processor        = $processor
        hardwarePlatform = $hardwarePlatform
        OS               = $OS
    } | Add-Member -TypeName 'nx.KernelInfo' -PassThru
}
#EndRegion './Public/System/Get-nxKernelInfo.ps1' 43
#Region './Public/System/Get-nxLinuxStandardBaseRelease.ps1' 0
# By default on Debian 10, lsb-release package is not installed, so lsb_release
# gives a command not found.
function Get-nxLinuxStandardBaseRelease
{
    [OutputType([PSCustomObject])]
    [CmdletBinding()]
    param
    (
    )

    $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters.Verbose) -or $VerbosePreference -ne 'SilentlyContinue'

    $properties = Invoke-NativeCommand -Executable 'lsb_release' -Parameters '--all' -Verbose:$Verbose |
        Get-PropertyHashFromListOutput -ErrorHandling {
            switch -Regex ($_)
            {
                ''                 { }
                'No\sLSB\smodules' { Write-Verbose $_ }
                default            { Write-Error "$_" }
            }
        }

    [PSCustomObject]$properties | Add-Member -TypeName 'nx.LsbRelease' -PassThru
}

Set-Alias -Name Get-LsbRelease -Value Get-nxLinuxStandardBaseRelease
#EndRegion './Public/System/Get-nxLinuxStandardBaseRelease.ps1' 27
#Region './Public/usersAndGroups/Add-nxLocalGroupMember.ps1' 0
function Add-nxLocalGroupMember
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'medium')]
    [OutputType([void])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String]
        $GroupName,

        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String[]]
        [Alias('Member')]
        $UserName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Switch]
        $PassThru
    )

    begin
    {
        $verbose = $VerbosePreference -or ($PSBoundParameters.ContainsKey('verbose') -and $PSBoundParameters['verbose'])
        $hasGroupChanged = $false
    }

    process
    {
        foreach ($UserNameItem in $UserName)
        {
            $gpasswdParams = @('-a', $UserNameItem, $GroupName)

            if ($PSCmdlet.ShouldProcess(
                "Performing the unix command 'gpasswd $($gpasswdParams -join ' ')'.",
                $UserNameItem,
                "Removing $userNameItem grom group '$GroupName'.")
            )
            {
                Invoke-NativeCommand -Executable 'gpasswd' -Parameters $gpasswdParams -Verbose:$verbose |
                    ForEach-Object -Process {
                        if ($_ -match '^gpasswd:')
                        {
                            throw $_
                        }
                        else
                        {
                            Write-Verbose -Message $_
                        }
                    }

                $hasGroupChanged = $true
            }
        }

        if ($hasGroupChanged -and $PassThru)
        {
            Get-nxLocalGroup -GroupName $GroupName
        }
    }
}
#EndRegion './Public/usersAndGroups/Add-nxLocalGroupMember.ps1' 61
#Region './Public/usersAndGroups/Add-nxLocalUserToGroup.ps1' 0
function Add-nxLocalUserToGroup
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'medium')]
    [OutputType([void])]
    param
    (

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [String]
        [ValidateNotNullOrEmpty()]
        $UserName,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [String[]]
        [ValidateNotNullOrEmpty()]
        $GroupName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [String]
        [ValidateNotNullOrEmpty()]
        $PrimaryGroupName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Switch]
        $PassThru
    )

    begin
    {
        $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose']) -or $VerbosePreference -ne 'SilentlyContinue'
    }

    process
    {
        $userModParams = @('-a', '-G')

        $userModParams += @($GroupName -join ',')


        if ($PSBoundParameters.ContainsKey('PrimaryGroupName'))
        {
            $userModParams += @('-g', $PrimaryGroupName)
        }

        $userModParams += @($UserName)

        if (
            $PScmdlet.ShouldProcess(
                "Performing the unix command 'usermod $(($userModParams -join ' '))'.",
                $UserName,
                "adding $userName to groups: '$($groupName -join ',')."
            )
        )
        {
            Invoke-NativeCommand -Executable 'usermod' -Parameters $userModParams -Verbose:$verbose -ErrorAction 'Stop' | ForEach-Object -Process {
                throw $_
            }

            if ($PSBoundParameters.ContainsKey('PassThru') -and $PSBoundParameters['PassThru'])
            {
                # return the created user
                Get-nxLocalUser -UserName $Username -ErrorAction Stop -Verbose:$verbose
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/Add-nxLocalUserToGroup.ps1' 67
#Region './Public/usersAndGroups/Disable-nxLocalUser.ps1' 0
function Disable-nxLocalUser
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    [outputType([nxLocalUser])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
        [System.String[]]
        $UserName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]
        $LockOnly,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]
        $SkipNologinShell,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]
        $DoNotExpireAccount,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]
        $PassThru
    )

    begin
    {
        $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose']) -or $VerbosePreference -ne 'SilentlyContinue'
    }

    process
    {
        foreach ($UserNameItem in $UserName)
        {
            $usermodParams = @()

            # at the very least, we lock the account (does not impact ssh pub keys or PAM except pam_unix)
            $usermodParams += @('-L')

            if (-not $SkipNologinShell)
            {
                $usermodParams += @('-s','/sbin/nologin')
            }

            $usermodParams += @($UserNameItem)

            if (-not $LockOnly.IsPresent -and -not $DoNotExpireAccount.IsPresent)
            {
                $chageParams = @('-E0',$UserNameItem)
                $ShouldProcessMessage = "Disabling account '$UserNameItem': 'usermod $(($usermodParams -join ' ')) && chage $(($chageParams -join ' '))'."
            }
            else
            {
                $ShouldProcessMessage = "Locking account '$UserNameItem': 'usermod $(($usermodParams -join ' '))'."
            }

            if ($PSCmdlet.ShouldProcess(
                    $ShouldProcessMessage,
                    "$UserNameItem",
                    "Disabling account '$UserNameItem'."
                )
            )
            {

                Invoke-NativeCommand -Executable 'usermod' -Parameters $usermodParams -Verbose:$verbose -ErrorAction 'Stop' |
                    ForEach-Object -Process {
                        throw $_
                    }

                if (-not $LockOnly.IsPresent -and -not $DoNotExpireAccount.IsPresent)
                {
                    Invoke-NativeCommand -Executable 'chage' -Parameters $chageParams -Verbose:$verbose -ErrorAction 'Stop' |
                        ForEach-Object -Process {
                            throw $_
                        }
                }


                if ($PassThru.IsPresent)
                {
                    Get-nxLocalUser -UserName $UserName
                }
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/Disable-nxLocalUser.ps1' 89
#Region './Public/usersAndGroups/Enable-nxLocalUser.ps1' 0
function Enable-nxLocalUser
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
        [System.String[]]
        $UserName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateSet([ValidShell],ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]
        [String]
        $ShellCommand,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [datetime]
        $ExpireOn,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]
        $PassThru
    )

    begin
    {
        $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose']) -or $VerbosePreference -ne 'SilentlyContinue'
    }

    process
    {
        foreach ($UserNameItem in $UserName)
        {
            $usermodParams = @()

            # at the very least, we lock the account (does not impact ssh pub keys or PAM except pam_unix)
            $usermodParams += @('-U')

            if ($PSBoundParameters.ContainsKey('ShellCommand'))
            {
                $usermodParams += @('-s', $ShellCommand)
            }

            if ($PSBoundParameters.ContainsKey('ExpireOn') -and $PSBoundParameters['ExpireOn'])
            {
                $usermodParams += @('-e', $ExpireOn.ToString('yyyy-MM-dd'))
            }

            $usermodParams += @($UserNameItem)

            if ($PSCmdlet.ShouldProcess(
                    "Performing the unix command 'usermod $(($usermodParams -join ' '))'.",
                    "$UserNameItem",
                    "Enabling account '$UserNameItem'."
                )
            )
            {
                Invoke-NativeCommand -Executable 'usermod' -Parameters $usermodParams -Verbose:$verbose -ErrorAction 'Stop' |
                    ForEach-Object -Process {
                        throw $_
                    }

                if ($PassThru.IsPresent)
                {
                    Get-nxLocalUser -UserName $UserName
                }
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/Enable-nxLocalUser.ps1' 71
#Region './Public/usersAndGroups/Get-nxEtcShadow.ps1' 0
function Get-nxEtcShadow
{
    [CmdletBinding(DefaultParameterSetName = 'byUserName')]
    [outputType([nxEtcShadowEntry])]
    param
    (
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'byUserName', Position = 0)]
        [System.String[]]
        [Alias('GroupMember')]
        $UserName,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'byRegexPattern', Position = 0)]
        [regex]
        $Pattern
    )

    begin
    {
        $readEtcShadow = {
            Get-Content -Path '/etc/shadow' | ForEach-Object -Process {
                [nxEtcShadowEntry]$_
            }
        }
    }

    process
    {
        if ($PSCmdlet.ParameterSetName -eq 'byUserName' -and -not $PSBoundParameters.ContainsKey('UserName'))
        {
            Write-Debug -Message "[Get-nxEtcShadowEntry] Reading /etc/shadow without filter."
            &$readEtcShadow
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'byRegexPattern')
        {
            Write-Debug -Message "[Get-nxEtcShadowEntry] Matching 'UserName' with regex pattern '$Pattern'."
            &$readEtcShadow | Where-Object -FilterScript {
                $_.username -match $Pattern
            }
        }
        else
        {
            $allUsers = &$readEtcShadow
            foreach ($userNameEntry in $UserName)
            {
                Write-Debug -Message "[Get-nxEtcShadowEntry] Finding Local users by UserName '$userNameEntry'."
                $allUsers | Where-Object -FilterScript {
                    $_.username -eq $userNameEntry
                }
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/Get-nxEtcShadow.ps1' 53
#Region './Public/usersAndGroups/Get-nxLocalGroup.ps1' 0
function Get-nxLocalGroup
{
    [CmdletBinding(DefaultParameterSetName = 'byGroupName')]
    [OutputType()]
    param
    (
        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'byGroupName', Position = 0)]
        [System.String[]]
        [Alias('Group')]
        $GroupName,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'byRegexPattern', Position = 0)]
        [regex]
        $Pattern
    )

    begin
    {
        # by doing this, we prefer content accuracy than IO/Speed (the /etc/group may be read many times).
        $readEtcGroupCmd = {
            Get-Content -Path '/etc/group' | ForEach-Object -Process {
                [nxLocalGroup]$_
            }
        }
    }

    process
    {
        if ($PSCmdlet.ParameterSetName -eq 'byGroupName' -and -not $PSBoundParameters.ContainsKey('GroupName'))
        {
            Write-Debug -Message "[Get-nxLocalGroup] Reading /etc/group without filter."
            &$readEtcGroupCmd
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'byRegexPattern')
        {
            Write-Debug -Message "[Get-nxLocalGroup] Matching 'GroupName' with regex pattern '$Pattern'."
            &$readEtcGroupCmd | Where-Object -FilterScript {
                $_.GroupName -match $Pattern
            }
        }
        else
        {
            $allGroups = &$readEtcGroupCmd
            foreach ($GroupNameEntry in $GroupName)
            {
                Write-Debug -Message "[Get-nxLocalGroup] Finding Local group by GroupName '$GroupNameEntry'."
                $allGroups | Where-Object -FilterScript {
                    $_.Groupname -eq $GroupNameEntry
                }
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/Get-nxLocalGroup.ps1' 54
#Region './Public/usersAndGroups/Get-nxLocalUser.ps1' 0
function Get-nxLocalUser
{
    [CmdletBinding(DefaultParameterSetName = 'byUserName')]
    [OutputType()]
    param
    (
        [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, ParameterSetName = 'byUserName', Position = 0)]
        [System.String[]]
        [Alias('GroupMember')]
        $UserName,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'byRegexPattern', Position = 0)]
        [regex]
        $Pattern
    )

    begin
    {
        $readPasswdCmd = {
            Get-Content -Path '/etc/passwd' | ForEach-Object -Process {
                [nxLocalUser]$_
            }
        }
    }

    process
    {
        if ($PSCmdlet.ParameterSetName -eq 'byUserName' -and -not $PSBoundParameters.ContainsKey('UserName'))
        {
            Write-Debug -Message "[Get-nxLocalUser] Reading /etc/passwd without filter."
            &$readPasswdCmd
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'byRegexPattern')
        {
            Write-Debug -Message "[Get-nxLocalUser] Matching 'UserName' with regex pattern '$Pattern'."
            &$readPasswdCmd | Where-Object -FilterScript {
                $_.username -match $Pattern
            }
        }
        else
        {
            $allUsers = &$readPasswdCmd
            foreach ($userNameEntry in $UserName)
            {
                Write-Debug -Message "[Get-nxLocalUser] Finding Local users by UserName '$userNameEntry'."
                $allUsers | Where-Object -FilterScript {
                    $_.username -eq $userNameEntry
                }
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/Get-nxLocalUser.ps1' 53
#Region './Public/usersAndGroups/Get-nxLocalUserMemberOf.ps1' 0
function Get-nxLocalUserMemberOf
{
    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String[]]
        [Alias('UserName','UserId')]
        $User
    )

    process {
        foreach ($UserItem in $User)
        {
            [string] $UserName = ''
            if ($UserItem -match '^\d+$')
            {
                # by User ID
                $UserName = Get-nxLocalUser | Where-Object -FilterScript { $_.UserId -eq $UserItem }
            }
            else
            {
                # by User Name
                $UserName = $UserItem
            }

            $memberOf = (Invoke-NativeCommand -Executable 'id' -Parameters @('-G', '-n', $UserName) -ErrorAction 'Stop') -split '\s+' | Foreach-Object -Process {
                if ($_ -match '^id:\s')
                {
                    throw $_
                }
                else
                {
                    Get-nxLocalGroup -GroupName $_
                }
            }

            [PSCustomObject]@{
                PsTypeName = 'nx.LocalUser.MemberOf'
                User       = $UserName
                MemberOf   = $memberOf
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/Get-nxLocalUserMemberOf.ps1' 47
#Region './Public/usersAndGroups/New-nxLocalGroup.ps1' 0
function New-nxLocalGroup
{
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUsernameAndPasswordParams', '')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([void])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidatePattern('^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$')]
        [System.String]
        [Alias('Group','Name')]
        $GroupName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [System.String]
        [Alias('Password')]
        # The encrypted password, as returned by crypt(3).
        # Note: This option is not recommended because the password (or encrypted password) will be visible by users listing the processes.
        # You should make sure the password respects the system's password policy.
        $EncryptedPassword,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        # Overrides /etc/login.defs defaults (UID_MIN, UID_MAX, UMASK, PASS_MAX_DAYS and others).
        # Example: -K PASS_MAX_DAYS=-1 can be used when creating system account to turn off password ageing, even though system account has no password at all.
        # Multiple -K options can be specified, e.g.: -K UID_MIN=100 -K UID_MAX=499
        # Note: -K UID_MIN=10,UID_MAX=499 doesn't work yet.
        [hashtable]
        $LoginDefsOverride,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        # Allow the creation of a group with a duplicate (non-unique) GID.
        # This option is only valid in combination with the -preferredGID option.
        [switch]
        $AllowNonUniqueGID,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        # Create a system group.
        [switch]
        $SystemAccount,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        # -R
        # Directory to chroot into
        $ChrootDirectory,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        # Preferred GID if not already used (unless -AllowNonUniqueUID is used).
        [Int]
        [Alias('GroupID', 'GID')]
        $preferredGID,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]
        $Force,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]
        $PassThru
    )

    begin
    {
        $verbose = $PSBoundParameters.ContainsKey('verbose') -and $PSBoundParameters['Verbose']
    }

    process
    {
        if ([nxLocalGroup]::Exists($GroupName))
        {
            throw ("A group account named '{0}' is already present." -f $GroupName)
        }

        $groupAddParams = @()

        if ($PSBoundParameters.ContainsKey('Force') -and $PSBoundParameters['Force'])
        {
            $groupAddParams += @('-f')
        }

        if ($PSBoundParameters.ContainsKey('EncryptedPassword') -and $PSBoundParameters['EncryptedPassword'])
        {
            $groupAddParams += @('-p', $EncryptedPassword)
        }

        if ($PSBoundParameters.ContainsKey('AllowNonUniqueGID') -and $PSBoundParameters['AllowNonUniqueGID'])
        {
            $groupAddParams += '-o'
        }

        if ($PSBoundParameters.ContainsKey('SystemAccount') -and $PSBoundParameters['SystemAccount'])
        {
            $groupAddParams += '-r'
        }

        if ($PSBoundParameters.ContainsKey('ChrootDirectory') -and $PSBoundParameters['ChrootDirectory'])
        {
            $groupAddParams += @('-R', $ChrootDirectory)
        }

        if ($PSBoundParameters.ContainsKey('preferredGID') -and $PSBoundParameters['preferredGID'])
        {
            $groupAddParams += @('-g', $preferredGID)
        }

        # LoginDefsOverride
        if ($PSBoundParameters.ContainsKey('LoginDefsOverride') -and $PSBoundParameters['LoginDefsOverride'])
        {
            $LoginDefsOverride.Keys.ForEach({
                $groupAddParams += ('-K {0}={1}' -f $_, $LoginDefsOverride[$_])
            })
        }

        if ($PScmdlet.ShouldProcess("Performing the unix command 'groupadd $(($groupAddParams + @($GroupName)) -join ' ')'.", "$GroupName", "Adding LocalGroup to $(hostname)?") -or $Force.IsPresent)
        {
            Invoke-NativeCommand -Executable 'groupadd' -Parameter ($groupAddParams + @($GroupName)) -Verbose:$verbose -ErrorAction 'Stop' | Foreach-Object {
                throw $_
            }

            if ($PSBoundParameters.ContainsKey('PassThru') -and $PSBoundParameters['PassThru'])
            {
                # return the created group
                Get-nxLocalGroup -GroupName $GroupName -ErrorAction Stop
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/New-nxLocalGroup.ps1' 131
#Region './Public/usersAndGroups/New-nxLocalUser.ps1' 0
function New-nxLocalUser
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUsernameAndPasswordParams', '')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
    [OutputType([void])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidatePattern('^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$')]
        [System.String]
        $UserName,

        [Parameter()]
        [System.String]
        [Alias('Password')]
        # The encrypted password, as returned by crypt(3).
        # Note: This option is not recommended because the password (or encrypted password) will be visible by users listing the processes.
        # You should make sure the password respects the system's password policy.
        $EncryptedPassword,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        # Any text string. It is generally a short description of the login, and is currently used as the field for the user's full name.
        [System.String]
        [Alias('Comment')]
        $UserInfo,

        [Parameter()]
        # The new user will be created using HOME_DIR as the value for the user's login directory.
        # The default is to append the LOGIN name to BASE_DIR and use that as the login directory name.
        # The directory HOME_DIR does not have to exist but will not be created if it is missing.
        [ValidateNotNullOrEmpty()]
        [System.String]
        $HomeDirectory,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String]
        [Alias('shell')]
        # The name of the user's login shell.
        # The default is to leave this field blank, which causes the system to select the default login shell
        # specified by the SHELL variable in /etc/default/useradd, or an empty string by default.
        $ShellCommand,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [datetime]
        # The date on which the user account will be disabled. The date is specified in the format YYYY-MM-DD.
        # If not specified, useradd will use the default expiry date specified by the EXPIRE variable in /etc/default/useradd,
        # or an empty string (no expiry) by default.
        $ExpireOn,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        # The default base directory for the system if -d HOME_DIR is not specified. BASE_DIR is concatenated with the account name to define the home directory.
        # If the -m option is not used, BASE_DIR must exist.
        # If this option is not specified, useradd will use the base directory specified by the HOME variable in /etc/default/useradd, or /home by default.
        [System.String]
        $HomeDirectoryBase,

        [Parameter()]
        # The number of days after a password expires until the account is permanently disabled.
        # A value of 0 disables the account as soon as the password has expired,
        # and a value of -1 disables the feature.
        # If not specified, useradd will use the default inactivity period specified by the INACTIVE variable in /etc/default/useradd, or -1 by default.)
        [int]
        $DayPasswordExpiredBeforeAutoDisable,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        # The group name or number of the user's initial login group. The group name must exist.
        # A group number must refer to an already existing group.
        # If not specified, the bahavior of useradd will depend on the USERGROUPS_ENAB variable in /etc/login.defs.
        # If this variable is set to yes (or -U/--user-group is specified on the command line),
        # a group will be created for the user, with the same name as her loginname.
        # If the variable is set to no (or -N/--no-user-group is specified on the command line),
        # useradd will set the primary group of the new user to the value specified by the GROUP variable in /etc/default/useradd,
        # or 100 by default.
        [System.String]
        [Alias('GroupId')]
        $PrimaryGroup,

        [Parameter()]
        # A list of supplementary groups which the user is also a member of.
        # The groups are subject to the same restrictions as the group given with the -g option. The default is for the user to belong only to the initial group.
        [System.String[]]
        $SupplementaryGroup,

        [Parameter()]
        # Overrides /etc/login.defs defaults (UID_MIN, UID_MAX, UMASK, PASS_MAX_DAYS and others).
        # Example: -K PASS_MAX_DAYS=-1 can be used when creating system account to turn off password ageing, even though system account has no password at all.
        # Multiple -K options can be specified, e.g.: -K UID_MIN=100 -K UID_MAX=499
        # Note: -K UID_MIN=10,UID_MAX=499 doesn't work yet.
        [hashtable]
        $LoginDefsOverride,

        [Parameter()]
        # -M
        # Do not create the user's home directory, even if the system wide setting from /etc/login.defs (CREATE_HOME) is set to yes.
        [switch]
        $SkipCreateHomeDirectory,

        [Parameter()]
        [switch]
        # Do not add the user to the lastlog and faillog databases.
        # By default, the user's entries in the lastlog and faillog databases are resetted to avoid reusing the
        # entry from a previously deleted user.
        $NoLogInit,

        [Parameter()]
        # Do not create a group with the same name as the user, but add the user to the group specified
        # by the -g option or by the GROUP variable in /etc/default/useradd.
        # The default behavior (if the -g, -N, and -U options are not specified) is defined by the
        # USERGROUPS_ENAB variable in /etc/login.defs.
        [switch]
        $SkipCreateUserGroup,

        [Parameter()]
        # Allow the creation of a user account with a duplicate (non-unique) UID.
        # This option is only valid in combination with the -preferredUID option.
        [switch]
        $AllowNonUniqueUID,

        [Parameter()]
        # Create a system account.
        # System users will be created with no aging information in /etc/shadow,
        # and their numeric identifiers are choosen in the SYS_UID_MIN-SYS_UID_MAX range, defined in /etc/login.defs,
        # instead of UID_MIN-UID_MAX (and their GID counterparts for the creation of groups).
        # Note that useradd will not create a home directory for such an user,
        # regardless of the default setting in /etc/login.defs (CREATE_HOME).
        # You have to specify the -m options if you want a home directory for a system account to be created.
        [switch]
        $SystemAccount,

        [Parameter()]
        # The skeleton directory, which contains files and directories to be copied in the user's home directory,
        # when the home directory is created by useradd.
        # This option is only valid if the -m (or --create-home) option is specified.
        #
        # If this option is not set, the skeleton directory is defined by the SKEL variable in
        # /etc/default/useradd or, by default, /etc/skel.
        [String]
        $SkeletonDirectory,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String]
        # -R
        # Directory to chroot into
        $ChrootDirectory,

        [Parameter()]
        # Preferred UID if not already used (unless -AllowNonUniqueUID is used).
        [Int]
        [Alias('UserID', 'uid')]
        $preferredUID,

        [Parameter()]
        [switch]
        $PassThru
    )

    begin
    {
        $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose']) -or $VerbosePreference -ne 'SilentlyContinue'
    }

    process
    {
        if ([nxLocalUser]::Exists($UserName))
        {
            throw ("A user account for '{0}' is already present." -f $UserName)
        }

        $userAddParams = @()

        if ($PSBoundParameters.ContainsKey('EncryptedPassword') -and $PSBoundParameters['EncryptedPassword'])
        {
            $userAddParams += @('-p', $EncryptedPassword)
        }

        if ($PSBoundParameters.ContainsKey('ShellCommand') -and $PSBoundParameters['ShellCommand'])
        {
            $userAddParams += @('-s', (Get-nxEscapedPath -Path $ShellCommand))
        }

        if ($PSBoundParameters.ContainsKey('HomeDirectory') -and $PSBoundParameters['HomeDirectory'])
        {
            $userAddParams += @('-d', (Get-nxEscapedPath -Path $HomeDirectory))
        }

        if ($PSBoundParameters.ContainsKey('UserInfo') -and $PSBoundParameters['UserInfo'])
        {
            $userAddParams += @('-c', $UserInfo)
        }

        if ($PSBoundParameters.ContainsKey('ExpireOn') -and $PSBoundParameters['ExpireOn'])
        {
            $userAddParams += @('-e', $ExpireOn.ToString('yyyy-MM-dd'))
        }

        if ($PSBoundParameters.ContainsKey('HomeDirectoryBase') -and $PSBoundParameters['HomeDirectoryBase'])
        {
            $userAddParams += @('-b', (Get-nxEscapedPath -Path $HomeDirectoryBase))
        }

        if ($PSBoundParameters.ContainsKey('DayPasswordExpiredBeforeAutoDisable') -and $PSBoundParameters['DayPasswordExpiredBeforeAutoDisable'])
        {
            $userAddParams += @('-f', $DayPasswordExpiredBeforeAutoDisable)
        }

        if ($PSBoundParameters.ContainsKey('PrimaryGroup') -and $PSBoundParameters['PrimaryGroup'])
        {
            $userAddParams += @('-f', $PrimaryGroup)
        }

        if ($PSBoundParameters.ContainsKey('NoLogInit') -and $PSBoundParameters['NoLogInit'])
        {
            $userAddParams += '-l'
        }

        if ($PSBoundParameters.ContainsKey('SkipCreateHomeDirectory') -and $PSBoundParameters['SkipCreateHomeDirectory'])
        {
            $userAddParams += '-M'
        }

        if ($PSBoundParameters.ContainsKey('SkipCreateUserGroup') -and $PSBoundParameters['SkipCreateUserGroup'])
        {
            $userAddParams += '-N'
        }
        else
        {
            # --user-group create a group with the same name as the user (by default)
            $userAddParams += '-U'
        }

        if ($PSBoundParameters.ContainsKey('AllowNonUniqueUID') -and $PSBoundParameters['AllowNonUniqueUID'])
        {
            $userAddParams += '-o'
        }

        if ($PSBoundParameters.ContainsKey('SystemAccount') -and $PSBoundParameters['SystemAccount'])
        {
            $userAddParams += '-r'
        }

        if ($PSBoundParameters.ContainsKey('SkeletonDirectory') -and $PSBoundParameters['SkeletonDirectory'])
        {
            $userAddParams += @('-k', $SkeletonDirectory)
        }

        if ($PSBoundParameters.ContainsKey('ChrootDirectory') -and $PSBoundParameters['ChrootDirectory'])
        {
            $userAddParams += @('-R', $ChrootDirectory)
        }

        if ($PSBoundParameters.ContainsKey('SupplementaryGroup') -and $PSBoundParameters['SupplementaryGroup'])
        {
            $userAddParams += @('-G', ($SupplementaryGroup -join ','))
        }

        if ($PSBoundParameters.ContainsKey('preferredUID') -and $PSBoundParameters['preferredUID'])
        {
            $userAddParams += @('-u', $preferredUID)
        }

        # LoginDefsOverride
        if ($PSBoundParameters.ContainsKey('LoginDefsOverride') -and $PSBoundParameters['LoginDefsOverride'])
        {
            $LoginDefsOverride.Keys.ForEach({
                $userAddParams += ('-K {0}={1}' -f $_, $LoginDefsOverride[$_])
            })
        }

        if ($PScmdlet.ShouldProcess("Performing the unix command 'useradd $(($userAddParams + @($UserName)) -join ' ')'.", "$UserName", "Adding LocalUser to $(hostname)"))
        {
            Invoke-NativeCommand -Executable 'useradd' -Parameter ($userAddParams + @($UserName)) -Verbose:$verbose -ErrorAction 'Stop' | Foreach-Object {
                throw $_
            }

            if ($PSBoundParameters.ContainsKey('PassThru') -and $PSBoundParameters['PassThru'])
            {
                # return the created user
                Get-nxLocalUser -UserName $Username -ErrorAction Stop
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/New-nxLocalUser.ps1' 291
#Region './Public/usersAndGroups/Remove-nxLocalGroup.ps1' 0
function Remove-nxLocalGroup
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([void])]
    param
    (

        [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        [string[]]
        [Alias('Group')]
        $GroupName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]
        $Force
    )

    begin
    {
        $verbose = $PSBoundParameters.ContainsKey('verbose') -and $PSBoundParameters['Verbose']
        $groupDelParams += @()
    }

    process
    {
        if ($PSBoundParameters.ContainsKey('RemoveHomeDirAndMailSpool') -and $PSBoundParameters['RemoveHomeDirAndMailSpool'])
        {
            $groupDelParams += @('-r')
        }

        if ($PSBoundParameters.ContainsKey('Force') -and $PSBoundParameters['Force'])
        {
            $groupDelParams += @('-f')
        }

        foreach ($GroupNameItem in $GroupName)
        {
            if ($PScmdlet.ShouldProcess("Performing the unix command 'groupdel $(($groupDelParams + @($GroupNameItem)) -join ' ')'.", $GroupNameItem, "Removing local group '$GroupNameItem' from '$(hostname)'."))
            {
                Invoke-NativeCommand -Executable 'groupdel' -Parameter ($groupDelParams + @($GroupNameItem)) -Verbose:$verbose -ErrorAction 'Stop' | Foreach-Object {
                    throw $_
                }
            }
        }
    }




}
#EndRegion './Public/usersAndGroups/Remove-nxLocalGroup.ps1' 51
#Region './Public/usersAndGroups/Remove-nxLocalGroupMember.ps1' 0
function Remove-nxLocalGroupMember
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([void])]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [String[]]
        $UserName,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [String]
        $GroupName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Switch]
        $PassThru
    )

    begin
    {
        $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose']) -or $VerbosePreference -ne 'SilentlyContinue'
        $hasGroupChanged = $false
    }

    process
    {

        foreach ($UserNameItem in $UserName)
        {
            $gpasswdParams = @('-d', $UserNameItem, $GroupName)
            if ($PSCmdlet.ShouldProcess(
                    "Performing the unix command 'gpasswd $($gpasswdParams -join ' ')'.",
                    $UserNameItem,
                    "Removing $userNameItem grom group '$GroupName'."
                )
            )
            {
                Invoke-NativeCommand -Executable 'gpasswd' -Parameters $gpasswdParams -Verbose:$verbose -ErrorAction 'Stop' |
                    ForEach-Object -Process {
                        if ($_ -match '^gpasswd:')
                        {
                            throw $_
                        }
                        else
                        {
                            Write-Verbose -Message $_
                        }
                    }

                $hasGroupChanged = $true
            }
        }

        if ($hasGroupChanged -and $PassThru)
        {
            Get-nxLocalGroup -GroupName $GroupName
        }
    }
}
#EndRegion './Public/usersAndGroups/Remove-nxLocalGroupMember.ps1' 60
#Region './Public/usersAndGroups/Remove-nxLocalUser.ps1' 0
function Remove-nxLocalUser
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([void])]
    param
    (

        [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        [string[]]
        [Alias('User','Name')]
        $UserName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]
        $RemoveHomeDirAndMailSpool,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]
        $Force
    )

    begin
    {
        $verbose = $PSBoundParameters.ContainsKey('verbose') -and $PSBoundParameters['Verbose']
        $userDelParams += @()
    }

    process
    {
        if ($PSBoundParameters.ContainsKey('RemoveHomeDirAndMailSpool') -and $PSBoundParameters['RemoveHomeDirAndMailSpool'])
        {
            $userDelParams += @('-r')
        }

        if ($PSBoundParameters.ContainsKey('Force') -and $PSBoundParameters['Force'])
        {
            $userDelParams += @('-f')
        }

        foreach ($UserNameItem in $UserName)
        {
            if ($PScmdlet.ShouldProcess("Performing the unix command 'userdel $(($userDelParams + @($userNameItem)) -join ' ')'.", $UserNameItem, "Removing local user '$UserNameItem' from '$(hostname)'."))
            {
                Invoke-NativeCommand -Executable 'userdel' -Parameter ($userDelParams + @($userNameItem)) -Verbose:$verbose -ErrorAction 'Stop' | Foreach-Object {
                    throw $_
                }
            }
        }
    }




}
#EndRegion './Public/usersAndGroups/Remove-nxLocalUser.ps1' 55
#Region './Public/usersAndGroups/Set-nxLocalGroup.ps1' 0
function Set-nxLocalGroup
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [OutputType([void])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'removePassword')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'restrict')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'setMemberOrAdmin')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'setMember')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'setAdmin')]
        [String]
        $GroupName,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'removePassword')]
        [Switch]
        $RemovePassword,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'restrict')]
        [Switch]
        $Restrict,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'setMemberOrAdmin')]
        [String[]]
        $Member,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'setMemberOrAdmin')]
        [String[]]
        $Administrators,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'removePassword')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'restrict')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'setMemberOrAdmin')]
        [switch]
        $PassThru
    )

    begin
    {
        $verbose = ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose']) -or $VerbosePreference -ne 'SilentlyContinue'
    }

    process
    {
        if ($PSCmdlet.ParameterSetName -eq 'setMemberOrAdmin' -and -not ($PSBoundParameters.ContainsKey('Administrators') -or $PSBoundParameters.ContainsKey('Member')))
        {
            throw "Parameter set cannot be resolved using the specified named parameters. One or more parameters issued cannot be used together or an insufficient number of parameters were provided."
            return
        }

        $gpasswdParams = @()

        if ($PSBoundParameters.ContainsKey('RemovePassword') -and $PSBoundParameters['RemovePassword'])
        {
            $gpasswdParams += @('-r')
        }

        if ($PSBoundParameters.ContainsKey('Restrict') -and $PSBoundParameters['Restrict'])
        {
            $gpasswdParams += @('-R')
        }

        if ($PSBoundParameters.ContainsKey('Member') -and $PSBoundParameters['Member'])
        {
            $gpasswdParams += @('-M', ($Member -join ','))
        }

        if ($PSBoundParameters.ContainsKey('Administrators') -and $PSBoundParameters['Administrators'])
        {
            $gpasswdParams += @('-A', ($Administrators -join ','))
        }

        $gpasswdParams += @($GroupName)

        if ($PSCmdlet.ShouldProcess(
                "Performing the unix command 'gpasswd $(($gpasswdParams -join ' '))'.",
                $GroupName,
                "Setting LocalGroup $GroupName"
            )
        )
        {
            Invoke-NativeCommand -Executable 'gpasswd' -Parameters $gpasswdParams -Verbose:$verbose | ForEach-Object -Process {
                if ($_ -match '^gpasswd:')
                {
                    throw $_
                }
                else
                {
                    Write-Verbose -Message "$_"
                }
            }

            if ($PSBoundParameters.ContainsKey('PassThru') -and $PSBoundParameters['PassThru'])
            {
                # return the group
                Get-nxLocalGroup -GroupName $GroupName -ErrorAction Stop
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/Set-nxLocalGroup.ps1' 101
#Region './Public/usersAndGroups/Set-nxLocalGroupGID.ps1' 0

function Set-nxLocalGroupGID
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([void])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $GroupName,

        [Parameter(Mandatory = $true)]
        [int]
        [Alias('GID')]
        $GroupID
    )

    $gpasswdParams = @('-g', $GroupID, $GroupName)


    if ($PSCmdlet.ShouldProcess(
                "Performing the unix command 'gpasswd $(($gpasswdParams -join ' '))'.",
                "$GroupName",
                "Setting LocalGroup $GroupName"
            )
        )
    {
        Invoke-NativeCommand -Executable 'groupmod' -Parameters $groupmodParams -Verbose:$verbose |
        Foreach-Object -ScriptBlock {
            throw $_
        }
    }
}
#EndRegion './Public/usersAndGroups/Set-nxLocalGroupGID.ps1' 34
#Region './Public/usersAndGroups/Set-nxLocalGroupMember.ps1' 0
function Set-nxLocalGroupMember
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [String[]]
        $Member,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [String]
        $GroupName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Switch]
        $PassThru
    )

    process
    {
        Set-nxLocalGroup @PSBoundParameters
    }
}
#EndRegion './Public/usersAndGroups/Set-nxLocalGroupMember.ps1' 25
#Region './Public/usersAndGroups/Set-nxLocalUser.ps1' 0
function Set-nxLocalUser
{
    [CmdletBinding(DefaultParameterSetName = 'ParameterizedGECOSAddGroupExpireOn', SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUsernameAndPasswordParams', '')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
    param
    (

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [System.String]
        $UserName,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [System.String]
        # compose with Description and build for -c
        $FullName,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [System.String]
        # same as above
        $Office,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [System.String]
        # same as above
        $OfficePhone,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [System.String]
        # same as above
        $HomePhone,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [System.String]
        # same as above
        $Description,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [ValidateNotNullOrEmpty()]
        [System.String]
        [Alias('Password')]
        # -p
        $EncryptedPassword,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [System.Management.Automation.SwitchParameter]
        # -L
        $Locked,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [ValidateNotNullOrEmpty()]
        [datetime]
        # The date on which the user account will be disabled. The date is specified in the format YYYY-MM-DD.
        # If not specified, useradd will use the default expiry date specified by the EXPIRE variable in /etc/default/useradd,
        # or an empty string (no expiry) by default.
        $ExpireOn,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [System.Management.Automation.SwitchParameter]
        $RequirePasswordChange,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [int]
        # -u
        $UserID,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [int]
        # -g
        $PrimaryGroup,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [System.String[]]
        # -a -G
        $GroupToSet,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [System.String[]]
        # -a -G
        $GroupToAdd,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [System.String]
        [Alias('GECOS')]
        # Set new value for GECOS field
        $UserInfo,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [System.String]
        # -d
        $HomeDirectory,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [System.Management.Automation.SwitchParameter]
        # -m only when -HomeDirectory is used
        $MoveHomeToNewHomeDirectory,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ParameterizedGECOSSetGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSAddGroupRequirePwdChange')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupExpireOn')]
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GECOSSetGroupRequirePwdChange')]
        [System.String]
        # -s
        $ShellCommand
    )

    begin
    {
        $verbose = $VerbosePreference -or ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose'])
    }

    process
    {
        $usermodParams = @()

        # Do we need to set the GECOS field?
        $compareObjectParams = @{
            ReferenceObject  = $PSBoundParameters.Keys
            DifferenceObject = @(
                'FullName'
                'Office'
                'OfficePhone'
                'HomePhone'
                'Description'
            )
            IncludeEqual     = $true
            ExcludeDifferent = $true
        }

        $ShouldChangeGECOS = $null -ne (Compare-Object @compareObjectParams)

        if (
            $PSCmdlet.ParameterSetName -match 'ParameterizedGECOS' -and
            $ShouldChangeGECOS
        )
        {
            $existingUser = Get-nxLocalUser -UserName $UserName -ErrorAction 'SilentlyContinue'
            $FullNameToSet = $existingUser.FullName
            $OfficeToSet = $existingUser.Office
            $OfficePhoneToSet = $existingUser.OfficePhone
            $HomePhoneToSet = $existingUser.HomePhone
            $DescriptionToSet = $existingUser.Description

            switch ($PSBoundParameters.keys)
            {
                'FullName'
                {
                    $FullNameToSet = $FullName
                }

                'Office'
                {
                    $OfficeToSet = $Office
                }

                'OfficePhone'
                {
                    $OfficePhoneToSet = $OfficePhone
                }

                'HomePhone'
                {
                    $HomePhoneToSet = $HomePhone
                }

                'Description'
                {
                    $DescriptionToSet = $Description
                }
            }

            $gecosField = '{0},{1},{2},{3},{4}' -f $FullNameToSet, $OfficeToSet, $OfficePhoneToSet, $HomePhoneToSet, $DescriptionToSet
            $usermodParams += @('-c', ($gecosField | Get-nxEscapedString))
        }
        elseif ($PSBoundParameters.ContainsKey('UserInfo'))
        {
            $usermodParams += @('-c', ($UserInfo | Get-nxEscapedString))
        }

        if ($PSBoundParameters.ContainsKey('EncryptedPassword'))
        {
            $usermodParams += @('-p', ($EncryptedPassword | Get-nxEscapedString))
        }

        if ($Locked.IsPresent)
        {
            $usermodParams += @('-L')
        }

        if ($PSBoundParameters.ContainsKey('ExpireOn') -and $PSBoundParameters['ExpireOn'])
        {
            $userAddParams += @('-e', $ExpireOn.ToString('yyyy-MM-dd'))
        }
        elseif ($RequirePasswordChange.IsPresent)
        {
            # Set the password as Expired
            $yesterday = ([DateTime]::Now).AddDays(-1)

            $usermodParams = @('-e', $yesterday.ToString('yyyy-MM-dd'))
        }

        if ($PSBoundParameters.ContainsKey('UserID'))
        {
            $usermodParams += @('-u', $UserID)
        }

        if ($PSBoundParameters.ContainsKey('PrimaryGroup'))
        {
            $usermodParams += @('-g', $PrimaryGroup)
        }

        if ($PSBoundParameters.ContainsKey('GroupToSet'))
        {
            $usermodParams += @('-G', $GroupToSet)
        }
        elseif ($PSBoundParameters.ContainsKey('GroupToAdd'))
        {
            $usermodParams += @('-a','-G', $($GroupToAdd -join ','))
        }

        if ($PSBoundParameters.ContainsKey('HomeDirectory'))
        {
            $usermodParams += @('-d', ($HomeDirectory | Get-nxEscapedPath))

            if ($MoveHomeToNewHomeDirectory.IsPresent)
            {
                $usermodParams += @('-m')
            }
        }

        if ($PSBoundParameters.ContainsKey('ShellCommand'))
        {
            $usermodParams += @('-s', ($ShellCommand | Get-nxEscapedPath))
        }

        $usermodParams += @($UserName)

        if ($PSCmdlet.ShouldProcess(
                "Performing the unix command 'usermod $(($usermodParams -join ' '))'.",
                $UserName,
                "Setting LocalUser $UserName"
            )
        )
        {
            Invoke-NativeCommand -Executable 'usermod' -Parameters $usermodParams -Verbose:$verbose | ForEach-Object -Process {
                if ($_ -match '^usermod:')
                {
                    Write-Error $_
                }
                else
                {
                    Write-Verbose -Message "$_"
                }
            }
        }
    }
}
#EndRegion './Public/usersAndGroups/Set-nxLocalUser.ps1' 349