PSPowerAdminTasks.psm1

#Region '.\Classes\01CLASS_COMPUTER.ps1' -1

class COMPUTER
{
    #region <Properties>
    [System.String]$Name
    [System.DateTime]$CheckTime
    [System.String]$Status
    [System.String]$SamAccountName
    [System.String]$CN
    [System.String]$Operatingsystem
    [System.String]$Description
    [System.String]$IPv4Address
    [System.String]$Created
    [System.String]$LastLogontimestamp
    [System.String]$CanonicalName
    [System.String]$MemberOF
    [System.String]$HotFixDescription
    [System.String]$HotfixID
    [System.String]$HotFixInstalledBy
    [System.String]$HotFixInstalledOn
    [System.String]$LastBootUptime
    [System.String]$RebootNeeded
    HIDDEN [System.Management.Automation.PSCredential]$Credential
    #endregion <Properties>

    #region <Constructor>
    COMPUTER()
    {
    }

    COMPUTER([string]$ComputerName)
    {
        $This.CheckTime = Get-Date
        $this.Name = $ComputerName
        $this.TestIfComputerIsOnline($ComputerName)
    }

    COMPUTER([string]$ComputerName, [System.Management.Automation.PSCredential]$Credential)
    {
        $This.Credential = $Credential
        $this.Name = $ComputerName
        $this.TestIfComputerIsOnline($ComputerName)
    }
    #endregion <Constructor>

    #region <Methods>
    [void] GetALlInformation ()
    {
        if ($this.Status -eq "Ping OK")
        {
            $This.CheckTime = Get-Date
            $this.TestIfComputerExistInAd()
            $this.GetComputerLastHotFix()
            $this.GetComputerLastBootUptime()
            $this.TestIfRebootNeeded()
        }
    }

    [void] TestIfComputerIsOnline([string]$ComputerName)
    {
        try
        {
            if (Test-Connection -ComputerName $ComputerName -Count 1 -ErrorAction Stop)
            {
                $this.Status = "Ping OK"
            }
            else
            {
                $this.Status = "Ping KO"
            }
        }
        catch
        {
            $this.Status = "Host Unknown"
        }
    }

    [Boolean] TestIfComputerExistInAd ()
    {
        $Parameter = @{
            Properties  = 'Name', 'SamAccountName', 'CN', 'Operatingsystem', 'Description', 'IPv4Address', 'Created', 'LastLogontimestamp', 'CanonicalName', 'MemberOF'
            Filter      = { Name -eq $This.Name }
            ErrorAction = "SilentlyContinue"
        }

        if ($null -ne $this.Credential)
        {
            $Parameter.Add('Credential', $this.Credential)
        }

        $Computer = Get-ADComputer @Parameter
        if ($Computer)
        {
            try
            {
                $this.SamAccountName = $Computer.SamAccountName
                $this.CN = $Computer.CN
                $this.Operatingsystem = $Computer.Operatingsystem
                $this.Description = $Computer.Description
                $this.IPv4Address = $Computer.IPv4Address
                $this.Created = $Computer.Created
                $this.LastLogontimestamp = [DateTime]::FromFileTime($Computer.LastLogontimestamp)
                $this.CanonicalName = $Computer.CanonicalName
                if ($Computer.MemberOF.Count -gt 0)
                {
                    $this.MemberOF = ($Computer.MemberOf -like "*WSUS*").split("=")[1].split(",")[0]
                } else {
                    $This.MemberOF = "No WSUS Group"
                }
            }
            catch [System.Management.Automation.MethodException]
            {
                   Write-Output ('[{0:O}] ErrorID: {1}' -f (get-date),$_.Exception.Message)
                   Write-Output ('[{0:O}] Exception: {1}' -f (get-date),$_.FullyQualifiedErrorId)
                   Write-Output ('[{0:O}] Category: {1}' -f (get-date),(($_.Exception.GetType() | Select-Object -ExpandProperty UnderlyingSystemType).FullName))
            }
            catch
            {
                Write-Output ('[{0:O}] ErrorID: {1}' -f (get-date),$_.Exception.Message)
                Write-Output ('[{0:O}] Exception: {1}' -f (get-date),$_.FullyQualifiedErrorId)
                Write-Output ('[{0:O}] Category: {1}' -f (get-date),(($_.Exception.GetType() | Select-Object -ExpandProperty UnderlyingSystemType).FullName))
            }
            return $true
        }
        else
        {
            $this.SamAccountName = "Unknown"
            $this.CN = "Unknown"
            $this.Operatingsystem = "Unknown"
            $this.Description = "Unknown"
            $this.IPv4Address = "Unknown"
            $this.Created = "Unknown"
            $this.LastLogontimestamp = "Unknown"
            $this.CanonicalName = "Unknown"
            $this.MemberOF = "Unknown"
            return $false
        }
    }

    [Void] GetComputerLastHotFix ()
    {
        try
        {
            $HotfixParameter = @{
                ComputerName = $this.CN
                ErrorAction  = "Stop"
            }
            if ($null -ne $this.Credential)
            {
                $HotfixParameter['Credential'] = $this.Credential
            }
            $hotfix = Get-HotFix @HotfixParameter  | Sort-Object -Descending -Property InstalledOn | Select-Object -First 1 | Select-Object Description, HotfixID, InstalledBy, InstalledOn
            $this.HotfixID = $hotfix.HotfixID
            $this.HotFixDescription = $hotfix.Description
            $this.HotFixInstalledBy = $hotfix.InstalledBy
            $this.HotFixInstalledOn = $hotfix.InstalledOn

        }
        catch [System.UnauthorizedAccessException]
        {
            $this.HotfixID = "UnauthorizedAccessException"
            $this.HotFixDescription = "UnauthorizedAccessException"
            $this.HotFixInstalledBy = "UnauthorizedAccessException"
            $this.HotFixInstalledOn = "UnauthorizedAccessException"
        }
        catch
        {
            $this.HotfixID = "Unknown"
            $this.HotFixDescription = "Unknown"
            $this.HotFixInstalledBy = "Unknown"
            $this.HotFixInstalledOn = "Unknown"
        }
    }

    [Void] GetComputerLastBootUptime ()
    {
        try
        {
            $Parameter = @{
                ComputerName = $this.CN
                ErrorAction  = "Stop"
            }
            if ($null -ne $this.Credential)
            {
                $Parameter['Credential'] = $this.Credential
            }
            $this.LastBootUptime = Invoke-Command @Parameter -ScriptBlock {(Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -ExpandProperty LastBootUpTime)}
        }
        catch
        {
            Write-Output ('[{0:O}] ErrorID: {1}' -f (get-date),$_.Exception.Message)
            Write-Output ('[{0:O}] Exception: {1}' -f (get-date),$_.FullyQualifiedErrorId)
            Write-Output ('[{0:O}] Category: {1}' -f (get-date),(($_.Exception.GetType() | Select-Object -ExpandProperty UnderlyingSystemType).FullName))
            $this.LastBootUptime = "Unknown"
        }
    }

    [void] TestIfRebootNeeded ()
    {
        if ($this.Status -eq "Ping OK")
        {
            try
            {
                $CmdParameter = @{
                    ComputerName   = $this.CN
                    ErrorAction    = "Stop"
                    Authentication = "Kerberos"
                }
                if ($null -ne $this.Credential)
                {
                    $CmdParameter['Credential'] = $this.Credential
                }
                $this.RebootNeeded = Invoke-Command @CmdParameter -ScriptBlock {
                    if ((Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -ErrorAction SilentlyContinue) -or (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -ErrorAction SilentlyContinue) )
                    {
                        write-output "YES"
                    }
                    else {
                        Write-output "NO"
                    }
                }
            }
            catch
            {
                $This.RebootNeeded = "Unknown"
            }
        }
    }
    #endregion <Methods>
}
#EndRegion '.\Classes\01CLASS_COMPUTER.ps1' 230
#Region '.\Private\Write-Log.ps1' -1

function Write-Log
{
<#
.SYNOPSIS
Write a log file
 
.DESCRIPTION
Write all operation passed as parameter to a log file
 
.PARAMETER LogPath
Path to logfile. By default, the logfile is created in the temp folder of the current user
 
.PARAMETER Message
Message to write in the log file
 
.PARAMETER Severity
Severity of the message. By default, the severity is set to Information
 
.PARAMETER Console
Show the message in the console
 
.PARAMETER Force
Forece the creation of the log file
 
.EXAMPLE
Write-log -Message 'Starting Set-ChangePasswordAtNextLogon' -Severity Information -Console
 
.NOTES
General notes
#>


    [CmdletBinding()]
    param
    (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String]$LogPath = "$env:Temp\LogFile.csv",

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Message,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Information', 'Warning', 'Error')]
        [string]$Severity = 'Information',

        [Parameter()]
        [switch]$Console,

        [Parameter()]
        [switch]$Force
    )

    $info = [pscustomobject]@{
        Severity = "[$($Severity)]"
        Time     = ((Get-Date).toString("yyyy/MM/dd HH:mm:ss"))
        Message  = $Message
    }

    if ($Console)
    {
        Write-Output "$($info.Severity) $($info.Time) $($info.Message)"
    }

    if ($Force)
    {
        $info | Export-Csv -Path $LogPath -NoTypeInformation -Force
    }
    else
    {
        $info | Export-Csv -Path $LogPath -NoTypeInformation -Append
    }
}
#EndRegion '.\Private\Write-Log.ps1' 75
#Region '.\Public\Disable-CompromisedUser.ps1' -1

