PSCommonCore.psm1

<#
    ===========================================================================
     Created with: SAPIEN Technologies, Inc., PowerShell Studio 2019 v5.6.166
     Created on: 8/6/2019 11:59 AM
     Created by: Gary Cook
     Organization: Quest
     Filename: PSCommonCore.psm1
    -------------------------------------------------------------------------
     Module Name: PSCommonCore
    ===========================================================================
    #requires the PShellLogging module found on the Powershell Gallery
#>





Function Test-Module
{
    param
    (
        [parameter(Mandatory = $true)]
        [string]$Module,
        [parameter(Mandatory = $true)]
        [bool]$Load = $false
        
    )
    [CmdletBinding]
    $Return = New-Object System.Management.Automation.PSObject
    $Return | Add-Member -MemberType NoteProperty -Name Status -Value $null
    $Return | Add-Member -MemberType NoteProperty -Name Message -Value $null
    
    if (!(get-module $Module))
    {
        If (!(Get-Module -ListAvailable $Module))
        {
            $Return.Status = "Not Loaded"
            $Return.Message = "The Module $($Module) was not available to load"
            
        }
        else
        {
            if ($Load -eq $true)
            {
                Import-Module ActiveDirectory -ErrorAction SilentlyContinue
                if (!(Get-Module activedirectory))
                {
                    $Return.Status = "Not Loaded"
                    $Return.Message = "The Module $($Module) was available but failed to load"
                }
                else
                {
                    $Return.Status = "Loaded"
                    $Return.Message = "Module $($Module) was loaded successfully"
                }
            }
            else
            {
                $Return.Status = "Not Loaded"
                $Return.Message = "The Module $($Module) was available but not loaded due to switch"
            }
            
        }
        
    }
    else
    {
        $Return.Status = "Loaded"
        $Return.Message = "Module $($Module) was already loaded"
    }
    return $Return
}

function Test-PSVersion 
{
    param
    (
        [parameter(Mandatory = $true)]
        [string]$Min,
        [parameter(Mandatory = $true)]
        [ValidateSet("Desktop","Core")]
        [string]$Edition = "Desktop"
    )
    
    
    if ($Min.contains(".") -eq $true)
    {
        #Write-Host "Checking Powershell against $($Min)"
        $Major = ($Min -split "\.")[0]
        #Write-Host "Major Version: $($Major)"
        $Minor = ($Min -split "\.")[1]
        #Write-Host "Minor Version: $($Minor)"
    }
    else
    {
        #Write-Host "Checking Powershell against $($Min)"
        $Major = $Min
        #Write-Host "Major Version: $($Major)"
        $Minor = "0"
        #Write-Host "Minor Version: $($Minor)"
    }
    $Version = $PSVersionTable.pscompatibleversions
    $Return = $false
    #Write-Host "Processing PowerShell Compatable Versions"
    foreach ($V in $Version)
    {
        #Write-Host "Checking Version Major:$($V.major) Minor:$($V.minor)"
        if ($V.major -eq $Major -and $V.minor -eq $Minor)
        {
            #Write-Host "Matches Minimum Version"
            $Return = $true
        }
    }
    if ($Edition -eq $PSVersionTable.PSEdition)
    {
        return $Return
    }
    else
    {
        return $false
    }
    
    
}

