oxygen.psm1

Set-StrictMode -Version Latest

Function Test-TcpConnection {
    <#
    .SYNOPSIS
        Tests connection to a port on a computer.
    .DESCRIPTION
        Tests connection to a port on a computer by attempting to open and then
        close it.
 
        This function is superceded in PowerShell v5+ by Test-NetConnection and
        has been kept for backwards compatibility.
    .EXAMPLE
        Test-TcpConnection -Server 'myserver' -Port '123'
 
        Tests if a connection can be established to port '123' on 'myserver'.
    .EXAMPLE
        Test-TcpConnection -Server 'server01', 'server02' -Port '80', '443'
 
        Tests if a connection can be established to ports 80 and 443 on server01
        and server02.
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .NOTES
        Author : Unknown - Refactored by Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : 1.0 - 21/04/18 - Initial release
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/test-tcpconnection.md
    #>

    [CmdletBinding()]
    [OutputType([boolean])]
    param (
        [Parameter(Mandatory, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Server,

        [Parameter(Mandatory, Position = 1)]
        [ValidateRange(0, 65535)]
        [int[]]
        $Port,

        [Parameter(Position = 2)]
        [int]
        $TimeOut = 2
    )

    $timeoutMs = $TimeOut * 1000

    ForEach ($s in $Server) {
        ForEach ($p in $Port) {
            $ip = [System.Net.Dns]::GetHostAddresses($s)
            $address = [System.Net.IPAddress]::Parse($ip[0])
            $socket = New-Object System.Net.Sockets.TCPClient

            Write-Verbose "Connecting to '$address' on port '$p'"
            try {
                $connect = $socket.BeginConnect($address, $p, $null, $null)
            }
            catch {
                Write-Warning "'$s' is not responding on port '$p'"
                return $false
            }

            Start-Sleep -Seconds $TimeOut

            if ($connect.IsCompleted) {
                $wait = $connect.AsyncWaitHandle.WaitOne($timeoutMs, $false)
                if (!$wait) {
                    $socket.Close()
                    Write-Warning "'$s' is not responding on port '$p'"
                    return $false
                }
                else {
                    try {
                        $socket.EndConnect($connect)
                        Write-Verbose "'$s' is responding on port '$p'"
                        return $true
                    }
                    catch { 
                        Write-Warning "'$s' is not responding on port '$p'" 
                    }
                    $socket.Close()
                    return $false
                }
            }
            else {
                Write-Warning "'$s' is not responding on port '$p'"
                return $false
            }
        } #end ForEach $Port
    } #end ForEach $Server
}

function Reset-Printer {
    <#
    .SYNOPSIS
        Reset the specified printer on the local computer.
    .DESCRIPTION
        Reset the specified printer on the local computer by removing the printer,
        driver and port and adding them back.
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : v1.0 - 20 April 2014
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .EXAMPLE
        Reset-Printer -Name "HP LaserJet 4" -DriverName "HP LaserJet PS" -PortName "HPLJ4" -PrinterHostAddress "192.168.10.100"
 
        Resets the printer called "HP LaserJet 4", driver named "HP LaserJet PS", port "HPLJ4" with IP address 192.168.10.100 by removing the port, driver and printer and then adding them back again.
    .LINK
        Reset-PrinterDriver
    .LINK
        Reset-PrinterPort
    .LINK
        Test-Printer
    .LINK
        Test-PrinterDriver
    .LINK
        Test-PrinterDriverStore
    .LINK
        Test-PrinterPort
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/reset-printer.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low')]
    [OutputType([boolean])]
    Param
    (
        # Specifies the name of the printer to reset on the local computer.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateScript( { Test-Printer -Name $_ } )]
        [string]$Name,

        # Specifies the name of the driver to reset on the local computer.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateScript( { Test-PrinterDriver -Name $_ } )]
        [string]$DriverName,

        # Specifies the name of the port to reset on the local computer.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateScript( { Test-PrinterPort -Name $_ } )]
        [string]$PortName,

        # Specifies the IP address of the printer to reset on the local computer.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateScript( { Test-Connection -ComputerName $_ -Count 1 -Quiet } )]
        [IPAddress]$IPAddress,

        # Force the removal
        [Switch]$Force
    )

    Begin {
        if (-not $PSBoundParameters.ContainsKey('Confirm')) {
            $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference')
        }
        if (-not $PSBoundParameters.ContainsKey('WhatIf')) {
            $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference')
        }
    }

    Process {
        Write-Verbose "Removing printer $Name"

        if ($PSCmdlet.ShouldProcess($Name, "Removing printer")) {
            Remove-Printer -Name $Name
        }

        Write-Verbose "Checking printer $Name has been removed"
        if (Test-Printer -Name $Name) {
            $false
        }
        else {
            # check each of these suceeds.
            if ((-not (Reset-PrinterPort -PortName $PortName -IPAddress $IPAddress)) -or
                    (-not (Reset-PrinterDriver -DriverName $DriverName)) -or
                    (-not (Add-Printer -Name $Name -DriverName $DriverName -PortName $PortName -Force:($Force.IsPresent))) ) {
                $false
            }
            else {
                $true
            }
        }
    }

    End { }
}

function Reset-PrinterDriver
{
    <#
    .SYNOPSIS
        Reset the specified printer driver.
    .DESCRIPTION
        Reset the specified printer driver by removing it and adding it back again.
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : v1.0 - 20 April 2018
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .EXAMPLE
        Reset-PrinterDriver -Name "HP LaserJet PS"
 
        Remove and add the printer driver called "HP LaserJet PS"
    .LINK
        Reset-Printer
    .LINK
        Reset-PrinterPort
    .LINK
        Test-Printer
    .LINK
        Test-PrinterDriver
    .LINK
        Test-PrinterDriverStore
    .LINK
        Test-PrinterPort
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/reset-printerdriver.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    [OutputType([boolean])]
    Param
    (
        # Specifies the printer driver to reset.
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    if (Test-PrinterDriver -Name $Name)
    {
        if (PSCmdlet.ShouldProcess($Name, 'Removing printer driver')) {
            Remove-PrinterDriver -Name $Name -ErrorAction SilentlyContinue
        }

        # check it's gone
        if (Test-PrinterDriver -Name $Name) {
            return $false
        }
    }

    # add the printer driver
    Add-PrinterDriver -Name $Name -ErrorAction SilentlyContinue

    # check it's been added
    if (Test-PrinterDriver -Name $Name) {
        return $true
    }
    else {
        return $false
    }
}

function Reset-PrinterPort
{
    <#
    .SYNOPSIS
        Resets the printer port.
    .DESCRIPTION
        Resets the printer port configuration by removing then adding it with the new one.
 
        You do not need administrator privileges to use Reset-PrinterPort
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen https://www.github.com/pauby/oxygen
        History : v1.0 - 20/04/18 - Initial
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .EXAMPLE]
        Reset-PrinterPort -PortName "HPLJ" -PrinterHostAddress "192.168.10.100"
 
        Removes the printer port called HPLJ and adds it back again with the same name and IP host address of 192.168.10.100
    .LINK
        Reset-Printer
    .LINK
        Reset-PrinterDriver
    .LINK
        Test-Printer
    .LINK
        Test-PrinterDriver
    .LINK
        Test-PrinterDriverStore
    .LINK
        Test-PrinterPort
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/reset-printerport.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    [OutputType([boolean])]
    Param (
        # Specifies the Name of the port to be reset. You can get this name using Get-PrinterPort.
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$PortName,

        # Specifies the IP address of the printer port to be added.
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$PrinterHostAddress
    )

    # check we have a port and remove it
    if (Test-PrinterPort -Name $PortName) {
        if (PSCmdlet.ShouldProcess($Name, 'Removing printer port')) {
            Remove-PrinterPort -Name $PortName -ErrorAction SilentlyContinue
        }

        # check the port is gone
        if (Test-PrinterPort -Name $PortName) {
            return $false
        }
    }

    # add the port and check it's now been added
    Add-PrinterPort -Name $PortName -PrinterHostAddress $PrinterHostAddress -ErrorAction SilentlyContinue
    if (Test-PrinterPort -Name $PortName) {
        $true
    }
    else {
        $false
    }
}

function Test-Printer
{
    <#
    .SYNOPSIS
        Tests if the specified printer exists on the local computer.
    .DESCRIPTION
        Tests if the specified printer exists on the local computer.
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://www.github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .EXAMPLE
        Test-Printer -Name "HP LaserJet 4"
 
        Tests if the printer named "HP LaserJet 4" exists on the local computer.
    .LINK
        Reset-Printer
    .LINK
        Reset-PrinterDriver
    .LINK
        Reset-PrinterPort
    .LINK
        Test-PrinterDriver
    .LINK
        Test-PrinterDriverStore
    .LINK
        Test-PrinterPort
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/test-printer.md
    #>

    [CmdletBinding()]
    [OutputType([boolean])]
    Param (
        # Specified printer name to test.
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    ([bool](Get-Printer | Where-Object { $_.Name -eq $Name } ))
}

function Test-PrinterDriver
{
    <#
    .SYNOPSIS
        Tests if the printer driver is present on the system.
    .DESCRIPTION
        Tests if the printer driver is present on the system.
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://www.github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .EXAMPLE
        Test-PrinterDriver "HP LaserJet 4"
 
        This would test if the printer driver 'HP LaserJet 4' was installed.
    .LINK
        Reset-Printer
    .LINK
        Reset-PrinterDriver
    .LINK
        Reset-PrinterPort
    .LINK
        Test-Printer
    .LINK
        Test-PrinterDriverStore
    .LINK
        Test-PrinterPort
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/test-printerdriver.md
    #>

    [CmdletBinding()]
    [OutputType([boolean])]
    Param (
        # Printer driver name
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    [bool](Get-PrinterDriver | Where-Object { $_.Name -eq $Name } )
}

function Test-PrinterDriverStore
{
    <#
    .SYNOPSIS
        Test if the specified printer driver exists in the printer driver store.
    .DESCRIPTION
        Test if the specified printer driver exists in the printer driver store.
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://www.github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .EXAMPLE
        Test-PrinterDriverStore -Name "HP LaserJet PS"
 
        Tests if the printer driver named "HP LaserJet PS" exists in the printer driver store.
    .LINK
        Reset-Printer
    .LINK
        Reset-PrinterDriver
    .LINK
        Reset-PrinterPort
    .LINK
        Test-Printer
    .LINK
        Test-PrinterDriver
    .LINK
        Test-PrinterPort
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/test-printerdriverstore.md
    #>

    [CmdletBinding()]
    [OutputType([boolean])]
    Param (
        # Specifies the name of the printer driver to test.
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    # check if the driver is in the list already
    if (Test-PrinterDriver -Name $Name) {
        return $true
    }
    else {
        # try and install the driver - if it fails then the driver most likely does not exist in the store
        try {
            Add-PrinterDriver -Name $DriverName
        }
        catch {
            # if we get here then the driver will most likely not exist in the store so will need to be added
            return $false
        }

        # if we get here then the printer driver was added successfully - remove it (as we only wanted to test it)
        Remove-PrinterDriver -Name $DriverName
        return $true
    }
 }

function Test-PrinterPort
{
    <#
    .SYNOPSIS
        Tests if the specified printer port exists on the computer.
    .DESCRIPTION
        Tests if the specified printer port exists on the computer.
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://www.github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .EXAMPLE
        Test-PrinterPort -PortName "TestPrinter"
 
        Tests whether the port named TestPrinter exisst on the computer.
    .LINK
        Reset-Printer
    .LINK
        Reset-PrinterDriver
    .LINK
        Reset-PrinterPort
    .LINK
        Test-Printer
    .LINK
        Test-PrinterDriver
    .LINK
        Test-PrinterDriverStore
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/test-printerport.md
    #>

    [CmdletBinding()]
    [OutputType([boolean])]
    Param (
        # Specifies the name of the port to test.
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    [bool](Get-PrinterPort | Where-Object { $_.Name -eq $Name } )
}

function Import-Registry {
    <#
    .SYNOPSIS
        This is just a wrapper around 'regedit /s'
    .DESCRIPTION
        This is just a wrapper around 'regedit /s'
    .INPUTS
        None
    .OUTPUTS
        None
    .EXAMPLE
        Import-Registry -Path 'mychanges.reg'
 
        Will import the file 'mychanges.reg' into the registry.
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : 1.0 - 20/04/18 - Initial release
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/import-registry.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    Param (
        [Parameter(Mandatory, Position = 0)]
        [ValidateScript({ Test-Path $_ })]
        [string]$Path
    )

    if ($PSCmdlet.ShouldProcess($Path, 'Import file to the registry')) {
        regedit /s $Path
    }
}

function Add-Acl {
    <#
    .SYNOPSIS
    Set an ace on an object.
    .DESCRIPTION
    Set an ace, created using New-Acl, on an object.
    .INPUTS
        None
    .OUTPUTS
        System.Security.AccessControl.FileSecurity
    .EXAMPLE
    Add-Acl -Path 'C:\Windows\Notepad.exe' -AceObject $aceObj
 
    Adds the access control entry object, $aceObj created using New-Acl, to 'C:\Windows\Notepad.exe'
    .NOTES
    Author : Paul Broadwith (https://github.com/pauby)
    Project : Oxygen (https://github.com/pauby/oxygen)
    History : v1.0 - 22/04/18 - Initial
 
    Code was created using https://technet.microsoft.com/en-us/library/ff730951.aspx as a basis.
    .LINK
        New-AclObject
    .LINK
        Set-Owner
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/add-acl.md
    #>

    [CmdletBinding()]
    Param (
        # Path to the object to set the acl on.
        [Parameter(Mandatory = $true)]
        [string]$Path,

        # Ace / Acl to set. Create this using New-Acl.
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias('Acl', 'AclObject')]
        [System.Security.AccessControl.FileSystemAccessRule]$AceObject
    )

    Write-Verbose "Retrieving existing ACL from $Path"
    $objACL = Get-ACL -Path $Path
    $objACL.AddAccessRule($AceObject) 
    Write-Verbose "Setting ACL on $Path"
    Set-ACL -Path $Path -AclObject $objACL
}



function New-AclObject {
    <#
    .SYNOPSIS
        Creates a new ACL object.
    .DESCRIPTION
        Creates a new ACL object for use with module -Acl* functions.
    .INPUTS
        None
    .OUTPUTS
        System.Security.Principal.NTAccount
    .EXAMPLE
        New-AclObject -SamAccountName 'testuser' -Permission 'Modify'
 
        Creates an ACL object to Allow Modify permissions without inheritance or propogation for the samAccountName 'testuser'
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
 
        Code was created using https://technet.microsoft.com/en-us/library/ff730951.aspx as a basis.
    .LINK
        Add-Acl
    .LINK
        Set-Owner
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/new-aclobject.md
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'No state is being changed')]
    Param (
        # samAccountName to create the object for
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias('Sam', 'Username')]
        [string]$SamAccountName,
    
        # Permissions / rights to be applied (see https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights(v=vs.110).aspx for information)
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias('AccessRight')]
        [System.Security.AccessControl.FileSystemRights]$Permission,

        # Allow or deny the access rule (see https://msdn.microsoft.com/en-us/library/w4ds5h86(v=vs.110).aspx for information).
        # Default is 'Allow'
        [ValidateNotNullOrEmpty()]
        [System.Security.AccessControl.AccessControlType]$AccessControl = 'Allow',

        # Inheritance rules to be applied (see https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.inheritanceflags(v=vs.110).aspx for information).
        # Default is 'None'
        [ValidateNotNullOrEmpty()]
        [Alias('InheritanceFlag')]
        [System.Security.AccessControl.InheritanceFlags]$Inheritance = 'None',

        # Propogation method for the rules (see https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.propagationflags(v=vs.110).aspx for information).
        # Default is 'None'
        [ValidateNotNullOrEmpty()]
        [Alias('PropogationFlag')]
        [System.Security.AccessControl.PropagationFlags]$Propagation = 'None'
    )

    [OutputType([System.Security.AccessControl.FileSystemAccessRule])]

    $objUser = New-Object System.Security.Principal.NTAccount($Username) 

    New-Object System.Security.AccessControl.FileSystemAccessRule($objUser, $Permission, $Inheritance, $Propagation, $AccessControl)
}

# < PS 5 compatibility
Add-Type -TypeDefinition @"
        public enum ComplexityOptions
        {
            Upper,
            Lower,
            Number,
            Symbol
        }
"@

function New-ComplexPassword {
    <#
    .SYNOPSIS
        Creates a complex password.
    .DESCRIPTION
        Creates a complex password using one or more of upper case, lower case,
        numbers and symbols.
    .INPUTS
        None
    .OUTPUTS
        [string]
    .EXAMPLE
        New-ComplexPassword
 
        Create a password with a length of 12 and using upper, lower, numbers and
        symbols.
    .EXAMPLE
        New-ComplexPassword -Length 25 -Complexity Upper, Lower
 
        Create a password with a length of 25 characters and using upper and lower
        case characters.
    .EXAMPLE
        New-ComplexPassword -Exclude @('0') -Complexity Upper, Lower, Number
 
        Create a password with a length of 12, using upper, lower and numbers but
        excluding '0' (zero)
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/new-complexpassword.md
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'No state is being changed')]
    [CmdletBinding()]
    [OutputType([string])]
    Param (
        # Length of the password to generate. By default this is 12.
        [ValidateRange(1, 999)]
        [Int]
        $Length = 12,

        # Characters to not include in the password. For example you may to
        # choose to exclude '0' (zero) as it is visually close to 'O' (capital
        # oh)
        [Char[]]
        $Exclude,

        # Create the password using these options. Valid values are Upper, Lower, Number and Symbol.
        [ComplexityOptions[]]
        $Complexity = @( [ComplexityOptions]::Upper, [ComplexityOptions]::Lower, [ComplexityOptions]::Number, [ComplexityOptions]::Symbol )
    )
    #! keep the Complexity default values all on one line or an error is generated when creating the external help file

    # initialise the pool of characters to create password from
    $pool = ''
    switch ($Complexity) {
        'Upper' {
            # uppercase chars 'A' to 'Z'
            Write-Verbose "Using upper case characters A-Z"
            $pool += (65..90 | ForEach-Object { [char]$_ }) -join ''
            continue
        }
        'Lower' {
            # lowercase chars 'a' to 'z'
            Write-Verbose "Using lower case characters a-z"
            $pool += (97..122 | ForEach-Object { [char]$_ }) -join ''
            continue
        }
        'Number' {
            # integers 0 to 9
            Write-Verbose "Using numbers 0-9"
            $pool += (0..9 | ForEach-Object { $_ }) -join ''
            continue
        }
        'Symbol' {
            Write-Verbose "Using symbols"
            $pool += '!"#$%&''()*+,-./:;<=>?@[\]^_`{|}~'
        }
    }   # end Switch

    # initialize the password
    $pwd = ''
    while ($pwd.Length -lt $Length) {
        # randomly generate the index of which pool character to use
        $index = Get-Random -Maximum $pool.Length

        # check that the character chosen is not on the exclude list
        if ($Exclude -cnotcontains $pool[$index]) {
            $pwd += $pool[$index]
        }
        else {
            Write-Verbose "Skipped $($pool[$index])"
        }
    }

    $pwd
}

function Set-AclOwner {
    <#
    .SYNOPSIS
        Set the owner of an object.
    .DESCRIPTION
        Sets the owner of an object. This is related to the 'Acl' group of cmdlets, hence the name.
    .INPUTS
        None
    .OUTPUTS
        System.Security.AccessControl.FileSecurity
    .EXAMPLE
        Set-AclOwner -Path c:\myfile.txt -SamAccountName 'chewie'
 
        Will set the account (group or user) 'chewie' to be the owner of 'c:\myfile.txt'
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .LINK
        Add-Acl
    .LINK
        New-AclObject
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/set-aclowner.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    Param (
        # Path to set the owner of.
        [Parameter(Mandatory, HelpMessage = 'Path to set owner of.')]
        [ValidateScript( { Test-Path $_ })]
        [string]$Path,

        # The samAccountName to set as the object owner.
        [Parameter(Mandatory, HelpMessage = 'The samAccountName to set as the object owner.')]
        [Alias('Sam', 'Username')]
        [string]$SamAccountName
    )

    Write-Verbose "Retrieving existing ACL from '$Path'"
    $acl = Get-ACL -Path $Path
    $account = New-Object System.Security.Principal.NTAccount($SamAccountName)
    Write-Verbose "Setting ACL owner to '$SamAccountName'"

    if ($PSCmdlet.ShouldProcess($Path, "Setting object owner to '$SamAccountName'")) {
        $acl.SetOwner($account)
        Write-Verbose "Setting ACL on '$Path'"
        Set-Acl -Path $Path -AclObject $acl
    }
}

function Set-UAC {
    <#
    .SYNOPSIS
        Enables or disables UAC.
    .DESCRIPTION
        Enables or disables UAC by change the EnableLUA key in the registry at
        HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System.
    .EXAMPLE
        Set-UAC -Enable
 
        Enables UAC on the local computer.
    .EXAMPLE
        Set-UAC -Disable
 
        Disables UAC on the local computer.
    .INPUTS
        None
    .OUTPUTS
        [PSCustomObject]
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : 1.0 - 21/04/18 - Initial release
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/set-uac.md
    #>


    [OutputType([PSCustomObject])]
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    Param(
        [Parameter(ParameterSetName = 'Enable')]
        [switch]
        $Enable,

        [Parameter(ParameterSetName = 'Disable')]
        [switch]
        $Disable
    )

    if ($Enable) {
        Write-Verbose "Enabling UAC."
        $action = 'Disabling'
        $state = 1
    }
    else {
        Write-Verbose "Disabling UAC."
        $action = 'Enabling'
        $state = 0
    }

    if ($PSCmdlet.ShouldProcess('UAC', $action)) {
        New-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" `
            -Name 'EnableLUA' -Value $state -PropertyType 'DWord' -Force
    }
}

function Add-WindowsDriverPackage {
    <#
    .SYNOPSIS
        Adds a Windows driver to the driver store.
    .DESCRIPTION
        This function is a wrapper for the pnputil.exe program. It adds a Windows
        driver to the driver store by passing the .inf file. $_
         
        Using the -Install switch will also install install the driver package(s) too.
 
        The functions' return value is dependent on the errorlevel from
        pnputil.exe. If the errorlevel is 0 then this indicates successfully
        and true is returned, otherwise false is returned.
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .EXAMPLE
        Add-WindowsDriverPackage -Path c:\drivers\usbcam.inf
 
        Adds c:\drivers\usbcam.inf driver package to the driver store.
    .EXAMPLE
        Add-WindowsDriverPackage -Path c:\drivers\*.inf -Install
         
        Adds and installs all *.inf driver packages from c:\drivers
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/add-windowsdriverpackage.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    [OutputType([boolean])]
    Param (
        # Path to the .inf file of the drive rpackage. Note that this can use wildcards.
        # So you can you c:\drivers\*.inf.
        # The path will be checked and exception thrown if it does not exist.
        [Parameter(Mandatory, Position = 0)]
        [ValidateScript( { Test-Path $_ } )]
        [string]$Path,

        # Will install the driver package(s).
        [switch]$Install
    )

    if ($Install.IsPresent) {
        Write-Verbose "Installing driver package '$Path' to the store."
        $cmd = "pnputil.exe -i -a $Path"
        $msg = 'Installing driver package and adding it to the store'
    }
    else {
        Write-Verbose "Adding driver package $Path to the store."
        $cmd = "pnputil.exe -a $Path"
        $msg = 'Adding driver package to the store'
    }

    if ($PSCmdlet.ShouldProcess($Path, $msg)) {
        $cmd | Out-Null
    }

    if ($LastExitCode -ne 0) {
        Write-Verbose "Driver package $Path failed to be added / installed."
        $false
    }
    else {
        Write-Verbose "Driver package $Path was added / installed"
    }
    
    $true
}

function Disable-WindowsPageFile {
    <#
    .SYNOPSIS
        Disables the Windows pagefile.
    .DESCRIPTION
        Disables the Windows pagefile on the local computer.
    .EXAMPLE
        Disable-WindowsPageFile
 
        Disables the Windows page file on the local computer.
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : 1.0 - 20/04/18 - Initial release
    .LINK
        Enable-PageFile
    .LINK
        Remove-PageFile
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/disable-windowspagefile.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    [OutputType([boolean])]
    Param(
        # Force disabling the pagefile regardless of it's current state.
        [switch]
        $Force
    )
    
    $cs = Get-CimInstance -ClassName Win32_ComputerSystem

    if ($Force.IsPresent -or $cs.AutomaticManagedPagefile) {
        if ($PSCmdlet.ShouldProcess('Windows PageFile', 'Disabling')) {
            try {
                $cs.AutomaticManagedPagefile = $false
                $PutOptions = New-Object System.Management.PutOptions
                $PutOptions.Type = 2
                $cs.PsBase.Put()

                return $true
            }
            catch {
                return $false
            }
        }
    }
    $false
}

function Enable-WindowsPageFile {
    <#
    .SYNOPSIS
        Enables the Windows pagefile.
    .DESCRIPTION
        Enables the Windows pagefile on the local computer.
    .EXAMPLE
        Enable-WindowsPageFile
 
        Enables the Windows page file on the local computer.
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : 1.0 - 20/04/18 - Initial release
    .LINK
        Disable-PageFile
    .LINK
        Remove-PageFile
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/enable-windowspagefile.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    [OutputType([boolean])]
    Param (
        # Force enbling the pagefile regardless of it's current state.
        [switch]
        $Force
    )

    $cs = Get-CimInstance -ClassName Win32_ComputerSystem

    if ($Force.IsPresent -or !$cs.AutomaticManagedPagefile) {
        if ($PSCmdlet.ShouldProcess('Windows PageFile', 'Enabling')) {
            try {
                $cs.AutomaticManagedPagefile = $true
                $PutOptions = New-Object System.Management.PutOptions
                $PutOptions.Type = 2
                $cs.PsBase.Put()

                return $true
            }
            catch {
                return $false
            }
        }
    }
    $false
}

function Get-OS {
    <#
    .SYNOPSIS
        Gets details of the operating system.
    .DESCRIPTION
        Gets details of the operating system. This function is a nice wrapper
        around the Get-CimInstance Win32_OperatingSystem call. It does create a
        hashtable with already determined information for the platform and type.
 
        The format of the data returned is:
 
        Name Value
        ---- -----
        version 10.0.16299 name Microsoft Windows 10
        Pro architecture 64-bit type Windows platform
        Workstation buildnumber 16299
 
        * The 'platform' field can be 'Workstation', 'Domain Controller' or
          'Server'.
        * The 'type' field will be 'Windows' or 'Unknown'.
    .EXAMPLE
        Get-OS
 
        Returns information about the operating system.
    .INPUTS
        None
    .OUTPUTS
        [hashtable]
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : 1.0 - 20/04/18 - Initial release
    .LINK
        Test-IsNonInteractiveShell
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/get-os.md
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    Param (
        # Get operating system information of this computer. Defaults to local
        # computer.
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string]$ComputerName = '.'
    )

    Begin {}

    Process {
        try {
            $osData = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $ComputerName
        }
        catch {
            throw $_
        }

        @{
            name         = $osData.Caption
            architecture = $osData.OSArchitecture
            platform     = switch ($osData.ProductType) { 
                1 { 'Workstation' }
                2 { 'Domain Controller' } 
                3 { 'Server' }
            }
            type         = switch ($osData.OSType) { 
                18 { 'Windows'; break }
                default { 'Unknown' } 
            }
            version      = $osData.Version
            buildnumber  = $osData.BuildNumber
        }
    }

    End {}
}