function Disable-CompromisedUser
{
<#
    .SYNOPSIS
    Disable compromised user
 
    .DESCRIPTION
    In case of compromission from some users, you can rapidly disable this users.
    You can pass to parameter :
        - a nominative list of user
        - a file with a nominative list of users (one user by line)
        - an OU to disable all users
    Check example for more details (get-help Disable-CompromisedUser -Examples)
    A log file is create in your temp directory ($env:temp)
 
    .PARAMETER Identity
    One or more user(s) to disable
 
    .PARAMETER FileName
    File with a list of users to disable. txt with one name by line
 
    .PARAMETER OU
    One or more OU(s) in which we want to disable all users
 
    .PARAMETER Check
    Only check if the users passed in parameter, whatever the way (Identity, Filename or OU), are disable
 
    .PARAMETER Credential
    Specifies the user account credentials to use when performing this task
 
    .PARAMETER Log
    Log information in a file (in $env:Temp )
 
    .PARAMETER Console
    Show information in console (need Log parameter)
 
    .EXAMPLE
    Disable-CompromisedUser -Identity "User1"
 
    Disable the user account : User1
 
    .EXAMPLE
    Disable-CompromisedUser -Identity "User1" -Log
 
    Disable the user account : User1 and log information in file in $env:temp
 
    .EXAMPLE
    Disable-CompromisedUser -Identity "User1" -Log -Console
 
    Disable the user account : User1, log information in file in $env:temp and log to console the same information
 
    .EXAMPLE
    Disable-CompromisedUser -Identity "User1" -Check
 
    Check if user account User1 is disable
 
    .EXAMPLE
    Disable-CompromisedUser -Identity "User1","User2","User3"
 
    Disable users account : User1, User2 and User3
 
    .EXAMPLE
    Disable-CompromisedUser -Identity "User1","User2","User3" -Check
 
    Check if users account User1, User2 and User3 are disable
 
    .EXAMPLE
    Disable-CompromisedUser -FileName "c:\temp\CompromisedUser.txt"
 
    File template CompromisedUser.txt :
    User1
    User2
    User3
 
    Disable users account : User1, User2 and User3
 
    .EXAMPLE
    Disable-CompromisedUser -FileName "c:\temp\CompromisedUser.txt" -Check
 
    File template CompromisedUser.txt :
    User1
    User2
    User3
 
    Check if users account User1, User2 and User3 are disable
 
    .EXAMPLE
    Disable-CompromisedUser -OU "OU=OU1,DC=contoso,DC=com"
 
    Disable all users present in OU1
 
    .EXAMPLE
    Disable-CompromisedUser -OU "OU=OU1,DC=contoso,DC=com" -Check
 
    Check if all users present in OU1 are disable
 
    .EXAMPLE
    Disable-CompromisedUser -OU "OU=OU1,DC=contoso,DC=com","OU=OU2,DC=contoso,DC=com"
 
    Disable all users present in OU1 and OU2
 
    .EXAMPLE
    Disable-CompromisedUser -OU "OU=OU1,DC=contoso,DC=com","OU=OU2,DC=contoso,DC=com" -check
 
    Check if all users present in OU1 and OU2 are disable
 
    .NOTES
    General notes
#>

    [CmdletBinding(DefaultParameterSetName = "ByUser")]
    param (
        [Parameter(
            ParameterSetName = "ByUser",
            HelpMessage = 'One or more user(s) to disable'
        )]
        [System.String[]]$Identity,
        [Parameter(
            ParameterSetName = "ByFileName",
            HelpMessage = 'File with a list of users to disable. txt with one name by line'
        )]
        [System.String]$FileName,
        [Parameter(
            ParameterSetName = "ByOu",
            HelpMessage = 'One or more OU(s) in which we want to disable all users'
        )]
        [System.String[]]$OU,
        [Parameter(
            HelpMessage = 'Only check if the users passed in parameter, whatever the way (Identity, Filename or OU), are disable'
        )]
        [Switch]$Check,

        [Parameter(
            HelpMessage = "Specifies the user account credentials to use when performing this task."
        )]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Parameter(
            HelpMessage = 'Log information in a file'
        )]
        [switch]$log,

        [Parameter(
            HelpMessage = 'Show information in console'
        )]
        [switch]$Console
    )


    begin
    {
        if ($log)
        {
            $LogPath = $env:Temp + '\Disable-CompromisedUser.csv'
            Write-Log -LogPath $LogPath -Message 'Starting Disable-CompromisedUser' -Severity Information -Console:$Console
        }
        $Users = @()

        switch ($PSCmdlet.ParameterSetName)
        {
            ByUser
            {
                if ($log) { write-log -LogPath $LogPath -Message "Retrieve AD User Account by user list" -Severity Information -Console:$Console}
                foreach ($User in $Identity)
                {
                    try
                    {
                        $Users += Get-ADUser -Identity $User -Properties SamAccountName,DisplayName,Enabled -ErrorAction Continue | Select-Object SamAccountName,DisplayName,Enabled
                        if ($log) { write-log -LogPath $LogPath -Message "User $($User) found" -Severity Information -Console:$Console}
                    }
                    catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
                    {
                        if ($log) { write-log -LogPath $LogPath -Message "User $($User) not found" -Severity Error -Console:$Console}
                    }
                }
            }
            ByFileName
            {
                if ($log) { write-log -LogPath $LogPath -Message "Retrieve AD User Account by file list" -Severity Information -Console:$Console}
                foreach ($User in (Get-Content -Path $FileName))
                {
                    try
                    {
                        $Users += Get-ADUser -Identity $User -Properties SamAccountName,DisplayName,Enabled -ErrorAction Continue | Select-Object SamAccountName,DisplayName,Enabled
                        if ($log) { write-log -LogPath $LogPath -Message "User $($User) found" -Severity Information -Console:$Console}
                    }
                    catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
                    {
                        if ($log) { write-log -LogPath $LogPath -Message "User $($User) not found" -Severity Error -Console:$Console}
                    }
                }
            }
            ByOu
            {
                if ($log) { write-log -LogPath $LogPath -Message "Retrieve AD User Account by OU list" -Severity Information -Console:$Console}
                foreach ($Organ in $OU)
                {
                    if ($log) { write-log -LogPath $LogPath -Message "Retrieve AD User Account in $($Organ)" -Severity Information -Console:$Console}
                    $Users += Get-ADUser -Filter * -SearchBase $Organ -Properties SamAccountName,DisplayName,Enabled | Select-Object SamAccountName,DisplayName,Enabled

                }
            }
        }
    }

    process
    {

        $Arguments = @{}
        if ($PSBoundParameters.ContainsKey('Credential'))
        {
            $Arguments['Credential'] = $Credential
        }

        foreach ($user in $Users)
        {
            if ($Check)
            {
                if ($log) { write-log -LogPath $LogPath -Message "Check state for user $( $User.SamAccountName)" -Severity Information -Console:$Console}
                if ($user.Enabled -eq "True")
                {
                    if ($log) { write-log -LogPath $LogPath -Message "user $($User.SamAccountName) is enabled" -Severity Information -Console:$Console}
                } else
                {
                    if ($log) { write-log -LogPath $LogPath -Message "user $($User.SamAccountName) is disabled" -Severity Information -Console:$Console}
                }
            } else
            {
                $Arguments['Identity'] = $Users.SamAccountName

                Disable-ADAccount @Arguments -Confirm:$false
                if ($log) { write-log -LogPath $LogPath -Message "Disabeling user $($User.SamAccountName)" -Severity Information -Console:$Console}
            }
        }
    }

    end
    {
        if ($log)
        {
            write-log -LogPath $LogPath -Message "Ending Disable-CompromisedUser" -Severity Information -Console:$Console
        }
    }
}
#EndRegion '.\Public\Disable-CompromisedUser.ps1' 247
#Region '.\Public\Find-InExcel.ps1' -1