function Write-Color([String[]]$Text, [ConsoleColor[]]$Color = (get-host).ui.rawui.ForegroundColor, [ConsoleColor[]]$BackColor = (get-host).ui.rawui.BackgroundColor, [int]$StartTab = 0, [int]$LinesBefore = 0, [int]$LinesAfter = 0)
{
    $DefaultColor = $Color[0]
    $DefaultBackColor = $BackColor[0]
    if ($LinesBefore -ne 0) { for ($i = 0; $i -lt $LinesBefore; $i++) { Write-Host "`n" -NoNewline } } # Add empty line before
    if ($StartTab -ne 0) { for ($i = 0; $i -lt $StartTab; $i++) { Write-Host "`t" -NoNewLine } } # Add TABS before text
    if ($Color.Count -ge $Text.Count -and $BackColor.count -ge $Text.count)
    {
        for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -backgroundcolor $BackColor[$i] -NoNewLine }
    }
    else
    {
        if ($Color.Count -ge $Text.Count -and $BackColor.Count -lt $Text.Count)
        {
            for ($i = 0; $i -lt $BackColor.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackColor[$i] -NoNewLine }
            for ($i = $BackColor.Length; $i -lt $Text.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $DefaultBackColor -NoNewLine }
        }
        if ($Color.Count -lt $Text.Count -and $BackColor.Count -ge $Text.Count)
        {
            for ($i = 0; $i -lt $Color.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackColor[$i] -NoNewLine }
            for ($i = $BackColor.Length; $i -lt $Text.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $BackColor[$i] -NoNewLine }
        }
        if ($Color.Count -lt $Text.Count -and $BackColor.Count -lt $Text.Count)
        {
            if ($Color.Count -lt $BackColor.count)
            {
                for ($i = 0; $i -lt $Color.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackColor[$i] -NoNewLine }
                for ($i = $Color.Length; $i -lt $BackColor.length; $i++) { Write-Host $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $BackColor[$i] -NoNewLine }
                for ($i = $BackColor.Length; $i -lt $Text.length; $i++) { Write-Host $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $DefaultBackColor -NoNewLine }
            }
            if ($Color.Count -gt $BackColor.count)
            {
                for ($i = 0; $i -lt $BackColor.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackColor[$i] -NoNewLine }
                for ($i = $BackColor.Length; $i -lt $Color.length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $DefaultBackColor -NoNewLine }
                for ($i = $Color.Length; $i -lt $Text.length; $i++) { Write-Host $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $DefaultBackColor -NoNewLine }
            }
            if ($Color.Count -eq $BackColor.count)
            {
                for ($i = 0; $i -lt $BackColor.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackColor[$i] -NoNewLine }
                for ($i = $BackColor.Length; $i -lt $text.length; $i++) { Write-Host $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $DefaultBackColor -NoNewLine }
            }
        }
        
        
    }
    Write-Host
    if ($LinesAfter -ne 0) { for ($i = 0; $i -lt $LinesAfter; $i++) { Write-Host "`n" } } # Add empty line after
}