function Get-WindowsSpecialFolderPath {
    <#
    .SYNOPSIS
        Gets the path of a Windows special folder.
    .DESCRIPTION
        Gets the path of a Windows special folder such as Network Shortcuts,
        Desktop, etc.
 
        This is a wrapper around [Environment]::GetFolderPath()
    .INPUTS
        None
    .OUTPUTS
        [string]
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .EXAMPLE
        Get-WindowsSpecialFolderPath -Name "NetworkShortcuts"
 
        Gets the network shortcuts folder path.
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/get-windowsspecialfolderpath.md
    #>

    [CmdletBinding()]
    [OutputType([string])]
    Param (
        # The name of the special folder. This name must match one from the list
        # at https://msdn.microsoft.com/en-us/library/system.environment.specialfolder.aspx
        [Parameter(Mandatory)]
        [ValidateScript( { $_ -in [Environment+SpecialFolder]::GetNames([Environment+SpecialFolder]) } )]
        [string]$Name
    )

    [Environment]::GetFolderPath($Name)
}

function Remove-WindowsPageFile {
    <#
    .SYNOPSIS
        Removes the Windows PageFile.
    .DESCRIPTION
        Removes the Windows PageFile.
    .EXAMPLE
        Remove-PageFile
 
        Removes the Windows Pagefile on the current computer.
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : 1.0 - 20/04/18 - Initial release
    .LINK
        Disable-PageFile
    .LINK
        Enable-PageFile
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/remove-windowspagefile.md
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    [OutputType([boolean])]
    Param ()

    $pg = Get-CimInstance -ClassName Win32_PageFileUsage
    if ($pg) {
        if ($PSCmdlet.ShouldProcess('Windows Pagefile', 'Removing')) {
            try {
                Remove-Item $pg.Name -Force -ErrorAction Stop
                return $true
            }
            catch {
                return $false
            }
        }
    }
    $false
}