function Find-InExcel
{
    <#
.SYNOPSIS
Find information in a excel file
 
.DESCRIPTION
This function return any informations from any Excel File. This function is based on the headers present in Excel file
 
.PARAMETER FilePath
Full path from de Excel file
 
.PARAMETER WorkSheetName
Name of the worksheet- where i was looking for the information. "Sheet1" by default
 
.PARAMETER HeaderRow
Number of the header row. "1" by defaults
 
.PARAMETER SearchRow
Column name used to filter the search
 
.PARAMETER DataRow
Column name return after the search. Can be on or more column
 
.PARAMETER SearchValue
The value tos search for in the column SearchRow
 
.EXAMPLE
Find-InExcel -FilePath "C:\PathToMyFile\MyFile.xlsx" -WorksheetName "Worksheet1" -HeaderRow "1" -SearchRow "Column1" -SearchValue "MyValue" -DataRow "Column3"
 
.EXAMPLE
Find-InExcel -FilePath "C:\PathToMyFile\MyFile.xlsx" -WorksheetName "Worksheet1" -HeaderRow "1" -SearchRow "Column1" -SearchValue "MyValue" -DataRow "Column3","Column1","Column2"
 
.NOTES
General notes
#>

    [CmdletBinding()]
    param (
        [Parameter()]
        [ValidateScript({
                if (-Not ($_ | Test-Path) )
                {
                    throw "File or folder does not exist"
                }
                if (-Not ($_ | Test-Path -PathType Leaf) )
                {
                    throw "The Path argument must be a file. Folder paths are not allowed."
                }
                if ($_ -notmatch "(\.xlsx)")
                {
                    throw "The file specified in the path argument must be either of type xlsx"
                }
                return $true
            })]
        [System.IO.FileInfo]$FilePath,
        [Parameter()]
        [ArgumentCompleter( {
            param (
                $Command,
                $Parameter,
                $WordToComplete,
                $CommandAst,
                $FakeBoundParams
                )
                Get-ExcelSheetInfo -Path $Fakeboundparams.FilePath | Where-Object { $_.Name -like "*$WordToComplete*" } | Select-Object -ExpandProperty Name
        })]
        [System.String]$WorkSheetName = "Sheet1",
        [Parameter()]
        [System.String]$HeaderRow = "1",
        [Parameter()]
        [ArgumentCompleter( {
            param (
                $Command,
                $Parameter,
                $WordToComplete,
                $CommandAst,
                $FakeBoundParams
                )

                Import-Excel -Path $Fakeboundparams.FilePath -WorksheetName $Fakeboundparams.WorkSheetName -HeaderRow $Fakeboundparams.HeaderRow | Get-Member -MemberType NoteProperty | Where-Object { $_.Name -like "*$WordToComplete*" } | Select-Object -ExpandProperty Name
        })]
        [System.String]$SearchRow,
        [Parameter()]
        [ArgumentCompleter( {
            param (
                $Command,
                $Parameter,
                $WordToComplete,
                $CommandAst,
                $FakeBoundParams
                )
                Import-Excel -Path $Fakeboundparams.FilePath -WorksheetName $Fakeboundparams.WorkSheetName -HeaderRow $Fakeboundparams.HeaderRow | Select-Object -exp $Fakeboundparams.SearchRow
        })]
        [System.string[]]$SearchValue = '*',
        [Parameter()]
        [ArgumentCompleter( {
            param (
                $Command,
                $Parameter,
                $WordToComplete,
                $CommandAst,
                $FakeBoundParams
                )
                Import-Excel -Path $Fakeboundparams.FilePath -WorksheetName $Fakeboundparams.WorkSheetName -HeaderRow $Fakeboundparams.HeaderRow | Get-Member -MemberType NoteProperty | Where-Object { $_.Name -like "*$WordToComplete*" } | Select-Object -ExpandProperty Name
        })]
        [System.String[]]$DataRow
    )

    begin
    {
        Import-Module ImportExcel -Verbose:$false
        Write-Verbose ('[{0:O}] Importing data from {1}' -f (get-date), $FilePath)
        $Data = Import-Excel -Path $FilePath -WorksheetName $WorkSheetName -HeaderRow $HeaderRow

        Write-Verbose ('[{0:O}] Headers validation' -f (get-date))
        $FileHeaders = $data | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
        foreach ($header in ($DataRow + $SearchRow))
        {
            if ($header -notin $FileHeaders)
            {
                throw "Header $($Header) not found"
            }
            else
            {
                Write-Verbose ('[{0:O}] Header "{1}" validate' -f (get-date), $Header)
            }
        }
    }

    process
    {

        $Result = @()
        if ($SearchValue -eq "*")
        {
            $Result = $data | Select-Object -Property $DataRow
        }
        else
        {
            foreach ($Value in $SearchValue)
            {
                Write-Verbose ('[{0:O}] Searching {1} in column {2}' -f (get-date), $Value, $SearchRow)
                $Result += $data | Where-Object { $_.$SearchRow -eq $Value } | Select-Object -Property $DataRow
            }
        }
    }

    end
    {
        $Result
    }
}
#EndRegion '.\Public\Find-InExcel.ps1' 153
#Region '.\Public\Find-InSupDB.ps1' -1

function Find-InSupDB
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$URL,
        # Parameter help description
        [Parameter()]
        [System.String]$ComputerName
    )

    begin
    {

    }

    process
    {
        # Récupérer le contenu HTML du site web
        $Html = Invoke-WebRequest -Uri $Url

        # récupérer les entêtes de colonnes et le contenu du tableau et les convertir en objet
        $Tableau = $Html.ParsedHtml.getElementsByTagName("tr") | Select-Object -Skip 1 | ForEach-Object {
            [PSCustomObject]@{
                Server     = $_.getElementsByTagName("td")[0].innerText
                Actif      = $_.getElementsByTagName("td")[1].innerText
                Ip_address = $_.getElementsByTagName("td")[2].innerText
                Plateform  = $_.getElementsByTagName("td")[3].innerText
                Port       = $_.getElementsByTagName("td")[5].innerText
                Type       =  $_.getElementsByTagName("td")[7].innerText
                RDBMS      = $_.getElementsByTagName("td")[10].innerText
                version    = $_.getElementsByTagName("td")[11].innerText
            }
        }
    }

    end
    {
        $Tableau | Where-Object {$_.Server -eq $ComputerName}
    }
}
#EndRegion '.\Public\Find-InSupDB.ps1' 42
#Region '.\Public\Find-UserLockoutsInformation.ps1' -1

Function Find-UserLockoutsInformation
{
<#
    .SYNOPSIS
    Find information about locked user account
 
    .DESCRIPTION
    This fonction search for locked user on PDC Emulator and return the lock source
    return :
    User : User1
    DomainController : PDCEmulator
    EventId : 4740
    LockoutTimeStamp : 8/3/2023 6:18:12 AM
    Message : A user account was locked out.
    LockoutSource : SourceComputer
    To find the reason use : Get-UserLockoutReason -Computer SourceComputer -Identity User1
 
    .PARAMETER Identity
    User to check (by default all)
 
    .PARAMETER DC
    Domain controller on which you want to look up information (by default PDC Emulator)
 
    .PARAMETER Credential
    Administrator credential to connect to the DC
 
    .EXAMPLE
    Find-UserLockoutsInformation -Credential (Get-Credential MyAdminAccount)
    Search information for all locked users in PDC Emulator
 
    .EXAMPLE
    Find-UserLockoutsInformation -Identity User1 -Credential (Get-Credential MyAdminAccount)
    Search information for user User1 in PDC Emulator
 
    .EXAMPLE
    Find-UserLockoutsInformation -Identity User1 -DC MyDC1 -Credential (Get-Credential MyAdminAccount)
    Search information for user User1 in specific domain controler MyDC1
 
    .NOTES
    General notes
#>

    [CmdletBinding(
        DefaultParameterSetName = 'All'
    )]
    param (
        [Parameter(
            ValueFromPipeline = $true,
            ParameterSetName = 'ByUser'
        )]
        [System.String]$Identity,
        [System.String]$DC = (Get-ADDomain).PDCEmulator,
        # Specifies the user account credentials to use when performing this task.
        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )
    Begin
    {
        Write-Verbose ('[{0:O}] Searching EventID : 4740 on Server : {1} ' -f (get-date), $DC)
        $WinEventArguments = @{
            ComputerName    = $DC
            FilterHashtable = @{LogName = 'Security'; Id = 4740 }
        }

        if ($PSBoundParameters.ContainsKey('Credential'))
        {
            $WinEventArguments['Credential'] = $Credential
        }
        try {
            $LockedOutEvents = Get-WinEvent @WinEventArguments -ErrorAction Stop | Sort-Object -Property TimeCreated -Descending
        }
        catch {
            if ($Error[-1].Exception.Message -like "*elevated user rights*") {
                throw ('[{0:O}] You need an admin account. Please provide with the -Credential parameter' -f (get-date))
            }
        }

        if ($LockedOutEvents) {
            Write-Verbose ('[{0:O}] {1} event found' -f (get-date), $LockedOutEvents.Count)
        } else {
            throw ('[{0:O}] No event found' -f (get-date))
        }
    }

    Process
    {
        switch ($PSCmdlet.ParameterSetName)
        {
            ByUser
            {
                Write-Verbose ('[{0:O}] Searching information for user : {1}' -f (get-date), $Identity)
                $UserInfo = Get-ADUser -Identity $Identity
                Foreach ($Event in $LockedOutEvents)
                {
                    If ($Event | Where-Object { $_.Properties[2].value -match $UserInfo.SID.Value })
                    {

                        $Event | Select-Object -Property @(
                            @{Label = 'User'; Expression = { $_.Properties[0].Value } }
                            @{Label = 'DomainController'; Expression = { $_.MachineName } }
                            @{Label = 'EventId'; Expression = { $_.Id } }
                            @{Label = 'LockoutTimeStamp'; Expression = { $_.TimeCreated } }
                            @{Label = 'Message'; Expression = { $_.Message -split "`r" | Select-Object -First 1 } }
                            @{Label = 'LockoutSource'; Expression = { $_.Properties[1].Value } }
                        )
                    }
                }
            }
            All
            {
                Write-Verbose ('[{0:O}] Searching information for all user(s) ' -f (get-date))
                Foreach ($Event in $LockedOutEvents)
                {

                    $Event | Select-Object -Property @(
                        @{Label = 'User'; Expression = { $_.Properties[0].Value } }
                        @{Label = 'DomainController'; Expression = { $_.MachineName } }
                        @{Label = 'EventId'; Expression = { $_.Id } }
                        @{Label = 'LockoutTimeStamp'; Expression = { $_.TimeCreated } }
                        @{Label = 'Message'; Expression = { $_.Message -split "`r" | Select-Object -First 1 } }
                        @{Label = 'LockoutSource'; Expression = { $_.Properties[1].Value } }
                    )
                }
            }
        }
    }
    End
    {
    }

}
#EndRegion '.\Public\Find-UserLockoutsInformation.ps1' 134
#Region '.\Public\Get-ADForestRootDomain.ps1' -1