function Connect-EOL
{
    param
    (
        [parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $Credential
    )
    if ($Credential -eq $null)
    {
        $Credential = Get-Credential -Message "Please Enter your Exchange Online Admin Credential."
    }
    $Session = New-PSSession -ConfigurationName "Microsoft.Exchange" -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $Credential -Authentication Basic -AllowRedirection
    Import-PSSession $Session -DisableNameChecking -AllowClobber
    return $session
}

function Disconnect-EOL
{
    param
    (
        [parameter(Mandatory = $true,ValueFromPipeline = $true)]
        $Session
    )
    Remove-PSSession -Session $Session
}

function Start-Log
{
    <#
        .SYNOPSIS
            Creates the supplied log file $Log.
        .DESCRIPTION
        .PARAMETER
            $Log
                the complete path to the log to write to. required.
            $type
                the type of log file to generate TXT is assumed. Possible Values are TXT, CSV, JSON.
        .EXAMPLE
            creates a log at location and returns object representing the log and type
            Start-Log -Log "C:\applog.txt" -Type CSV
        .NOTES
            FunctionName : Write-Log
            Created by : Gary Cook
            Date Coded : 07/26/2019
        .OUTPUTS
            Returns and object containing the path to the log and the type of the log.
    #>

    [CmdletBinding()]
    Param
    (
        [parameter (position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string]$Log,
        [Parameter (Position = 1, Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateSet("TXT", "CSV", "JSON")]
        [string]$Type = "TXT"
    )
    Begin
    {
    }
    Process
    {
        
        # Format Date for our Log File
        $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        #if log does not exists create log with application runtime banner
        if (!(Test-Path $Log -PathType Leaf))
        {
            if (!(Test-Path $Log))
            {
                #create file including path if the path does not exist
                $NewLogFile = New-Item $Log -Force -ItemType File
            }
            if ($Type -eq "TXT")
            {
                #create file with banner
                $Banner = "*************************************************"
                $Banner | Out-File -FilePath $Log -Append -force
                $Banner = "Application log created $($FormattedDate) on computer $($env:COMPUTERNAME)"
                $Banner | Out-File -FilePath $Log -Append
                $Banner = "*************************************************"
                $Banner | Out-File -FilePath $Log -Append
            }
            if ($Type -eq "CSV")
            {
                #open out file with headder
                $Banner = "Date,Level,Message"
                $Banner | Out-File -FilePath $Log -Append -force
                $Banner = "$($FormattedDate),INFO:,Application Log file Created for computer $($env:COMPUTERNAME)"
                $Banner | Out-File -FilePath $Log -Append
            }
            if ($Type -eq "JSON")
            {
                $Banner = "{`"DATE`": `"$($FormattedDate)`",`"LEVEL`": `"INFO:`",`"MESSAGE`": `"Application Log file Created for computer $($env:COMPUTERNAME)`"}"
                $Banner | Out-File -FilePath $Log -Append -force
            }
            
        }
        $obj = new-object System.Management.Automation.PSObject
        $obj | Add-Member -MemberType NoteProperty -Name Log -Value (get-item $log).VersionInfo.filename
        $obj | Add-Member -MemberType NoteProperty -Name Type -Value $Type
        
        return $obj
        
    }
    end
    {
        
    }
    
    
}



Function Write-Log
{
    <#
        .SYNOPSIS
            Writes the Entry in $Line to the supplied log file $Log. Built to take pipeline input from object returned from start-log.
        .DESCRIPTION
        .PARAMETER
            $Line
                String of data to write to the log file. required.
            $Log
                the complete path to the log to write to. required.
            $Level
                The type of line to write to the log. Valid vales are Error,Warn,Info. Default is Info.
            $Type
                the type of log file to generate TXT is assumed. Possible Values are TXT, CSV, JSON.
        .EXAMPLE
            $mylog | Write-Log -Line "This is an entry for the log" -level Info
        .NOTES
            FunctionName : Write-Log
            Created by : Gary Cook
            Date Coded : 07/26/2019
        .OUTPUTS
            Returns 0 if log exists or -1 if the log file does not exist
    #>

    [CmdletBinding()]
    Param
    (
        [parameter (position = 0, Mandatory = $true)]
        [string]$Line,
        [parameter (position = 1, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string]$Log,
        [Parameter (position = 2, Mandatory = $false)]
        [ValidateSet("Error", "Warn", "Info")]
        [string]$Level = "Info",
        [Parameter (Position = 3, Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateSet("TXT", "CSV", "JSON")]
        [string]$Type = "TXT"
    )
    Begin
    {
    }
    Process
    {
        
        # Format Date for our Log File
        $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        # Write message to error, warning, or verbose pipeline and specify $LevelText
        switch ($Level)
        {
            'Error' {
                
                $LevelText = 'ERROR:'
            }
            'Warn' {
                
                $LevelText = 'WARNING:'
            }
            'Info' {
                
                $LevelText = 'INFO:'
            }
        }
        #if log does not exists reutrn -1 else return 0
        if (!(Test-Path $Log -PathType Leaf))
        {
            if (!(Test-Path $Log))
            {
                return -1
                break
                
            }
            
            
        }
        # Write message to proper log type
        switch ($Type)
        {
            'TXT' {
                "$($FormattedDate) $($LevelText) $($Line)" | Out-File -FilePath $Log -Append
            }
            'CSV' {
                "$($FormattedDate),$($LevelText),$($Line)" | Out-File -FilePath $Log -Append
            }
            'JSON' {
                "{`"DATE`": `"$($FormattedDate)`",`"LEVEL`": `"$($LevelText)`",`"MESSAGE`": `"$($Line)`"}" | Out-File -FilePath $Log -Append
            }
        }
        
        
        return 0
    }
    End
    {
    }
}


Function Close-Log
{
    <#
        .SYNOPSIS
            Closes the supplied log file $Log. Built to take pipeline input from object returned from start-log.
        .DESCRIPTION
        .PARAMETER
            $Log
                the complete path to the log to write to. required.
            $Type
                the type of log file to generate TXT is assumed. Possible Values are TXT, CSV, JSON.
        .EXAMPLE
            $mylog | Close-Log
        .NOTES
            FunctionName : Write-Log
            Created by : Gary Cook
            Date Coded : 07/26/2019
    #>

    [CmdletBinding()]
    Param
    (
        [parameter (position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string]$Log,
        [Parameter (Position = 1, Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateSet("TXT", "CSV", "JSON")]
        [string]$Type = "TXT"
    )
    Begin
    {
    }
    Process
    {
        
        # Format Date for our Log File
        $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        if ($Type -eq "TXT")
        {
            #close out file with footer
            $Footer = "*************************************************"
            $Footer | Out-File -FilePath $Log -Append
            $Footer = "Application log end $($FormattedDate) on computer $($env:COMPUTERNAME)"
            $Footer | Out-File -FilePath $Log -Append
            $Footer = "*************************************************"
            $Footer | Out-File -FilePath $Log -Append
        }
        if ($Type -eq "CSV")
        {
            #close out file with footer
            $Footer = "$($FormattedDate),INFO:,Application Log file end for computer $($env:COMPUTERNAME)"
            $Footer | Out-File -FilePath $Log -Append
        }
        if ($Type -eq "JSON")
        {
            $Footer = "{`"DATE`": `"$($FormattedDate)`",`"LEVEL`": `"INFO:`",`"MESSAGE`": `"Application Log file end for computer $($env:COMPUTERNAME)`"}"
            $Footer | Out-File -FilePath $Log -Append
        }
        
        
        
    }
    End
    {
    }
    
    
}

function New-RandomString([string]$inputString)
{
    $characterArray = $inputString.ToCharArray()
    $scrambledStringArray = $characterArray | Get-Random -Count $characterArray.Length
    $outputString = -join $scrambledStringArray
    return $outputString
}

function Get-RandomCharacters($length, $characters)
{
    $random = 1 .. $length | ForEach-Object { Get-Random -Maximum $characters.length }
    $private:ofs = ""
    return [String]$characters[$random]
}

function New-password
{
    param
    (
        [parameter(Mandatory = $true)]
        [int]$Length,
        [parameter(Mandatory = $false)]
        [switch]$Upper = $false,
        [parameter(Mandatory = $false)]
        [switch]$Lower = $false,
        [parameter(Mandatory = $false)]
        [switch]$Number = $false,
        [parameter(Mandatory = $false)]
        [switch]$Symbol = $false,
        [parameter(Mandatory = $false)]
        [int]$MinLower = 1,
        [parameter(Mandatory = $false)]
        [int]$MinUpper = 1,
        [parameter(Mandatory = $false)]
        [int]$MinNumber = 1,
        [parameter(Mandatory = $false)]
        [int]$MinSymbol = 1,
        [parameter(Mandatory = $false)]
        [switch]$NonAmbiguous = $false
    )
    # Fix password length if minchars is more than length
    $BaseCount = 1
    if ($Upper)
    {
        $BaseCount += $MinUpper
    }
    if ($Lower)
    {
        $BaseCount += $MinLower
    }
    if ($Number)
    {
        $BaseCount += $MinNumber
    }
    if ($Symbol)
    {
        $BaseCount += $MinSymbol
    }
    if ($BaseCount -gt $Length)
    {
        $Length = $BaseCount
    }
    $remaining = $Length - $BaseCount
    
    # Creates character arrays for the different character classes, based on ASCII character values.
    [string]$charsLower = (97 .. 122 | %{ [Char]$_ })
    $charsLower = $charsLower -replace " ", ""
    [string]$charsUpper = (65 .. 90 | %{ [Char]$_ })
    $charsUpper = $charsUpper -replace " ", ""
    [string]$charsNumber = (48 .. 57 | %{ [Char]$_ })
    $charsNumber = $charsNumber -replace " ", ""
    [string]$charsSymbol = (35, 36, 40, 41, 42, 44, 45, 46, 47, 58, 59, 63, 64, 92, 95 | %{ [Char]$_ })
    $charsSymbol = $charsSymbol -replace " ", ""
    
    # Create character arrays for non ambiguous characters l (ell), 1 (one), I (capital i), O (capital o), 0 (zero), B (capital b), 8 (eight), q (queue), g (gee), | (pipe)
    [string]$charsLowera = "abcdefhijkmnoprstuvwxyz"
    $charsLowera = $charsLowera -replace " ", ""
    [string]$charsUppera = "ACDEFGHJKLMNPQRTUVWXYZ"
    $charsUppera = $charsUppera -replace " ", ""
    [string]$charsNumbera = "234679"
    $charsNumbera = $charsNumbera -replace " ", ""
    [string]$charsSymbola = "!@#$%^&*()-=+:,?_.~"
    $charsSymbola = $charsSymbola -replace " ", ""
    
    
    if ($NonAmbiguous -eq $false)
    {
        if ($Upper)
        {
            $RandomUString = Get-RandomCharacters -length $MinUpper -characters $charsUpper
        }
        else
        {
            $RandomUString = ""
        }
        if ($Lower)
        {
            $RandomLString = Get-RandomCharacters -length $MinLower -characters $charsLower
        }
        else
        {
            $RandomLString = ""
        }
        if ($Number)
        {
            $RandomNString = Get-RandomCharacters -length $MinNumber -characters $charsNumber
        }
        else
        {
            $RandomNString = ""
        }
        if ($Symbol)
        {
            $RandomSString = Get-RandomCharacters -length $MinSymbol -characters $charsSymbol
        }
        else
        {
            $RandomSString = ""
        }
        
        
        
        if ($remaining -gt 0)
        {
            $Tempc = ""
            if ($Upper)
            {
                $Tempc += $charsUpper
            }
            if ($Lower)
            {
                $Tempc += $charsLower
            }
            if ($Number)
            {
                $Tempc += $charsNumber
            }
            if ($Symbol)
            {
                $Tempc += $charsSymbol
            }
            $RandomString = Get-RandomCharacters -length $remaining -characters $Tempc
        }
        else
        {
            $RandomString = ""
        }
    }
    else
    {
        if ($Upper)
        {
            $RandomUString = Get-RandomCharacters -length $MinUpper -characters $charsUppera
        }
        else
        {
            $RandomUString = ""
        }
        if ($Lower)
        {
            $RandomLString = Get-RandomCharacters -length $MinLower -characters $charsLowera
        }
        else
        {
            $RandomLString = ""
        }
        if ($Number)
        {
            $RandomNString = Get-RandomCharacters -length $MinNumber -characters $charsNumbera
        }
        else
        {
            $RandomNString = ""
        }
        if ($Symbol)
        {
            $RandomSString = Get-RandomCharacters -length $MinSymbol -characters $charsSymbola
        }
        else
        {
            $RandomSString = ""
        }
        if ($remaining -gt 0)
        {
            $Tempc = ""
            if ($Upper)
            {
                $Tempc += $charsUppera
            }
            if ($Lower)
            {
                $Tempc += $charsLowera
            }
            if ($Number)
            {
                $Tempc += $charsNumbera
            }
            if ($Symbol)
            {
                $Tempc += $charsSymbola
            }
            $RandomString = Get-RandomCharacters -length $remaining -characters $Tempc
        }
        else
        {
            $RandomString = ""
        }
    }
    
    #combine all into a single string
    $Return = $RandomUString + $RandomLString + $RandomNString + $RandomSString + $RandomString
    
    #scramble the scring
    $Return = New-RandomString -inputString $Return
    
    return $Return
    
    

}


function get-loggedonuser ()
{
    [CmdletBinding()]
    Param
    (
        [parameter (position = 0, Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string]$computername ,
        [Parameter (Position = 1, Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateSet($true,$false)]
        [bool]$local = $false,
        [Parameter (Position = 2, Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [System.Management.Automation.PSCredential]$credential
    )
    Begin
    {
    }
    Process
    {
        
        
        if ($local -eq $false -and $computername -eq $null)
        {
            $computername = Read-Host "Remote computername was not provided please enter remote computername"
            if ($computername -eq "")
            {
                Write-Host "Computername not provided exiting"
                end
            }
        }
        if ($credential -eq $null)
        {
            write-host "User Credential not provided using logged on user"
            $localuser = $true
        }
        
        $regexa = '.+Domain="(.+)",Name="(.+)"$'
        $regexd = '.+LogonId="(\d+)"$'
        
        $logontype = @{
            "0"  = "Local System"
            "2"  = "Interactive" #(Local logon)
            "3"  = "Network" # (Remote logon)
            "4"  = "Batch" # (Scheduled task)
            "5"  = "Service" # (Service account logon)
            "7"  = "Unlock" #(Screen saver)
            "8"  = "NetworkCleartext" # (Cleartext network logon)
            "9"  = "NewCredentials" #(RunAs using alternate credentials)
            "10" = "RemoteInteractive" #(RDP\TS\RemoteAssistance)
            "11" = "CachedInteractive" #(Local w\cached credentials)
        }
        switch ($local) {
            $true {
                $logon_sessions = @(gwmi win32_logonsession -ComputerName "localhost")
                $logon_users = @(gwmi win32_loggedonuser -ComputerName "localhost")
            }
            $false {
                if ($localuser -ne $true)
                {
                    $logon_sessions = @(gwmi win32_logonsession -ComputerName $computername -Credential $credential)
                    $logon_users = @(gwmi win32_loggedonuser -ComputerName $computername -Credential $credential)
                }
                else
                {
                    $logon_sessions = @(gwmi win32_logonsession -ComputerName $computername )
                    $logon_users = @(gwmi win32_loggedonuser -ComputerName $computername )
                }
                
            }
            
        }
        
        
        $session_user = @{ }
        
        $logon_users | % {
            $_.antecedent -match $regexa > $nul
            $username = $matches[1] + "\" + $matches[2]
            $_.dependent -match $regexd > $nul
            $session = $matches[1]
            $session_user[$session] += $username
        }
        
        
        $logon_sessions | %{
            $starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
            
            $loggedonuser = New-Object -TypeName psobject
            $loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
            $loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
            $loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
            $loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
            $loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
            
            $loggedonuser
        }
    }
    end
    {
        
    }
    
}

Function Set-Owner
{
    <#
        .SYNOPSIS
            Changes owner of a file or folder to another user or group.
 
        .DESCRIPTION
            Changes owner of a file or folder to another user or group.
 
        .PARAMETER Path
            The folder or file that will have the owner changed.
 
        .PARAMETER Account
            Optional parameter to change owner of a file or folder to specified account.
 
            Default value is 'Builtin\Administrators'
 
        .PARAMETER Recurse
            Recursively set ownership on subfolders and files beneath given folder.
 
        .NOTES
            Name: Set-Owner
            Author: Boe Prox
            Version History:
                 1.0 - Boe Prox
                    - Initial Version
                 2.0 - Gary Cook
                    - Added PShellLogging to capture Success and Error Information
                    - Fixed issues processing owner on specific server OS's
     
            Requires the PSHellLogging Modules Available on PowerShell gallery
            https://www.powershellgallery.com/packages/PShellLogging/1.1.13
            Use "Install-Module -Name PShellLogging" on powershell 5.0 and higher to install
 
        .EXAMPLE
            Set-Owner -Path C:\temp\test.txt -Log c:\logs\ownerlog.csv -logtype CSV
 
            Description
            -----------
            Changes the owner of test.txt to Builtin\Administrators, Logs output to c:\logs\ownerlog.csv in comma Seperated Format
 
        .EXAMPLE
            Set-Owner -Path C:\temp\test.txt -Account 'Domain\bprox -Log c:\logs\ownerlog.TXT -logtype TXT
 
            Description
            -----------
            Changes the owner of test.txt to Domain\bprox, Logs output to c:\logs\ownerlog.csv in text format
 
        .EXAMPLE
            Set-Owner -Path C:\temp -Recurse -Log c:\logs\ownerlog.csv -logtype CSV
 
            Description
            -----------
            Changes the owner of all files and folders under C:\Temp to Builtin\Administrators, Logs output to c:\logs\ownerlog.csv in comma Seperated Format
 
        .EXAMPLE
            Get-ChildItem C:\Temp | Set-Owner -Recurse -Account 'Domain\bprox' -Log c:\logs\ownerlog.csv -logtype CSV
 
            Description
            -----------
            Changes the owner of all files and folders under C:\Temp to Domain\bprox, Logs output to c:\logs\ownerlog.csv in comma Seperated Format
    #>

    [cmdletbinding(
                   SupportsShouldProcess = $True
                   )]
    Param (
        [parameter(mandatory = $true, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias('FullName')]
        [string[]]$Path,
        [parameter()]
        [string]$Account = 'Builtin\Administrators',
        [parameter()]
        [switch]$Recurse,
        [parameter()]
        [string]$Log,
        [parameter()]
        [ValidateSet("TXT", "CSV", "JSON")]
        [string]$logtype = "CSV"
    )
    Begin
    {
        #Create Log if necessary
        if ($Log -ne $null)
        {
            $MyLog = Start-Log -Log $Log -Type $logtype
            $logging = $true
        }
        #Prevent Confirmation on each Write-Debug command when using -Debug
        If ($PSBoundParameters['Debug'])
        {
            $DebugPreference = 'Continue'
        }
        Try
        {
            [void][TokenAdjuster]
        }
        Catch
        {
            $AdjustTokenPrivileges = @"
            using System;
            using System.Runtime.InteropServices;
 
             public class TokenAdjuster
             {
              [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
              internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
              ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
              [DllImport("kernel32.dll", ExactSpelling = true)]
              internal static extern IntPtr GetCurrentProcess();
              [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
              internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr
              phtok);
              [DllImport("advapi32.dll", SetLastError = true)]
              internal static extern bool LookupPrivilegeValue(string host, string name,
              ref long pluid);
              [StructLayout(LayoutKind.Sequential, Pack = 1)]
              internal struct TokPriv1Luid
              {
               public int Count;
               public long Luid;
               public int Attr;
              }
              internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
              internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
              internal const int TOKEN_QUERY = 0x00000008;
              internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
              public static bool AddPrivilege(string privilege)
              {
               try
               {
                bool retVal;
                TokPriv1Luid tp;
                IntPtr hproc = GetCurrentProcess();
                IntPtr htok = IntPtr.Zero;
                retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
                tp.Count = 1;
                tp.Luid = 0;
                tp.Attr = SE_PRIVILEGE_ENABLED;
                retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
                retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
                return retVal;
               }
               catch (Exception ex)
               {
                throw ex;
               }
              }
              public static bool RemovePrivilege(string privilege)
              {
               try
               {
                bool retVal;
                TokPriv1Luid tp;
                IntPtr hproc = GetCurrentProcess();
                IntPtr htok = IntPtr.Zero;
                retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
                tp.Count = 1;
                tp.Luid = 0;
                tp.Attr = SE_PRIVILEGE_DISABLED;
                retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
                retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
                return retVal;
               }
               catch (Exception ex)
               {
                throw ex;
               }
              }
             }
"@

            Add-Type $AdjustTokenPrivileges
        }
        
        #Activate necessary admin privileges to make changes without NTFS perms
        [void][TokenAdjuster]::AddPrivilege("SeRestorePrivilege") #Necessary to set Owner Permissions
        [void][TokenAdjuster]::AddPrivilege("SeBackupPrivilege") #Necessary to bypass Traverse Checking
        [void][TokenAdjuster]::AddPrivilege("SeTakeOwnershipPrivilege") #Necessary to override FilePermissions
    }
    Process
    {
        ForEach ($Item in $Path)
        {
            Write-Verbose "FullName: $Item"
            if ($logging)
            {
                $MyLog | Write-Log -Line "Proicessing item $($Item)" -Level Info
            }
            #The ACL objects do not like being used more than once, so re-create them on the Process block
            $DirOwner = New-Object System.Security.AccessControl.DirectorySecurity
            $DirOwner.SetOwner([System.Security.Principal.NTAccount]$Account)
            $FileOwner = New-Object System.Security.AccessControl.FileSecurity
            $FileOwner.SetOwner([System.Security.Principal.NTAccount]$Account)
            $DirAdminAcl = New-Object System.Security.AccessControl.DirectorySecurity
            $FileAdminAcl = New-Object System.Security.AccessControl.DirectorySecurity
            $AdminACL = New-Object System.Security.AccessControl.FileSystemAccessRule('Builtin\Administrators', 'FullControl', 'ContainerInherit,ObjectInherit', 'InheritOnly', 'Allow')
            $FileAdminAcl.AddAccessRule($AdminACL)
            $DirAdminAcl.AddAccessRule($AdminACL)
            Try
            {
                $Item = Get-Item -LiteralPath $Item -Force -ErrorAction Stop
                If (-NOT $Item.PSIsContainer)
                {
                    If ($PSCmdlet.ShouldProcess($Item, 'Set File Owner'))
                    {
                        Try
                        {
                            $Item.SetAccessControl($FileOwner)
                        }
                        Catch
                        {
                            Write-Warning "Couldn't take ownership of $($Item.FullName)! Taking FullControl of $($Item.Directory.FullName)"
                            $Item.Directory.SetAccessControl($FileAdminAcl)
                            $Item.SetAccessControl($FileOwner)
                        }
                    }
                }
                Else
                {
                    If ($PSCmdlet.ShouldProcess($Item, 'Set Directory Owner'))
                    {
                        Try
                        {
                            $Item.SetAccessControl($DirOwner)
                        }
                        Catch
                        {
                            Write-Warning "Couldn't take ownership of $($Item.FullName)! Taking FullControl of $($Item.Parent.FullName)"
                            $Item.Parent.SetAccessControl($DirAdminAcl)
                            $Item.SetAccessControl($DirOwner)
                        }
                    }
                    If ($Recurse)
                    {
                        [void]$PSBoundParameters.Remove('Path')
                        Get-ChildItem $Item -Force | Set-Owner @PSBoundParameters
                    }
                }
                if ($logging)
                {
                    $MyLog | Write-Log -Line "Item $($Item) was successfully processed" -Level Info
                }
            }
            Catch
            {
                Write-Warning "$($Item): $($_.Exception.Message)"
                if ($logging)
                {
                    $MyLog | Write-Log -Line "Item $($Item) processing failed error message $($_.Exception.Message)" -Level Error
                }
            }
        }
    }
    End
    {
        #Remove priviledges that had been granted
        [void][TokenAdjuster]::RemovePrivilege("SeRestorePrivilege")
        [void][TokenAdjuster]::RemovePrivilege("SeBackupPrivilege")
        [void][TokenAdjuster]::RemovePrivilege("SeTakeOwnershipPrivilege")
        if ($logging)
        {
            $MyLog | Close-Log
        }
    }
}