function Set-WindowsRegion {
    <#
    .SYNOPSIS
        Sets the Regional Settings for Windows.
    .DESCRIPTION
        Sets the Regional Settings for Windows like you would do by going to
        Control Panel -> Regional Settings
 
        This function is basically a wrapper around rundll32.exe.
    .INPUTS
        None
    .OUTPUTS
        None
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .EXAMPLE
        Set-Region -Path c:\temp\region-uk.xml
 
        Sets the regional settings to that contained in c:\temp\region-uk.xml
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/set-windowsregion.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    Param (
        # Path to the regional settings XML file.
        [Parameter(Mandatory, Position = 0)]
        [ValidateScript( { Test-Path $_ } )]
        [string]$Path
    )

    if ($PSCmdlet.ShouldProcess('Regional Settings', "Applying the settings in '$Path'")) {
        $cmd = "intl.cpl,,/f:$Path"
        rundll32.exe shell32,Control_RunDLL "$cmd"
    }
}


function Test-IsInteractiveShell {
    <#
    .SYNOPSIS
        Tests if the current shell is noninteractive.
    .DESCRIPTION
        First, we check `[Environment]::UserInteractive` to determine if the
        shell is running interactively. An example of not running interactively
        would be if the shell is running as a service. If we are running
        interactively, we check the Command Line Arguments to see if the
        `-NonInteractive` switch was used or an abbreviation of the switch.
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .NOTES
        Author : Vertigion (https://github.com/Vertigion/Test-IsNonInteractiveShell)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .LINK
        Get-OS
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/test-isinteractiveshell.md
    #>


    [CmdletBinding()]
    [OutputType([boolean])]
    Param()

    ([Environment]::UserInteractive -and (-not ([Environment]::GetCommandLineArgs() | Where-Object { $_ -like '-NonI*' })))
}

# Taken from https://social.technet.microsoft.com/Forums/scriptcenter/en-US/fd34260e-ee4c-47c5-8c69-872a5239745f/add-a-network-location-via-script?forum=ITCG
function Set-NetworkShortcut {
    <#
    .SYNOPSIS
        Creates or replaces a network shortcut.
    .DESCRIPTION
        Creates or replaces a network shortcut.
    .EXAMPLE
        Set-NetworkShortcut -Name 'MyShare' -DestinationUri '\\myserver\share'
 
        Creates a network shortcut called 'MyShare' pointing to '\\myserver\share'.
    .EXAMPLE
        Set-NetworkShortcut -Name 'MyShare' -DestinationUri '\\myserver\share' -Icon 304
 
        Creates a network shortcut called 'MyShare' pointing to '\\myserver\share' with icon number 304.
    .INPUTS
        None
    .OUTPUTS
        [PSObject]
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : 1.0 - 20/04/18 - Initial release
    .LINK
        Set-Shortcut
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/set-networkshortcut.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    Param (
        # Display name of the short to be created.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Name,

        # The destination URI for the shortcut.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string]
        $DestinationUri,

        # Username to set the shortcut for.
        [ValidateNotNullOrEmpty()]
        [ValidateScript( { Test-Path (Join-Path -Path (Split-Path -Path $env:PUBLIC -Parent) -ChildPath $_) })]
        [string]
        $Username = $env:USERNAME,

        # Number of the icon to use. By default this is 275.
        # Find more icons http://help4windows.com/windows_7_shell32_dll.shtml
        [ValidateNotNullOrEmpty()]
        [ValidateRange(0, 305)]
        [int32]
        $Icon = 275,

        [switch]
        $Force
    )

    $shortcutPath = Join-Path -Path (Split-Path -Path $env:PUBLIC -Parent) -ChildPath "$Username\appdata\Roaming\Microsoft\Windows\Network Shortcuts"

    # check if the .lnk file exists for the shortcut
    $shortcutFullPath = Join-Path -Path $shortcutPath -ChildPath $Name
    if ($Force.IsPresent -and (Test-Path -Path $shortcutFullPath)) {
        if ($PSCmdlet.ShouldProcess($shortcutFullPath, 'Removing network shortcut')) {
            Write-Verbose "Deleting network shortcut '$shortcutFullPath' as '-Force' parameter provided."
            Remove-Item -Path $shortcutFullPath -Force
        }
    }

    # create the folder
    Write-Verbose "Creating the shortcut folder '$shortcutFullPath'."
    New-Item -Name $Name -Path $shortcutFullPath -Type Directory | Out-Null

    # Create the ini file
    $iniPath = Join-Path -Path $shortcutFullPath -ChildPath 'Desktop.ini'
    Write-Verbose "Creating the INI file '$iniPath'."
    @"
[.ShellClassInfo]
CLSID2={0AFACED1-E828-11D1-9187-B532F1E9575D}
Flags=2
ConfirmFileOp=1
"@
 | Out-File -FilePath $iniPath 

    # Create the shortcut file
    $lnkPath = Join-Path -Path $shortcutFullPath -ChildPath 'target.lnk'
    Write-Verbose "Creating shortcut .lnk '$lnkPath'."
    $shortcut = (New-Object -ComObject WScript.Shell).Createshortcut($lnkPath)
    $shortcut.TargetPath = $DestinationUri
    $shortcut.IconLocation = "%SystemRoot%\system32\SHELL32.DLL, $Icon"
    $shortcut.Description = $DestinationUri
    $shortcut.WorkingDirectory = $DestinationUri
    $shortcut.Save()

    Set-ItemProperty (Join-Path -Path $shortcutFullPath -ChildPath 'Desktop.ini') `
        -Name Attributes -Value ([IO.FileAttributes]::System -bxor [IO.FileAttributes]::Hidden)
    Set-ItemProperty $shortcutFullPath -Name Attributes -Value ([IO.FileAttributes]::ReadOnly)
}

function Set-Shortcut {
    <#
    .SYNOPSIS
        Sets a Windows shortcut.
    .DESCRIPTION
        Sets a Windows shortcut. Do not sue this to create a network shortcut as
        it will not give you the results you need. Use Set-NetworkShoertcut
        instead.
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://www.github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .EXAMPLE
        Set-Shortcut -Path 'c:\user\joe\desktop\notepad.lnk' -Target 'c:\windows\system32\notepad.exe'
 
        Creates a shortcut to c:\windows\system32\notepad.exe on the desktop of the user 'joe'.
    .LINK
        Set-NetworkShortcut
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/set-shortcut.md
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    [OutputType([boolean])]
    Param (
        # Path to the shortcut file. Should end with '.lnk'. The parent path of this must exist.
        # For example, if the path is c:\windows\system32\notepad.exe then c:\windows\system32 must exist.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateScript( { Test-Path -Path (Split-Path -Path $_ -Parent) } )]
        [string]
        $Path,

        # The destination of the shortcut. This could be an application or a URL.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string]
        $DestinationUri,

        # Remove the destination shortcut if it exists
        [switch]
        $Force
    )

    # delete the shortcut if it already exists
    if ($Force.IsPresent -and (Test-Path -Path $Path)) {
        if ($PSCmdlet.ShouldProcess($Path, 'Removing shortcut')) {
            Write-Verbose "Deleting shortcut '$Path' as '-Force' parameter provided."
            Remove-Item -Path $Path -Force
        }
    }

    # Create the shortcut
    $WScriptShell = New-Object -ComObject WScript.Shell
    $Shortcut = $WScriptShell.CreateShortcut($Path)
    $Shortcut.TargetPath = $DestinationUri

    try {
        $Shortcut.Save()
    }
    catch {
        $false
    }

    $true
}

function Test-IsAccountEmptyPassword {
    <#
    .SYNOPSIS
        Checks if an account password is empty.
    .DESCRIPTION
        Tests whether an account password is empty / blank.
    .EXAMPLE
        Test-EmptyPassword -SamAccountName 'Administrator'
 
        Tests if the 'Administrator' account on the local computer has an
        empty / blank password.
    .EXAMPLE
        Test-EmptyPassword -SamAccountName 'Administrator' -ComputerName 'server01'
 
        Tests if the 'Administrator' account on'server01' has an empty / blank
        password.
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : 1.0 - 20/04/18 - Initial release
    .LINK
        Test-IsAdmin
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/test-isaccountemptypassword.md
    #>

    [CmdletBinding()]
    [OutputType([boolean])]
    Param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [Alias('Username')]
        [string]
        $SamAccountName,
        
        [ValidateNotNullOrEmpty()]
        [string]
        $ComputerName = '.'
    )
    
    $user = [ADSI]("WinNT://$Computername/$SamAccountName, user")
    try {
        $user.invoke("ChangePassword", "", "DummyPassword")    
    }
    catch {
        return $false
    }
    
    $user.invoke("ChangePassword", "DummyPassword", "")  
    $true
}

function Test-IsAdmin {
    <#
    .SYNOPSIS
        Test for elevated privileges.
    .DESCRIPTION
        Test if the current session has elevated privileges.
 
        Returns true if the current session has elevated privileges and false
        otherwise.
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .EXAMPLE
        Test-IsAdmin
 
        Tests to see if the current session has elevated privileges.
    .NOTES
        Author : Andy Arismendi (http://stackoverflow.com/users/251123/andy-arismendi)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : v1.0 - 20/04/18 - Initial
 
        Also see http://stackoverflow.com/questions/9999963/powershell-test-admin-rights-within-powershell-script
    .LINK
        Test-IsInteractiveUser
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/test-isadmin.md
    #>

    [CmdletBinding()]
    [OutputType([boolean])]
    Param ()

    $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = New-Object Security.Principal.WindowsPrincipal -ArgumentList $identity
    $principal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator)
}

function Test-IsInteractiveUser {
    <#
    .SYNOPSIS
        Tests if the current user is interactive.
    .DESCRIPTION
        Tests if the current user is interactive.
    .EXAMPLE
        Test-IsInteractiveUser
 
        Return $true if the current user is interactive, $false otherwise.
    .INPUTS
        None
    .OUTPUTS
        [boolean]
    .NOTES
        Author : Paul Broadwith (https://github.com/pauby)
        Project : Oxygen (https://github.com/pauby/oxygen)
        History : 1.0 - 21/04/18 - Initial release
    .LINK
        Test-IsAdmin
    .LINK
        https://github.com/pauby/oxygen/blob/master/docs/test-isinteractiveuser.md
    #>


    [CmdletBinding()]
    [OutputType([boolean])]
    Param()

    [Environment]::UserInteractive
}