Function Get-ADForestRootDomain {
    <#
    .SYNOPSIS
    Récupère l’objet AD Domain de la racine pour la forêt courante.
    .DESCRIPTION
    Remonte l’arborescence de domaine jusqu’à la racine et renvoi l’objet
    .PARAMETERS domain
    Le domaine de départ, par défaut le courant
    .OUTPUTS
    l’objet AD Domaine racine de la forêt
    .EXAMPLE
    PS C:> Get-ADForestRootDomain -domain unico.rn
    .AUTHORS
    TiTi
    .LASTUPDATE
    2015-06-08
    #>

        [CmdletBinding()]
        Param (
                $domain
        )
        if (!((Get-WindowsCapability -Name "Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0" -Online).State -eq "Installed")) {
            Write-Verbose ('[{0:O}] Installing Active Directory PowerShell module, please wait....' -f (get-date))
            Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0
        }

        $current = Get-ADDomain
        #on remonte dans l’arborescence, c’est-à-dire : tant que le domaine parent n’est pas vide.
        while($current.ParentDomain -ne $null){
            $current = Get-ADDomain $current.ParentDomain
        }
        Return $current
    }
#EndRegion '.\Public\Get-ADForestRootDomain.ps1' 34
#Region '.\Public\Get-ADGroupSubGroupMember.ps1' -1

Function Get-ADGroupSubGroupMember {
    <#
    .SYNOPSIS
    Fournie la liste des groupes imbriqués pour un groupe donné
    .DESCRIPTION
    Appel récursif sur l’attribut member du groupe en entrée pour obtenir la liste des membres de types « group » du groupe en entrée, tout en gérant les boucles éventuelles dans les groupes enfant.
    .PARAMETERS Group
    Le groupe à traiter
    .PARAMETERS ProcessedGroup
    Les groupes déjà traités pour éviter les boucles imbriquée, et appels infinis.
    .OUTPUTS
    La liste des sous-groupes du groupe en entrée (incluant le groupe initial).
    .EXAMPLE
    PS C:> Get-ADGroupSubGroupMember 'test3'
    .AUTHORS
    TiTi
    .LASTUPDATE
    2015-06-11
    #>

        [CmdletBinding()]
        Param(
              $Group,
              $ProcessedGroup = @()
        )
        #groupe traité
        $ProcessedGroup += Get-ADGroup $Group
        #On récupère les sous-groupes
        $SubGroupMember = @(Get-ADGroupMember $Group | Where-Object {$_.objectClass -eq 'group'})
        if($SubGroupMember -eq $null){
              #si pas de sous-groupe, on retourne les groupes traités
              return $ProcessedGroup
        }else{
              #Pour chaque sous-groupe(s), on rappelle la fonction
              foreach($SubGroup in $SubGroupMember){
                    if($SubGroup.SID -notin $ProcessedGroup.SID){
                          $ProcessedGroup = @(Get-ADGroupSubGroupMember -Group $SubGroup -ProcessedGroup $ProcessedGroup)
                    }
              }
              # Et on retourne les groupes traités
              return $ProcessedGroup
        }
    }
#EndRegion '.\Public\Get-ADGroupSubGroupMember.ps1' 43
#Region '.\Public\Get-BitLockerInformation.ps1' -1


function Get-BitLockerInformation {
<#
.SYNOPSIS
Retreive BitLocker information from Active Directory
 
.DESCRIPTION
Retreive BitLocker information for computer pass as parameter from Active Directory
 
.PARAMETER Computer
Computer name
 
.PARAMETER Credential
Credential with permission to read bitlocker information in Active Directory
 
.EXAMPLE
Get-BitlockerInformation -Computer "ComputerName" -Credential $MyCredential
 
Find bitlocker information for computer "ComputerName" with credential
 
.NOTES
General notes
#>

    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        [System.String[]]$Computer,
        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )

    begin {
        $Result = @()
        $Argument = @{
            Filter     = { ObjectClass -eq 'msFVE-RecoveryInformation' }
            Properties = 'msFVE-RecoveryPassword'
        }

        If ($PSBoundParameters.ContainsKey("Credential")) {

            $Argument.Add("Credential", $Credential)
        }
    }

    process {
        foreach ($Comp in $Computer) {
            Write-Verbose "Check computer : $($Comp)"
            $objComputer = Get-ADComputer -Identity $Comp
            $BitLockerObject = Get-ADObject @Argument -SearchBase $objComputer.DistinguishedName
            if ($BitLockerObject) {
                $Bitlock = $BitLockerObject | Sort-Object Name | Select-Object -last 1
                $object = [PSCustomObject]@{
                    "Computer"     = $Comp
                    "Password ID"  = $Bitlock.Name.split("{")[1].split("}")[0]
                    "Date"         = $Bitlock.Name.split("{")[0]
                    "Recovery Key" = $Bitlock.'msFVE-RecoveryPassword'
                }
                $Result += $object
            }
            else {
                $object = [PSCustomObject]@{
                    "Computer"     = $Comp
                    "Password ID"  = "null"
                    "Date"         = "null"
                    "Recovery Key" = "null"
                }
                $Result += $object
            }

        }
    }

    end {
        return $Result
    }
}
#EndRegion '.\Public\Get-BitLockerInformation.ps1' 80
#Region '.\Public\Get-DirectorySize.ps1' -1

function Get-DirectorySize
{
    param (
        [Parameter(ValueFromPipeline)] [Alias('PSPath')]
        [string] $LiteralPath = '.',
        [switch] $Recurse,
        [switch] $ExcludeSelf,
        [int] $Depth = -1,
        [int] $__ThisDepth = 0 # internal use only
    )

    process
    {

        # Resolve to a full filesystem path, if necessary
        $fullName = if ($__ThisDepth)
        {
            $LiteralPath
        }
        else
        {
            Convert-Path -ErrorAction Stop -LiteralPath $LiteralPath
        }

        if ($ExcludeSelf)
        {
            # Exclude the input dir. itself; implies -Recurse

            $Recurse = $True
            $ExcludeSelf = $False

        }
        else
        {
            # Process this dir.

            $NbOfFiles = (Get-ChildItem -Force -File -LiteralPath $fullName).Count
            # Calculate this dir's total logical size.
            # Note: [System.IO.DirectoryInfo].EnumerateFiles() would be faster,
            # but cannot handle inaccessible directories.
            $size = [Linq.Enumerable]::Sum(
                [long[]] (Get-ChildItem -Force -Recurse -File -LiteralPath $fullName).ForEach('Length')
            )

            # Create a friendly representation of the size.
            $decimalPlaces = 2
            $padWidth = 8
            $scaledSize = switch ([double] $size)
            {
                { $_ -ge 1tb }
                {
                    $_ / 1tb; $suffix = 'tb'; break
                }
                { $_ -ge 1gb }
                {
                    $_ / 1gb; $suffix = 'gb'; break
                }
                { $_ -ge 1mb }
                {
                    $_ / 1mb; $suffix = 'mb'; break
                }
                { $_ -ge 1kb }
                {
                    $_ / 1kb; $suffix = 'kb'; break
                }
                default
                {
                    $_; $suffix = 'b'; $decimalPlaces = 0; break
                }
            }

            # Construct and output an object representing the dir. at hand.
            [pscustomobject] @{
                FullName     = $fullName
                FriendlySize = ("{0:N${decimalPlaces}}${suffix}" -f $scaledSize).PadLeft($padWidth, ' ')
                Size         = $size
                Files        = $NbOfFiles
            }

        }

        # Recurse, if requested.
        if ($Recurse -or $Depth -ge 1)
        {
            if ($Depth -lt 0 -or (++$__ThisDepth) -le $Depth)
            {
                # Note: This top-down recursion is inefficient, because any given directory's
                # subtree is processed in full.
                Get-ChildItem -Force -Directory -LiteralPath $fullName |
                    ForEach-Object { Get-DirectorySize -LiteralPath $_.FullName -Recurse -Depth $Depth -__ThisDepth $__ThisDepth }
            }
        }

    }

}
#EndRegion '.\Public\Get-DirectorySize.ps1' 97
#Region '.\Public\Get-LastUpdateInformation.ps1' -1

function Get-LastUpdateInformation
{
    <#
    .SYNOPSIS
    Retrieve update information
 
    .DESCRIPTION
    Retrieve update information for a server
    the data is :
    Name
    CheckTime
    Status
    SamAccountName
    CN
    Operatingsystem
    Description
    IPv4Address
    Created
    LastLogontimestamp
    CanonicalName
    MemberOF
    HotFixDescription
    HotfixID
    HotFixInstalledBy
    HotFixInstalledOn
    LastBootUptime
    RebootNeeded
 
    .PARAMETER ComputerName
    One or more computer(s) to check
 
    .PARAMETER Credential
    Administrator credential to connect to the computer
 
    .EXAMPLE
    Get-LastUpdateInformation -ComputerName MyServer -Credential (Get-Credential MyAdminAccount) -Verbose
    Search last update information foe server MyServer using credential to connect
 
    .NOTES
    General notes
#>

    [CmdletBinding()]
    [OutputType([System.Object[]])]
    param (
        [Parameter(Mandatory = $true)]
        [System.String[]]$ComputerName,
        # Specifies the user account credentials to use when performing this task.
        [Parameter()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]$Credential
    )

    begin
    {
        $result = @()
    }

    process
    {
        if ($null -ne $Credential)
        {
            foreach ($computer in $ComputerName)
            {
                Write-Verbose ('[{0:O}] Computer {1}' -f (get-date),$computer)
                $Infos = [COMPUTER]::new($computer, $Credential)
                $Infos.GetALlInformation()
                $result += $Infos
            }
        }
        else
        {
            foreach ($computer in $ComputerName)
            {
                Write-Verbose ('[{0:O}] Computer {1}' -f (get-date),$computer)
                $Infos = [COMPUTER]::new($computer)
                $Infos.GetALlInformation()
                $result += $Infos
            }
        }
    }

    end
    {
        return $result
    }
}
#EndRegion '.\Public\Get-LastUpdateInformation.ps1' 87
#Region '.\Public\Get-UserLockoutReason.ps1' -1

function Get-UserLockoutReason
{
    <#
    .SYNOPSIS
    Search user lockout reason
 
    .DESCRIPTION
    You can search the reason of locked user on a specific computer
    To find the source you can use : Find-UserLockoutsInformation -Identity User1 -DC MyDC1 -Credential (Get-Credential MyAdminAccount)
 
    .PARAMETER Computer
    User lockout source computer
 
    .PARAMETER Identity
    Name of the user for whom we are looking for the source of the lock
 
    .PARAMETER Credential
    Administrator credential to connect to the computer
 
    .EXAMPLE
    Get-UserLockoutReason -Computer ComputerSource -Identity User1 -Credential (Get-Credential MyAdminAccount)
 
    .NOTES
    General notes
#>

    [CmdletBinding(
        DefaultParameterSetName = 'All'
    )]
    [OutputType([System.Object[]])]
    param (
        [System.String]$Computer,
        [Parameter(
            ValueFromPipeline = $true,
            ParameterSetName = 'ByUser'
        )]
        [System.String]$Identity,
        # Specifies the user account credentials to use when performing this task.
        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )

    begin
    {
        $LogonInfo = Import-PSFPowerShellDataFile -Path $PSScriptRoot/PSPowerAdminTasks.psd1
        Write-Verbose ('[{0:O}] Searching EventID : 4625 on Computer : {1} ' -f (get-date), $Computer)

        $WinEventArguments = @{
            ComputerName    = $Computer
            FilterHashtable = @{LogName = 'Security'; Id = 4625 }
        }

        if ($PSBoundParameters.ContainsKey('Credential'))
        {
            $WinEventArguments['Credential'] = $Credential
        }

        $lockoutEvents = $null
        Write-Verbose ('[{0:O}] Test if computer : {1} is alive ' -f (get-date), $Computer)
        if (Test-Connection -ComputerName $Computer -Quiet -Count 2)
        {
            try
            {
                $lockoutEvents = Get-WinEvent @WinEventArguments -ErrorAction Stop
            }
            catch
            {
                if ($_.Exception.Message -match "No events were found that match the specified selection criteria")
                {
                    Write-Verbose ('[{0:O}] No logs found' -f (get-date))
                }
                if ($Error[-1].Exception.Message -like "*elevated user rights*")
                {
                    throw ('[{0:O}] You need an admin account. Please provide with the -Credential parameter' -f (get-date))
                }
            }
        }
        else
        {
            throw ('[{0:O}] computer {1} is not alive' -f (get-date), $Computer)
        }

        if ($lockoutEvents)
        {
            Write-Verbose ('[{0:O}] {1} event found' -f (get-date), $lockoutEvents.Count)
        }
        else
        {
            throw ('[{0:O}] No event found' -f (get-date))
        }
    }

    process
    {
        switch ($PSCmdlet.ParameterSetName)
        {
            All
            {
                Write-Verbose ('[{0:O}] Searching information for all user(s) ' -f (get-date))
                Foreach ($Event in $lockoutEvents)
                {
                    $eventXML = [xml]$event.ToXml()
                    $Event | Select-Object -Property @(
                        @{label = 'LockedUserName' ; Expression = {$eventXML.Event.EventData.Data[5].'#text'}}
                        @{label = 'LogonType' ; Expression = {$LogonInfo.PrivateData.LogonType."$($eventXML.Event.EventData.Data[10].'#text')"}}
                        @{label = 'LogonProcessName' ; Expression = {$eventXML.Event.EventData.Data[11].'#text'}}
                        @{label = 'ProcessName' ; Expression = {$eventXML.Event.EventData.Data[18].'#text'}}
                        @{label = 'FailureReason' ; Expression = {$LogonInfo.PrivateData.FailureReason."$($eventXML.Event.EventData.Data[8].'#text')"}}
                        @{label = 'FailureStatus' ; Expression = {$LogonInfo.PrivateData.FailureType."$($eventXML.Event.EventData.Data[7].'#text')"}}
                        @{label = 'FailureSubStatus' ; Expression = {$LogonInfo.PrivateData.FailureType."$($eventXML.Event.EventData.Data[9].'#text')"}}
                    )
                }
            }
            ByUser
            {
                Write-Verbose ('[{0:O}] Searching information for user : {1}' -f (get-date), $Identity)
                Foreach ($Event in $lockoutEvents)
                {
                    $eventXML = [xml]$event.ToXml()
                    If ($Event | Where-Object { $eventXML.Event.EventData.Data[5].'#text' -match $Identity })
                    {
                        $Event | Select-Object -Property @(
                            @{label = 'LockedUserName' ; Expression = {$eventXML.Event.EventData.Data[5].'#text'}}
                            @{label = 'LogonType' ; Expression = {$LogonInfo.PrivateData.LogonType."$($eventXML.Event.EventData.Data[10].'#text')"}}
                            @{label = 'LogonProcessName' ; Expression = {$eventXML.Event.EventData.Data[11].'#text'}}
                            @{label = 'ProcessName' ; Expression = {$eventXML.Event.EventData.Data[18].'#text'}}
                            @{label = 'FailureReason' ; Expression = {$LogonInfo.PrivateData.FailureReason."$($eventXML.Event.EventData.Data[8].'#text')"}}
                            @{label = 'FailureStatus' ; Expression = {$LogonInfo.PrivateData.FailureType."$($eventXML.Event.EventData.Data[7].'#text')"}}
                            @{label = 'FailureSubStatus' ; Expression = {$LogonInfo.PrivateData.FailureType."$($eventXML.Event.EventData.Data[9].'#text')"}}
                        )
                    }

                }

            }
        }


    }

    end
    {
    }
}
#EndRegion '.\Public\Get-UserLockoutReason.ps1' 147
#Region '.\Public\Remove-OldUserProfile.ps1' -1

function Remove-OldUserProfile
{
    [CmdletBinding(DefaultParameterSetName = "ByUserName")]
    param (
        [Parameter(ParameterSetName = "ByUserName", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]$UserName
    )

    begin
    {

    }

    process
    {
        switch ($PSCmdlet.ParameterSetName)
        {
            "ByUserName"
            {
                foreach ($user in $UserName)
                {
                    $registryKeyRemoved = $false
                    Write-Verbose ('[{0:O}] remove user {1}' -f (Get-Date), $user)

                    try
                    {
                        $ProfileList = Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -ErrorAction SilentlyContinue
                        if ($ProfileList)
                        {
                            foreach ($profile in $ProfileList)
                            {
                                $ProfileSID = $profile.PSChildName
                                $ProfilePath = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$ProfileSID").ProfileImagePath
                                if ($ProfilePath -eq "$($env:systemdrive)\Users\$($user)")
                                {
                                    Write-Verbose ('[{0:O}] remove ProfileList key {1}' -f (Get-Date), $ProfileSID)
                                    Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$ProfileSID" -Recurse -Force -ErrorAction Stop
                                    $registryKeyRemoved = $true
                                }
                            }
                        }
                    }
                    catch [System.Security.SecurityException]
                    {
                        Write-Error ('[{0:O}] Use a privileged user' -f (get-date))
                    }
                    catch
                    {
                        Write-Output ('[{0:O}] ErrorID: {1}' -f (Get-Date), $_.Exception.Message)
                        Write-Output ('[{0:O}] Exception: {1}' -f (Get-Date), $_.FullyQualifiedErrorId)
                        Write-Output ('[{0:O}] Category: {1}' -f (Get-Date), (($_.Exception.GetType() | Select-Object -ExpandProperty UnderlyingSystemType).FullName))
                    }

                    if ($registryKeyRemoved)
                    {
                        try
                        {
                            Remove-Item -Path "$($env:systemdrive)\Users\$($user)" -Recurse -Force -ErrorAction Stop
                        }
                        catch [System.Management.Automation.ItemNotFoundException]
                        {
                            Write-Warning ('[{0:O}] User {1} not found' -f (Get-Date), $user)
                        }
                        catch [System.UnauthorizedAccessException]
                        {
                            Write-Warning ('[{0:O}] Error during profile deletion, do it manually' -f (Get-Date), $user)
                        }
                        catch
                        {
                            Write-Output ('[{0:O}] ErrorID: {1}' -f (Get-Date), $_.Exception.Message)
                            Write-Output ('[{0:O}] Exception: {1}' -f (Get-Date), $_.FullyQualifiedErrorId)
                            Write-Output ('[{0:O}] Category: {1}' -f (Get-Date), (($_.Exception.GetType() | Select-Object -ExpandProperty UnderlyingSystemType).FullName))
                        }
                    }
                }
            }
        }
    }
    end
    {

    }
}
#EndRegion '.\Public\Remove-OldUserProfile.ps1' 84
#Region '.\Public\Remove-UserFromGroup.ps1' -1

#take a list of users and for each users, check if the user is in a list of groups. If user is found in a group then remove the user from the group.
function Remove-UserFromGroup
{
    <#
.SYNOPSIS
Remove a user from a group
 
.DESCRIPTION
Remove one or more users from one or more groups
 
.PARAMETER Users
One user or more users to remove from one or more groups
 
.PARAMETER Groups
One or more groups to remove one or more users from
 
.PARAMETER FilePathUser
Path to a file containing one user per line
 
.PARAMETER FilePathGroup
Path to a file containing one group per line
 
.PARAMETER Credential
Credentials to use when performing this task
 
.PARAMETER Log
Log the operation into a file (in $env:temp by default)
 
.PARAMETER Console
Show the message in the console
 
.EXAMPLE
Remove-UserFromGroup -Users 'User1' -Groups 'Group1'
 
Remove USer1 from Group1
 
.EXAMPLE
Remove-UserFromGroup -Users 'User1','User2" -Groups 'Group1'
 
Remove User1 and User2 from Group1
 
.EXAMPLE
Remove-UserFromGroup -Users 'User1','User2" -Groups 'Group1','Group2'
 
Remove User1 and User2 from Group1 and Group2
 
.EXAMPLE
Remove-UserFromGroup -FilePathUser 'C:\Users.txt' -FilePathGroup 'C:\Groups.txt'
 
Users.txt contains one user per line
Groups.txt contains one group per line
Remove users from groups based on a file containing users and a file containing groups
 
.NOTES
General notes
#>

    [CmdletBinding(DefaultParameterSetName = 'ByUser')]
    param (
        [Parameter(ParameterSetName = 'ByUser')]
        [string[]]$Users,
        [Parameter(ParameterSetName = 'ByUser')]
        [string[]]$Groups,
        [Parameter(ParameterSetName = 'ByFile')]
        [System.String]$FilePathUser,
        [Parameter(ParameterSetName = 'ByFile')]
        [System.String]$FilePathGroup,       # Specifies the user account credentials to use when performing this task.
        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,
        [Parameter()]
        [switch]$log,
        [Parameter()]
        [switch]$Console
    )

    begin
    {
        if ($log)
        {
            $LogPath = $env:Temp + '\Remove-UserFromGroup.csv'
            Write-Log -LogPath $LogPath -Message 'Starting Remove-UserFromGroup' -Severity Information -Console:$Console
        }
    }
    process
    {
        if ($PSCmdlet.ParameterSetName -eq 'ByFile')
        {
            $Users = Get-Content -Path $FilePathUser
            $Groups = Get-Content -Path $FilePathGroup
        }

        try
        {
            # Loop through each user
            foreach ($user in $users)
            {
                # Loop through each group
                foreach ($group in $groups)
                {
                    if ($log) { write-log -LogPath $LogPath -Message "Searching user $($user) in group $($group)" -Severity Information -Console:$Console}
                    # Check if the user is a member of the group
                    $isMember = (Get-ADGroupMember -Identity $group -ErrorAction Stop | Where-Object { $_.SamAccountName -eq $user })

                    # If the user is a member of the group, remove them
                    if ($isMember)
                    {
                        if ($log) { write-log -LogPath $LogPath -Message "User $($user) found in group $($group) => remove it" -Severity Information -Console:$Console}
                        $Arguments = @{
                            Identity = $group
                            Members  = $user
                            Confirm  = $false
                            ErrorAction = "Stop"
                        }
                        if ($PSBoundParameters.ContainsKey('Credential'))
                        {
                            $Arguments['Credential'] = $Credential
                        }

                        Remove-ADGroupMember @Arguments
                    }
                }
            }
        }
        catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
        {
            if ($log) { write-log -LogPath $LogPath -Message "group $($group) not found...." -Severity Error -Console:$Console}
            Write-Error ('[{0:O}] group {1} not found....' -f (Get-Date),$group)
        }
        catch [Microsoft.ActiveDirectory.Management.ADException]
        {
            if ($log) { write-log -LogPath $LogPath -Message "Insufficient access rights to perform the operation" -Severity Error -Console:$Console}
            Write-Error ('[{0:O}] Insufficient access rights to perform the operation' -f (Get-Date))
        }
        catch
        {
            Write-Output ('[{0:O}] ErrorID: {1}' -f (Get-Date), $_.Exception.Message)
            Write-Output ('[{0:O}] Exception: {1}' -f (Get-Date), $_.FullyQualifiedErrorId)
            Write-Output ('[{0:O}] Category: {1}' -f (Get-Date), (($_.Exception.GetType() | Select-Object -ExpandProperty UnderlyingSystemType).FullName))
        }

    }

    end
    {
        if ($log)
        {
            Write-Log -LogPath $LogPath -Message 'Ending Remove-UserFromGroup' -Severity Information -Console:$Console
        }
    }
}
#EndRegion '.\Public\Remove-UserFromGroup.ps1' 153
#Region '.\Public\Reset-AdminCountParameter.ps1' -1


function Reset-AdminCountParameter
{
    [CmdletBinding(DefaultParameterSetName = "ByUser")]
    [OutputType([type])]
    param
    (
        [Parameter(ParameterSetName = "ByUser")]
        [System.String[]]$Identity,
        [Parameter()]
        [System.String]$Server,
        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )

    begin
    {
        $RootDomainSid = [String] (Get-ADForestRootDomain).DomainSID
        $CurrentDomainSid = [String] (Get-ADDomain).DomainSID
    }

    process
    {
        $Arguments = @{}
        if ($PSBoundParameters.ContainsKey('Credential'))
        {
            Write-Verbose ('[{0:O}] Add Credential to Argument list' -f (get-date))
            $Arguments['Credential'] = $Credential
        }
        if ($PSBoundParameters.ContainsKey('Server'))
        {
            Write-Verbose ('[{0:O}] Add Server to Argument list' -f (get-date))
            $Arguments['Server'] = $Server
        }

        $DefaultProtectedGroupSids = @('S-1-5-32-548', 'S-1-5-32-544', 'S-1-5-32-551', "$CurrentDomainSid-512", "$CurrentDomainSid-516", "$RootDomainSid-519", 'S-1-5-32-550', "$CurrentDomainSid-498", 'S-1-5-32-552', "$RootDomainSid-518", 'S-1-5-32-549')
        foreach ($group in $DefaultProtectedGroupSids)
        {
            Write-Verbose ('[{0:O}] Define default protected group {1}' -f (get-date),$group)
            $DefaultProtectedGroupSids += @(Get-ADGroupSubGroupMember $group -ErrorAction SilentlyContinue)
        }

        $ProtectedGroupActualUserMember = @()
        foreach ($Group in $DefaultProtectedGroupSids)
        {
            Write-Verbose ('[{0:O}] Seach user in default protected group {1}' -f (get-date),$group)
            $ProtectedGroupActualUserMember += Get-ADUser -Filter ("memberOf -RecursiveMatch"+"'"+((Get-ADGroup $Group).DistinguishedName -replace "'","''") +"'") -SearchBase (Get-ADDomain).DistinguishedName
        }

        switch ($PSCmdlet.ParameterSetName)
        {
            "ByUser"
            {
                try
                {
                    foreach ($User in $Identity)
                    {
                        Write-Verbose ('[{0:O}] Work with {1}' -f (get-date),$User)
                        $AdminCountUser = Get-ADObject -Filter {(Name -like $User)} -Properties @('admincount','memberof')
                        if ($AdminCountUser.DistinguishedName -notin $ProtectedGroupActualUserMember.DistinguishedName)
                        {
                            Write-Verbose ('[{0:O}] Reset AdminCount for {1}' -f (get-date),$AdminCountUser.DistinguishedName)
                            Set-ADObject -Identity $AdminCountUser.DistinguishedName -Clear admincount @Arguments
                            Write-Verbose ('[{0:O}] Reset Security for {1}' -f (get-date),$AdminCountUser.DistinguishedName)
                            if (($PSBoundParameters.ContainsKey('Server')) -and ($PSBoundParameters.ContainsKey('Credential')))
                            {
                                $process = Invoke-Command -ComputerName $Arguments.Server -Credential $Arguments.Credential -ScriptBlock {
                                    param($username)
                                    & dsacls.exe "$($username)" /resetDefaultDACL  /resetDefaultSACL
                                    Write-Output $LASTEXITCODE
                                    } -ArgumentList $AdminCountUser.DistinguishedName -AsJob
                                $process | Wait-Job
                                if (!($process | Receive-Job) -contains "The command completed successfully")
                                {
                                    Write-Warning ('[{0:O}] Error during reseting security on {1}' -f (Get-Date),$adminCountUserList.DistinguishedName)
                                }
                            } else {
                                Start-Process -FilePath "C:\Windows\System32\dsacls.exe"-ArgumentList "$($AdminCountUser.DistinguishedName) /resetDefaultDACL /resetDefaultSACL"
                            }
                        }
                        else
                        {
                            Write-Verbose ('[{0:O}] {1} is member of a protected group' -f (get-date),$AdminCountUser.DistinguishedName)
                        }
                    }
                }
                catch
                {
                    Write-Output ('[{0:O}] ErrorID: {1}' -f (Get-Date), $_.Exception.Message)
                    Write-Output ('[{0:O}] Exception: {1}' -f (Get-Date), $_.FullyQualifiedErrorId)
                    Write-Output ('[{0:O}] Category: {1}' -f (Get-Date), (($_.Exception.GetType() | Select-Object -ExpandProperty UnderlyingSystemType).FullName))
                }
            }
        }
    }

    end
    {
    }
}
#EndRegion '.\Public\Reset-AdminCountParameter.ps1' 104
#Region '.\Public\Set-ChangePasswordAtNextLogon.ps1' -1

function Set-ChangePasswordAtNextLogon
{
    <#
    .SYNOPSIS
    Force user to change password at next logon
 
    .DESCRIPTION
    Force user(s) passed as parameter to change password at next logon
 
    .PARAMETER Identity
    Name of user(s) to force to change password at next logon
 
    .PARAMETER DomainController
    Domain controler on which to perform the operation
 
    .PARAMETER Credential
    Credentials to use when performing the operation
 
    .PARAMETER Log
    Log the operation into a file
 
    .PARAMETER Console
    Show the message in the console
 
 
    .EXAMPLE
    Set-ChangePasswordAtNextLogon -Identity user1,user2,user3
 
    .EXAMPLE
    Set-ChangePasswordAtNextLogon -Identity user1,user2,user3 -DomainController dc1
 
    .EXAMPLE
    Set-ChangePasswordAtNextLogon -Identity user1,user2,user3 -Credential (Get-Credential)
 
    .EXAMPLE
    Set-ChangePasswordAtNextLogon -Identity user1,user2,user3 -Log
 
    .EXAMPLE
    Set-ChangePasswordAtNextLogon -Identity user1,user2,user3 -Console -Log
 
    .NOTES
    General notes
#>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        # UserName
        [Parameter()]
        [System.String[]]$Identity,
        # Parameter help description
        [Parameter()]
        [System.String]$DomainController = (Get-ADDomainController -Discover -Service PrimaryDC).HostName[0],
        # Specifies the user account credentials to use when performing this task.
        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,
        [Parameter()]
        [switch]$log,
        [Parameter()]
        [switch]$Console
    )

    begin
    {
        if ($log)
        {
            $LogPath = $env:Temp + '\Set-ChangePasswordAtNextLogon.csv'
            Write-Log -LogPath $LogPath -Message 'Starting Set-ChangePasswordAtNextLogon' -Severity Information -Console:$Console
        }
    }

    process
    {
        $count = 1
        $Arguments = @{
            ChangePasswordAtLogon = $True
            ErrorAction           = "Stop"
            Server                = $DomainController
        }

        if ($PSBoundParameters.ContainsKey('Credential'))
        {
            $Arguments['Credential'] = $Credential
        }

        foreach ($user in $Identity)
        {
            if ($log) { write-log -LogPath $LogPath -Message "Processing user $($count) of $($Identity.Count)" -Severity Information -Console:$Console}
            if ($log) { write-log -LogPath $LogPath -Message "Processing user $($user)" -Severity Information -Console:$Console}
            $count++
            $Arguments['Identity'] = $user
            try
            {
                if ($PSCmdlet.ShouldProcess($user, 'Set-ADUser'))
                {
                    if ($log) { write-log -LogPath $LogPath -Message "Processing user $($user)" -Severity Information -Console:$Console}
                    Set-ADUser @Arguments
                }
            }
            catch [Microsoft.ActiveDirectory.Management.ADException]
            {
                if ($log) { write-log -LogPath $LogPath -Message "Insufficient access rights to perform the operation" -Severity Error -Console:$Console}
                Write-Error ('[{0:O}] Insufficient access rights to perform the operation' -f (Get-Date))
            }
            catch
            {
                if ($log)
                {
                    write-log -LogPath $LogPath -Message "$($_.Exception.Message)" -Severity Error -Console:$Console
                    write-log -LogPath $LogPath -Message "$($_.FullyQualifiedErrorId)" -Severity Error -Console:$Console
                    write-log -LogPath $LogPath -Message "$($_.Exception.GetType() | Select-Object -ExpandProperty UnderlyingSystemType).FullName)" -Severity Error
                }
                Write-Output ('[{0:O}] ErrorID: {1}' -f (Get-Date), $_.Exception.Message)
                Write-Output ('[{0:O}] Exception: {1}' -f (Get-Date), $_.FullyQualifiedErrorId)
                Write-Output ('[{0:O}] Category: {1}' -f (Get-Date), (($_.Exception.GetType() | Select-Object -ExpandProperty UnderlyingSystemType).FullName))
            }
        }
    }

    end
    {
        if ($log)
        {
            Write-Log -LogPath $LogPath -Message 'Ending Set-ChangePasswordAtNextLogon' -Severity Information -Console:$Console
        }
    }
}
#EndRegion '.\Public\Set-ChangePasswordAtNextLogon.ps1' 129
#Region '.\Public\Set-LocalUserPassword.ps1' -1


function Set-LocalUserPassword
{
    <#
.SYNOPSIS
Set the password of a local user account
 
.DESCRIPTION
Set the password of a local user account. Password pass as a plain text string will be converted to a secure string.
 
.PARAMETER Username
UserName of the local user account to set the password
 
.PARAMETER Password
Password of the local user account to set. Pass as a plain text string will be converted to a secure string.
 
.PARAMETER ComputerName
ComputerName where the local user account is located. Default is the local computer.
 
.PARAMETER Credential
Specifies the user account credentials to use when performing this task.
 
.EXAMPLE
Set-LocalUserPassword -Username "MyUser" -Password "P@sswOrd" -Credential ($MyAdminAccount) -ComputerName Myserver
 
Set password of the local user account "MyUser" on the computer "Myserver" with the credential stored in the variable $MyAdminAccount
 
.NOTES
General notes
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.String]$Username,
        [Parameter(Mandatory = $true)]
        [System.String]$Password,
        [Parameter()]
        [System.String]$ComputerName = $env:COMPUTERNAME,
        # Specifies the user account credentials to use when performing this task.
        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )

    begin
    {
        $ErrorActionPreference = 'Stop'
    }

    process
    {
        $NewSecurePwd = $Password | ConvertTo-SecureString -AsPlainText -Force

        try
        {
            if ($ComputerName -ne $env:COMPUTERNAME)
            {
                $Session = New-PSSession -ComputerName $ComputerName -Credential $Credential
                Invoke-Command -Session $Session -ScriptBlock {
                    Get-LocalUser -Name $Using:Username | Set-LocalUser -Password $Using:NewSecurePwd
                }
                Remove-PSSession -Session $Session
                return
            }
            else
            {
                Get-LocalUser -Name $Username | Set-LocalUser -Password $NewSecurePwd
                return
            }

        }
        catch [Microsoft.PowerShell.Commands.UserNotFoundException]
        {
            Write-Error ('[{0:O}] User {1} not found' -f (Get-Date), $Username)
        }
        catch [Microsoft.PowerShell.Commands.AccessDeniedException]
        {
            Write-Error ('[{0:O}] Access denied, use account with suffisant privilege' -f (Get-Date))
        }
        catch
        {
            Write-Output ('[{0:O}] ErrorID: {1}' -f (Get-Date), $_.Exception.Message)
            Write-Output ('[{0:O}] Exception: {1}' -f (Get-Date), $_.FullyQualifiedErrorId)
            Write-Output ('[{0:O}] Category: {1}' -f (Get-Date), (($_.Exception.GetType() | Select-Object -ExpandProperty UnderlyingSystemType).FullName))
        }

    }

    end
    {
        $ErrorActionPreference = 'Continue'
    }
}
#EndRegion '.\Public\Set-LocalUserPassword.ps1' 96
#Region '.\Public\Set-SmbSigningConfiguration.ps1' -1

function Set-SmbSigningConfiguration
{
<#
.SYNOPSIS
Enable or disable EnableSecuritySignature and RequireSecuritySignature
 
.DESCRIPTION
Enable or disable EnableSecuritySignature and RequireSecuritySignature
 
.PARAMETER ComputerName
On or more computer name
 
.PARAMETER EnableSecuritySignature
Apply configuration (Enable by default)
 
.PARAMETER RequireSecuritySignature
Apply configuration (Enable by default)
 
.PARAMETER Disabled
Apply configuration disable to one or two parameters EnableSecuritySignature and RequireSecuritySignature
 
.PARAMETER Credential
Specifies the user account credentials to use when performing this task
 
.PARAMETER log
Log information in a file (in $env:Temp )
 
.PARAMETER Console
Show information in console (need Log parameter)
 
.EXAMPLE
Set-SmbSigningConfiguration -ComputerName MyServer -EnableSecuritySignature -RequireSecuritySignature
 
Enable the two parameters EnableSecuritySignature and RequireSecuritySignature on the computer MyServer
 
.EXAMPLE
Set-SmbSigningConfiguration -ComputerName MyServer -EnableSecuritySignature -RequireSecuritySignature -Disabled
 
Disable the two parameters EnableSecuritySignature and RequireSecuritySignature on the computer MyServer
 
.EXAMPLE
Set-SmbSigningConfiguration -ComputerName MyServer -RequireSecuritySignature
 
Enable only the parameter RequireSecuritySignature on the computer MyServer
 
.EXAMPLE
Set-SmbSigningConfiguration -ComputerName MyServer -RequireSecuritySignature -Disabled
 
Disable only the parameter RequireSecuritySignature on the computer MyServer
 
.NOTES
General notes
#>

    [CmdletBinding()]
    [OutputType([type])]
    param
    (
        [Parameter(Mandatory=$true)]
        [System.String[]]$ComputerName,
        [Parameter()]
        [Switch]$EnableSecuritySignature,
        [Parameter()]
        [Switch]$RequireSecuritySignature,
        [Parameter()]
        [Switch]$Disabled,
        # Specifies the user account credentials to use when performing this task.
        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,
        [Parameter()]
        [switch]$log,
        [Parameter()]
        [switch]$Console
    )

    begin {

    }

    process {
        if ($log)
        {
            $LogPath = $env:Temp + '\Set-SmbSigningConfiguration.csv'
            Write-Log -LogPath $LogPath -Message 'Starting Set-SmbSigningConfiguration' -Severity Information -Console:$Console
        }

        $Parameter = @{}
        if ($PSBoundParameters.ContainsKey('Credential'))
        {
            $Parameter['Credential'] = $Credential
        }

        foreach ($Computer in $ComputerName)
        {
            $Parameter['ComputerName'] = $Computer
            if ($log)
            {
                write-log -LogPath $LogPath -Message "ComputerName $($Computer)" -Severity Information -Console:$Console
            }

            if ($EnableSecuritySignature)
            {
                if ($Disabled)
                {
                    if ($log)
                    {
                        write-log -LogPath $LogPath -Message "Disable Security Signature" -Severity Information -Console:$Console
                    }

                    Invoke-Command @Parameter -ScriptBlock {
                        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters' -Name 'EnableSecuritySignature' -Value 0 -Force
                        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters' -Name 'EnableSecuritySignature' -Value 0 -Force
                    }
                }
                else {
                    if ($log)
                    {
                        write-log -LogPath $LogPath -Message "Enable Security Signature" -Severity Information -Console:$Console
                    }
                    Invoke-Command @Parameter -ScriptBlock {
                        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters' -Name 'EnableSecuritySignature' -Value 1 -Force
                        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters' -Name 'EnableSecuritySignature' -Value 1 -Force
                    }
                }
            }

            if ($RequireSecuritySignature)
            {
                if ($Disabled)
                {
                    if ($log)
                    {
                        write-log -LogPath $LogPath -Message "Disable Require Security Signature" -Severity Information -Console:$Console
                    }

                    Invoke-Command @Parameter -ScriptBlock {
                        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters' -Name 'RequireSecuritySignature' -Value 0 -Force
                        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters' -Name 'RequireSecuritySignature' -Value 0 -Force
                    }
                }
                else {
                    if ($log)
                    {
                        write-log -LogPath $LogPath -Message "Enable Require Security Signature" -Severity Information -Console:$Console
                    }

                    Invoke-Command @Parameter -ScriptBlock {
                        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters' -Name 'RequireSecuritySignature' -Value 1 -Force
                        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters' -Name 'RequireSecuritySignature' -Value 1 -Force
                    }
                }

            }
        }
    }

    end {
        if ($log)
        {
            Write-Log -LogPath $LogPath -Message 'Ending Set-SmbSigningConfiguration' -Severity Information -Console:$Console
        }
    }
}
#EndRegion '.\Public\Set-SmbSigningConfiguration.ps1' 166
#Region '.\Public\Update-LastPasswordDate.ps1' -1

function Update-LastPasswordDate
{
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        [System.String[]]$Identity,
        # Specifies the user account credentials to use when performing this task.
        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,
        [Parameter()]
        [switch]$log,
        [Parameter()]
        [switch]$Console
    )

    begin
    {
        if ($log)
        {
            $LogPath = $env:Temp + '\Update-LastPasswordDate.csv'
            Write-Log -LogPath $LogPath -Message 'Starting Update-LastPasswordDate' -Severity Information -Console:$Console
        }
        $Properties = "PwdLastSet", "PasswordLastSet", "SamAccountName"
        $Parameter = @{}
        if ($PSBoundParameters.ContainsKey('Credential'))
        {
            $Parameter['Credential'] = $Credential
        }

    }

    process
    {
        foreach ($User in $Identity)
        {
            $Parameter['Identity'] = $User
            $InfoUser = Get-ADUser @Parameter -Properties $Properties
            if ($log)
            {
                write-log -LogPath $LogPath -Message "UserName $($InfoUser.SamAccountName)" -Severity Information -Console:$Console
            }
            if ($log)
            {
                write-log -LogPath $LogPath -Message "Last password change date $($InfoUser.PasswordLastSet) " -Severity Information -Console:$Console
            }
            $InfoUser.PwdLastSet = 0
            Set-ADUser -Instance $InfoUser

            $InfoUser.PwdLastSet = -1
            Set-ADUser -Instance $InfoUser
            $InfoUser = Get-ADUser @Parameter -Properties $Properties
            if ($log)
            {
                write-log -LogPath $LogPath -Message "New last password change date $($InfoUser.PasswordLastSet)" -Severity Information -Console:$Console
            }
        }
    }

    end
    {
        if ($log)
        {
            Write-Log -LogPath $LogPath -Message 'Ending Update-LastPasswordDate' -Severity Information -Console:$Console
        }
    }
}
#EndRegion '.\Public\Update-LastPasswordDate.ps1' 70