AdsiPS.psm1

function Add-ADSIGroupMember
{
<#
.SYNOPSIS
    Function to add a group member
 
.DESCRIPTION
    Function to add a group member
 
.PARAMETER Identity
    Specifies the Identity of the group
 
    You can provide one of the following properties
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
        UserPrincipalName
 
    Those properties come from the following enumeration:
        System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Member
    Specifies the member account.
    Performing an Ambiguous Name Resolution LDAP query to find the account.
    http://social.technet.microsoft.com/wiki/contents/articles/22653.active-directory-ambiguous-name-resolution.aspx
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current domain.
 
.EXAMPLE
    Add-ADSIGroupMember -Identity TestADSIGroup -Member 'UserTestAccount1'
 
    Adding the User account 'UserTestAccount1' to the group 'TestADSIGroup'
 
.EXAMPLE
    Add-ADSIGroupMember -Identity TestADSIGroup -Member 'GroupTestAccount1'
 
    Adding the Group account 'GroupTestAccount1' to the group 'TestADSIGroup'
 
.EXAMPLE
    Add-ADSIGroupMember -Identity TestADSIGroup -Member 'ComputerTestAccount1'
 
    Adding the Computer account 'ComputerTestAccount1' to the group 'TestADSIGroup'
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        $Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName,

        $Member
    )

    begin
    {
        $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).Mycommand

        Write-Verbose -Message "[$FunctionName] Loading assembly System.DirectoryServices.AccountManagement"
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement -ErrorAction Stop

        # Create Context splatting
        Write-Verbose -Message "[$FunctionName] Create context splatting"
        $ContextSplatting = @{
            Contexttype = "Domain"
        }

        if ($PSBoundParameters['Credential'])
        {
            Write-Verbose -Message "[$FunctionName] Context splatting - Add Credential"
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            Write-Verbose -Message "[$FunctionName] Context splatting - Add DomainName"
            $ContextSplatting.DomainName = $DomainName
        }

        Write-Verbose -Message "[$FunctionName] Create New Principal Context using Context Splatting"
        $Context = New-ADSIPrincipalContext @ContextSplatting -ErrorAction Stop
    }
    process
    {
        try
        {
            # Resolving member
            # Directory Entry object
            Write-Verbose -Message "[$FunctionName] Copy Context splatting and remove ContextType property"
            $DirectoryEntryParams = $ContextSplatting
            $DirectoryEntryParams.remove('ContextType')
            Write-Verbose -Message "[$FunctionName] Create New Directory Entry using using the copied context"
            $DirectoryEntry = New-ADSIDirectoryEntry @DirectoryEntryParams

            # Principal Searcher
            Write-Verbose -Message "[$FunctionName] Create a System.DirectoryServices.DirectorySearcher"
            $DirectorySearcher = new-object -TypeName System.DirectoryServices.DirectorySearcher
            Write-Verbose -Message "[$FunctionName] Append DirectoryEntry to in the property SearchRoot of DirectorySearcher"
            $DirectorySearcher.SearchRoot = $DirectoryEntry

            # Adding an Ambiguous Name Resolution (ANR) LDAP Filter
            Write-Verbose -Message "[$FunctionName] Append LDAP Filter '(anr=$member)' to the property Filter of DirectorySearcher"
            $DirectorySearcher.Filter = "(anr=$member)"

            # Retrieve a single object
            Write-Verbose -Message "[$FunctionName] Retrieve the account"
            $Account = $DirectorySearcher.FindOne().GetDirectoryEntry()

            if ($Account)
            {
                Write-Verbose -Message "[$FunctionName] Account Retrieved"
                switch ($Account.SchemaClassName)
                {
                    'user'
                    {
                        $member = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($Context, $Account.distinguishedname)
                    }
                    'group'
                    {
                        $member = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($Context, $Account.distinguishedname)
                    }
                    'computer'
                    {
                        $member = [System.DirectoryServices.AccountManagement.ComputerPrincipal]::FindByIdentity($Context, $Account.distinguishedname)
                    }
                }
            }
            else
            {
                Write-Error -Message "[$FunctionName] Can't retrieve the identity '$identity'"
            }

            if ($pscmdlet.ShouldProcess("$Identity", "Add Account member $member"))
            {
                Write-Verbose -Message "[$FunctionName] Retrieve group with the identity '$identity' using Get-ADSIGroup using the Context Splatting"
                $group = (Get-ADSIGroup -Identity $Identity @ContextSplatting -ErrorAction Stop)
                $group.members.add($Member)
                $group.Save()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
    end
    {
        Write-Verbose -Message "[$FunctionName] Done."
    }
}
Function Compare-ADSITeamGroups
{
    <#
.SYNOPSIS
    Function to compare AD groups of a team
 
.DESCRIPTION
    See if all your team's members have the same AD groups. Make a snapshot of your team's members current AD groups
 
.PARAMETER Credential
    Specifies alternative credential
 
.PARAMETER DomainName
    Specifies the Domain Name where the function should look
 
.PARAMETER BaseGroupIdentity
    Specifies the Identity of one team's users group
    You can provide one of the following properties
    DistinguishedName
    Guid
    Name
    SamAccountName
    Sid
    UserPrincipalName
    Those properties come from the following enumeration:
    System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER TeamUsersIdentity
    Specifies the Identity of team's users (array)
 
.EXAMPLE
    Compare-ADSITeamGroups -BaseGroupIdentity 'MainGroup'
 
    Get groups of all users in MainGroup
 
.EXAMPLE
    Compare-ADSITeamGroups -TeamUsersIdentity @('User1','User2','User3')
 
    Get groups of all users in the array
 
.EXAMPLE
    Compare-ADSITeamGroups -TeamUsersIdentity @('User1','User2','User3') -Credential (Get-Credential)
 
    Use a different credential to perform the comparison
 
.EXAMPLE
    Compare-ADSITeamGroups -TeamUsersIdentity @('User1','User2','User3') -DomainName "CONTOSO.local"
 
    Use a different domain name to perform the comparison
 
.EXAMPLE
    Compare-ADSITeamGroups -BaseGroupIdentity 'MainGroup' -DomainDistinguishedName 'DC=CONTOSO,DC=local'
 
    Use a different domain distinguished name to perform the comparison
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding()]
    param
    (
        [Parameter(ParameterSetName = 'TeamUsers', Mandatory = $true)]
        [array]$TeamUsersIdentity,

        [Parameter(ParameterSetName = 'Identity', Mandatory = $true)]
        [string]$BaseGroupIdentity,

        [Alias('RunAs')]
        [System.Management.Automation.pscredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.pscredential]::Empty,

        [Alias('Domain')]
        [ValidateScript( { if ($_ -match '^(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,6}$')
                {
                    $true
                }
                else
                {
                    throw ("DomainName must be FQDN. Ex: contoso.locale - Hostname like '{0}' is not working" -f $_)
                } })]
        [String]$DomainName
    )

    begin
    {
        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }
    }
    process
    {
        $AllUsersGroups = @()

        if ($PSBoundParameters['BaseGroupIdentity'])
        {
            $TeamUsersIdentity = @((Get-ADSIGroupMember -Identity ('{0}' -f $BaseGroupIdentity) -Recurse).SamAccountName)
        }

        $Result = @()
        $ResultUsersInfos = @()
        $ResultGoupsInfos = @()

        foreach ($User in $TeamUsersIdentity)
        {
            # Get all groups of a user
            $Usergroups = $null
            $UserInfos = Get-ADSIUser -Identity $user @ContextSplatting

            $Usergroups = Get-ADSIPrincipalGroupMembership -UserInfos $UserInfos

            $AllUsersGroups += $Usergroups

            $ResultUsersInfos += [pscustomobject]@{
                SamAccountName = [string]$UserInfos.name
                DisplayName    = [string]$UserInfos.description
                Groups         = $Usergroups
            }
        }

        $AllUsersGroups = $AllUsersGroups | Sort-Object -Property name -Unique
        $ResultGoupsInfos += $AllUsersGroups

        $ResultAuditUsersGroups = @()
        foreach ($item in $ResultUsersInfos)
        {
            $Object = $null
            $Object = [ordered]@{}
            $Object.SamAccountName = $item.SamAccountName
            $Object.DisplayName = $item.DisplayName

            foreach ($group in $AllUsersGroups)
            {
                if ($item.Groups.name -contains $group.name)
                {
                    $Object.$($group.name) = 'x'
                }
                else
                {
                    $Object.$($group.name) = ''
                }

            }

            $ResultAuditUsersGroups += [pscustomobject]$Object
        }

        $Result += $ResultAuditUsersGroups, $ResultGoupsInfos
        $Result
    }

}
function Copy-ADSIGroupMembership{
<#
.SYNOPSIS
    Function to Copy the Group Memberships of a User or Computer to another in Active Directory
 
.DESCRIPTION
    Function to Copy the Group Memberships of a User or Computer to another in Active Directory
 
.PARAMETER SourceIdentity
    Specifies the Identity of the Source User or Computer
    You can provide one of the following properties
    DistinguishedName
    Guid
    Name
    SamAccountName
    Sid
    UserPrincipalName
    Those properties come from the following enumeration:
    System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER DestinationIdentity
    Specifies the Identity of the Destination User or Computer
    You can provide one of the following properties
    DistinguishedName
    Guid
    Name
    SamAccountName
    Sid
    UserPrincipalName
    Those properties come from the following enumeration:
    System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER DomainName
    Specifies the Domain Name where the function should look
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.EXAMPLE
    Copy-ADSIGroupMembership -SourceIdentity User1 -DestinationIdentity User2 -DomainName "my.domain"
 
.EXAMPLE
    Copy-ADSIGroupMembership -SourceIdentity User1 -DestinationIdentity Computer2 -DomainName "my.domain"
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
.LINK
    https://msdn.microsoft.com/en-us/library/System.DirectoryServices.AccountManagement.UserPrincipal(v=vs.110).aspx
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true,
            Position = 0,
            ParameterSetName = "Identity")]
        [System.string]$SourceIdentity,
        
        [Parameter(Mandatory = $true,
            Position = 1,
            ParameterSetName = "Identity")]
        [System.string]$DestinationIdentity,
    
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,
    
        [System.String]$DomainName
    )
    
    begin{

        $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).Mycommand
    
        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential']){
            Write-Verbose "[$FunctionName] Found Credential Parameter"
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName']){
            Write-Verbose "[$FunctionName] Found DomainName Parameter"
            $ContextSplatting.DomainName = $DomainName
        }

        #Get SourceIdentity Type
        $SourceObject = Get-ADSIObject -Identity $SourceIdentity @ContextSplatting
        $DestinationObject = Get-ADSIObject -Identity $DestinationIdentity @ContextSplatting

        switch -Wildcard ($SourceObject.objectclass){
            "*group" {$SourceType = "Group"}
            "*computer" {$SourceType = "Computer"}
            "*user" {$SourceType = "User"}
        }

        switch -Wildcard ($DestinationObject.objectclass){
            "*group" {$DestinationType = "Group"}
            "*computer" {$DestinationType = "Computer"}
            "*user" {$DestinationType = "User"}
        }
    }

    process{
        #GetSourceGroups
        If($SourceType -eq "User"){
            $SourceGroups = (Get-ADSIUser -Identity $SourceIdentity @ContextSplatting).GetGroups()
            Write-Verbose "[$FunctionName] SourceType: User"
        } elseif ($SourceType -eq "Computer") {
            $SourceGroups = (Get-ADSIComputer -Identity $SourceIdentity @ContextSplatting).GetGroups()
            Write-Verbose "[$FunctionName] SourceType: Computer"
        } elseif ($SourceType -eq "Group") {
            $SourceGroups = (Get-ADSIGroup -Identity $SourceIdentity @ContextSplatting).GetGroups()
            Write-Verbose "[$FunctionName] SourceType: Group"
        }

        #GetDestinationGroups
        If($DestinationType -eq "User"){
            $DestinationGroups = (Get-ADSIUser -Identity $DestinationIdentity @ContextSplatting).GetGroups()
            Write-Verbose "[$FunctionName] DestinationType: User"
        } elseif ($DestinationType -eq "Computer") {
            $DestinationGroups = (Get-ADSIComputer -Identity $DestinationIdentity @ContextSplatting).GetGroups()
            Write-Verbose "[$FunctionName] DestinationType: Computer"
        } elseif ($DestinationType -eq "Group") {
            $DestinationGroups = (Get-ADSIGroup -Identity $DestinationIdentity @ContextSplatting).GetGroups()
            Write-Verbose "[$FunctionName] DestinationType: Group"
        }

        #Get only new Groups
        $MissingGroups = Compare-Object -ReferenceObject $SourceGroups.SamAccountName -DifferenceObject $DestinationGroups.SamAccountName | Where-Object {$_.SideIndicator -eq "<="}
        if($MissingGroups -eq $null){
            Write-Verbose "[$FunctionName] Nothing to do"
        } else {
            Write-Verbose "[$FunctionName] Missing Groups: $($MissingGroups.InputObject)"
        }

        #Add Destination to Missing Groups
        foreach($Group in $MissingGroups.InputObject){
                If($PSCmdlet.ShouldProcess($Group, "Adding $DestinationIdentity to group")){
                    Add-ADSIGroupMember -Identity $Group -Member $DestinationIdentity @ContextSplatting
                }
        }
    }
}
function Disable-ADSIComputer
{
<#
.SYNOPSIS
    Function to disable a Computer Account
 
.DESCRIPTION
    Function to disable a Computer Account
 
.PARAMETER Identity
    Specifies the Identity of the Computer.
 
    You can provide one of the following properties
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain.
    By default it will use the current domain.
 
.EXAMPLE
    Disable-ADSIComputer TESTSERVER01
 
    This command will disable the account TESTSERVER01
 
.EXAMPLE
    Disable-ADSIComputer TESTSERVER01 -whatif
 
    This command will emulate disabling the account TESTSERVER01
 
.EXAMPLE
    Disable-ADSIComputer TESTSERVER01 -credential (Get-Credential)
 
    This command will disable the account TESTSERVER01 using the alternative credential specified
 
.EXAMPLE
    Disable-ADSIComputer TESTSERVER01 -credential (Get-Credential) -domain LazyWinAdmin.local
 
    This command will disable the account TESTSERVER01 using the alternative credential specified in the domain lazywinadmin.local
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.computerprincipal(v=vs.110).aspx
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        $Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName)

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

    }
    process
    {
        try
        {
            if ($pscmdlet.ShouldProcess("$Identity", "Disable Account"))
            {
                $Account = Get-ADSIComputer -Identity $Identity @ContextSplatting
                $Account.enabled = $false
                $Account.Save()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Disable-ADSIUser
{
<#
.SYNOPSIS
    Function to Disable a User Account
 
.DESCRIPTION
    Function to Disable a User Account
 
.PARAMETER Identity
    Specifies the Identity of the User.
 
    You can provide one of the following properties
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
        UserPrincipalName
 
    Those properties come from the following enumeration:
        System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain.
    By default it will use the current domain.
 
.EXAMPLE
    Disable-ADSIUser fxtest02
 
    This will disable the fxtest02 account
 
.EXAMPLE
    Disable-ADSIUser fxtest02 -whatif
 
    This will emulate disabling the fxtest02 account
 
.EXAMPLE
    Disable-ADSIUser fxtest02 -credential (Get-Credential)
 
    This will disable the fxtest02 account using the credential specified
 
.EXAMPLE
    Disable-ADSIUser fxtest02 -credential (Get-Credential) -DomainName LazyWinAdmin.local
 
    This will disable the fxtest02 account using the credential specified in the domain LazyWinAdmin.local
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
.LINK
    https://msdn.microsoft.com/en-us/library/System.DirectoryServices.AccountManagement.UserPrincipal(v=vs.110).aspx
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        $Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName)

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

    }
    process
    {
        try
        {
            if ($pscmdlet.ShouldProcess("$Identity", "Disable Account"))
            {
                $Account = Get-ADSIUser -Identity $Identity @ContextSplatting
                $Account.Enabled = $false
                $Account.Save()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Enable-ADSIComputer
{
<#
.SYNOPSIS
    Function to enable a Computer Account
 
.DESCRIPTION
    Function to enable a Computer Account
 
.PARAMETER Identity
    Specifies the Identity of the Computer.
 
    You can provide one of the following properties
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain.
    By default it will use the current domain.
 
.EXAMPLE
    Enable-ADSIComputer TESTSERVER01
 
    This command will enable the account TESTSERVER01
 
.EXAMPLE
    Enable-ADSIComputer TESTSERVER01 -whatif
 
    This command will emulate disabling the account TESTSERVER01
 
.EXAMPLE
    Enable-ADSIComputer TESTSERVER01 -credential (Get-Credential)
 
    This command will enable the account TESTSERVER01 using the alternative credential specified
 
.EXAMPLE
    Enable-ADSIComputer TESTSERVER01 -credential (Get-Credential) -domain LazyWinAdmin.local
 
    This command will enable the account TESTSERVER01 using the alternative credential specified in the domain lazywinadmin.local
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.computerprincipal(v=vs.110).aspx
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        $Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName)

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

    }
    process
    {
        try
        {
            if ($pscmdlet.ShouldProcess("$Identity", "enable Account"))
            {
                $Account = Get-ADSIComputer -Identity $Identity @ContextSplatting
                $Account.enabled = $true
                $Account.Save()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Enable-ADSIDomainControllerGlobalCatalog
{
<#
.SYNOPSIS
    Function to enable the Global Catalog role on a Domain Controller
 
.DESCRIPTION
    Function to enable the Global Catalog role on a Domain Controller
 
.PARAMETER ComputerName
    Specifies the Domain Controller
 
.PARAMETER Credential
    Specifies alternate credentials to use. Use Get-Credential to create proper credentials.
 
.EXAMPLE
    Enable-ADSIDomainControllerGlobalCatalog -ComputerName dc1.ad.local
 
    Connects to remote domain controller dc1.ad.local using current credentials and enable the GC role.
 
.EXAMPLE
    Enable-ADSIDomainControllerGlobalCatalog -ComputerName dc2.ad.local -Credential (Get-Credential SuperAdmin)
 
    Connects to remote domain controller dc2.ad.local using SuperAdmin credentials and enable the GC role.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
    Version History
        1.0 Initial Version (Micky Balladelli)
        1.1 Update (Francois-Xavier Cat)
                Rename from Enable-ADSIReplicaGC to Enable-ADSIDomainControllerGlobalCatalog
                Add New-ADSIDirectoryContext to take care of the Context
                Other minor modifications
 
#>


    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$ComputerName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )

    process
    {
        try
        {
            $Context = New-ADSIDirectoryContext -ContextType 'DirectoryServer' @PSBoundParameters
            $DomainController = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($context)

            if ($DomainController.IsGlobalCatalog())
            {
                Write-Verbose -Message "[Enable-ADSIDomainControllerGlobalCatalog][PROCESS] $($DomainController.name) is already a Global Catalog"
            }
            else
            {
                Write-Verbose -Message "[Enable-ADSIDomainControllerGlobalCatalog][PROCESS] $($DomainController.name) Enabling Global Catalog ..."
                $DomainController.EnableGlobalCatalog()
            }

            Write-Verbose -Message "[Enable-ADSIDomainControllerGlobalCatalog][PROCESS] $($DomainController.name) Done."
        }
        catch
        {
            Write-Error -Message "[Enable-ADSIDomainControllerGlobalCatalog][PROCESS] Something wrong happened"
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Enable-ADSIUser
{
<#
.SYNOPSIS
    Function to Enable a User Account
 
.DESCRIPTION
    Function to Enable a User Account
 
.PARAMETER Identity
    Specifies the Identity of the User.
 
    You can provide one of the following properties
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
        UserPrincipalName
 
    Those properties come from the following enumeration:
        System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain.
    By default it will use the current domain.
 
.EXAMPLE
    Enable-ADSIUser fxtest02
 
    This will Enable the fxtest02 account
 
.EXAMPLE
    Enable-ADSIUser fxtest02 -whatif
 
    This will emulate the following action: Enable the fxtest02 account
 
.EXAMPLE
    Enable-ADSIUser fxtest02 -credential (Get-Credential)
 
    This will enable the fxtest02 account using the credential specified
 
.EXAMPLE
    Enable-ADSIUser fxtest02 -credential (Get-Credential) -DomainName LazyWinAdmin.local
 
    This will enable the fxtest02 account using the credential specified in the domain LazyWinAdmin.local
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/System.DirectoryServices.AccountManagement.UserPrincipal(v=vs.110).aspx
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        $Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName)

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

    }
    process
    {
        try
        {
            if ($pscmdlet.ShouldProcess("$Identity", "Enable Account"))
            {
                $Account = Get-ADSIUser -Identity $Identity @ContextSplatting
                $Account.Enabled = $true
                $Account.Save()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSIClass
{
<#
.SYNOPSIS
    Find all the object classes available in the current or
    specified Active Directory forest.
 
.DESCRIPTION
    This function is mostly a wrapper around Get-ADSISchema.
 
.PARAMETER ClassName
    Specify the name of the Class to retrieve
 
.PARAMETER AllClasses
    This will list all the property present in the domain.
    This parameter is the default one and is hidden.
 
.PARAMETER ForestName
    Specifies the Forest name
 
.PARAMETER Credential
    Specifies alternative credential to use
 
.EXAMPLE
    Get-ADSIClass
 
    Retrieve all the Class available in the forest
 
.EXAMPLE
    Get-ADSIClass -ClassName user
 
    Retrieve the 'user' class.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(DefaultParameterSetName = 'AllClass')]
    param
    (
        [Parameter(ParameterSetName = 'ClassName',
            Mandatory = $false)]
        [String]$ClassName,

        [Parameter(DontShow=$true,ParameterSetName = 'AllClasses',
            Mandatory = $false)]
        [Switch]$AllClasses,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest()
    )
    process{
        if($ClassName){
            Get-ADSISchema @PSBoundParameters
        }else{
            Get-ADSISchema @PSBoundParameters -AllClasses
        }
    }
}
function Get-ADSIComputer
{
<#
.SYNOPSIS
    Function to retrieve a Computer in Active Directory
 
.DESCRIPTION
    Function to retrieve a Computer in Active Directory
 
.PARAMETER Identity
    Specifies the Identity of the computer
 
    You can provide one of the following:
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
 
    System.DirectoryService.AccountManagement.IdentityType
    https://msdn.microsoft.com/en-us/library/bb356425(v=vs.110).aspx
 
.PARAMETER Credential
    Specifies alternative credential
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain.
    By default it will use the current domain.
 
.EXAMPLE
    Get-ADSIComputer -Identity 'SERVER01'
 
    This command will retrieve the computer account SERVER01
 
.EXAMPLE
    Get-ADSIComputer -Identity 'SERVER01' -Credential (Get-Credential)
 
    This command will retrieve the computer account SERVER01 with the specified credential
 
.EXAMPLE
    Get-ADSIComputer TESTSERVER01 -credential (Get-Credential) -domain LazyWinAdmin.local
 
    This command will retrieve the account TESTSERVER01 using the alternative credential specified in the domain lazywinadmin.local
 
.EXAMPLE
    $Comp = Get-ADSIComputer -Identity 'SERVER01'
    $Comp.GetUnderlyingObject()| Select-Object -Property *
 
    Help you find all the extra properties
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.computerprincipal(v=vs.110).aspx
#>

    [CmdletBinding(DefaultParameterSetName = "All")]
    param ([Parameter(Mandatory = $true, ParameterSetName = "Identity")]
        [string]$Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName
    )
    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting

    }
    process
    {
        try
        {
            if ($Identity)
            {
                [System.DirectoryServices.AccountManagement.ComputerPrincipal]::FindByIdentity($Context, $Identity)
            }
            else
            {
                $ComputerPrincipal = New-object -TypeName System.DirectoryServices.AccountManagement.ComputerPrincipal -ArgumentList $Context
                $Searcher = new-object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher
                $Searcher.QueryFilter = $ComputerPrincipal

                $Searcher.FindAll()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSIComputerSite
{
<#
.SYNOPSIS
    Function to retrieve the AD Site of a Computer
 
.DESCRIPTION
    Function to retrieve the AD Site of a Computer
 
    This function does not rely on the .NET Framework to retrieve the information
    http://www.pinvoke.net/default.aspx/netapi32.dsgetsitename
 
    There is .NET method to get this information but only works on the local machine.
    [System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite()
 
.PARAMETER ComputerName
    Specifies the computer name(s) that you want to know the site.
 
.EXAMPLE
    Get-ADSIComputerName -ComputerName TestServer01
 
    This will retrieve the Site of the Computer TestServer01
 
.EXAMPLE
    Get-ADSIComputerName -ComputerName TestServer01,TestServer02
 
    This will retrieve the Site of the Computers TestServer01 and TestServer02
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
    Thanks to the Reddit folks for their help! :-)
    https://www.reddit.com/r/PowerShell/comments/4cjdk8/get_the_ad_site_name_of_a_computer/
#>


    [CmdletBinding()]
    [OutputType('System.Management.Automation.PSCustomObject')]
    param
    (
        [parameter()]
        [String[]]$ComputerName = $env:computername
    )

    begin
    {
        $code = @"
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
 
public static class NetApi32 {
    private class unmanaged {
        [DllImport("NetApi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        internal static extern UInt32 DsGetSiteName([MarshalAs(UnmanagedType.LPTStr)]string ComputerName, out IntPtr SiteNameBuffer);
 
        [DllImport("Netapi32.dll", SetLastError=true)]
        internal static extern int NetApiBufferFree(IntPtr Buffer);
    }
 
    public static string DsGetSiteName(string ComputerName) {
        IntPtr siteNameBuffer = IntPtr.Zero;
        UInt32 hResult = unmanaged.DsGetSiteName(ComputerName, out siteNameBuffer);
        string siteName = Marshal.PtrToStringAuto(siteNameBuffer);
        unmanaged.NetApiBufferFree(siteNameBuffer);
        if(hResult == 0x6ba) { throw new Exception("ComputerName not found"); }
        return siteName;
    }
}
"@


        Add-Type -TypeDefinition $code
    }
    process
    {
        foreach ($Computer in $ComputerName)
        {
            try
            {
                $Properties = @{
                    ComputerName = $Computer
                    SiteName     = [NetApi32]::DsGetSiteName($Computer)
                }

                New-Object -TypeName PSObject -property $Properties
            }
            catch
            {
                $pscmdlet.ThrowTerminatingError($_)
            }
        }
    }
}
Function Get-ADSIDefaultDomainAccountLockout
{
<#
.SYNOPSIS
    Function to retrieve default Domain Account Lockout Policy
 
.DESCRIPTION
    Function to retrieve default Domain Account Lockout Policy
 
.PARAMETER Credential
    Specifies alternative credential
 
.PARAMETER DomainName
    Specifies the Domain Name where the function should look
 
.PARAMETER DomainDistinguishedName
    Specifies the DistinguishedName of the Domain to query
 
.EXAMPLE
    Get-ADSIDefaultDomainAccountLockout
 
.EXAMPLE
    Get-ADSIDefaultDomainAccountLockout -Credential (Get-Credential)
 
.EXAMPLE
    Get-ADSIDefaultDomainAccountLockout -DomainName "CONTOSO.local"
 
.EXAMPLE
    Get-ADSIDefaultDomainAccountLockout -DomainDistinguishedName 'DC=CONTOSO,DC=local'
 
.OUTPUTS
    LockoutDuration
        This attribute specifies the lockout duration for locked-out user accounts
        Unit : minutes
 
        EXAMPLE
        lockoutDuration : 10 minutes
 
    LockoutObservationWindow
        This attribute specifies the observation window for lockout of user accounts.
        Unit : minutes
 
        EXAMPLE
        lockoutObservationWindow : 10 minutes
 
    LockoutThreshold
        This attribute specifies the lockout threshold for lockout of user accounts.
 
        EXAMPLE
        lockoutThreshold : 7
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Alias("Domain")]
        [ValidateScript( { if ($_ -match "^(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,6}$")
                {
                    $true
                }
                else
                {
                    throw "DomainName must be FQDN. Ex: contoso.locale - Hostname like '$_' is not working"
                } })]
        [String]$DomainName,

        [Alias("DomainDN")]
        [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path)
    )

    begin
    {
    }
    process
    {

        if ($PSBoundParameters['DomainName'])
        {
            $DomainDistinguishedName = "LDAP://DC=$($DomainName.replace(".", ",DC="))"

            Write-Verbose -Message "Current Domain: $DomainDistinguishedName"

        }
        elseif ($PSBoundParameters['DomainDistinguishedName'])
        {
            if ($DomainDistinguishedName -notlike "LDAP://*")
            {
                $DomainDistinguishedName = "LDAP://$DomainDistinguishedName"
            }
            Write-Verbose -Message "Different Domain specified: $DomainDistinguishedName"

        }

        if ($PSBoundParameters['Credential'])
        {
            $DomainAccount = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password)

        }
        else
        {

            $DomainAccount = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName
        }


        $Properties = @{
            "lockoutDuration"          = ($DomainAccount.ConvertLargeIntegerToInt64($DomainAccount.'lockoutDuration'[0]) / -600000000) -as [int]
            "lockoutObservationWindow" = ($DomainAccount.ConvertLargeIntegerToInt64($DomainAccount.'lockoutObservationWindow'[0]) / -600000000) -as [int]
            "lockoutThreshold"         = $DomainAccount.lockoutThreshold -as [int]
        }
        New-Object -TypeName psobject -Property $Properties
    }

}
Function Get-ADSIDefaultDomainPasswordPolicy
{
<#
.SYNOPSIS
        Function to retrieve default Domain Password Policy
 
.DESCRIPTION
    Function to retrieve default Domain Password Policy
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current domain.
 
.EXAMPLE
    Get-ADSIDefaultDomainPasswordPolicy
 
.EXAMPLE
    Get-ADSIDefaultDomainPasswordPolicy -Credential (Get-Credential)
 
.EXAMPLE
    Get-ADSIDefaultDomainPasswordPolicy -DomainName "CONTOSO.local"
 
.OUTPUTS
    DomainMinimumPasswordAge
        specifies the minimum amount of time that a password can be used
        Unit : days
 
        EXAMPLE
        minPwdAge : 3 days
 
    DomainMaximumPasswordAge
        specifies the maximum amount of time that a password is valid
        Unit : days
 
        EXAMPLE
        maxPwdAge : 180 days
 
    DomainMinimumPasswordLength
        specifies the minimum number of characters that a password has to contain
 
        EXAMPLE
        minPwdLength : 8
 
    DomainPasswordHistoryLength
        specifies the number of old passwords to save
 
        EXAMPLE
        pwdHistoryLength : 5
 
    PasswordProperties
        Part of Domain Policy. A bitfield to indicate complexity and storage restrictions.
 
        EXAMPLE
        pwdProperties : 1 DOMAIN_PASSWORD_COMPLEX : The server enforces password complexity policy
                        2 DOMAIN_PASSWORD_NO_ANON_CHANGE : Reserved. No effect on password policy
                        4 DOMAIN_PASSWORD_NO_CLEAR_CHANGE : Change-password methods that provide the cleartext password are disabled by the server
                        8 DOMAIN_LOCKOUT_ADMINS : Reserved. No effect on password policy
                        16 DOMAIN_PASSWORD_STORE_CLEARTEXT : The server MUST store the cleartext password, not just the computed hashes
                        32 DOMAIN_REFUSE_PASSWORD_CHANGE : Reserved. No effect on password policy
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName
    )

    begin
    {
        $DirectoryEntryParams = @{}

        if ($PSBoundParameters['Credential'])
        {
            $DirectoryEntryParams.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $DirectoryEntryParams.DomainName = $DomainName
        }
    }
    process
    {
        $DirectoryEntry = New-ADSIDirectoryEntry @DirectoryEntryParams

        $Properties = @{
            "minPwdAge"        = ($DirectoryEntry.ConvertLargeIntegerToInt64($DirectoryEntry.'minPwdAge'[0]) / -864000000000) -as [int]
            "maxPwdAge"        = ($DirectoryEntry.ConvertLargeIntegerToInt64($DirectoryEntry.'maxPwdAge'[0]) / -864000000000) -as [int]
            "minPwdLength"     = $DirectoryEntry.minPwdLength.value
            "pwdHistoryLength" = $DirectoryEntry.pwdHistoryLength.value
            "pwdProperties"    = switch ($DirectoryEntry.pwdProperties)
            {
                1
                {
                    "DOMAIN_PASSWORD_COMPLEX : The server enforces password complexity policy"; break
                }
                2
                {
                    "DOMAIN_PASSWORD_NO_ANON_CHANGE : Reserved. No effect on password policy"; break
                }
                4
                {
                    "DOMAIN_PASSWORD_NO_CLEAR_CHANGE : Change-password methods that provide the cleartext password are disabled by the server"; break
                }
                8
                {
                    "DOMAIN_LOCKOUT_ADMINS : Reserved. No effect on password policy"; break
                }
                16
                {
                    "DOMAIN_PASSWORD_STORE_CLEARTEXT : The server MUST store the cleartext password, not just the computed hashes."; break
                }
                32
                {
                    "DOMAIN_REFUSE_PASSWORD_CHANGE : Reserved. No effect on password policy"; break
                }
                DEFAULT
                {
                    $DirectoryEntry.pwdProperties
                }
            }
        }

        New-Object -TypeName psobject -Property $Properties
    }
}
Function Get-ADSIDomain
{
    <#
.SYNOPSIS
    Function to retrieve the current or specified domain
 
.DESCRIPTION
    Function to retrieve the current or specified domain
 
.PARAMETER Credential
    Specifies alternative credential to use
 
.PARAMETER DomainName
    Specifies the DomainName to query
 
.EXAMPLE
    Get-ADSIDomain
 
    Retrieve the current domain
 
.EXAMPLE
    Get-ADSIDomain -DomainName lazywinadmin.com
 
    Retrieve the domain lazywinadmin.com
 
.EXAMPLE
    Get-ADSIDomain -Credential (Get-Credential superAdmin) -Verbose
 
    Retrieve the current domain with the specified credential.
 
.EXAMPLE
    Get-ADSIDomain -DomainName lazywinadmin.com -Credential (Get-Credential superAdmin) -Verbose
 
    Retrieve the domain lazywinadmin.com with the specified credential.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.OUTPUTS
    'System.DirectoryServices.ActiveDirectory.Domain'
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.domain(v=vs.110).aspx
#>

    [cmdletbinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.Domain')]
    param (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
    )
    process
    {
        try
        {
            if ($PSBoundParameters['Credential'] -or $PSBoundParameters['DomainName'])
            {
                Write-Verbose -Message '[PROCESS] Credential or DomainName specified'
                $Splatting = @{ }
                if ($PSBoundParameters['Credential'])
                {
                    $Splatting.Credential = $Credential
                }
                if ($PSBoundParameters['DomainName'])
                {
                    $Splatting.DomainName = $DomainName
                }

                $DomainContext = New-ADSIDirectoryContext @splatting -contextType Domain
                [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
            }
            else
            {
                [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
            }

        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSIDomainBackup {
<#
.SYNOPSIS
    Function to retrieve last backup for each partitions of a given domain
 
.DESCRIPTION
    Function to retrieve last backup for each partitions of a given domain.
    It look at the dsaSignature for each naming context.
    dsaSignature attribute change after a succesfull backup
 
.PARAMETER Credential
    Specifies alternative credential to use
 
.PARAMETER DomainName
    Specifies the DomainName to query
 
.EXAMPLE
    get-ADSIDomainBackup
 
    Retrieve Backup information for the current domain
 
.EXAMPLE
    get-ADSIDomainBackup -DomainName mytest.local
 
    Retrieve Backup information for the domain mytest.local
 
.EXAMPLE
    get-ADSIDomainBackup -Credential (Get-Credential superAdmin) -Verbose
 
    Retrieve Backup information for the current domain with the specified credential.
 
.EXAMPLE
    get-ADSIDomainBackup -DomainName mytest.local -Credential (Get-Credential superAdmin) -Verbose
 
    Retrieve Backup information for the domain mytest.local with the specified credential.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.OUTPUTS
    'Ps'
 
 
#>

    [cmdletbinding()]
    [OutputType('pscustomobject[]')]
    param (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().name
    )



    process {
        try {



            if ($PSBoundParameters['Credential']){
                $ContextObject = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext($ContextObjectType, $DomainName, $Credential.UserName, $Credential.GetNetworkCredential().password)
            } else {
                $ContextObjectType = [System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Domain
            }



            $DomainControlerObject = [System.DirectoryServices.ActiveDirectory.DomainController]::findOne($ContextObject)

            $ActiveDirectoryPartitions = $DomainControlerObject.Partitions

            $ArrayListBackupInfo = [System.Collections.ArrayList]::new()

            foreach ($partition in $ActiveDirectoryPartitions) {

                $ActiveDirectoryPartitionMetaData = $domainController.GetReplicationMetadata($partition)
                $DsaSignatureAtribute = $ActiveDirectoryPartitionMetaData.item("dsaSignature")
                $LastBackupDate = $DsaSignatureAtribute.LastOriginatingChangeTime.DateTime

                $PartitionBackupInfo =   [pscustomobject]@{
                    PartitionName=$partition
                    BackupDateTime=$LastBackupDate
                }

                [void]$ArrayListBackupInfo.Add($PartitionBackupInfo)
            }

            return $ArrayListBackupInfo

        }
        catch {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }

}
function Get-ADSIDomainController
{
<#
.SYNOPSIS
    Function to retrieve Domain Controllers
 
.DESCRIPTION
    Function to retrieve Domain Controllers
 
.PARAMETER Credential
    Specifies alternative credential
 
.PARAMETER DomainName
    Specifies the Domain Name where the function should look
 
.EXAMPLE
    Get-ADSIDomainController
 
.EXAMPLE
    Get-ADSIDomainController -Credential (Get-Credential)
 
.EXAMPLE
    Get-ADSIDomainController -DomainName "FXTEST.local"
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.DomainController')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::Getcurrentdomain()
    )

    begin
    {


        if ($PSBoundParameters['Credential'])
        {
            $Context = New-ADSIDirectoryContext -Credential $Credential -contextType Domain
            if ($PSBoundParameters['DomainName'])
            {
                $Context = New-ADSIDirectoryContext -Credential $Credential -contextType Domain -DomainName $DomainName
            }
        }
        else
        {
            $Context = New-ADSIDirectoryContext -contextType Domain
            if ($PSBoundParameters['DomainName'])
            {
                $Context = New-ADSIDirectoryContext -contextType Domain -DomainName $DomainName
            }
        }
    }
    process
    {
        [System.DirectoryServices.ActiveDirectory.DomainController]::FindAll($Context)
    }
}
Function Get-ADSIDomainMode
{
<#
.SYNOPSIS
    Function to retrieve Domain mode
 
.DESCRIPTION
    Function to retrieve Domain mode
 
.PARAMETER Credential
    Specifies alternative credential
 
.PARAMETER DomainName
    Specifies the Domain Name where the function should look
 
.EXAMPLE
    Get-ADSIDomainMode
 
.EXAMPLE
    Get-ADSIDomainMode -Credential (Get-Credential)
 
.EXAMPLE
    Get-ADSIDomainMode -DomainName "FXTEST.local"
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [cmdletbinding()]
    param (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::Getcurrentdomain()
    )
    process
    {
        try
        {
            if ($PSBoundParameters['Credential'] -or $PSBoundParameters['DomainName'])
            {
                Write-Verbose -Message '[PROCESS] Credential or DomainName specified'
                $Splatting = @{ }
                if ($PSBoundParameters['Credential'])
                {
                    $Splatting.Credential = $Credential
                }
                if ($PSBoundParameters['DomainName'])
                {
                    $Splatting.DomainName = $DomainName
                }

                (Get-ADSIDomain @splatting).DomainMode

            }
            else
            {
                (Get-ADSIDomain).DomainMode
            }

        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSIDomainRoot
{
    <#
.SYNOPSIS
    Function to retrieve the Domain Root in the Forest
 
.DESCRIPTION
    Function to retrieve the Domain Root in the Forest
 
.PARAMETER Credential
    Specifies the alternative credential to use. Default is the current one.
 
.PARAMETER ForestName
    Specifies the alternative forest name to query. Default is the current one.
 
.EXAMPLE
    Get-ADSIDomainRoot
 
    Retrieve the current Domain Root
 
.EXAMPLE
    Get-ADSIDomainRoot -ForestName ForestTest.lab
 
    Retrieve the Domain root of ForestTest.lab
 
.EXAMPLE
    Get-ADSIDomainRoot -ForestName ForestTest.lab -credential (Get-Credential)
 
    Retrieve the Domain root of ForestTest.lab with the specified credential
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.OUTPUTS
    System.DirectoryServices.ActiveDirectory.Domain
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.domain(v=vs.110).aspx
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.Domain')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest()
    )

    (Get-ADSIForest @PSBoundParameters).RootDomain
}
function Get-ADSIDomainTrustRelationship
{
<#
.SYNOPSIS
    Function to retrieve the Trust relationship of a domain. Current one by default.
 
.DESCRIPTION
    Function to retrieve the Trust relationship of a domain. Current one by default.
 
.PARAMETER Credential
    Specifies the alternative credential to use. Default is the current user.
 
.PARAMETER DomainName
    Specifies the alternative domain name to use. Default is the current one.
 
.EXAMPLE
    Get-ADSIDomainTrustRelationship
 
    Retrieve the Trust relationship(s) of a current domain
 
.EXAMPLE
    Get-ADSIDomainTrustRelationship -DomainName FX.lab
 
    Retrieve the Trust relationship(s) of domain fx.lab
 
.EXAMPLE
    Get-ADSIDomainTrustRelationship -DomainName FX.lab -Credential (Get-Credential)
 
    Retrieve the Trust relationship(s) of domain fx.lab with the credential specified
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.OUTPUTS
    System.DirectoryServices.ActiveDirectory.TrustRelationshipInformation
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.trustrelationshipinformation(v=vs.110).aspx
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.TrustRelationshipInformation')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::GetcurrentDomain()
    )

    process
    {
        try
        {
            if ($PSBoundParameters['Credential'] -or $PSBoundParameters['DomainName'])
            {
                Write-Verbose -Message '[PROCESS] Credential or FirstName specified'
                $Splatting = @{ }
                if ($PSBoundParameters['Credential'])
                {
                    $Splatting.Credential = $Credential
                }
                if ($PSBoundParameters['DomainName'])
                {
                    $Splatting.DomainName = $DomainName
                }

                (Get-ADSIDomain @splatting).GetAllTrustRelationships()

            }
            else
            {
                (Get-ADSIDomain).GetAllTrustRelationships()
            }

        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSIFineGrainedPasswordPolicy
{
<#
.SYNOPSIS
    This function will query and list Fine-Grained Password Policies in Active Directory
 
.DESCRIPTION
    This function will query and list Fine-Grained Password Policies in Active Directory
    it return
    name : the Name of the Policy
    MinimumPasswordLength : Minimum PassWord Size int
    passwordreversibleencryptionenabled : Encryption Type reversible (not secure) or not
    minimumpasswordage : The number of day before the user Can change the password
    maximumpasswordage : The number of day that a password can be used before the system requires the user to change it
    passwordcomplexityenabled : True of Fals
    passwordsettingsprecedence : Integer needed to determine which policy to apply in a user is mapped to more than one policy
    lockoutduration : Time in minute before reseting failed logon count
    lockoutobservationwindow : Time between 2 failed logoin attemps before reseting the counter to 0
    lockoutthreshold : Number of failed login attempt to trigger lockout
    psoappliesto : Groups and/or Users
    WhenCreated
    WhenChanged
 
.PARAMETER Name
    Specify the name of the policy to retreive
 
.PARAMETER Credential
    Specify the Credential to use
 
.PARAMETER DomainDistinguishedName
    Specify the DistinguishedName of the Domain to query
 
.PARAMETER SizeLimit
    Specify the number of item(s) to output
 
.EXAMPLE
    Get-ADSIFineGrainedPasswordPolicy
    Retreive all the password policy on the current domain
 
.EXAMPLE
    Get-ADSIFineGrainedPasswordPolicy -Name Name
    Retreive the password policy nammed 'Name' on the current domain
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
#>




    [CmdletBinding()]
    param (
        [Parameter(ParameterSetName = "Name")]
        [String]$Name,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias("Domain", "DomainDN", "SearchRoot", "SearchBase")]
        [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path),

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Alias("ResultLimit", "Limit")]
        [int]$SizeLimit = '100'
    )
    process
    {
        try
        {
            $FunctionName = (Get-Variable -Name MyInvocation -ValueOnly -Scope 0).MyCommand

            Write-Verbose -Message "[$FunctionName] Create DirectorySearcher"
            $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop'
            $Search.SizeLimit = $SizeLimit
            $Search.SearchRoot = $DomainDistinguishedName
            $Search.filter = "(objectclass=msDS-PasswordSettings)"


            if ($PSBoundParameters['name'])
            {
                $Search.filter = "(|(name=$name))"
                Write-Verbose -Message "[$FunctionName] Set Filter to '$($Search.filter)'"
            }

            if ($PSBoundParameters['DomainDistinguishedName'])
            {
                if ($DomainDistinguishedName -notlike "LDAP://*")
                {
                    $DomainDistinguishedName = "LDAP://$DomainDistinguishedName"
                }#if
                Write-Verbose -Message "Different Domain specified: $DomainDistinguishedName"
                $Search.SearchRoot = $DomainDistinguishedName
                Write-Verbose -Message "[$FunctionName] Set SearchRoot to '$($Search.SearchRoot)'"
            }
            if ($PSBoundParameters['Credential'])
            {
                Write-Verbose -Message "[$FunctionName] Add Credential'"
                $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password)
                $Search.SearchRoot = $Cred
            }


            foreach ($Object in $($Search.FindAll()))
            {
                # Define the properties
                # The properties need to be lowercase!
                $Properties = @{
                    "name"                                = $Object.properties.name -as [string]
                    "passwordhistorylength"               = $Object.Properties.Item("msds-passwordhistorylength") -as [int]
                    "minimumpasswordlength"               = $Object.Properties.Item("msds-minimumpasswordlength") -as [int]
                    "passwordreversibleencryptionenabled" = $Object.Properties.Item("msds-passwordreversibleencryptionenabled") -as [string]
                    "minimumpasswordage"                  = $Object.Properties.Item("msds-minimumpasswordage") -as [string]
                    "maximumpasswordage"                  = $Object.Properties.Item("msds-maximumpasswordage") -as [string]
                    "passwordcomplexityenabled"           = $Object.Properties.Item("msds-passwordcomplexityenabled") -as [string]
                    "passwordsettingsprecedence"          = $Object.Properties.Item("msds-passwordsettingsprecedence") -as [string]
                    "lockoutduration"                     = $Object.Properties.Item("msds-lockoutduration") -as [string]
                    "lockoutobservationwindow"            = $Object.Properties.Item("msds-lockoutobservationwindow") -as [string]
                    "lockoutthreshold"                    = $Object.Properties.Item("msds-lockoutthreshold") -as [string]
                    "psoappliesto"                        = $Object.Properties.Item("msds-psoappliesto") -as [string]
                    "WhenCreated"                         = $Object.properties.whencreated -as [string]
                    "WhenChanged"                         = $Object.properties.whenchanged -as [string]
                }

                # Output the info
                New-Object -TypeName PSObject -Property $Properties
            }

        }
        catch
        {
            # Return current error
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
    end
    {
        Write-Verbose -Message "[$FunctionName] Done"
    }
}
function Get-ADSIForest
{
    <#
.SYNOPSIS
    Function to retrieve the current or specified forest
 
.DESCRIPTION
    Function to retrieve the current or specified forest
 
.PARAMETER Credential
    Specifies alternative credential to use
 
.PARAMETER ForestName
    Specifies the ForestName to query
 
.EXAMPLE
    Get-ADSIForest
 
.EXAMPLE
    Get-ADSIForest -ForestName lazywinadmin.com
 
.EXAMPLE
    Get-ADSIForest -Credential (Get-Credential superAdmin) -Verbose
 
.EXAMPLE
    Get-ADSIForest -ForestName lazywinadmin.com -Credential (Get-Credential superAdmin) -Verbose
 
.OUTPUTS
    System.DirectoryServices.ActiveDirectory.Forest
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.forest(v=vs.110).aspx
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.Forest')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest()
    )

    process
    {
        try
        {
            if ($PSBoundParameters['Credential'] -or $PSBoundParameters['ForestName'])
            {
                Write-Verbose -Message "[PROCESS] Credential or FirstName specified"
                $Splatting = @{ }
                if ($PSBoundParameters['Credential'])
                {
                    $Splatting.Credential = $Credential
                }
                if ($PSBoundParameters['ForestName'])
                {
                    $Splatting.ForestName = $ForestName
                }

                $ForestContext = New-ADSIDirectoryContext @splatting -contextType Forest
                [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
            }
            else
            {
                [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
            }

        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
Function Get-ADSIForestDomain
{
<#
.SYNOPSIS
    Function to retrieve the forest domain(s)
 
.DESCRIPTION
    Function to retrieve the forest domain(s)
 
.PARAMETER Credential
    Specifies alternative credential to use
 
.PARAMETER ForestName
    Specifies the ForestName to query
 
.EXAMPLE
    Get-ADSIForest
 
.EXAMPLE
    Get-ADSIForest -ForestName lazywinadmin.com
 
.EXAMPLE
    Get-ADSIForest -Credential (Get-Credential superAdmin) -Verbose
 
.EXAMPLE
    Get-ADSIForest -ForestName lazywinadmin.com -Credential (Get-Credential superAdmin) -Verbose
 
.OUTPUTS
    System.DirectoryServices.ActiveDirectory.Forest
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [cmdletbinding()]
    param (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest()
    )
    process
    {
        try
        {
            if ($PSBoundParameters['Credential'] -or $PSBoundParameters['ForestName'])
            {
                Write-Verbose -Message '[PROCESS] Credential or FirstName specified'
                $Splatting = @{ }
                if ($PSBoundParameters['Credential'])
                {
                    $Splatting.Credential = $Credential
                }
                if ($PSBoundParameters['ForestName'])
                {
                    $Splatting.ForestName = $ForestName
                }

                (Get-ADSIForest @splatting).Domains

            }
            else
            {
                (Get-ADSIForest).Domains
            }

        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
Function Get-ADSIForestMode
{
    <#
.SYNOPSIS
    Function to retrieve the forest mode
 
.DESCRIPTION
    Function to retrieve the forest mode
 
.PARAMETER Credential
    Specifies alternative credential to use
 
.PARAMETER ForestName
    Specifies the ForestName to query
 
.EXAMPLE
    Get-ADSIForestMode
 
    Retrieve the forest mode of the current forest
 
.EXAMPLE
    Get-ADSIForestMode -ForestName lazywinadmin.com
 
    Retrieve the forest mode of the forest lazywinadmin.com
 
.EXAMPLE
    Get-ADSIForestMode -Credential (Get-Credential superAdmin) -Verbose
 
    Retrieve the forest mode of the current forest using the credentials specified
 
.EXAMPLE
    Get-ADSIForestMode -ForestName lazywinadmin.com -Credential (Get-Credential superAdmin) -Verbose
 
    Retrieve the forest mode of the forest lazywinadmin.com using the credentials specified
 
.OUTPUTS
    System.directoryservices.activedirectory.forest.forestmode
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.forest.forestmode(v=vs.110).aspx
#>

    [cmdletbinding()]
    [OutputType('System.directoryservices.activedirectory.forest.forestmode')]
    param (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest()
    )
    process
    {
        try
        {
            if ($PSBoundParameters['Credential'] -or $PSBoundParameters['ForestName'])
            {
                Write-Verbose -Message '[PROCESS] Credential or FirstName specified'
                $Splatting = @{ }
                if ($PSBoundParameters['Credential'])
                {
                    $Splatting.Credential = $Credential
                }
                if ($PSBoundParameters['ForestName'])
                {
                    $Splatting.ForestName = $ForestName
                }

                (Get-ADSIForest @splatting).ForestMode

            }
            else
            {
                (Get-ADSIForest).ForestMode
            }

        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSIForestTrustRelationship
{
<#
.SYNOPSIS
    Function to retrieve the Forest Trust Relationship(s)
 
.DESCRIPTION
    Function to retrieve the Forest Trust Relationship(s)
 
.PARAMETER Credential
    Specifies the alternative credential to use. Default is the current user.
 
.PARAMETER ForestName
    Specifies the alternative Forest name to query. Default is the current one.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.EXAMPLE
    Get-ADSIForestTrustRelationship
 
    Retrieve the Forest Trust Relationship of the current domain
 
.EXAMPLE
    Get-ADSIForestTrustRelationship -ForestName 'lazywinadmin.com'
 
    Retrieve the Forest Trust Relationship of the forest lazywinadmin.com
 
.EXAMPLE
    Get-ADSIForestTrustRelationship -ForestName 'lazywinadmin.com' -credential (Get-Credential)
 
    Retrieve the Forest Trust Relationship of the forest lazywinadmin.com using the specified credential
 
.OUTPUTS
    System.DirectoryServices.ActiveDirectory.ForestTrustRelationshipInformation
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.foresttrustrelationshipinformation(v=vs.110).aspx
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.ForestTrustRelationshipInformation')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest()
    )

    process
    {
        try
        {
            Write-Verbose -Message '[Get-ADSIForestTrustRelationship][PROCESS] Credential or FirstName specified'
            (Get-ADSIForest @PSBoundParameters).GetAllTrustRelationships()
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSIFsmo
{
<#
.SYNOPSIS
    Function to retrieve the Flexible single master operation (FSMO) roles owner(s)
 
.DESCRIPTION
    Function to retrieve the Flexible single master operation (FSMO) roles owner(s)
 
.PARAMETER Credential
    Specifies the Alternative credential to use
 
.PARAMETER ForestName
    Specifies the alternative forest name
 
.EXAMPLE
    Get-ADSIFsmo
 
    Retrieve the Flexible single master operation (FSMO) roles owner(s) of the current domain/forest
 
.EXAMPLE
    Get-ADSIFsmo -ForestName 'lazywinadmin.com'
 
    Retrieve the Flexible single master operation (FSMO) roles owner(s) of the root domain/forest lazywinadmin.com
 
.EXAMPLE
    Get-ADSIFsmo -ForestName 'lazywinadmin.com' -credential (Get-Credential)
 
    Retrieve the Flexible single master operation (FSMO) roles owner(s) of the root domain/forest lazywinadmin.com using
    the specified credential.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.OUTPUTS
    System.Management.Automation.PSCustomObject
#>


    [CmdletBinding()]
    [OutputType('System.Management.Automation.PSCustomObject')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest()
    )

    process
    {
        try
        {
            $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).Mycommand

            if ($PSBoundParameters['Credential'] -or $PSBoundParameters['ForestName'])
            {
                $Splatting = @{ }
                if ($PSBoundParameters['Credential'])
                {
                    Write-Verbose -message "[$FunctionName] Add Credential to splatting"
                    $Splatting.Credential = $Credential
                }
                if ($PSBoundParameters['ForestName'])
                {
                    Write-Verbose -message "[$FunctionName] Add ForestName to splatting"
                    $Splatting.ForestName = $ForestName
                }

                # Forest Query
                Write-Verbose -message "[$FunctionName] Retrieve Forest information '$ForestName'"
                $Forest = (Get-ADSIForest @splatting)

                # Domain Splatting cleanup
                $Splatting.Remove("ForestName")
                $Splatting.DomainName = $Forest.RootDomain.name

                # Domain Query
                Write-Verbose -message "[$FunctionName] Retrieve Domain information '$($Forest.RootDomain.name)'"
                $Domain = (Get-ADSIDomain @Splatting)

            }
            else
            {
                Write-Verbose -message "[$FunctionName] Retrieve Forest information '$ForestName'"
                $Forest = Get-ADSIForest
                Write-Verbose -message "[$FunctionName] Retrieve Domain information"
                $Domain = Get-ADSIDomain
            }

            Write-Verbose -message "[$FunctionName] Prepare Output"
            $Properties = @{
                SchemaRoleOwner         = $Forest.SchemaRoleOwner
                NamingRoleOwner         = $Forest.NamingRoleOwner
                InfrastructureRoleOwner = $Domain.InfrastructureRoleOwner
                RidRoleOwner            = $Domain.RidRoleOwner
                PdcRoleOwner            = $Domain.PdcRoleOwner
            }

            New-Object -TypeName PSObject -property $Properties

        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
    end
    {
        Write-Verbose -message "[$FunctionName] Done."
    }
}
function Get-ADSIGlobalCatalog
{
<#
.SYNOPSIS
    Function to retrieve the Global Catalog in the Forest
 
.DESCRIPTION
    Function to retrieve the Global Catalog in the Forest
 
.PARAMETER Credential
    Specifies the alternative credential to use. Default is the current user.
 
.PARAMETER ForestName
    Specifies the alternative Forest name to query. Default is the current one.
 
.EXAMPLE
    Get-ADSIGlobalCatalog
 
    Retrieve the Global Catalog in the current Forest
 
.EXAMPLE
    Get-ADSIGlobalCatalog -forestname 'lazywinadmin.com'
 
    Retrieve the Global Catalog in the forest 'lazywinadmin.com'
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.GlobalCatalog')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest()
    )

    process
    {
        try
        {
            Write-Verbose -Message '[Get-ADSIGlobalCatalog][PROCESS] Credential or FirstName specified'
            (Get-ADSIForest @PSBoundParameters).GlobalCatalogs
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSIGroup
{
<#
.SYNOPSIS
    Function to retrieve a group in Active Directory
 
.DESCRIPTION
    Function to retrieve a group in Active Directory
 
.PARAMETER Identity
    Specifies the Identity of the group
 
    You can provide one of the following properties
    DistinguishedName
    Guid
    Name
    SamAccountName
    Sid
    UserPrincipalName
 
    Those properties come from the following enumeration:
    System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current Domain.
 
.PARAMETER GroupScope
    Specifies the Group Scope (Global, Local or Universal)
 
.PARAMETER IsSecurityGroup
    Specifies if you look for security group, default is $true.
 
.PARAMETER Description
    Specifies the description of the group
 
.PARAMETER UserPrincipalName
    Specifies the UPN
 
.PARAMETER Displayname
    Specifies the DisplayName
 
.PARAMETER Name
    Specifies the Name
 
.PARAMETER SID
    Specifies the SID
 
.PARAMETER LDAPFilter
    Specifies the LDAP query to perform
 
.EXAMPLE
    Get-ADSIGroup -Identity 'SERVER01'
 
    Retrieve the group TestGroup in the current domain.
 
.EXAMPLE
    Get-ADSIGroup -Identity 'TestGroup' -Credential (Get-Credential)
 
    Retrieve the group TestGroup in the current domain using alternative credential
 
.EXAMPLE
    Get-ADSIGroup -Name "*ADSIPS*"
 
    Retrieve all the group(s) that contains 'ADSIPS' in their name
 
.EXAMPLE
    Get-ADSIGroup -ISSecurityGroup $true -Description "*"
 
    Retrieve all the security group(s) that have a description
 
.EXAMPLE
    $Comp = Get-ADSIGroup -Identity 'Finance'
    $Comp.GetUnderlyingObject()| Select-Object -Property *
 
    Help you find all the extra properties of the Finance group object
 
.EXAMPLE
    Get-ADSIGroup -GroupScope Universal -IsSecurityGroup:$false
 
    This will retrieve the Universal groups object with the SamAccountName TestGroup01 in the current domain.
 
.EXAMPLE
    Get-ADSIGroup -LDAPFilter "(SamAccountName=TestGroup01)"
 
    This will retrieve the group object with the SamAccountName TestGroup01 in the current domain.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.OUTPUTS
    System.DirectoryServices.AccountManagement.GroupPrincipal
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.groupprincipal(v=vs.110).aspx
#>


    [CmdletBinding(DefaultParameterSetName = 'All')]
    [OutputType('System.DirectoryServices.AccountManagement.GroupPrincipal')]
    param
    (
        [Parameter(ParameterSetName = 'Identity')]
        [string]$Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Alias('Domain', 'Server')]
        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::Getcurrentdomain(),

        [Parameter(ParameterSetName = 'Filter')]
        [System.DirectoryServices.AccountManagement.GroupScope]$GroupScope,

        [Parameter(ParameterSetName = 'Filter')]
        [bool]$IsSecurityGroup,

        [Parameter(ParameterSetName = 'Filter')]
        $Description,

        [Parameter(ParameterSetName = 'Filter')]
        $UserPrincipalName,

        [Parameter(ParameterSetName = 'Filter')]
        $Displayname,

        [Parameter(ParameterSetName = 'Filter')]
        $Name,

        [Parameter(ParameterSetName = 'Filter')]
        $SID,

        [Parameter(ParameterSetName = 'LDAPFilter')]
        $LDAPFilter

    )

    begin
    {
        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting
    }
    process
    {
        try
        {
            if ($Identity)
            {
                Write-Verbose -Message "Identity"
                [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($Context, $Identity)
            }
            elseif ($PSBoundParameters['LDAPFilter'])
            {
                Write-Verbose -Message "LDAPFilter"

                # Directory Entry object
                $DirectoryEntryParams = $ContextSplatting
                $DirectoryEntryParams.remove('ContextType')
                $DirectoryEntry = New-ADSIDirectoryEntry @DirectoryEntryParams

                # Principal Searcher
                $DirectorySearcher = new-object -TypeName System.DirectoryServices.DirectorySearcher
                $DirectorySearcher.SearchRoot = $DirectoryEntry
                $DirectorySearcher.Filter = "(&(objectCategory=group)$LDAPFilter)"


                $DirectorySearcher.FindAll() | Foreach-Object -Process {
                    [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($Context, ($_.path -replace 'LDAP://'))
                }
            }
            else
            {
                Write-Verbose -Message "Other Filters"

                $GroupPrincipal = New-object -TypeName System.DirectoryServices.AccountManagement.GroupPrincipal -ArgumentList $Context
                #$GroupPrincipal.Name = $Identity
                $searcher = new-object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher
                $searcher.QueryFilter = $GroupPrincipal
                if ($PSBoundParameters['IsSecurityGroup'])
                {
                    $searcher.QueryFilter.IsSecurityGroup = $IsSecurityGroup
                }
                if ($PSBoundParameters['GroupScope'])
                {
                    $searcher.QueryFilter.GroupScope = $GroupScope
                }
                if ($PSBoundParameters['UserPrincipalName'])
                {
                    $searcher.QueryFilter.UserPrincipalName = $UserPrincipalName
                }
                if ($PSBoundParameters['Description'])
                {
                    $searcher.QueryFilter.Description = $Description
                }
                if ($PSBoundParameters['DisplayName'])
                {
                    $searcher.QueryFilter.DisplayName = $DisplayName
                }
                #if($PSBoundParameters['DistinguishedName']){$searcher.QueryFilter.DistinguishedName = $DistinguishedName}
                if ($PSBoundParameters['Sid'])
                {
                    $searcher.QueryFilter.Sid.Value = $SID
                }
                if ($PSBoundParameters['Name'])
                {
                    $searcher.QueryFilter.Name = $Name
                }

                $searcher.FindAll()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSIGroupManagedBy
{
<#
.SYNOPSIS
    This function retrieve the group that the current user manage in the ActiveDirectory.
 
.DESCRIPTION
    This function retrieve the group that the current user manage in the ActiveDirectory.
    Typically the function will search for group(s) and look at the 'ManagedBy' property where it matches the current user.
 
.PARAMETER SamAccountName
    Specify the SamAccountName of the Manager of the group
    You can also use the alias: ManagerSamAccountName.
 
.PARAMETER AllManagedGroups
    Specify to search for groups with a Manager (managedby property)
 
.PARAMETER NoManager
    Specify to search for groups without Manager (managedby property)
 
.PARAMETER Credential
    Specify the Credential to use for the query
 
.PARAMETER SizeLimit
    Specify the number of item maximum to retrieve
 
.PARAMETER DomainDistinguishedName
    Specify the Domain or Domain DN path to use
 
.EXAMPLE
    Get-ADSIGroupManagedBy -SamAccountName fxcat
 
    This will list all the group(s) where fxcat is designated as Manager.
 
.EXAMPLE
    Get-ADSIGroupManagedBy
 
    This will list all the group(s) where the current user is designated as Manager.
 
.EXAMPLE
    Get-ADSIGroupManagedBy -NoManager
 
    This will list all the group(s) without Manager
 
.EXAMPLE
    Get-ADSIGroupManagedBy -AllManagedGroup
 
    This will list all the group(s) without Manager
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(DefaultParameterSetName = "One")]
    param (
        [Parameter(ParameterSetName = "One")]
        [Alias("ManagerSamAccountName")]
        [String]$SamAccountName = $env:USERNAME,

        [Parameter(ParameterSetName = "All")]
        [Switch]$AllManagedGroups,

        [Parameter(ParameterSetName = "No")]
        [Switch]$NoManager,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Alias("DomainDN", "Domain", "SearchBase", "SearchRoot")]
        [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path),

        [Alias("ResultLimit", "Limit")]
        [int]$SizeLimit = '100'
    )

    begin
    {
    }
    process
    {
        try
        {
            # Building the basic search object with some parameters
            $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop'
            $Search.SizeLimit = $SizeLimit
            $Search.SearchRoot = $DomainDN

            if ($PSBoundParameters['DomainDistinguishedName'])
            {
                # Fixing the path if needed
                if ($DomainDistinguishedName -notlike "LDAP://*")
                {
                    $DomainDistinguishedName = "LDAP://$DomainDistinguishedName"
                }#if

                Write-Verbose -Message "Different Domain specified: $DomainDistinguishedName"
                $Search.SearchRoot = $DomainDistinguishedName
            }

            if ($PSBoundParameters['Credential'])
            {
                Write-Verbose -Message "Different Credential specified: $($Credential.UserName)"
                $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password)
                $Search.SearchRoot = $Cred
            }

            if ($PSBoundParameters['SamAccountName'])
            {
                Write-Verbose -Message "SamAccountName"
                #Look for User DN
                $UserSearch = $search
                $UserSearch.Filter = "(&(SamAccountName=$SamAccountName))"
                $UserDN = $UserSearch.FindOne().Properties.distinguishedname -as [string]

                # Define the query to find the Groups managed by this user
                $Search.Filter = "(&(objectCategory=group)(ManagedBy=$UserDN))"
            }

            if ($PSBoundParameters['AllManagedGroups'])
            {
                Write-Verbose -Message "All Managed Groups Param"
                $Search.Filter = "(&(objectCategory=group)(managedBy=*))"
            }

            if ($PSBoundParameters['NoManager'])
            {
                Write-Verbose -Message "No Manager param"
                $Search.Filter = "(&(objectCategory=group)(!(!managedBy=*)))"
            }

            if (-not ($PSBoundParameters['SamAccountName']) -and -not ($PSBoundParameters['AllManagedGroups']) -and -not ($PSBoundParameters['NoManager']))
            {
                Write-Verbose -Message "No parameters used"
                #Look for User DN
                $UserSearch = $search
                $UserSearch.Filter = "(&(SamAccountName=$SamAccountName))"
                $UserDN = $UserSearch.FindOne().Properties.distinguishedname -as [string]

                # Define the query to find the Groups managed by this user
                $Search.Filter = "(&(objectCategory=group)(ManagedBy=$UserDN))"
            }

            foreach ($group in $Search.FindAll())
            {
                $Properties = @{
                    "SamAccountName"    = $group.properties.samaccountname -as [string]
                    "DistinguishedName" = $group.properties.distinguishedname -as [string]
                    "GroupType"         = $group.properties.grouptype -as [string]
                    "Mail"              = $group.properties.mail -as [string]
                }
                New-Object -TypeName psobject -Property $Properties
            }
        }#try
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }#Process
    end
    {
        Write-Verbose -Message "[END] Function Get-ADSIGroupManagedBy End."
    }
}
function Get-ADSIGroupMember
{
<#
.SYNOPSIS
    Function to retrieve the members from a specific group in Active Directory
 
.DESCRIPTION
    Function to retrieve the members from a specific group in Active Directory
 
.PARAMETER Identity
    Specifies the Identity of the Group
 
    You can provide one of the following properties
    DistinguishedName
    Guid
    Name
    SamAccountName
    Sid
    UserPrincipalName
 
    Those properties come from the following enumeration:
    System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Credential
    Specifies alternative credential
 
.PARAMETER Recurse
    Retrieves all the recursive members (Members of group(s)'s members)
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current domain.
 
.PARAMETER GroupsOnly
    Specifies that you only want to retrieve the members of type Group only.
 
.EXAMPLE
    Get-ADSIGroupMember -Identity 'Finance'
 
    Retrieve the direct members of the group 'Finance'
 
.EXAMPLE
    Get-ADSIGroupMember -Identity 'Finance' -Recursive
 
    Retrieve the direct and nested members of the group 'Finance'
 
.EXAMPLE
    Get-ADSIGroupMember -Identity 'Finance' -GroupsOnly
 
    Retrieve the direct groups members of the group 'Finance'
 
.EXAMPLE
    Get-ADSIGroupMember -Identity 'Finance' -Credential (Get-Credential)
 
    Retrieve the direct members of the group 'Finance' using alternative Credential
 
.EXAMPLE
    Get-ADSIGroupMember -Identity 'Finance' -Credential (Get-Credential) -DomainName FX.LAB
 
    Retrieve the direct members of the group 'Finance' using alternative Credential in the domain FX.LAB
 
.EXAMPLE
    $Comp = Get-ADSIGroupMember -Identity 'SERVER01'
    $Comp.GetUnderlyingObject()| Select-Object -Property *
 
    Help you find all the extra properties
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.groupprincipal%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(DefaultParameterSetName = 'All')]
    param ([Parameter(Mandatory = $true)]
        [System.String]$Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [System.String]$DomainName,

        [Parameter(ParameterSetName = 'All')]
        [Switch]$Recurse,

        [Parameter(ParameterSetName = 'Groups')]
        [Switch]$GroupsOnly
    )
    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting
    }
    process
    {
        try
        {

            if ($PSBoundParameters['GroupsOnly'])
            {
                Write-Verbose -Message "GROUP: $($Identity.toUpper()) - Retrieving Groups only"
                $Account = ([System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($Context, $Identity))
                $Account.GetGroups()
            }
            else
            {
                Write-Verbose -Message "GROUP: $($Identity.toUpper()) - Retrieving All members"
                if ($PSBoundParameters['Recursive'])
                {
                    Write-Verbose -Message "GROUP: $($Identity.toUpper()) - Recursive parameter Specified"
                }
                # Returns a collection of the principal objects that is contained in the group.
                # When the $recurse flag is set to true, this method searches the current group recursively and returns all nested group members.
                ([System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($Context, $Identity)).GetMembers($Recurse)
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSIGroupMembershipTreeView{
<#
.SYNOPSIS
    Function to get all Group Memberships of a User or Computer including all Groups inherited by another group in a Tree View in Active Directory
.DESCRIPTION
    Function to get all Group Memberships of a User or Computer including all Groups inherited by another group in a Tree View in Active Directory
.PARAMETER Identity
    Specifies the Identity of the Source User or Computer
    You can provide one of the following properties
    DistinguishedName
    Guid
    Name
    SamAccountName
    Sid
    UserPrincipalName
    Those properties come from the following enumeration:
    System.DirectoryServices.AccountManagement.IdentityType
.PARAMETER DomainName
    Specifies the Domain Name where the function should look
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
.EXAMPLE
    Get-ADSIGroupMembershipTreeView -Identity user1
.EXAMPLE
    Get-ADSIGroupMembershipTreeView -Identity computer1
.NOTES
    https://github.com/lazywinadmin/ADSIPS
.LINK
    https://msdn.microsoft.com/en-us/library/System.DirectoryServices.AccountManagement.UserPrincipal(v=vs.110).aspx
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true,
            Position = 0,
            ParameterSetName = "Identity")]
        [System.string]$Identity,
    
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,
    
        [System.String]$DomainName
    )
    
    begin{
        $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).Mycommand
    
        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential']){
            Write-Verbose "[$FunctionName] Found Credential Parameter"
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName']){
            Write-Verbose "[$FunctionName] Found DomainName Parameter"
            $ContextSplatting.DomainName = $DomainName
        }
        
        #Get Identity Type
        $Object = Get-ADSIObject -Identity $Identity @ContextSplatting

        switch -Wildcard ($Object.objectclass){
            "*group" {$IdentityType = "Group"}
            "*computer" {$IdentityType = "Computer"}
            "*user" {$IdentityType = "User"}
        }
    }
    
    process{

        function Get-RecursiveGroups([System.DirectoryServices.AccountManagement.Principal]$Group){
            Write-Output "$(" "*$Spacecount)-$($Group.Name)"
            $MemberOf = $Group.GetGroups()
            If($null -ne $MemberOf){
                foreach($MemberOfGroup in $MemberOf){
                    $Spacecount ++
                    Get-RecursiveGroups -Group $MemberOfGroup
                }
            }
        }

        #GetGroups
        If($IdentityType -eq "User"){
            $Groups = (Get-ADSIUser -Identity $Identity @ContextSplatting).GetGroups()
            Write-Verbose "[$FunctionName] Type: User"
        } elseif ($IdentityType -eq "Computer") {
            $Groups = (Get-ADSIComputer -Identity $Identity @ContextSplatting).GetGroups()
            Write-Verbose "[$FunctionName] Type: Computer"
        } elseif ($IdentityType -eq "Group") {
            $Groups = (Get-ADSIGroup -Identity $Identity @ContextSplatting).GetGroups()
            Write-Verbose "[$FunctionName] Type: Group"
        }
        
        if($Groups){
            Write-Verbose "Groups found: $($Groups.Name)"
            Write-Output $(Get-ADSIUser -Identity $Identity).Name
            foreach($Group in $Groups){
                $Spacecount = 1
                Get-RecursiveGroups -Group $Group
            }
        } else {
            Write-Verbose "No Groups found"
        }
    }
}
function Get-ADSIGroupPolicyObject
{
<#
.SYNOPSIS
    This function will query Active Directory Group Policy Objects
 
.DESCRIPTION
    This function will query Active Directory Group Policy Objects
 
.PARAMETER Credential
    Specifies alternative Credential to use
 
.PARAMETER DomainDistinguishedName
    Specify the DistinguishedName of the Domain to query.
 
.PARAMETER SizeLimit
    Specify the number of item(s) to output.
    Default is 100.
 
.EXAMPLE
    Get-ADSIGroupPolicyObject
 
    Retrieve all the group policy in the current domain
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/cc232507.aspx
#>

    [CmdletBinding()]
    param (
        [Parameter()]
        [Alias("Domain", "DomainDN")]
        [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path),

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Alias("ResultLimit", "Limit")]
        [int]$SizeLimit = '100'
    )
    begin
    {
    }
    process
    {
        try
        {
            $ScriptName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand

            # Building the basic search object with some parameters
            Write-Verbose -message "[$ScriptName] Create DirectorySearcher"
            $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop'
            Write-Verbose -message "[$ScriptName] Set SizeLimit of '$SizeLimit'"
            $Search.SizeLimit = $SizeLimit
            Write-Verbose -message "[$ScriptName] Set Filter '(objectCategory=groupPolicyContainer)'"
            $Search.Filter = "(objectCategory=groupPolicyContainer)"

            if ($PSBoundParameters['DomainDistinguishedName'])
            {
                Write-Verbose -message "[$ScriptName] DomainDistinguishedName specified = '$DomainDistinguishedName'"
                if ($DomainDistinguishedName -notlike "LDAP://*")
                {
                    Write-Verbose -message "[$ScriptName] DomainDistinguishedName notlike 'LDAP://*', prepending LDAP://"
                    $DomainDistinguishedName = "LDAP://$DomainDistinguishedName"
                }#if
                $Search.SearchRoot = $DomainDistinguishedName
            }
            if ($PSBoundParameters['Credential'])
            {
                Write-Verbose -message "[$ScriptName] Different Credential specified: '$($credential.username)'"
                $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password)
                $Search.SearchRoot = $Cred
            }
            if (-not $PSBoundParameters["SizeLimit"])
            {
                Write-Warning -Message "[$ScriptName] Default SizeLimit: 100 Results"
            }

            Write-Verbose -message "[$ScriptName] Looking for objects..."
            foreach ($GPO in $($Search.FindAll()))
            {
                # Define the properties
                # The properties need to be lowercase!!!!!!!!
                $Properties = @{
                    adspath                  = $gpo.properties.adspath -as [system.string]
                    gpcfunctionalityversion  = $gpo.properties.gpcfunctionalityversion -as [system.int32]
                    usnchanged               = $gpo.properties.usnchanged -as [system.string]
                    showinadvancedviewonly   = $gpo.properties.showinadvancedviewonly -as [system.string]
                    displayname              = $gpo.properties.displayname -as [system.string]
                    whencreated              = $gpo.properties.whencreated -as [System.DateTime]
                    gpcmachineextensionnames = $gpo.properties.gpcmachineextensionnames -as [system.string]
                    instancetype             = $gpo.properties.instancetype -as [system.string]
                    versionnumber            = $gpo.properties.versionnumber -as [system.int32]
                    gpcfilesyspath           = $gpo.properties.gpcfilesyspath -as [system.string]
                    usncreated               = $gpo.properties.usncreated -as [system.string]
                    flags                    = $gpo.properties.flags -as [system.int32]
                    whenchanged              = $gpo.properties.whenchanged -as [System.DateTime]
                    cn                       = $gpo.properties.cn -as [system.string]
                    objectguid               = $gpo.properties.objectguid
                    distinguishedname        = $gpo.properties.distinguishedname -as [system.string]
                    objectcategory           = $gpo.properties.objectcategory -as [system.string]
                    iscriticalsystemobject   = $gpo.properties.iscriticalsystemobject -as [system.string]
                    objectclass              = $gpo.properties.objectclass
                    systemflags              = $gpo.properties.systemflags -as [system.string]
                    dscorepropagationdata    = $gpo.properties.dscorepropagationdata -as [system.string]
                    name                     = $gpo.properties.name -as [system.string]
                    raw                      = $gpo
                }

                # Output the info
                New-Object -TypeName PSObject -Property $Properties

            }
        }#try
        catch
        {
            Write-Warning -Message "[$ScriptName] Something wrong happened!"
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }#process
    end
    {
        Write-Verbose -message "[$ScriptName] End."
    }
}
function Get-ADSIObject
{
    <#
.SYNOPSIS
    This function will query any kind of object in Active Directory
 
.DESCRIPTION
    This function will query any kind of object in Active Directory
 
.PARAMETER Identity
    Specifies the Identity of the Object
    You can provide one of the following properties
    DistinguishedName
    Name
    SamAccountName
    UserPrincipalName
    Guid
    Sid
 
.PARAMETER Credential
    Specify the Credential to use
 
.PARAMETER DomainDistinguishedName
    Specify the DistinguishedName of the Domain to query
 
.PARAMETER SizeLimit
    Specify the number of item(s) to output
 
.PARAMETER IncludeDeletedObjects
    Deleted objects are included in the search
 
.PARAMETER DeletedOnly
    Return only deleted objects
 
.EXAMPLE
    Get-ADSIObject -Identity Fxcat
 
    Get informations on objects with a Identity equal to Fxcat
 
.EXAMPLE
    Get-ADSIObject -Identity Fx*
 
    Get informations on objects with a Identity starting with Fx*
 
.EXAMPLE
    Get-ADSIObject -Identity Fx* -IncludeDeletedObjects
 
    Get informations on objects deleted or not with a Identity starting with Fx*
 
.EXAMPLE
    Get-ADSIObject -Identity Fx* -IncludeDeletedObjects -DeletedOnly
 
    Get informations on deleted objects with a Identity starting with Fx*
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    param (
        [Parameter(ParameterSetName = "Identity", Mandatory = $true)]
        [Parameter(ParameterSetName = "Deleted")]
        [Alias("SamAccountName", "DistinguishedName")]
        [System.String]$Identity,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias("Domain", "DomainDN", "SearchRoot", "SearchBase")]
        [System.String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path),

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Alias("ResultLimit", "Limit")]
        [int]$SizeLimit = '100',

        [Parameter(ParameterSetName = "Deleted", Mandatory = $true)]
        [switch]$IncludeDeletedObjects,

        [Parameter(ParameterSetName = "Deleted")]
        [switch]$DeletedOnly
    )
    begin
    {
    }
    process
    {
        try
        {
            # Building the basic search object with some parameters
            $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop'
            $Search.SizeLimit = $SizeLimit
            $Search.SearchRoot = $DomainDistinguishedName

            #Convert Identity Input String to HEX
            $IdentityGUID = ""
            Try{
                ([System.Guid]$Identity).ToByteArray() | %{ $IdentityGUID += $("\{0:x2}" -f $_) }
            } Catch {
                $IdentityGUID="null"
            }

            if ($PSBoundParameters['Identity'])
            {
                if ($PSBoundParameters['DeletedOnly'])
                {
                    $Search.filter = "(&(isDeleted=True)(|(DistinguishedName=$Identity)(Name=$Identity)(SamAccountName=$Identity)(UserPrincipalName=$Identity)(objectGUID=$IdentityGUID)(objectSid=$Identity)))"
                }
                else
                {
                    $Search.filter = "(|(DistinguishedName=$Identity)(Name=$Identity)(SamAccountName=$Identity)(UserPrincipalName=$Identity)(objectGUID=$IdentityGUID)(objectSid=$Identity))"
                }
            }

            if ($PSBoundParameters['DomainDistinguishedName'])
            {

                if ($DomainDistinguishedName -notlike "LDAP://*")
                {
                    $DomainDistinguishedName = "LDAP://$DomainDistinguishedName"
                }

                Write-Verbose -Message "Different Domain specified: $DomainDistinguishedName"
                $Search.SearchRoot = $DomainDistinguishedName

            }

            if ($PSBoundParameters['Credential'])
            {

                $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password)
                $Search.SearchRoot = $Cred

            }

            if ($PSBoundParameters['IncludeDeletedObjects'])
            {

                $Search.Tombstone = $true

            }

            foreach ($Object in $($Search.FindAll()))
            {

                # Define the properties
                $Properties = @{
                    "displayname"       = $Object.properties.displayname -as [string]
                    "name"              = $Object.properties.name -as [string]
                    "objectcategory"    = $Object.properties.objectcategory -as [string]
                    "objectclass"       = $Object.properties.objectclass -as [string]
                    "samaccountName"    = $Object.properties.samaccountname -as [string]
                    "description"       = $Object.properties.description -as [string]
                    "distinguishedname" = $Object.properties.distinguishedname -as [string]
                    "adspath"           = $Object.properties.adspath -as [string]
                    "lastlogon"         = $Object.properties.lastlogon -as [string]
                    "whencreated"       = $Object.properties.whencreated -as [string]
                    "whenchanged"       = $Object.properties.whenchanged -as [string]
                    "deleted"           = $Object.properties.isDeleted -as [string]
                    "recycled"          = $Object.properties.isRecycled -as [string]
                    "userPrincipalName" = $Object.properties.userprincipalname -as [string]
                }

                # Output the info
                New-Object -TypeName PSObject -Property $Properties

            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
    end
    {
        Write-Verbose -Message "[END] Function Get-ADSIObject End."
    }
}
function Get-ADSIOrganizationalUnit
{
<#
.SYNOPSIS
    This function will query Active Directory for Organization Unit Objects
 
.DESCRIPTION
    This function will query Active Directory for Organization Unit Objects
 
.PARAMETER Name
    Specify the Name of the OU
 
.PARAMETER DistinguishedName
    Specify the DistinguishedName path of the OU
 
.PARAMETER All
    Will show all the OU in the domain
 
.PARAMETER GroupPolicyInheritanceBlocked
    Will show only the OU that have Group Policy Inheritance Blocked enabled.
 
.PARAMETER Credential
    Specify the Credential to use
 
.PARAMETER DomainDistinguishedName
    Specify the DistinguishedName of the Domain to query
 
.PARAMETER SizeLimit
    Specify the number of item(s) to output
 
.EXAMPLE
    Get-ADSIOrganizationalUnit
 
    This returns all the OU in the Domain (Result Size is 100 per default)
 
.EXAMPLE
    Get-ADSIOrganizationalUnit -name FX
 
    This returns the OU with the name FX
 
.EXAMPLE
    Get-ADSIOrganizationalUnit -name FX*
 
    This returns the OUs where the name starts by FX
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(DefaultParameterSetName = "All")]
    param (
        [Parameter(ParameterSetName = "Name")]
        [String]$Name,

        [Parameter(ParameterSetName = "DistinguishedName")]
        [String]$DistinguishedName,

        [Parameter(ParameterSetName = "All")]
        [String]$All,

        [Switch]$GroupPolicyInheritanceBlocked,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias("Domain", "DomainDN")]
        [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path),

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Alias("ResultLimit", "Limit")]
        [int]$SizeLimit = '100'
    )
    begin
    {
    }
    process
    {
        try
        {
            # Building the basic search object with some parameters
            $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop'
            $Search.SizeLimit = $SizeLimit
            $Search.SearchRoot = $DomainDistinguishedName


            if ($Name)
            {
                $Search.filter = "(&(objectCategory=organizationalunit)(name=$Name))"
                if ($psboundparameters["GroupPolicyInheritanceBlocked"])
                {
                    $Search.filter = "(&(objectCategory=organizationalunit)(name=$Name)(gpoptions=1))"
                }
            }
            if ($DistinguishedName)
            {
                $Search.filter = "(&(objectCategory=organizationalunit)(distinguishedname=$distinguishedname))"
                if ($psboundparameters["GroupPolicyInheritanceBlocked"])
                {
                    $Search.filter = "(&(objectCategory=organizationalunit)(distinguishedname=$distinguishedname)(gpoptions=1))"
                }
            }
            if ($all)
            {
                $Search.filter = "(&(objectCategory=organizationalunit))"
                if ($psboundparameters["GroupPolicyInheritanceBlocked"])
                {
                    $Search.filter = "(&(objectCategory=organizationalunit)(gpoptions=1))"
                }
            }
            if ($DomainDistinguishedName)
            {
                if ($DomainDistinguishedName -notlike "LDAP://*")
                {
                    $DomainDistinguishedName = "LDAP://$DomainDistinguishedName"
                }#if
                Write-Verbose -Message "Different Domain specified: $DomainDistinguishedName"
                $Search.SearchRoot = $DomainDistinguishedName
            }
            if ($PSBoundParameters['Credential'])
            {
                $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password)
                $Search.SearchRoot = $Cred
            }
            if (-not $PSBoundParameters["SizeLimit"])
            {
                Write-Warning -Message "Default SizeLimit: 100 Results"
            }

            foreach ($ou in $($Search.FindAll()))
            {
                # Define the properties
                # The properties need to be lowercase!!!!!!!!
                $Properties = @{
                    "Name"                  = $ou.properties.name -as [string]
                    "DistinguishedName"     = $ou.properties.distinguishedname -as [string]
                    "ADsPath"               = $ou.properties.adspath -as [string]
                    "ObjectCategory"        = $ou.properties.objectcategory -as [string]
                    "ObjectClass"           = $ou.properties.objectclass -as [string]
                    "ObjectGuid"            = $ou.properties.objectguid
                    "WhenCreated"           = $ou.properties.whencreated -as [string] -as [datetime]
                    "WhenChanged"           = $ou.properties.whenchanged -as [string] -as [datetime]
                    "usncreated"            = $ou.properties.usncreated -as [string]
                    "usnchanged"            = $ou.properties.usnchanged -as [string]
                    "dscorepropagationdata" = $ou.properties.dscorepropagationdata
                    "instancetype"          = $ou.properties.instancetype -as [string]
                }

                # Output the info
                New-Object -TypeName PSObject -Property $Properties
            }
        }#try
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }#process
    end
    {
        Write-Verbose -Message "[END] Function Get-ADSIOrganizationalUnit End."
    }
}
Function Get-ADSIPrincipalGroupMembership
{ 
    <#
            .SYNOPSIS
            Function to retrieve groups from a user in Active Directory
 
            .DESCRIPTION
            Get all AD groups of a user, primary one and others
 
            .PARAMETER Identity
            Specifies the Identity of the User
            You can provide one of the following properties
            DistinguishedName
            Guid
            Name
            SamAccountName
            Sid
            UserPrincipalName
            Those properties come from the following enumeration:
            System.DirectoryServices.AccountManagement.IdentityType
 
            .PARAMETER UserInfos
            UserInfos is a UserPrincipal object.
 
            Type System.DirectoryServices.AccountManagement.AuthenticablePrincipal
 
            .PARAMETER GroupInfos
            GroupInfos is a GroupPrincipal object.
 
            Type System.DirectoryServices.AccountManagement.Principal
 
            .PARAMETER Credential
            Specifies the alternative credential to use.
 
            By default it will use the current user windows credentials.
 
            .PARAMETER DomainName
            Specifies the alternative Domain where the user should be created
 
            By default it will use the current domain.
 
            .EXAMPLE
            Get-ADSIPrincipalGroupMembership -Identity 'User1'
 
            Get all AD groups of user User1
 
            .EXAMPLE
            Get-ADSIPrincipalGroupMembership -Identity 'User1' -Credential (Get-Credential)
 
            Use a different credential to perform the query
 
            .EXAMPLE
            Get-ADSIPrincipalGroupMembership -Identity 'User1' -DomainName "CONTOSO.local"
 
            Use a different domain name to perform the query
 
            .EXAMPLE
            Get-ADSIPrincipalGroupMembership -Identity 'Group1'
 
            Get all AD groups of group Group1
 
            .EXAMPLE
            Get-ADSIPrincipalGroupMembership -Identity 'Group1' -Credential (Get-Credential)
 
            Use a different credential to perform the query
 
            .EXAMPLE
            Get-ADSIPrincipalGroupMembership -Identity 'Group1' -DomainName "CONTOSO.local"
 
            Use a different domain name to perform the query
 
            .EXAMPLE
            Get-ADSIPrincipalGroupMembership -UserInfos (Get-ADSIUser -Identity "User1")
 
            Get all ad groups of User1 using type System.DirectoryServices.AccountManagement.AuthenticablePrincipal
     
            .EXAMPLE
            Get-ADSIPrincipalGroupMembership -UserInfos (Get-ADSIUser -Identity "User1" -DomainName "CONTOSO.local")
 
            Get all ad groups of User1 using type System.DirectoryServices.AccountManagement.AuthenticablePrincipal on a different domain
 
            .EXAMPLE
            Get-ADSIPrincipalGroupMembership -GroupInfos (Get-ADSIGroup -Identity "Group1")
 
            Get all ad groups of Group1 using type System.DirectoryServices.AccountManagement.Principal
     
            .EXAMPLE
            Get-ADSIPrincipalGroupMembership -GroupInfos (Get-ADSIGroup -Identity "Group1" -DominName "CONTOSO.local")
 
            Get all ad groups of Group1 using type System.DirectoryServices.AccountManagement.Principal on a different domain
 
            .NOTES
            https://github.com/lazywinadmin/ADSIPS
            CHANGE LOG
            - 0.1 | 2019/06/22 | Matt Oestreich (oze4)
                - Initial Change Log creation
                - Fixed issue 70 where primary group was not being pulled in for users
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ParameterSetName = "Identity")]
        [string]$Identity,

        [Parameter(Mandatory = $true, ParameterSetName = "UserInfos")]
        [System.DirectoryServices.AccountManagement.AuthenticablePrincipal]$UserInfos,

        [Parameter(Mandatory = $true, ParameterSetName = "GroupInfos")]
        [System.DirectoryServices.AccountManagement.Principal]$GroupInfos,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Parameter(ParameterSetName = "Identity")]
        [String]$DomainName
    )

    begin
    {
        # Stores our output
        $ObjectGroups = @()

        switch($PSBoundParameters.Keys)
        {
            'UserInfos'  { 
                $UnderlyingProperties = $UserInfos.GetUnderlyingObject()
                $ObjectGroups += Get-ADSIUserPrimaryGroup -Identity $UserInfos -ReturnNameAndDescriptionOnly
            }

            'GroupInfos' {
                $UnderlyingProperties = $GroupInfos.GetUnderlyingObject()
            }
            
            'Identity'   {

                $ObjectSplatting = @{}
                if ($PSBoundParameters["DomainName"]) {                        
                    # Turn Domain Name into DN
                    $ObjectSplatting.DomainDistinguishedName = ($DomainName.Split(".").ForEach({ "DC=$($_)," }) -join '').TrimEnd(',')                        
                }

                if ($PSBoundParameters["Credential"]) {
                    $ObjectSplatting.Credential  = $Credential
                }
                                    
                $FoundObject = $null
                # Get the ADSIObject for what we were provided
                foreach($IdType in [System.DirectoryServices.AccountManagement.IdentityType].GetEnumNames()) {                        
                    $splat = @{ 
                        $IdType = $Identity
                    }
                        
                    try { 
                        if ($ObjectSplatting) { $FoundObject = Get-ADSIObject @splat @ObjectSplatting } 
                        else { $FoundObject = Get-ADSIObject @splat }
                    } catch {
                        # do nothing, only here to suppress errors
                    } 
                        
                    if ($FoundObject -ne $null) { 
                        $FoundObjectObjectClass = $FoundObject.objectclass.Split(" ")
                        break; 
                    }
                }

                if (($FoundObjectObjectClass -contains "person") -or ($FoundObjectObjectClass -contains "user")) {
                    $UserInfos = Get-ADSIUser -Identity $FoundObject.samaccountName
                    $UnderlyingProperties = $UserInfos.GetUnderlyingObject()
                    $ObjectGroups += Get-ADSIUserPrimaryGroup -Identity $UserInfos -ReturnNameAndDescriptionOnly
                }

                if ($FoundObjectObjectClass -contains "group") {
                    $GroupInfos = Get-ADSIGroup -Identity $FoundObject.samaccountName
                    $UnderlyingProperties = $GroupInfos.GetUnderlyingObject()
                }
                
            }
        }
    }
    process
    {
    
        $Objectmemberof = $UnderlyingProperties.memberOf

        if ($Objectmemberof)
        {
            foreach ($item in $Objectmemberof)
            {
                $ADSIobjectgroup = [adsi]"LDAP://$item"

                $ObjectGroups += [pscustomobject]@{
                    'name'        = [string]$ADSIobjectgroup.Properties.name
                    'description' = [string]$ADSIobjectgroup.Properties.description
                }
            }
        }
        
        $ObjectGroups

    }
}
function Get-ADSIPrintQueue
{
    <#
.SYNOPSIS
    Function to retrieve PrintQueue in Active Directory from PrinterQueue name or server name
 
.DESCRIPTION
    Function to retrieve PrintQueue in Active Directory from PrinterQueue name or server name
 
.PARAMETER PrinterQueueName
    Specify the PrinterQueue, you can use * as wildcard
 
.PARAMETER ServerName
    Specify the ServerName to use
 
.PARAMETER DomainName
    Specify the Domain to use
 
.PARAMETER Credential
    Specify the Credential to use
 
.PARAMETER DomainDistinguishedName
    Specify the DistinguishedName of the Domain to query
 
.PARAMETER SizeLimit
    Specify the number of item(s) to output (1 to 1000)
    Use NoResultLimit for more than 1000 objects
 
.PARAMETER NoResultLimit
    Remove the SizeLimit of 1000
    Warning : can take time! depend number of queues on your domain
    NoResultLimit parameter override SizeLimit parameter
 
.EXAMPLE
    Get-ADSIPrintQueue
 
    Get all published printQueue on your current domain (default function SizeLimit return 100 objects Max)
 
.EXAMPLE
    Get-ADSIPrintQueue -SizeLimit 200
 
    Get 200 published printQueue on your current domain
 
.EXAMPLE
    Get-ADSIPrintQueue -NoResultLimit
 
    Get all published printQueue on your current domain
    Warning : can take time! depend number of queues on your domain
 
.EXAMPLE
    Get-ADSIPrintQueue -PrinterQueueName MyPrinterQueue
 
.EXAMPLE
    Get-ADSIPrintQueue -PrinterQueueName *Printer*
 
.EXAMPLE
    Get-ADSIPrintQueue -ServerName TestServer01
 
    Get all published printQueue for the server TestServer01 (default function SizeLimit return 100 objects Max)
 
.EXAMPLE
    Get-ADSIPrintQueue -ServerName TestServer01.contoso.com
 
    Get all published printQueue for the server TestServer01.contoso.com (default function SizeLimit return 100 objects Max)
 
.EXAMPLE
    Get-ADSIPrintQueue -ServerName TestServer01 -SizeLimit 200
 
    Get only 200 published printQueue for the server TestServer01
 
.EXAMPLE
    Get-ADSIPrintQueue -ServerName TestServer01 -NoResultLimit
 
    This example will retrieve all printQueue on TestServer01 without limit of 1000 objects returned.
 
.EXAMPLE
    Get-ADSIPrintQueue -DomainDistinguishedName 'OU=Mut,DC=CONTOSO,DC=COM'
 
    This example will retrieve all printQueue located in the 'Mut' OU (default function SizeLimit return 100 objects Max)
 
.EXAMPLE
    Get-ADSIPrintQueue -PrinterQueueName MyPrinterQueue -ServerName TestServer01 -DomainDistinguishedName 'OU=Mut,DC=CONTOSO,DC=COM'
 
    This define the searchbase to the 'Mut' OU and filter on server and printQueue
 
.EXAMPLE
    Get-ADSIPrintQueue -ServerName TestServer01 -DomainName contoso2.com -NoResultLimit
 
    You will get all the printQueue from TestServer01 on contoso2.com domain
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName = $true)]

        [Alias("PrinterQueue")]
        [String]$PrinterQueueName,

        [Alias("Server")]
        [String]$ServerName,

        [Alias("Domain")]
        [String]$DomainName,

        [Alias("DomainDN", "SearchRoot", "SearchBase")]
        [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path),

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Alias("ResultLimit", "Limit")]
        [int]$SizeLimit = '100',

        [Switch]$NoResultLimit
    )

    begin
    {
    }
    process
    {
        try
        {
            # Building the basic search object with some parameters
            $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop'
            $Search.SearchRoot = $DomainDistinguishedName
            $Search.filter = "(&(objectClass=printQueue))"

            if ($PSBoundParameters['ServerName'])
            {
                $Search.filter = "(&(objectClass=printQueue)(|(serverName=$ServerName)(shortServerName=$ServerName)))"
            }
            elseif ($PSBoundParameters['PrinterQueue'])
            {
                $Search.filter = "(&(objectClass=printQueue)(printerName=$PrinterQueueName))"
            }
            else
            {
                $Search.filter = "(objectClass=printQueue)"
            }

            if ($PSBoundParameters['DomainName'])
            {
                $DomainDistinguishedName = "LDAP://DC=$($DomainName.replace(".", ",DC="))"
                $Search.SearchRoot = $DomainDistinguishedName
            }
            elseif ($PSBoundParameters['DomainDistinguishedName'])
            {
                if ($DomainDistinguishedName -notlike "LDAP://*")
                {
                    $DomainDistinguishedName = "LDAP://$DomainDistinguishedName"
                }
                Write-Verbose -Message "Different Domain specified: $DomainDistinguishedName"
                $Search.SearchRoot = $DomainDistinguishedName
            }

            if ($PSBoundParameters['Credential'])
            {
                $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password)
                $Search.SearchRoot = $Cred
            }

            if (-not$PSBoundParameters['NoResultLimit'])
            {
                $Search.SizeLimit = $SizeLimit
                Write-Warning -Message "Result is limited to $SizeLimit entries, specify a specific number on the parameter SizeLimit or use -NoResultLimit switch to remove the limit"
            }
            else
            {

                Write-Verbose -Message "Use NoResultLimit switch, all objects will be returned. no limit"
                $Search.PageSize = 10000
            }


            foreach ($Object in $($Search.FindAll()))
            {
                # Define the properties
                # The properties need to be lowercase!!!!!!!!
                $Properties = @{
                    "DisplayName"       = $Object.properties.displayname -as [string]
                    "Name"              = $Object.properties.name -as [string]
                    "printerName"       = $Object.properties.printername -as [string]
                    "location"          = $Object.properties.location -as [string]
                    "Description"       = $Object.properties.description -as [string]
                    "portName"          = $Object.properties.portname -as [string]
                    "driverName"        = $Object.properties.drivername -as [string]
                    "ObjectCategory"    = $Object.properties.objectcategory -as [string]
                    "ObjectClass"       = $Object.properties.objectclass -as [string]
                    "DistinguishedName" = $Object.properties.distinguishedname -as [string]
                    "WhenCreated"       = $Object.properties.whencreated -as [string]
                    "WhenChanged"       = $Object.properties.whenchanged -as [string]
                    "serverName"        = $Object.properties.servername -as [string]
                    "uNCName"           = $Object.properties.uncname -as [string]
                    "printShareName"    = $Object.properties.printsharename -as [string]
                    "printStatus"       = $Object.properties.printstatus -as [string]

                }

                # Output the info
                New-Object -TypeName PSObject -Property $Properties
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
    end
    {
        Write-Verbose -Message "[END] Function Get-ADSIPrintQueue End."
    }

}
function Get-ADSIReplicaCurrentTime
{
<#
.SYNOPSIS
    Get-ADSIReplicaCurrentTime retrieves the current time of a given DC.
 
.DESCRIPTION
    Get-ADSIReplicaCurrentTime retrieves the current time of a given DC.
    When using the verbose switch, this cmdlet will display the time difference with the current system.
 
.PARAMETER ComputerName
    Defines the remote computer to connect to.
 
.PARAMETER Credential
    Defines alternate credentials to use. Use Get-Credential to create proper credentials.
 
.EXAMPLE
    Get-ADSIReplicaCurrentTime -ComputerName dc1.ad.local
 
    Connects to remote domain controller dc1.ad.local using current credentials and retrieves the current time.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding()]
    param ([Parameter(Mandatory = $true)]
        [string]$ComputerName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )

    if ($ComputerName)
    {
        if ($Credential)
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName, $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        else
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName
        }
    }

    if ($context)
    {
        Write-Verbose -Message "Connecting to $ComputerName"
        $dc = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($context)
    }

    if ($dc)
    {
        $now = Get-Date
        $minDiff = (New-TimeSpan -start $dc.CurrentTime -end ([System.TimeZoneInfo]::ConvertTimeToUtc($now))).minutes
        Write-Verbose -Message "Difference in minutes between $($dc.name) and current system is $minDiff"
        $dc.CurrentTime
    }
}
function Get-ADSIReplicaDomainInfo
{
<#
.SYNOPSIS
    Get-ADSIReplicaDomainInfo returns information about the connected DC's Domain.
 
.DESCRIPTION
    Get-ADSIReplicaDomainInfo returns information about the connected DC's Domain.
 
.PARAMETER ComputerName
    Defines the remote computer to connect to.
 
.PARAMETER Credential
    Defines alternate credentials to use. Use Get-Credential to create proper credentials.
 
.PARAMETER Recurse
    Recursively retrieves information about child domains
 
.EXAMPLE
    Get-ADSIReplicaDomainInfo -ComputerName dc1.ad.local
 
        Forest : ad.local
        DomainControllers : {DC1.ad.local, DC2.ad.local}
        Children : {}
        DomainMode : Windows2012R2Domain
        DomainModeLevel : 6
        Parent :
        PdcRoleOwner : DC1.ad.local
        RidRoleOwner : DC1.ad.local
        InfrastructureRoleOwner : DC1.ad.local
        Name : ad.local
 
    Connects to remote domain controller dc1.ad.local using current credentials retrieves domain info.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding()]
    param ([Parameter(Mandatory = $true)]
        [string]$ComputerName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Switch]$Recurse
    )

    if ($ComputerName)
    {
        if ($Credential)
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName, $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        else
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName
        }
    }

    if ($context)
    {
        Write-Verbose -Message "Connecting to $ComputerName"
        $dc = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($context)
    }

    if ($dc)
    {
        $dc.domain
        if ($Recurse.IsPresent)
        {
            $dc.domain.children | Foreach-Object -Process { $_ }
        }

    }
}
function Get-ADSIReplicaForestInfo
{
<#
.SYNOPSIS
    Get-ADSIReplicaForestInfo returns information about the connected DC's Forest.
 
.DESCRIPTION
 
    Get-ADSIForestInfo returns information about the connected DC's Forest.
 
.PARAMETER ComputerName
 
    Defines the remote computer to connect to.
 
.PARAMETER Credential
 
    Defines alternate credentials to use. Use Get-Credential to create proper credentials.
 
.EXAMPLE
 
    Get-ADSIReplicaForestInfo -ComputerName dc1.ad.local
 
        Name : ad.local
        Sites : {Default-First-Site-Name}
        Domains : {ad.local}
        GlobalCatalogs : {DC1.ad.local, DC2.ad.local}
        ApplicationPartitions : {DC=DomainDnsZones,DC=ad,DC=local, DC=ForestDnsZones,DC=ad,DC=local}
        ForestModeLevel : 6
        ForestMode : Windows2012R2Forest
        RootDomain : ad.local
        Schema : CN=Schema,CN=Configuration,DC=ad,DC=local
        SchemaRoleOwner : DC1.ad.local
        NamingRoleOwner : DC1.ad.local
 
    Connects to remote domain controller dc1.ad.local using current credentials retrieves forest info.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding()]
    param ([Parameter(Mandatory = $true)]
        [string]$ComputerName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )

    if ($ComputerName)
    {
        if ($Credential)
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName, $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        else
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName
        }
    }

    if ($context)
    {
        Write-Verbose -Message "Connecting to $ComputerName"
        $dc = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($context)
    }

    if ($dc)
    {
        Write-Verbose -Message "Information about forest $($dc.forest.name)"
        $dc.forest
    }
}
function Get-ADSIReplicaGCInfo
{
<#
.SYNOPSIS
    Get-ADSIReplicaGCInfo finds out if a given DC holds the GC role.
 
.DESCRIPTION
    Get-ADSIReplicaGCInfo finds out if a given DC holds the Global Catalog role.
 
.PARAMETER ComputerName
    Defines the remote computer to connect to.
 
.PARAMETER Credential
    Defines alternate credentials to use. Use Get-Credential to create proper credentials.
 
.EXAMPLE
    Get-ADSIReplicaGCInfo -ComputerName dc1.ad.local
 
    Connects to remote domain controller dc1.ad.local using current credentials retrieves GC info.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding()]
    param ([Parameter(Mandatory = $true)]
        [string]$ComputerName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )

    if ($ComputerName)
    {
        if ($Credential)
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName, $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        else
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName
        }
    }

    if ($context)
    {
        Write-Verbose -Message "Connecting to $ComputerName"
        $dc = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($context)
    }

    if ($dc)
    {
        $IsGC = $dc.IsGlobalCatalog()
        if ($IsGC)
        {
            Write-Verbose -Message "$($dc.name) is a Global Catalog"
        }
        else
        {
            Write-Verbose -Message "$($dc.name) is a normal Domain Controller"
        }
        $IsGC
    }
}
function Get-ADSIReplicaInfo
{
<#
.SYNOPSIS
    Get-ADSIReplicaInfo retrieves Active Directory replication information
 
.DESCRIPTION
    Get-ADSIReplicaInfo connects to an Active Directory Domain Controller and retrieves Active Directory replication information
    such as latency of replication and replication status.
    If no switches are used, latency information is returned.
 
.PARAMETER ComputerName
    Defines the remote computer to connect to.
    If ComputerName and Domain are not used, Get-ADSIReplicaInfo will attempt at connecting to the Active Directory using information
    stored in environment variables.
 
.PARAMETER Domain
    Defines the domain to connect to. If Domain is used, Get-ADSIReplicaInfo will find a domain controller to connect to.
    This parameter is ignored if ComputerName is used.
 
.PARAMETER Credential
    Defines alternate credentials to use. Use Get-Credential to create proper credentials.
 
.PARAMETER NamingContext
    Defines for which naming context replication information is to be displayed: All, Configuration, Schema, Domain. The default is Domain.
 
.PARAMETER Neighbors
    Displays replication partners for the current Domain Controller.
 
.PARAMETER Latency
    Organizes replication latency information by groups, such as Hour, Day, Week, Month, TooLong, Other
 
.PARAMETER Cursors
    Displays replication cursors for the current Domain Controller.
 
.PARAMETER DisplayDC
    Displays additional information about the currently connected Domain Controller.
 
.PARAMETER DisplayDC
    Displays additional information about the errors
 
.PARAMETER errors
    Display additional information on the replication errors
 
.PARAMETER FormatTable
    Formats the output as a auto-sized table and rearranges elements according to relevance.
 
    Get-ADSIReplicaInfo -Latency -FormatTable
 
    Hour Day Week Month TooLong Other
    ---- --- ---- ----- ------- -----
    {DC1.ad.local, DC2.ad.local} {} {} {} {} {}
 
.EXAMPLE
    Get-ADSIReplicaInfo
 
    Tries to find a domain to connect to and if it succeeds, it will find a domain controller to retrieve replication information.
 
.EXAMPLE
    Get-ADSIReplicaInfo -ComputerName dc1.ad.local -Credential $Credential
 
    Connects to remote domain controller dc1.ad.local using alternate credentials.
 
.EXAMPLE
    Get-ADSIReplicaInfo -Domain ad.local
 
    Connects to remote domain controller dc1.ad.local using current credentials.
 
.EXAMPLE
    Get-ADSIReplicaInfo -Domain ad.local
 
    Connects to remote domain controller dc1.ad.local using current credentials.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding()]
    param ([string]$ComputerName = $null,

        [string]$Domain = $null,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [ValidateSet("Schema", "Configuration", "Domain", "All")]
        [String]$NamingContext = "Domain",

        [Switch]$Neighbors,

        [Switch]$Latency,

        [Switch]$Cursors,

        [Switch]$Errors,

        [Switch]$DisplayDC,

        [Switch]$FormatTable
    )


    # Try to determine how to connect to the remote DC.
    # A few possibilities:
    # A computername was provided
    # A domain name was provided
    # None of the above was provided, so try with either USERDNSDOMAIN or LOGONSERVER
    # Use alternate credentials if provided
    if ($ComputerName)
    {
        if ($Credential)
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName, $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        else
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName
        }
    }
    elseif ($domain)
    {
        if ($Credential)
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "Domain", $domain, $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        else
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "Domain", $domain
        }
    }
    elseif ($env:USERDNSDOMAIN)
    {
        if ($Credential)
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "Domain", $env:USERDNSDOMAIN, $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        else
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "Domain", $env:USERDNSDOMAIN
        }
    }
    elseif ($env:LOGONSERVER -ne '\\MicrosoftAccount')
    {
        $logonserver = $env:LOGONSERVER.replace('\\', '')
        if ($Credential)
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $logonserver, $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        else
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $logonserver
        }
    }
    else
    {
        Write-Error -Message "Could not determine where to connect to"
        return
    }

    # If none of switches are present, default to at least one, so we have something to show
    if (!$Latency.IsPresent -and !$Neighbors.IsPresent -and !$Errors.IsPresent -and !$Cursors.IsPresent)
    {
        [switch]$Latency = $true
    }

    # Determine which DC to use depending on the context type.
    # If the context is Directory Server, simply get the provided domain controller,
    # if the context is a domain, then find a DC.
    switch ($context.ContextType)
    {
        "DirectoryServer"{ $dc = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($context) }
        "Domain" { $dc = [System.DirectoryServices.ActiveDirectory.DomainController]::FindOne($context) }
        default { return }
    }

    if ($dc)
    {
        if ($DisplayDC.IsPresent)
        {
            Write-Verbose -Message "Information about $($dc.Name)"
            $dc
        }
        $domainDN = ""
        $obj = $domain.Replace(',', '\,').Split('/')
        $obj[0].split(".") | Foreach-Object -Process { $domainDN += ",DC=" + $_ }
        $domainDN = $domainDN.Substring(1)

        if ($Cursors.IsPresent)
        {
            foreach ($partition in $dc.Partitions)
            {
                if ($NamingContext -eq "All" -or
                ($NamingContext -eq "Domain" -and $partition -eq $domainDN) -or
                ($NamingContext -eq "Schema" -and $partition.Contains("Schema")) -or
                ($NamingContext -eq "Configuration" -and $partition.split(",")[0].Contains("Configuration"))
                )
                {
                    Write-Verbose -Message "Replication cursors for partition $partition on $($dc.Name)"

                    $dc.GetReplicationCursors($partition) | Foreach-Object -Process { $_ }

                }
            }
        }
        if ($Latency.IsPresent)
        {
            foreach ($partition in $dc.Partitions)
            {
                if ($NamingContext -eq "All" -or
                ($NamingContext -eq "Domain" -and $partition -eq $domainDN) -or
                ($NamingContext -eq "Schema" -and $partition.Contains("Schema")) -or
                ($NamingContext -eq "Configuration" -and $partition.split(",")[0].Contains("Configuration"))
                )
                {
                    Write-Verbose -Message "Replication latency for partition $partition on $($dc.Name)"

                    $cursorsArray = $dc.GetReplicationCursors($partition)
                    $sortedCursors = $cursorsArray | Sort-Object -Descending -Property LastSuccessfulSyncTime

                    $hour = @()
                    $day = @()
                    $week = @()
                    $month = @()
                    $tooLong = @()
                    $other = @()

                    foreach ($cursor in $sortedCursors)
                    {
                        $timespan = New-TimeSpan -Start $cursor.LastSuccessfulSyncTime -End $(Get-Date)

                        if ($timespan)
                        {
                            if ($timespan.Days -eq 0 -and $timespan.Hours -eq 0)
                            {
                                $hour += $cursor.SourceServer
                            }
                            elseif ($timespan.Days -eq 0 -and $timespan.Hours -ge 1)
                            {
                                $day += $cursor.SourceServer
                            }
                            elseif ($timespan.Days -lt 7)
                            {
                                $week += $cursor.SourceServer
                            }
                            elseif ($timespan.Days -le 30 -and $timespan.Days -gt 7)
                            {
                                $month += $cursor.SourceServer
                            }
                            else
                            {
                                $tooLong += $cursor.SourceServer
                            }
                        }
                        else
                        {
                            # no timestamp we might have a Windows 2000 server here
                            $other += $cursor.SourceServer
                        }
                    }

                    $latencyObject = New-Object -TypeName PsCustomObject -Property @{
                        Hour = $hour;
                        Day = $day;
                        Week = $week;
                        Month = $month;
                        TooLong = $tooLong;
                        Other = $other
                    }
                    if ($FormatTable.IsPresent)
                    {
                        $latencyObject | Select-Object -Property Hour, Day, Week, Month, TooLong, Other | Format-Table -AutoSize
                    }
                    else
                    {
                        $latencyObject
                    }
                }
            }
        }

        if ($Neighbors.IsPresent -or $Errors.IsPresent)
        {
            $replicationNeighbors = $dc.GetAllReplicationNeighbors()

            foreach ($neighbor in $replicationNeighbors)
            {
                if ($NamingContext -eq "All" -or
                ($NamingContext -eq "Domain" -and $neighbor.PartitionName -eq $domainDN) -or
                ($NamingContext -eq "Schema" -and $neighbor.PartitionName.Contains("Schema")) -or
                ($NamingContext -eq "Configuration" -and $neighbor.PartitionName.split(",")[0].Contains("Configuration"))
                )
                {
                    Write-Verbose -Message "Replication neighbors for partition $($neighbor.PartitionName) on $($dc.Name)"

                    if (($Errors.IsPresent -and $neighbor.LastSyncResult -ne 0) -or $Neighbors.IsPresent)
                    {
                        if ($FormatTable.IsPresent)
                        {
                            $neighbor | Select-Object SourceServer, LastSyncMessage, LastAttemptedSync, LastSuccessfulSync, PartitionName | Format-Table -AutoSize
                        }
                        else
                        {
                            $neighbor
                        }
                    }
                }
            }
        }
    }
}
function Get-ADSIRIDsPool
{
<#
    .SYNOPSIS
    Function to retrieve RID Master information like role owner, RIDs issued, propagation date and remaining RIDs
 
    .DESCRIPTION
    Function to retrieve RID Master information like role owner, RIDs issued, propagation date and remaining RIDs
    Relative ID is the incremental part of a Security ID, SID (the other part is the Domain Identifier). RID are distribued by pool of 500 by the RID master
    There is 2^30 RIDs for a domain and once a pool is issued, the RID are never reused
    Return a PS Object with
        RIDIssued
        RIDRoleOwner
        RIDLastPropagation
        RIDRemaining
 
    .PARAMETER Credential
        Specifies alternative credential to use
 
    .PARAMETER DomainName
        Specifies the DomainName to query
 
    .EXAMPLE
        Get-ADSIRIDsPool
        Retrieve RID information for the current domain
        return :
            RIDLastPropagation : {1/1/1601 12:00:00 AM}
            RIDRoleOwner : {CN=NTDS Settings,CN=reqlab01,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=adsips,DC=local}
            RIDIssued : 2100
            RIDRemaining : 1073741823
    .EXAMPLE
        Get-ADSIRIDsPool -DomainName mytest.local
        Retrieve RID information for the domain mytest.local
    .EXAMPLE
        Get-ADSIRIDsPool -Credential (Get-Credential superAdmin) -Verbose
        Retrieve RID information for the current domain with the specified credential.
    .EXAMPLE
        Get-ADSIRIDsPool -DomainName mytest.local -Credential (Get-Credential superAdmin) -Verbose
        Retrieve RID information for the domain mytest.local with the specified credential.
    .NOTES
        https://github.com/lazywinadmin/ADSIPS
    .OUTPUTS
        [pscustomobject]
#>

    [cmdletbinding()]
    [OutputType('pscustomobject')]
    param (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().name
    )

    process {
        try {

            $FunctionName = (Get-Variable -Name MyInvocation -ValueOnly -Scope 0).MyCommand


            if ($PSBoundParameters['Credential'])
            {
                $ContextObjectType = New-ADSIDirectoryContext -Credential $Credential -contextType Domain
                if ($PSBoundParameters['DomainName'])
                {
                    $ContextObjectType = New-ADSIDirectoryContext -Credential $Credential -contextType Domain -DomainName $DomainName
                }
            }
            else
            {
                $ContextObjectType = New-ADSIDirectoryContext -contextType Domain
                if ($PSBoundParameters['DomainName'])
                {
                    $ContextObjectType = New-ADSIDirectoryContext -contextType Domain -DomainName $DomainName
                }
            }

            Write-Verbose -Message "[$FunctionName] Create ActiveDirectory Domain Object"
            $DomainObject = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($ContextObjectType)


            $DomainDN = $DomainObject.GetDirectoryEntry().distinguishedName

            $AdsiRidManagerObject = [ADSI]"LDAP://CN=RID Manager$,CN=System,$($DomainDN)"

            Write-Verbose -Message "[$FunctionName] Create a request to LDAP://CN=RID Manager$,CN=System,$($DomainDN)"
            $LdapResultObject = new-object system.DirectoryServices.DirectorySearcher($AdsiRidManagerObject)

            Write-Verbose -Message "[$FunctionName] Retreive RID Manager properties"
            $RidProperties = ($LdapResultObject.FindOne()).properties

            [int32]$SIDtotal = $($RidProperties.ridavailablepool) / ([math]::Pow(2,32))

            [int64]$Int64Tempvar = $SIDtotal * ([math]::Pow(2,32))

            [int32]$RIDPoolCount = $($RidProperties.ridavailablepool)  - $Int64Tempvar

            $RemaningRID = $SIDtotal - $currentRIDPoolCount

            return [pscustomobject]@{
                RIDLastPropagation  = $RidProperties.dscorepropagationdata
                RIDRoleOwner        = $RidProperties.fsmoroleowner
                RIDIssued           = $RIDPoolCount
                RIDRemaining        = $RemaningRID
            }


        }
        catch {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }

}
function Get-ADSIRootDSE
{
    <#
.SYNOPSIS
    Get-ADSIRootDSE Gets the root of a directory server information tree.
 
.DESCRIPTION
    Get-ADSIRootDSE Gets the root of a directory server information tree.
 
.PARAMETER Credential
    Defines alternate credentials to use. Use Get-Credential to create proper credentials.
 
.PARAMETER DomainName
    Specifies the DomainName to query
 
.EXAMPLE
    Get-ADSIRootDSE
 
    Retrieve informations for the current domain
 
.EXAMPLE
    Get-ADSIRootDSE -DomainName lazywinadmin.com
 
    Retrieve informations for the domain lazywinadmin.com
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [cmdletbinding()]
    param (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
    )
    process
    {
        try
        {

            $Splatting = @{ }

            if ($PSBoundParameters['Credential'])
            {
                Write-Verbose -Message '[PROCESS] Credential specified'
                $Splatting.ArgumentList += $($Credential.UserName)
                $Splatting.ArgumentList += $($Credential.GetNetworkCredential().password)
            }
            if ($PSBoundParameters['DomainName'])
            {
                Write-Verbose -Message '[PROCESS] DomainName specified'
                $Splatting.ArgumentList += "LDAP://$DomainName/RootDSE"
            }
            else
            {
                $Splatting.ArgumentList += "LDAP://RootDSE"
            }

            $DomainRootDSE = New-Object -TypeName System.DirectoryServices.DirectoryEntry @splatting

        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }

        # Define the properties
        $Properties = @{
            "currentTime"                   = $DomainRootDSE.currentTime
            "subschemaSubentry"             = $DomainRootDSE.subschemaSubentry
            "dsServiceName"                 = $DomainRootDSE.dsServiceName
            "namingContexts"                = $DomainRootDSE.namingContexts
            "defaultNamingContext"          = $DomainRootDSE.defaultNamingContext
            "schemaNamingContext"           = $DomainRootDSE.schemaNamingContext
            "configurationNamingContext"    = $DomainRootDSE.configurationNamingContext
            "rootDomainNamingContext"       = $DomainRootDSE.rootDomainNamingContext
            "supportedControl"              = $DomainRootDSE.supportedControl
            "supportedLDAPVersion"          = $DomainRootDSE.supportedLDAPVersion
            "supportedLDAPPolicies"         = $DomainRootDSE.supportedLDAPPolicies
            "highestCommittedUSN"           = $DomainRootDSE.highestCommittedUSN
            "supportedSASLMechanisms"       = $DomainRootDSE.supportedSASLMechanisms
            "dnsHostName"                   = $DomainRootDSE.dnsHostName
            "ldapServiceName"               = $DomainRootDSE.ldapServiceName
            "serverName"                    = $DomainRootDSE.serverName
            "supportedCapabilities"         = $DomainRootDSE.supportedCapabilities
            "isSynchronized"                = $DomainRootDSE.isSynchronized
            "isGlobalCatalogReady"          = $DomainRootDSE.isGlobalCatalogReady
            "domainFunctionality"           = $DomainRootDSE.domainFunctionality
            "forestFunctionality"           = $DomainRootDSE.forestFunctionality
            "domainControllerFunctionality" = $DomainRootDSE.domainControllerFunctionality
            "distinguishedName"             = $DomainRootDSE.distinguishedName
        }

        # Output the info
        New-Object -TypeName PSObject -Property $Properties
    }
}
function Get-ADSISchema
{
<#
.SYNOPSIS
    The Get-ADSISchema function gather information about the current Active Directory Schema
 
.DESCRIPTION
    The Get-ADSISchema function gather information about the current Active Directory Schema
 
.PARAMETER PropertyType
    Specify the type of property to return
 
.PARAMETER ClassName
    Specify the name of the Class to retrieve
 
.PARAMETER AllClasses
    This will list all the property present in the domain
 
.PARAMETER FindClassName
    Specify the exact or partial name of the class to search
 
.PARAMETER ForestName
    Specifies the Forest name
 
.PARAMETER Credential
    Specifies alternative credential to use
 
.EXAMPLE
    Get-ADSISchema -PropertyType Mandatory -ClassName user
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param
    (
        [Parameter(ParameterSetName = 'Default',
            Mandatory = $false)]
        [ValidateSet("mandatory", "optional")]
        [String]$PropertyType,

        [Parameter(ParameterSetName = 'Default',
            Mandatory = $true)]
        [String]$ClassName,

        [Parameter(ParameterSetName = 'AllClasses',
            Mandatory = $true)]
        [Switch]$AllClasses,

        [Parameter(ParameterSetName = 'FindClasses',
            Mandatory = $true)]
        [String]$FindClassName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest()
    )

    begin
    {
        try
        {
            $FunctionName = (Get-Variable -Name MyInvocation -ValueOnly -Scope 0).MyCommand
            if ($PSBoundParameters['Credential'] -or $PSBoundParameters['ForestName'])
            {
                Write-Verbose -Message "[$FunctionName] Credential or ForestName specified"
                $Splatting = @{ }
                if ($PSBoundParameters['Credential'])
                {
                    Write-Verbose -Message "[$FunctionName] Set Credential"
                    $Splatting.Credential = $Credential
                }
                if ($PSBoundParameters['ForestName'])
                {
                    Write-Verbose -Message "[$FunctionName] Set ForestName"
                    $Splatting.ForestName = $ForestName
                }

                $SchemaContext = New-ADSIDirectoryContext @splatting -contextType Forest
                Write-Verbose -Message "[$FunctionName] Get Schema for forest '$forestName'"
                $schema = [DirectoryServices.ActiveDirectory.ActiveDirectorySchema]::GetSchema($SchemaContext)
            }
            else
            {
                Write-Verbose -Message "[$FunctionName] Get Current Schema"
                $schema = [DirectoryServices.ActiveDirectory.ActiveDirectorySchema]::GetCurrentSchema()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }

    process
    {
        if ($PSBoundParameters['AllClasses'])
        {
            Write-Verbose -Message "[$FunctionName] Retrieving all classes..."
            $schema.FindAllClasses()
        }elseif ($PSBoundParameters['FindClassName'])
        {
            Write-Verbose -Message "[$FunctionName] Looking up for class pattern '$FindClassName'"
            $schema.FindAllClasses() | Where-Object -FilterScript { $_.name -match $FindClassName }
        }elseif ($PropertyType -and $ClassName)
        {
            switch ($PropertyType)
            {
                "mandatory"
                {
                    Write-Verbose -Message "[$FunctionName] Retrieving MandatoryProperties for class '$ClassName'"
                    ($schema.FindClass("$ClassName")).MandatoryProperties
                }
                "optional"
                {
                    Write-Verbose -Message "[$FunctionName] Retrieving OptionalProperties for class '$ClassName'"
                    ($schema.FindClass("$ClassName")).OptionalProperties
                }
            }#switch
        }elseif (-not $propertyType -and $ClassName){
            Write-Verbose -Message "[$FunctionName] Retrieving class '$ClassName'"
            $schema.FindClass("$ClassName")
        }
    }#process
}
function Get-ADSISite
{
<#
.SYNOPSIS
    Function to retrieve the Active Directory Site(s)
 
.DESCRIPTION
    Function to retrieve the Active Directory Site(s)
 
.PARAMETER Credential
    Specifies alternative credential to use. Default is the current user.
 
.PARAMETER ForestName
    Specifies the ForestName to query. Default is the current one
 
.PARAMETER SiteName
    Specifies the Site Name to find.
 
.EXAMPLE
    Get-ADSISite
 
.EXAMPLE
    Get-ADSISite -ForestName lazywinadmin.com
 
.EXAMPLE
    Get-ADSISite -Credential (Get-Credential superAdmin) -Verbose
 
.EXAMPLE
    Get-ADSISite -ForestName lazywinadmin.com -Credential (Get-Credential superAdmin) -Verbose
 
.EXAMPLE
    Get-ADSISite -Name 'Montreal'
 
.OUTPUTS
    System.DirectoryServices.ActiveDirectory.ActiveDirectorySite
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.ActiveDirectorySite')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest(),

        [Alias("Name")]
        [String]$SiteName
    )

    process
    {
        try
        {
            if ($PSBoundParameters['Name'])
            {
                # Remove Name from the PSBoundParameters Splatting
                [Void]$PSBoundParameters.Remove('Name')

                # Create a Forest Context
                $Context = New-ADSIDirectoryContext -ContextType Forest @PSBoundParameters

                # Get the site name specified
                [System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::FindByName($Context, $Name)
            }
            else
            {
                [Void]$PSBoundParameters.Remove('Name')
                (Get-ADSIForest @PSBoundParameters).Sites
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSISiteLink
{
<#
.SYNOPSIS
    Function to retrieve the Active Directory Site Link(s)
 
.DESCRIPTION
    Function to retrieve the Active Directory Site Link(s)
 
.PARAMETER Credential
    Specifies alternative credential to use. Default is the current user.
 
.PARAMETER ForestName
    Specifies the ForestName to query. Default is the current one
 
.PARAMETER Name
    Specifies the Site Name to find.
 
.EXAMPLE
    Get-ADSISiteLink
 
.EXAMPLE
    Get-ADSISiteLink -ForestName lazywinadmin.com
 
.EXAMPLE
    Get-ADSISiteLink -Credential (Get-Credential superAdmin) -Verbose
 
.EXAMPLE
    Get-ADSISiteLink -ForestName lazywinadmin.com -Credential (Get-Credential superAdmin) -Verbose
 
.EXAMPLE
    Get-ADSISiteLink -Name 'Azure'
 
.OUTPUTS
    System.DirectoryServices.ActiveDirectory.ActiveDirectorySiteLink
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.ActiveDirectorySiteLink')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest(),

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [String]$Name
    )

    process
    {
        try
        {
            (Get-ADSISite @PSBoundParameters).Sitelinks
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSISiteServer
{
<#
.SYNOPSIS
    Function to retrieve the Active Directory Site Servers
 
.DESCRIPTION
    Function to retrieve the Active Directory Site Servers
 
.PARAMETER Credential
    Specifies alternative credential to use. Default is the current user.
 
.PARAMETER ForestName
    Specifies the ForestName to query. Default is the current one
 
.PARAMETER Name
    Specifies the Site Name to find.
 
.EXAMPLE
    Get-ADSISiteServer
 
.EXAMPLE
    Get-ADSISiteServer -ForestName lazywinadmin.com
 
.EXAMPLE
    Get-ADSISiteServer -Credential (Get-Credential superAdmin) -Verbose
 
.EXAMPLE
    Get-ADSISiteServer -ForestName lazywinadmin.com -Credential (Get-Credential superAdmin) -Verbose
 
.EXAMPLE
    Get-ADSISiteServer -Name 'Azure'
 
.OUTPUTS
    System.DirectoryServices.ActiveDirectory.DomainController
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.DomainController')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest(),

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [String]$Name
    )

    process
    {
        try
        {
            (Get-ADSISite @PSBoundParameters).servers
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSISiteSubnet
{
<#
.SYNOPSIS
    Function to retrieve the Active Directory Site subnets
 
.DESCRIPTION
    Function to retrieve the Active Directory Site subnets
 
.PARAMETER Credential
    Specifies alternative credential to use. Default is the current user.
 
.PARAMETER ForestName
    Specifies the ForestName to query. Default is the current one
 
.PARAMETER SubnetName
    Specifies the Site Name to find.
 
.EXAMPLE
    Get-ADSISiteSubnet
 
.EXAMPLE
    Get-ADSISiteSubnet -ForestName lazywinadmin.com
 
.EXAMPLE
    Get-ADSISiteSubnet -Credential (Get-Credential superAdmin) -Verbose
 
.EXAMPLE
    Get-ADSISiteSubnet -ForestName lazywinadmin.com -Credential (Get-Credential superAdmin) -Verbose
 
.EXAMPLE
    Get-ADSISiteSubnet -Name 'Azure'
 
.OUTPUTS
    System.DirectoryServices.ActiveDirectory.ActiveDirectorySubnet
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.ActiveDirectory.ActiveDirectorySubnet')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest(),

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias("Name")]
        [String]$SubnetName
    )
    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Forest" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['ForestName'])
        {
            $ContextSplatting.ForestName = $ForestName
        }

        $Context = New-ADSIDirectoryContext @ContextSplatting -contextType Forest
    }
    process
    {
        try
        {
            if ($PSBoundParameters['SubnetName'])
            {
                [System.DirectoryServices.ActiveDirectory.ActiveDirectorySubnet]::FindByName($Context, $SubnetName)
            }
            if (-not $PSBoundParameters['SubnetName'])
            {
                (Get-ADSISite @PSBoundParameters).subnets
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Get-ADSITokenGroup
{
<#
.SYNOPSIS
    Retrieve the list of group present in the tokengroups of a user or computer object.
 
.DESCRIPTION
    Retrieve the list of group present in the tokengroups of a user or computer object.
 
    TokenGroups attribute
    https://msdn.microsoft.com/en-us/library/ms680275%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
 
.PARAMETER SamAccountName
    Specifies the SamAccountName to retrieve
 
.PARAMETER Credential
    Specifies Credential to use
 
.PARAMETER DomainDistinguishedName
    Specify the Domain or Domain DN path to use
 
.PARAMETER SizeLimit
    Specify the number of item maximum to retrieve
 
.EXAMPLE
    Get-ADSITokenGroup -SamAccountName 'testaccount'
 
    Retrieve the list of groups present in the TokenGroups for the user 'testaccount'
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
#>

    [CmdletBinding()]
    param
    (
        [Parameter(ValueFromPipeline = $true)]
        [Alias('UserName', 'Identity')]
        [String]$SamAccountName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Alias('DomainDN', 'Domain')]
        [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path),

        [Alias('ResultLimit', 'Limit')]
        [int]$SizeLimit = '100'
    )

    process
    {
        try
        {
            # Building the basic search object with some parameters
            $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop'
            $Search.SizeLimit = $SizeLimit
            $Search.SearchRoot = $DomainDN
            #$Search.Filter = "(&(anr=$SamAccountName))"
            $Search.Filter = "(&((objectclass=user)(samaccountname=$SamAccountName)))"

            # Credential
            if ($PSBoundParameters['Credential'])
            {
                $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password)
                $Search.SearchRoot = $Cred
            }

            # Different Domain
            if ($DomainDistinguishedName)
            {
                if ($DomainDistinguishedName -notlike "LDAP://*")
                {
                    $DomainDistinguishedName = "LDAP://$DomainDistinguishedName"
                }#if
                Write-Verbose -Message "[PROCESS] Different Domain specified: $DomainDistinguishedName"
                $Search.SearchRoot = $DomainDistinguishedName
            }

            foreach ($Account in $Search.FindAll())
            {

                $AccountGetDirectory = $Account.GetDirectoryEntry();

                # Add the properties tokenGroups
                $AccountGetDirectory.GetInfoEx(@("tokenGroups"), 0)


                foreach ($Token in $($AccountGetDirectory.Get("tokenGroups")))
                {
                    # Create SecurityIdentifier to translate into group name
                    $Principal = New-Object -TypeName System.Security.Principal.SecurityIdentifier($token, 0)

                    # Prepare Output
                    $Properties = @{
                        SamAccountName = $Account.properties.samaccountname -as [string]
                        GroupName      = $principal.Translate([System.Security.Principal.NTAccount])
                    }

                    # Output Information
                    New-Object -TypeName PSObject -Property $Properties
                }
            }

        }

        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }#process
    end
    {
        Write-Verbose -Message "[END] Function Get-ADSITokenGroup End."
    }
}#Function
function Get-ADSITombstoneLifetime
{
    <#
.SYNOPSIS
    Get-ADSITombstoneLifetime returns the number of days before a deleted object is removed from the directory services.
 
.DESCRIPTION
    Get-ADSITombstoneLifetime returns the number of days before a deleted object is removed from the directory services.
 
.PARAMETER Credential
    Defines alternate credentials to use. Use Get-Credential to create proper credentials.
 
.PARAMETER DomainName
    Specifies the DomainName to query
 
.EXAMPLE
    Get-ADSITombstoneLifetime
 
    For the current domain, returns the number of days before a deleted object is removed from the directory services.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding()]
    param (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
    )

    try
    {

        if ($PSBoundParameters['Credential'] -or $PSBoundParameters['DomainName'])
        {
            Write-Verbose -Message '[PROCESS] Credential or DomainName specified'
            $Splatting = @{ }
            if ($PSBoundParameters['Credential'])
            {
                $Splatting.Credential = $Credential
            }
            if ($PSBoundParameters['DomainName'])
            {
                $Splatting.DomainName = $DomainName
            }

            $configurationNamingContext = (Get-ADSIRootDSE @splatting).configurationNamingContext

        }
        else
        {
            $configurationNamingContext = (Get-ADSIRootDSE).configurationNamingContext
        }

    }
    catch
    {
        $pscmdlet.ThrowTerminatingError($_)
    }

    $nTDSService = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://CN=Directory Service,CN=Windows NT,CN=Services,$configurationNamingContext"

    Write-Verbose "Domain : $DomainName"
    $nTDSService.tombstoneLifetime
}
function Get-ADSIUser
{
    <#
.SYNOPSIS
    Function to retrieve a User in Active Directory
 
.DESCRIPTION
    Function to retrieve a User in Active Directory
 
.PARAMETER Identity
    Specifies the Identity of the User
    You can provide one of the following properties
    DistinguishedName
    Guid
    Name
    SamAccountName
    Sid
    UserPrincipalName
    Those properties come from the following enumeration:
    System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current domain.
 
.PARAMETER NoResultLimit
    Remove the SizeLimit of 1000
    SizeLimit is useless, it can't go over the server limit which is 1000 by default
 
.PARAMETER LDAPFilter
    Specifies the LDAP query to apply
 
.EXAMPLE
    Get-ADSIUser
    This example will retrieve all accounts in the current domain using
    the current user credential. There is a limit of 1000 objects returned.
 
.EXAMPLE
    Get-ADSIUser -NoResultLimit
    This example will retrieve all accounts in the current domain using
    the current user credential. Using the parameter -NoResultLimit will remove the Sizelimit on the Result.
 
.EXAMPLE
    Get-ADSIUser -Identity 'testaccount'
    This example will retrieve the account 'testaccount' in the current domain using
    the current user credential
 
.EXAMPLE
    Get-ADSIUser -Identity 'testaccount' -Credential (Get-Credential)
    This example will retrieve the account 'testaccount' in the current domain using
    the specified credential
 
.EXAMPLE
    Get-ADSIUSer -LDAPFilter "(&(objectClass=user)(samaccountname=*fx*))" -DomainName 'fx.lab'
    This example will retrieve the user account that contains fx inside the samaccountname
    property for the domain fx.lab. There is a limit of 1000 objects returned.
 
.EXAMPLE
    Get-ADSIUSer -LDAPFilter "(&(objectClass=user)(samaccountname=*fx*))" -DomainName 'fx.lab' -NoResultLimit
    This example will retrieve the user account that contains fx inside the samaccountname
    property for the domain fx.lab. There is NO limit of 1000 objects returned.
 
.EXAMPLE
    $user = Get-ADSIUser -Identity 'testaccount'
    $user.GetUnderlyingObject()| Select-Object -Property *
    Help you find all the extra properties and methods available
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
.LINK
    https://msdn.microsoft.com/en-us/library/System.DirectoryServices.AccountManagement.UserPrincipal(v=vs.110).aspx
#>


    [CmdletBinding(DefaultParameterSetName = "All")]
    [OutputType('System.DirectoryServices.AccountManagement.UserPrincipal')]
    param
    (
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Identity")]
        [string]$Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName,

        [Parameter(Mandatory = $true, ParameterSetName = "LDAPFilter")]
        [string]$LDAPFilter,

        [Parameter(ParameterSetName = "LDAPFilter")]
        [Parameter(ParameterSetName = "All")]
        [Switch]$NoResultLimit

    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting
    }
    process
    {
        if ($Identity)
        {
            Write-Verbose -Message "Identity"
            [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($Context, $Identity)

        }
        elseif ($PSBoundParameters['LDAPFilter'])
        {

            # Directory Entry object
            $DirectoryEntryParams = $ContextSplatting
            $DirectoryEntryParams.remove('ContextType')
            $DirectoryEntry = New-ADSIDirectoryEntry @DirectoryEntryParams

            # Principal Searcher
            $DirectorySearcher = new-object -TypeName System.DirectoryServices.DirectorySearcher
            $DirectorySearcher.SearchRoot = $DirectoryEntry

            $DirectorySearcher.Filter = "(&(objectCategory=user)$LDAPFilter)"
            #$DirectorySearcher.PropertiesToLoad.AddRange("'Enabled','SamAccountName','DistinguishedName','Sid','DistinguishedName'")

            if (-not$PSBoundParameters['NoResultLimit'])
            {
                Write-Warning -Message "Result is limited to 1000 entries, specify a specific number on the parameter SizeLimit or 0 to remove the limit"
            }
            else
            {
                # SizeLimit is useless, even if there is a$Searcher.GetUnderlyingSearcher().sizelimit=$SizeLimit
                # the server limit is kept
                $DirectorySearcher.PageSize = 10000
            }

            $DirectorySearcher.FindAll() | Foreach-Object -Process {
                [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($Context, $_.Properties["distinguishedname"])
            }# Return UserPrincipale object
        }
        else
        {
            Write-Verbose -Message "Searcher"

            $UserPrincipal = New-object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal -ArgumentList $Context
            $Searcher = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher
            $Searcher.QueryFilter = $UserPrincipal

            if (-not$PSBoundParameters['NoResultLimit'])
            {
                Write-Warning -Message "Result is limited to 1000 entries, specify a specific number on the parameter SizeLimit or 0 to remove the limit"
            }
            else
            {
                # SizeLimit is useless, even if there is a$Searcher.GetUnderlyingSearcher().sizelimit=$SizeLimit
                # the server limit is kept
                $Searcher.GetUnderlyingSearcher().pagesize = 10000

            }
            #$Searcher.GetUnderlyingSearcher().propertiestoload.AddRange("'Enabled','SamAccountName','DistinguishedName','Sid','DistinguishedName'")
            $Searcher.FindAll() # Return UserPrincipale
        }
    }
}
function Get-ADSIUserPrimaryGroup
{
    <#
            .SYNOPSIS
            Function to retrieve User's primary group
 
            .DESCRIPTION
            Get primary AD group of a user
 
            .PARAMETER Identity
            Specifies the Identity of the User
            Uses the return of "Get-ADSIUser"
 
            .PARAMETER ReturnNameAndDescriptionOnly
            Returns a PSCustomObject of just the name and description
            ex: $return = [pscustomobject]@{
            'name' = [string]$primaryGroup.Properties.name
            'description' = [string]$primaryGroup.Properties.description
            }
 
            .EXAMPLE
            Get-ADSIUserPrimaryGroup -Identity (Get-ADSIUser 'User1')
 
            Get primary AD group of user User1
 
            .NOTES
            https://github.com/lazywinadmin/ADSIPS
            CHANGE LOG
            -1.0 | 2019/06/22 | Matt Oestreich (oze4)
                - Initial creation
    #>

    
    param(
        [Parameter(Mandatory=$true)]
        [System.DirectoryServices.AccountManagement.AuthenticablePrincipal]$Identity,
        
        [Parameter(Mandatory=$false)]
        [switch]$ReturnNameAndDescriptionOnly
    )

    try { 
        $UnderlyingProperties = $Identity.GetUnderlyingObject()
        $userSid  = (New-Object System.Security.Principal.SecurityIdentifier ($($UnderlyingProperties.properties.objectSID), 0)).AccountDomainSid.Value
        $groupSid = ('{0}-{1}' -f $userSid, $UnderlyingProperties.properties.primarygroupid.ToString())
        $primaryGroup = [adsi]("LDAP://<SID=$groupSid>")
        if ($PSBoundParameters["ReturnNameAndDescriptionOnly"]) {
            [pscustomobject]@{
                'name'        = [string]$primaryGroup.Properties.name
                'description' = [string]$primaryGroup.Properties.description
            }
        } else {
            $primaryGroup
        }
    } catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }

}
function Move-ADSIComputer
{
<#
.SYNOPSIS
    Function to Move a Computer in Active Directory
 
.DESCRIPTION
    Function to Move a Computer in Active Directory
 
.PARAMETER Identity
    Specifies the Identity of the computer
 
    You can provide one of the following:
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
 
    System.DirectoryService.AccountManagement.IdentityType
    https://msdn.microsoft.com/en-us/library/bb356425(v=vs.110).aspx
 
.PARAMETER Credential
    Specifies alternative credential
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain.
    By default it will use the current domain.
 
.PARAMETER Destination
    Specifies the Distinguished Name where the object will be moved
 
.EXAMPLE
    Move-ADSIComputer -identity 'TESTCOMP01' -Destination 'OU=Servers,DC=FX,DC=LAB'
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.computerprincipal(v=vs.110).aspx
#>

    [CmdletBinding()]
    param ([Parameter(Mandatory = $true)]
        [string]$Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        $DomainName,

        $Destination
    )
    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting

    }
    process
    {
        try
        {
            $Computer = [System.DirectoryServices.AccountManagement.ComputerPrincipal]::FindByIdentity($Context, $Identity)

            # Retrieve DirectoryEntry
            #$Computer.GetUnderlyingObject()

            # Create DirectoryEntry object
            $NewDirectoryEntry = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$Destination"

            # Move the computer
            $Computer.GetUnderlyingObject().psbase.moveto($NewDirectoryEntry)
            $Computer.Save()
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Move-ADSIDomainControllerRole
{
<#
.SYNOPSIS
    Function to transfers or Seizes Active Directory roles to the current DC.
 
.DESCRIPTION
    Function to transfers or Seizes Active Directory roles to the current DC.
 
.PARAMETER ComputerName
    Specifies the Domain Controller
 
.PARAMETER Credential
    Specifies alternate credentials to use. Use Get-Credential to create proper credentials.
 
.PARAMETER Role
    Specifies the Role(s) to transfer to Seize
 
.PARAMETER Force
    Forces the role(s) to be seized
 
.EXAMPLE
    Move-ADSIDomainControllerRole -ComputerName dc1.ad.local -Roles "PDCRole"
 
    Connects to remote domain controller dc1.ad.local using current credentials and
    attempts to transfer the PDCrole to dc1.ad.local.
 
.EXAMPLE
    Move-ADSIDomainControllerRole -ComputerName DC1 -Credential $cred -Verbose -Roles InfrastructureRole,PDCRole,RidRole,NamingRole,SchemaRole -Force
 
    Connects to remote domain controller dc1.ad.local using alternate credentials and seizes all the roles.
 
.NOTES
    Version History
    1.0 Initial Version (Micky Balladelli)
    1.1 Update (Francois-Xavier Cat)
        Rename from Move-ADSIDomainControllerRole to Move-ADSIDomainControllerRole
        Add New-ADSIDirectoryContext to take care of the Context
        Other minor modifications
 
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$ComputerName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Parameter(Mandatory = $true)]
        [System.Directoryservices.ActiveDirectory.ActiveDirectoryRole[]]$Role,

        [Switch]$Force
    )

    process
    {
        try
        {
            # DirectoryContext Splatting
            $Splatting = $PSBoundParameters.Remove("Force")
            $Splatting = $Splatting.Remove("Role")

            # Create the Context
            $Context = New-ADSIDirectoryContext -ContextType 'DirectoryServer' @Splatting

            # Get the DomainController
            $DomainController = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($Context)

            if ($PSBoundParameters['Force'])
            {
                foreach ($RoleObj in $Role)
                {
                    Write-Verbose -Message "[Move-ADSIDomainControllerRole][PROCESS] $($DomainController.name) Forcing a role transfer of role $RoleObj"
                    $DomainController.SeizeRoleOwnership($RoleObj)
                }
            }
            else
            {
                foreach ($RoleObj in $Role)
                {
                    Write-Verbose -Message "[Move-ADSIDomainControllerRole][PROCESS] $($DomainController.name) Transferring role $RoleObj"
                    $DomainController.TransferRoleOwnership($RoleObj)
                }
            }
            Write-Verbose -Message "[Move-ADSIDomainControllerRole][PROCESS] $($DomainController.name) Done."
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Move-ADSIDomainControllerToSite
{
<#
.SYNOPSIS
    Move-ADSIDomainControllerToSite moves the current DC to another site.
 
.DESCRIPTION
    Move-ADSIDomainControllerToSite moves the current DC to another site.
 
    MSDN Documention on 'DirectoryServer.MoveToAnotherSite Method'
    https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.directoryserver.movetoanothersite(v=vs.110).aspx
 
.PARAMETER ComputerName
    Specifies the Domain Controller
 
.PARAMETER Credential
    Specifies alternate credentials to use. Use Get-Credential to create proper credentials.
 
.PARAMETER Site
    Name of the Active Directory site
 
.EXAMPLE
    Move-ADSIDomainControllerToSite -ComputerName dc1.ad.local -site "Paris"
 
    Connects to remote domain controller dc1.ad.local using current credentials and
    moves it to the site "Paris".
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
    Version History
        1.0 Initial Version (Micky Balladelli)
        1.1 Update (Francois-Xavier Cat)
                Rename from Move-ADSIReplicaToSite to Move-ADSIDomainControllerToSite
                Add New-ADSIDirectoryContext to take care of the Context
                Other minor modifications
#>


    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [string]$ComputerName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Parameter(Mandatory = $true)]
        [string]$Site
    )
    process
    {
        try
        {
            # DirectoryContext Splatting
            $Splatting = $PSBoundParameters.Remove("Site")
            # Create the Context
            $Context = New-ADSIDirectoryContext -ContextType 'DirectoryServer' @Splatting

            $DomainController = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($context)

            Write-Verbose -Message "[Move-ADSIDomainControllerToSite][PROCESS] $($DomainController.name) to site $Site"
            $DomainController.MoveToAnotherSite($Site)
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Move-ADSIGroup
{
<#
.SYNOPSIS
    Function to Move an Active Directory group in a different Organizational Unit (OU)
 
.DESCRIPTION
    Function to Move an Active Directory group in a different Organizational Unit (OU)
 
.PARAMETER Identity
    Specifies the Identity of the group
 
    You can provide one of the following properties
    DistinguishedName
    Guid
    Name
    SamAccountName
    Sid
    UserPrincipalName
 
    Those properties come from the following enumeration:
    System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current Domain.
 
.PARAMETER Destination
    Specifies the Distinguished Name where the object will be moved
 
.EXAMPLE
    Move-ADSIGroup -Identity 'FXGROUPTEST01' -Destination 'OU=TEST,DC=FX,DC=lab'
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.groupprincipal(v=vs.110).aspx
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.AccountManagement.GroupPrincipal')]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Alias('Domain', 'Server')]
        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::Getcurrentdomain(),

        $Destination
    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting
    }
    process
    {
        try
        {
            $Group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($Context, $Identity)

            # Create DirectoryEntry object
            $NewDirectoryEntry = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$Destination"

            # Move the computer
            $Group.GetUnderlyingObject().psbase.moveto($NewDirectoryEntry)
            $Group.Save()

        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Move-ADSIUser
{
<#
.SYNOPSIS
    Function to move a User in Active Directory
 
.DESCRIPTION
    Function to move a User in Active Directory
 
.PARAMETER Identity
    Specifies the Identity of the User
 
    You can provide one of the following properties
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
        UserPrincipalName
 
    Those properties come from the following enumeration:
        System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current domain.
 
.PARAMETER Destination
    Specifies the Distinguished Name where the object will be moved
 
.EXAMPLE
    Move-ADSIUser -Identity 'fxtest01' -Destination "OU=Test,DC=FX,DC=lab"
 
.EXAMPLE
    Move-ADSIUser -Identity 'fxtest01' -Destination "OU=Test,DC=FX,DC=lab" -Credential (Get-Credential)
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/System.DirectoryServices.AccountManagement.UserPrincipal(v=vs.110).aspx
#>


    [CmdletBinding()]
    [OutputType('System.DirectoryServices.AccountManagement.UserPrincipal')]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName,

        $Destination
    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting
    }
    process
    {
        if ($Identity)
        {
            $user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($Context, $Identity)

            # Retrieve DirectoryEntry
            #$User.GetUnderlyingObject()

            # Create DirectoryEntry object
            $NewDirectoryEntry = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$Destination"

            # Move the computer
            $User.GetUnderlyingObject().psbase.moveto($NewDirectoryEntry)
            $User.Save()
        }
    }
}
function New-ADSIComputer
{
<#
.SYNOPSIS
    function to create a new computer
 
.DESCRIPTION
    function to create a new computer
 
.PARAMETER Name
    Specifies the property Name
 
.PARAMETER DisplayName
    Specifies the property DisplayName
 
.PARAMETER Description
    Specifies the property Description
 
.PARAMETER Enable
    Specifies you want the account enabled after creation.
    By Default the account is disable
 
.PARAMETER Passthru
    Specifies if you want to see the object created after running the command.
 
.PARAMETER Credential
    Specifies if you want to specifies alternative credentials
 
.PARAMETER DomainName
    Specifies if you want to specifies alternative DomainName
 
.EXAMPLE
    New-ADSIComputer FXTEST01 -Description 'Dev system'
 
    Create a new computer account FXTEST01 and add the description 'Dev System'
 
.EXAMPLE
    New-ADSIComputer FXTEST01 -enable
 
    Create a new computer account FXTEST01 inside the default Computers Organizational Unit and Enable the account
 
.EXAMPLE
    New-ADSIComputer FXTEST01 -Description 'Dev system'
 
    Create a new computer account FXTEST01 and add the description 'Dev System'
 
.EXAMPLE
    New-ADSIComputer FXTEST01 -Passthru
 
    Create a new computer account FXTEST01 and return the object created and its properties.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.computerprincipal(v=vs.110).aspx
#>


    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        $Name,

        [String]$DisplayName,

        [String]$Description,

        [switch]$Passthru,

        [Switch]$Enable,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName
    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting
    }
    process
    {
        try
        {

            if ($PSCmdlet.ShouldProcess($Name, "Create Computer"))
            {
                $newObject = New-Object -TypeName System.DirectoryServices.AccountManagement.ComputerPrincipal -ArgumentList $Context
                $newObject.SamAccountName = $Name

                if ($PSBoundParameters['Enable'])
                {
                    $newObject.Enabled = $true
                }

                if ($PSBoundParameters['Description'])
                {
                    $newObject.Description = $Description
                }

                if ($PSBoundParameters['DisplayName'])
                {
                    $newObject.DisplayName
                }

                # Push to ActiveDirectory
                $newObject.Save($Context)

                if ($PSBoundParameters['Passthru'])
                {
                    $ContextSplatting.Remove('ContextType')
                    Get-ADSIComputer -Identity $Name @ContextSplatting
                }
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }

    }
    end
    {

    }
}
function New-ADSIDirectoryContext
{
<#
.SYNOPSIS
    Function to create an Active Directory DirectoryContext objects
 
.DESCRIPTION
    Function to create an Active Directory DirectoryContext objects
 
.PARAMETER Credential
    Specifies the alternative credentials to use.
    It will use the current credential if not specified.
 
.PARAMETER ContextType
    Specifies the ContextType. The following choices are available:
        ApplicationPartition
        ConfigurationSet
        DirectoryServer
        Domain
        Forest
 
.PARAMETER DomainName
    Specifies the domain to query. Default is the current domain.
    This need to be used with the ContextType Domain
 
.PARAMETER ForestName
    Specifies the forest to query. Default is the current forest.
    This need to be used with the ContextType Forest
 
.PARAMETER Server
    Specifies the Domain Controller to use
    This need to be used with the ContextType DirectoryServer
 
.EXAMPLE
    New-ADSIDirectoryContext -ContextType Domain
 
    This will create a new Directory Context of type Domain in the current domain
 
.EXAMPLE
    New-ADSIDirectoryContext -ContextType Domain -DomainName "FXTEST.local"
 
    This will create a new Directory Context of type Domain in the domain "FXTEST.local"
 
.EXAMPLE
    New-ADSIDirectoryContext -ContextType Forest
 
    This will create a new Directory Context of type Forest in the current forest
 
.EXAMPLE
    New-ADSIDirectoryContext -ContextType Forest -ForestName "FXTEST.local"
 
    This will create a new Directory Context of type Forest in the forest FXTEST.local
 
.EXAMPLE
    New-ADSIDirectoryContext -ContextType Forest -ForestName "FXTEST.local" -credential (Get-Credential)
 
    This will create a new Directory Context of type Forest with Alternative credentials
 
.EXAMPLE
    New-ADSIDirectoryContext -ContextType DirectoryServer -Server "DCSERVER01.FXTEST.local"
 
    This will create a new Directory Context of type DirectoryServer against the Domain Controller DCSERVER01.FXTEST.local
 
.EXAMPLE
    $Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($(New-ADSIDirectoryContext -ContextType Domain -Credential (Get-Credential)))
    $Domain.DomainControllers
    $Domain.InfrastructureRoleOwner
 
    This will retrieve all the Domain Controllers and the Infrastructure Role owner (FSMO Role)
 
.EXAMPLE
    [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController(New-ADSIDirectoryContext -ContextType DirectoryServer -Server "DC01.FXTEST.local").forest.sites
 
    This will retrieve all the sites in the forest
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.directorycontext(v=vs.110).aspx
#>


    [CmdletBinding(DefaultParameterSetName = 'Server', SupportsShouldProcess = $true)]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Parameter(Mandatory)]
        [System.DirectoryServices.ActiveDirectory.DirectoryContextType]$ContextType,

        [Parameter(ParameterSetName = 'Domain')]
        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::Getcurrentdomain(),

        [Parameter(ParameterSetName = 'Forest')]
        $ForestName = [System.DirectoryServices.ActiveDirectory.Forest]::Getcurrentforest(),

        [Parameter(ParameterSetName = 'Server')]
        [ValidateNotNullOrEmpty]
        [Alias("ComputerName", "DomainController")]
        $Server
    )

    process
    {
        try
        {
            switch ($ContextType)
            {
                "Domain"
                {
                    $ArgumentList = $ContextType, $DomainName
                }
                "Forest"
                {
                    $ArgumentList = $ContextType, $ForestName
                }
                "DirectoryServer"
                {
                    $ArgumentList = $ContextType, $Server
                }
                "ApplicationPartition"
                {
                    $ArgumentList = $ContextType
                }
                "ConfigurationSet"
                {
                    $ArgumentList = $ContextType
                }
            }
            if ($PSBoundParameters['Credential'])
            {
                # Query the specified domain or current if not entered, with the specified credentials
                $ArgumentList += $($Credential.UserName), $($Credential.GetNetworkCredential().password)
            }

            if ($PSCmdlet.ShouldProcess("Create Directory Entry"))
            {
                # Query the specified domain or current if not entered, with the current credentials
                New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ArgumentList
            }
        } #try
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    } #process
}
function New-ADSIDirectoryEntry
{
<#
.SYNOPSIS
    Function to create a DirectoryEntry instance
 
.DESCRIPTION
    Function to create a DirectoryEntry instance
 
    This function is typically a helper function used by some of the other functions
    in the module ADSIPS
 
.PARAMETER Path
    The path of this DirectoryEntry.
    Default is $(([adsisearcher]"").Searchroot.path)
 
    https://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.path.aspx
 
.PARAMETER Credential
    Specifies alternative credential to use
 
.PARAMETER AuthenticationType
    Specifies the optional AuthenticationType secure flag(s) to use
 
    The Secure flag can be used in combination with other flags such as ReadonlyServer, FastBind.
 
    See the full detailed list here:
    https://msdn.microsoft.com/en-us/library/system.directoryservices.authenticationtypes(v=vs.110).aspx
 
.EXAMPLE
    New-ADSIDirectoryEntry
 
    Create a new DirectoryEntry object for the current domain
 
.EXAMPLE
    New-ADSIDirectoryEntry -Path "LDAP://DC=FX,DC=lab"
 
    Create a new DirectoryEntry object for the domain FX.Lab
 
.EXAMPLE
    New-ADSIDirectoryEntry -Path "LDAP://DC=FX,DC=lab" -Credential (Get-Credential)
 
    Create a new DirectoryEntry object for the domain FX.Lab with the specified credential
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.aspx
 
.LINK
    http://www.lazywinadmin.com/2013/10/powershell-using-adsi-with-alternate.html
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Alias('DomainName')]
        $Path = $(([adsisearcher]"").Searchroot.path),

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [System.DirectoryServices.AuthenticationTypes[]]$AuthenticationType
    )
    try
    {
        #If path isn't prefixed with LDAP://, add it
        if ($PSBoundParameters['Path'])
        {
            if ($Path -notlike "LDAP://*")
            {
                $Path = "LDAP://$Path"
            }
        }

        #Building Argument
        if ($PSBoundParameters['Credential'])
        {
            $ArgumentList = $Path, $($Credential.UserName), $($Credential.GetNetworkCredential().password)
        }
        else
        {
            $ArgumentList = $Path
        }

        if ($PSBoundParameters['AuthenticationType'])
        {
            $ArgumentList += $AuthenticationType
        }

        if ($PSCmdlet.ShouldProcess($Path, "Create Directory Entry"))
        {
            # Create object
            New-Object -TypeName DirectoryServices.DirectoryEntry -ArgumentList $ArgumentList
        }
    }
    catch
    {
        $PSCmdlet.ThrowTerminatingError($_)

    }
}
function New-ADSIGroup
{
<#
.SYNOPSIS
    function to create a new group
 
.DESCRIPTION
    function to create a new group
 
.PARAMETER Name
    Specifies the property Name
 
.PARAMETER DisplayName
    Specifies the property DisplayName
 
.PARAMETER UserPrincipalName
    Specifies the property UserPrincipalName
 
.PARAMETER Description
    Specifies the property Description
 
.PARAMETER GroupScope
    Specifies the Group Scope (Global, Local or Universal)
 
.PARAMETER IsSecurityGroup
    Specify if you want to create a Security Group.
    By default this is $true.
 
.PARAMETER Passthru
    Specifies if you want to see the object created after running the command.
 
.PARAMETER Credential
    Specifies if you want to specifies alternative credentials
 
.PARAMETER DomainName
    Specifies if you want to specifies alternative DomainName
 
.EXAMPLE
    PS C:\> New-ADSIGroup -Name "TestfromADSIPS3" -Description "some description" -GroupScope Local -IsSecurityGroup
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.groupprincipal(v=vs.110).aspx
#>


    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        $Name,

        [String]$DisplayName,

        [String]$UserPrincipalName,

        [String]$Description,

        [Parameter(Mandatory = $true)]
        [system.directoryservices.accountmanagement.groupscope]$GroupScope,
        [switch]$IsSecurityGroup = $true,

        [switch]$Passthru,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName
    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting
    }
    process
    {
        try
        {

            if ($PSCmdlet.ShouldProcess($Name, "Create Group"))
            {
                $newGroup = [System.DirectoryServices.AccountManagement.GroupPrincipal]::new($Context, $Name)
                $newGroup.Description = $Description
                $newGroup.GroupScope = $GroupScope
                $newGroup.IsSecurityGroup = $IsSecurityGroup
                $newGroup.DisplayName
                #$newGroup.DistinguishedName =
                #$newGroup.Members
                $newGroup.SamAccountName = $Name

                if ($PSBoundParameters['UserPrincipalName'])
                {
                    $newGroup.UserPrincipalName = $UserPrincipalName
                }

                # Push to ActiveDirectory
                $newGroup.Save($Context)

                if ($PSBoundParameters['Passthru'])
                {
                    $ContextSplatting.Remove('ContextType')
                    Get-ADSIGroup -Identity $Name @ContextSplatting
                }
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }

    }
    end
    {

    }
}
function New-ADSIPrincipalContext
{
    <#
.SYNOPSIS
    Function to create an Active Directory PrincipalContext object
 
.DESCRIPTION
    Function to create an Active Directory PrincipalContext object
 
.PARAMETER Credential
    Specifies the alternative credentials to use.
    It will use the current credential if not specified.
 
.PARAMETER ContextType
    Specifies which type of Context to use. Domain, Machine or ApplicationDirectory.
 
.PARAMETER DomainName
    Specifies the domain to query. Default is the current domain.
    Should only be used with the Domain ContextType.
 
.PARAMETER Container
    Specifies the scope. Example: "OU=MyOU"
 
.PARAMETER ContextOptions
    Specifies the ContextOptions.
    Negotiate
    Sealing
    SecureSocketLayer
    ServerBind
    Signing
    SimpleBind
 
.EXAMPLE
    New-ADSIPrincipalContext -ContextType 'Domain'
 
.EXAMPLE
    New-ADSIPrincipalContext -ContextType 'Domain' -DomainName "Contoso.com" -Cred (Get-Credential)
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.principalcontext(v=vs.110).aspx
#>


    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType('System.DirectoryServices.AccountManagement.PrincipalContext')]
    param
    (
        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Parameter(Mandatory = $true)]
        [System.DirectoryServices.AccountManagement.ContextType]$ContextType,

        $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::Getcurrentdomain(),

        $Container,

        [System.DirectoryServices.AccountManagement.ContextOptions[]]$ContextOptions
    )

    begin
    {
        $ScriptName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand
        Write-Verbose -Message "[$ScriptName] Add Type System.DirectoryServices.AccountManagement"
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement
    }
    process
    {
        try
        {
            switch ($ContextType)
            {
                "Domain"
                {
                    $ArgumentList = $ContextType, $DomainName
                }
                "Machine"
                {
                    $ArgumentList = $ContextType, $ComputerName
                }
                "ApplicationDirectory"
                {
                    $ArgumentList = $ContextType
                }
            }

            if ($PSBoundParameters['Container'])
            {
                $ArgumentList += $Container
            }

            if ($PSBoundParameters['ContextOptions'])
            {
                $ArgumentList += $($ContextOptions)
            }

            if ($PSBoundParameters['Credential'])
            {
                # Query the specified domain or current if not entered, with the specified credentials
                $ArgumentList += $($Credential.UserName), $($Credential.GetNetworkCredential().password)
            }

            if ($PSCmdlet.ShouldProcess($DomainName, "Create Principal Context"))
            {
                # Query
                New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ArgumentList
            }
        } #try
        catch
        {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    } #process
}
function New-ADSISite
{
    <#
.SYNOPSIS
    Function to create a new Site
 
.DESCRIPTION
    Function to create a new Site
 
.PARAMETER SiteName
    Specifies the SiteName
 
.PARAMETER Location
    Specifies the Location of the site
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER ForestName
    Specifies the alternative Forest where the subnet should be created
    By default it will use the current forest.
 
.EXAMPLE
    PS C:\> New-ADSISite -SiteName "MTL01" -Location "Montreal, QC, Canada"
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.activedirectorysite(v=vs.110).aspx
#>


    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias('Name')]
        [String]$SiteName,

        [String]$Location,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$ForestName
    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Forest" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['ForestName'])
        {
            $ContextSplatting.ForestName = $ForestName
        }

        $Context = New-ADSIDirectoryContext @ContextSplatting
    }
    process
    {
        try
        {
            if ($PSCmdlet.ShouldProcess($SiteName, "Create Site"))
            {
                $Site = New-Object -TypeName System.DirectoryServices.ActiveDirectory.ActiveDirectorySite -ArgumentList $Context, $SiteName
                $Site.Location = $Location
                $Site.Save()

                #$site.GetDirectoryEntry()
            }
        }
        catch
        {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
    end
    {

    }
}
function New-ADSISiteSubnet
{
    <#
.SYNOPSIS
    Function to create a new Site Subnet
 
.DESCRIPTION
    Function to create a new Site Subnet
 
.PARAMETER SubnetName
    Specifies the SubnetName.
    Example '192.168.8.0/24'
 
.PARAMETER SiteName
    Specifies the SiteName where the subnet will be assigned
 
.PARAMETER Location
    Specifies the location of the subnet
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER ForestName
    Specifies the alternative Forest where the subnet should be created
    By default it will use the current forest.
 
.EXAMPLE
    PS C:\> New-ADSISiteSubnet -SubnetName "5.5.5.0/24" -SiteName "FX3" -Location "test"
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.activedirectorysubnet(v=vs.110).aspx
#>


    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]$SubnetName,

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

        [String]$Location,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$ForestName
    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Forest" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['ForestName'])
        {
            $ContextSplatting.ForestName = $ForestName
        }

        $Context = New-ADSIDirectoryContext @ContextSplatting
    }
    process
    {
        try
        {
            if ($PSCmdlet.ShouldProcess($SubnetName, "Create new Subnet"))
            {
                $Subnet = New-Object -TypeName System.DirectoryServices.ActiveDirectory.ActiveDirectorysubnet -ArgumentList $Context, $SubnetName, $SiteName

                if ($PSBoundParameters['Location'])
                {
                    $Subnet.Location = $Location
                }

                $Subnet.Save()

                #$SubnetEntry = $Subnet.GetDirectoryEntry()
                #$SubnetEntry.Description = $subnetdescription
                #$SubnetEntry.CommitChanges()
                #$SubnetEntry
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
            break
        }
    }
    end
    {
    }
}
function New-ADSIUser
{

    <#
.SYNOPSIS
    Function to create a new User
 
.DESCRIPTION
    Function to create a new User
 
.PARAMETER SamAccountName
    Specifies the SamAccountName parameter
 
.PARAMETER AccountPassword
    Specifies the password parameter
 
.PARAMETER Enabled
    Specifies if the user need to be enabled on creation.
    Default is $False.
 
.PARAMETER GivenName
    Specifies the GivenName parameter
 
.PARAMETER SurName
    Specifies the Surname parameter
 
.PARAMETER UserPrincipalName
    Specifies the UserPrincipalName parameter.
 
.PARAMETER DisplayName
    Specifies the DisplayName parameter.
 
.PARAMETER Name
    Specifies the Name parameter.
 
.PARAMETER PasswordNeverExpires
    Specifies if the Password Never Expires
 
.PARAMETER UserCannotChangePassword
    Specifies if the User Cannot Change Password
 
.PARAMETER PasswordNotRequired
    Specifies if the Password is Not Required
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current domain.
 
.PARAMETER Passthru
    Specifies if you want to see the object created after running the command.
 
.EXAMPLE
    PS C:\> New-ADSIUser -SamAccountName "fxtest04" -Enabled -AccountPassword (Read-Host -AsSecureString "AccountPassword") -Passthru
 
.EXAMPLE
    PS C:\> New-ADSIUser -SamAccountName "fxtest04" -Enabled -AccountPassword (Read-Host -AsSecureString "AccountPassword") -Passthru
 
    # You can test the credential using the following function
    Test-ADSICredential -AccountName "fxtest04" -AccountPassword (Read-Host -AsSecureString "AccountPassword")
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/System.DirectoryServices.AccountManagement.UserPrincipal(v=vs.110).aspx
#>


    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]$SamAccountName,

        [System.Security.SecureString]$AccountPassword,

        [switch]$Enabled = $false,

        [String]$GivenName,

        [String]$SurName,

        [String]$UserPrincipalName,

        [String]$DisplayName,

        [String]$Name,

        [Switch]$PasswordNeverExpires = $false,

        [Switch]$UserCannotChangePassword = $false,

        [Switch]$PasswordNotRequired = $false,

        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName,

        [Switch]$Passthru
    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting

    }
    process
    {
        try
        {
            if ($PSCmdlet.ShouldProcess($SamAccountName, "Create User Account"))
            {
                Write-Verbose -message "Build the user object"
                $User = New-Object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal -ArgumentList $context

                Write-Verbose -message "set the properties"
                $User.SamAccountName = $SamAccountName
                $User.Enabled = $Enabled
                $user.PasswordNeverExpires = $PasswordNeverExpires
                $user.UserCannotChangePassword = $UserCannotChangePassword
                $User.PasswordNotRequired = $PasswordNotRequired

                if ($PSBoundParameters['Name'])
                {
                    $User.Name = $Name
                }
                if ($PSBoundParameters['DisplayName'])
                {
                    $User.DisplayName = $DisplayName
                }
                if ($PSBoundParameters['GivenName'])
                {
                    $User.GivenName = $GivenName
                }
                if ($PSBoundParameters['SurName'])
                {
                    $User.SurName = $SurName
                }
                if ($PSBoundParameters['UserPrincipalName'])
                {
                    $User.UserPrincipalName = $UserPrincipalName
                }
                if ($PSBoundParameters['Description'])
                {
                    $user.Description = $Description
                }
                if ($PSBoundParameters['EmployeeId'])
                {
                    $user.EmployeeId = $EmployeeId
                }
                if ($PSBoundParameters['HomeDirectory'])
                {
                    $user.HomeDirectory = $HomeDirectory
                }
                if ($PSBoundParameters['HomeDrive'])
                {
                    $user.HomeDrive = $HomeDrive
                }
                if ($PSBoundParameters['MiddleName'])
                {
                    $user.MiddleName = $MiddleName
                }
                if ($PSBoundParameters['VoiceTelephoneNumber'])
                {
                    $user.VoiceTelephoneNumber
                }
                if ($PSBoundParameters['AccountPassword'])
                {
                    $User.SetPassword((New-Object -TypeName PSCredential -ArgumentList "user", $AccountPassword).GetNetworkCredential().Password)
                }

                Write-Verbose -message "Create the Account in Active Directory"
                $User.Save($Context)
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
            break
        }
    }
    end
    {
        if ($PSBoundParameters['Passthru'])
        {
            $ContextSplatting.Remove("ContextType")
            Get-ADSIUser -Identity $SamAccountName @ContextSplatting
        }
    }
}
function Remove-ADSIComputer
{
    <#
.SYNOPSIS
    Function to Remove a Computer Account
 
.DESCRIPTION
    Function to Remove a Computer Account
 
.PARAMETER Identity
    Specifies the Identity of the Computer.
 
    You can provide one of the following:
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain.
    By default it will use the current domain.
 
.PARAMETER Recursive
    Specifies that any child object should be deleted as well
    Typically you would use this parameter if you get the error "The directory service can perform the requested operation only on a leaf object"
    when you try to delete the object without the -recursive param
 
.EXAMPLE
    Remove-ADSIComputer -identity TESTSERVER01
 
    This command will Remove the account TESTSERVER01
 
.EXAMPLE
    Remove-ADSIComputer -identity TESTSERVER01 -recursive
 
    This command will Remove the account TESTSERVER01 and all the child leaf
 
.EXAMPLE
    Remove-ADSIComputer -identity TESTSERVER01 -whatif
 
    This command will emulate removing the account TESTSERVER01
 
.EXAMPLE
    Remove-ADSIComputer -identity TESTSERVER01 -credential (Get-Credential)
 
    This command will Remove the account TESTSERVER01 using the alternative credential specified
 
.EXAMPLE
    Remove-ADSIComputer -identity TESTSERVER01 -credential (Get-Credential) -domain LazyWinAdmin.local
 
    This command will Remove the account TESTSERVER01 using the alternative credential specified in the domain lazywinadmin.local
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.computerprincipal(v=vs.110).aspx
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        $Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName,

        [Switch]$Recursive
    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

    }
    process
    {
        try
        {
            # Not Recursive
            if (-not $PSBoundParameters['Recursive'])
            {
                if ($pscmdlet.ShouldProcess("$Identity", "Remove Account"))
                {
                    $Account = Get-ADSIComputer -Identity $Identity @ContextSplatting
                    $Account.delete()
                }
            }

            # Recursive (if the computer is the parent of one leaf or more)
            if ($PSBoundParameters['Recursive'])
            {
                if ($pscmdlet.ShouldProcess("$Identity", "Remove Account and any child objects"))
                {
                    $Account = Get-ADSIComputer -Identity $Identity @ContextSplatting
                    $Account.GetUnderlyingObject().deletetree()
                }
            }

        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Remove-ADSIGroup
{
    <#
.SYNOPSIS
    function to remove a group
 
.DESCRIPTION
    function to remove a group
 
.PARAMETER Identity
    Specifies the Identity
 
    You can provide one of the following properties
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
        UserPrincipalName
 
    Those properties come from the following enumeration:
        System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current domain.
 
.EXAMPLE
    Remove-ADSIGroup FXTESTGROUP
 
.EXAMPLE
    Remove-ADSIGroup FXTESTGROUP -whatif
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        $Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName)

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{
            Contexttype = "Domain"
        }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

    }
    process
    {
        try
        {
            if ($pscmdlet.ShouldProcess("$Identity", "Delete Account"))
            {
                (Get-ADSIGroup -Identity $Identity @ContextSplatting).delete()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Remove-ADSIGroupMember
{
<#
.SYNOPSIS
    Function to Remove a group member
 
.DESCRIPTION
    Function to Remove a group member
 
.PARAMETER Identity
    Specifies the Identity of the group
 
    You can provide one of the following properties
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
        UserPrincipalName
 
    Those properties come from the following enumeration:
        System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Member
    Specifies the member account.
    Performing an Ambiguous Name Resolution LDAP query to find the account.
    http://social.technet.microsoft.com/wiki/contents/articles/22653.active-directory-ambiguous-name-resolution.aspx
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current domain.
 
.EXAMPLE
    Remove-ADSIGroupMember -Identity TestADSIGroup -Member 'UserTestAccount1'
 
    Removing the User account 'UserTestAccount1' to the group 'TestADSIGroup'
 
.EXAMPLE
    Remove-ADSIGroupMember -Identity TestADSIGroup -Member 'GroupTestAccount1'
 
    Removing the Group account 'GroupTestAccount1' to the group 'TestADSIGroup'
 
.EXAMPLE
    Remove-ADSIGroupMember -Identity TestADSIGroup -Member 'ComputerTestAccount1'
 
    Removing the Computer account 'ComputerTestAccount1' to the group 'TestADSIGroup'
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true)]
        $Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [System.String]$DomainName,

        [System.String]$Member
    )

    begin
    {
        $FunctionName = (Get-Variable -Name MyInvocation -ValueOnly -Scope 0).MyCommand
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        Write-Verbose -Message "[$FunctionName] Building context parameters"
        # Create Context splatting
        $ContextSplatting = @{
            Contexttype = "Domain"
        }

        if ($PSBoundParameters['Credential'])
        {
            Write-Verbose -Message "[$FunctionName] Append Credential to context parameters for username '$($credential.UserName)'"
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            Write-Verbose -Message "[$FunctionName] Append DomainName to context parameters '$DomainName'"
            $ContextSplatting.DomainName = $DomainName
        }

        Write-Verbose -Message "[$FunctionName] Creating context object"
        $Context = New-ADSIPrincipalContext @ContextSplatting
    }
    process
    {
        try
        {
            # Resolving member
            # Directory Entry object
            Write-Verbose -Message "[$FunctionName] Building DirectoryEntry parameters"
            $DirectoryEntryParams = $ContextSplatting
            $DirectoryEntryParams.remove('ContextType')
            Write-Verbose -Message "[$FunctionName] Creating DirectoryEntry object"
            $DirectoryEntry = New-ADSIDirectoryEntry @DirectoryEntryParams

            # Principal Searcher
            Write-Verbose -Message "[$FunctionName] Creating DirectorySearcher object"
            $DirectorySearcher = new-object -TypeName System.DirectoryServices.DirectorySearcher
            $DirectorySearcher.SearchRoot = $DirectoryEntry

            # Adding an Ambiguous Name Resolution LDAP Filter
            $DirectorySearcher.Filter = "(anr=$member)"

            # Retrieve a single object
            Write-Verbose -Message "[$FunctionName] Querying Directory Entry"
            $Account = $DirectorySearcher.FindOne().GetDirectoryEntry()

            if ($Account)
            {
                switch ($Account.SchemaClassName)
                {
                    'user'
                    {
                        $member = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($Context, $Account.distinguishedname)
                    }
                    'group'
                    {
                        $member = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($Context, $Account.distinguishedname)
                    }
                    'computer'
                    {
                        $member = [System.DirectoryServices.AccountManagement.ComputerPrincipal]::FindByIdentity($Context, $Account.distinguishedname)
                    }
                }
            }


            if ($pscmdlet.ShouldProcess("$Identity", "Remove Account member $member"))
            {
                Write-Verbose -Message "[$FunctionName] Retrieving Group '$Identity'"
                $group = (Get-ADSIGroup -Identity $Identity @ContextSplatting)

                Write-Verbose -Message "[$FunctionName] Removing member '$member'"
                if($group.members.remove($Member) -eq $false){
                    Write-Verbose -Message "[$FunctionName] Account '$Member' is not a member of Group '$Identity'"
                    #False = Not Part of the Group / True = removed GroupMembership
                }else{
                    Write-Verbose -Message "[$FunctionName] Saving changes"
                    $group.Save()
                }
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
Function Remove-ADSISite
{
    <#
.SYNOPSIS
    function to remove a Site
 
.DESCRIPTION
    function to remove a Site
 
.PARAMETER SiteName
    Specifies the Site Name
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER ForestName
    Specifies the alternative Forest where the user should be created
    By default it will use the current Forest.
 
.EXAMPLE
    Remove-ADSISite -SiteName WOW01
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        [String]$SiteName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$ForestName

    )
    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{}

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['ForestName'])
        {
            $ContextSplatting.ForestName = $ForestName
        }
    }
    process
    {
        try
        {
            if ($PSCmdlet.ShouldProcess($SiteName, "Delete"))
            {
                # Delete Site
                (Get-ADSISite -Name $SiteName @ContextSplatting).Delete()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
            break
        }
    }
}

function Remove-ADSISiteSubnet
{
    <#
.SYNOPSIS
    function to remove a Subnet
 
.DESCRIPTION
    function to remove a Subnet
 
.PARAMETER SubnetName
    Specifies the Subnet Name
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER ForestName
    Specifies the alternative Forest where the user should be created
    By default it will use the current Forest.
 
.EXAMPLE
    Remove-ADSISiteSubnet -SubnetName '192.168.8.0/24'
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]$SubnetName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$ForestName
    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['ForestName'])
        {
            $ContextSplatting.ForestName = $ForestName
        }
    }
    process
    {
        try
        {
            if ($PSCmdlet.ShouldProcess($SubnetName, "Remove Subnet"))
            {
                (Get-ADSISiteSubnet -SubnetName $SubnetName @ContextSplatting).Delete()
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
            break
        }
    }
    end
    {
    }
}




function Remove-ADSIUser
{
    <#
.SYNOPSIS
    Function to delete a User Account
 
.DESCRIPTION
    Function to delete a User Account
 
.PARAMETER Identity
    Specifies the Identity of the User.
 
    You can provide one of the following properties
        DistinguishedName
        Guid
        Name
        SamAccountName
        Sid
        UserPrincipalName
 
    Those properties come from the following enumeration:
        System.DirectoryServices.AccountManagement.IdentityType
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain.
    By default it will use the current domain.
 
.PARAMETER Recursive
    Specifies that any child object should be deleted as well
    Typically you would use this parameter if you get the error "The directory service can perform the requested operation only on a leaf object"
    when you try to delete the object without the -recursive param
 
    Typically used when you have Exchange/ActiveSync in your domain, some users happens to have sub child items.
 
.EXAMPLE
    Remove-ADSIUser -identity fxtest02
 
    This command will Remove the account fxtest02 from the current domain
 
.EXAMPLE
    Remove-ADSIUser -identity fxtest02 -whatif
 
    This command will emulate removing the account fxtest02
 
.EXAMPLE
    Remove-ADSIUser -identity fxtest02 -credential (Get-Credential)
 
    This command will Remove the account fxtest02 using the alternative credential specified
 
.EXAMPLE
    Remove-ADSIUser -identity fxtest02 -credential (Get-Credential) -domain LazyWinAdmin.local
 
    This command will Remove the account fxtest02 using the alternative credential specified in the domain lazywinadmin.local
 
.EXAMPLE
    Remove-ADSIUser -identity fxtest02 -recursive
 
    This command will Remove the account fxtest02 and all the child objects.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
 
.LINK
    https://msdn.microsoft.com/en-us/library/System.DirectoryServices.AccountManagement.UserPrincipal(v=vs.110).aspx
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        $Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName,

        [Switch]$Recursive
    )

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

    }
    process
    {
        try
        {
            # Not Recursive
            if (-not $PSBoundParameters['Recursive'])
            {
                if ($pscmdlet.ShouldProcess("$Identity", "Remove Account"))
                {
                    (Get-ADSIUser -Identity $Identity @ContextSplatting).Delete()
                }
            }

            # Recursive
            if ($PSBoundParameters['Recursive'])
            {
                if ($pscmdlet.ShouldProcess("$Identity", "Remove Account and any child objects"))
                {
                    (Get-ADSIUser -Identity $Identity @ContextSplatting).GetUnderlyingObject().deletetree()
                }
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Reset-ADSIUserPasswordAge
{
    <#
.SYNOPSIS
    Function to reset a User's password age to zero
 
.DESCRIPTION
    Function to reset a User's password age to zero
 
.PARAMETER Identity
    Specifies the Identity
 
.PARAMETER Credential
    Specifies alternative credential
 
.EXAMPLE
    Reset-ADSIUserPasswordAge -Identity 'testaccount'
 
.EXAMPLE
    Reset-ADSIUserPasswordAge -Identity 'testaccount' -Credential (Get-Credential)
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )
    process
    {
        if ($pscmdlet.ShouldProcess("$Identity", "Change Account Password"))
        {
            (Get-ADSIUser @PSBoundParameters).RefreshExpiredPassword()
        }
    }
}
Function Search-ADSIAccount
{
    <#
.SYNOPSIS
    Function to retrieve AD accounts from differents filters
 
.DESCRIPTION
    Function to retrieve AD accounts from differents filters
 
.PARAMETER Credential
    Specifies alternative credential
 
.PARAMETER DomainName
    Specifies the Domain Name where the function should look
 
.PARAMETER DomainDistinguishedName
    Specifies the DistinguishedName of the Domain to query
 
.PARAMETER SizeLimit
    Specify the number of item(s) to output (1 to 1000)
    Use NoResultLimit for more than 1000 objects
 
.PARAMETER NoResultLimit
    Remove the SizeLimit of 1000
    Warning : can take time! it depends on the number of objects in your domain
    NoResultLimit parameter override SizeLimit parameter
 
.PARAMETER Users
    Users accounts only
 
.PARAMETER Computers
    Computer accounts only
 
.PARAMETER AccountNeverLogged
    Accounts that never logged on
 
.PARAMETER ChangePassword
    Specify that you want to see the account that need to change password
 
.PARAMETER PasswordNeverExpire
    Accounts where the password never expire
 
.PARAMETER AccountDisabled
    Accounts disabled
 
.PARAMETER PasswordNeverExpires
    Accounts where the password never expire
 
.PARAMETER AccountExpired
    Expired Accounts
 
.PARAMETER AccountExpiring
    Accounts Expiring (By default 30 days, see -Days parameter)
 
.PARAMETER AccountInactive
    Inactive accounts (By default 30 days, see -Days parameter)
 
.PARAMETER Days
    Used with inactive and Expiring parameters. 30 days by default
 
.PARAMETER PasswordExpired
    Expired Accounts
 
.PARAMETER AccountNeverExpire
    Accounts where the password never expire
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountNeverLogged
 
    Get all not disabled user accounts that have never logged
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountNeverLogged -PasswordNeverExpire
 
    Get all not disabled user accounts that have never logged in and whose password never expires
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountNeverLogged -ChangePassword
 
    Get all not disabled user accounts that have never logged in and need to change their password
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountDisabled
 
    Get all disabled user accounts
 
.EXAMPLE
    Search-ADSIAccount -Users -PasswordNeverExpires
 
    Get all not disabled user accounts whose password never expire
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountExpired
 
    Get all not disabled user accounts that have expired
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountExpiring -Days 10
 
    Get all not disabled user accounts that will expire within the next 10 days.
 
    Valide Range : 1 to 365
    Default expiration : 30 Days
 
.EXAMPLE
    Search-ADSIAccount -Users -PasswordExpired
 
    Get all not disabled user accounts whose password has expired and are not disabled
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountNeverExpire
 
    Get all not disabled user accounts that never expire
 
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountDisabled -SizeLimit 10
 
    Get only 10 user accounts disabled
 
    Default AD limit :1000
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountDisabled -NoResultLimit
 
    Remove the default AD limit of 1000 objects returned, in example the search is about disabled user accounts
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountDisabled -Credential (Get-Credential)
 
    Use a different credential to perform the search, in example the search is about disabled user accounts
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountDisabled -DomainName "CONTOSO.local"
 
    Use a different domain name to perform the search, in example the search is about disabled user accounts
 
.EXAMPLE
    Search-ADSIAccount -Users -AccountDisabled -DomainDistinguishedName 'DC=CONTOSO,DC=local'
 
    Use a different domain distinguished name to perform the search, in example the search is about disabled user accounts
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    param
    (
        [Parameter(ParameterSetName = 'uAccountInactive', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountNeverExpire', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uPasswordExpired', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountExpiring', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountExpired', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uPasswordNeverExpires', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountDisabled', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountNeverLoggedChangePassword', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountNeverLoggedPasswordNeverExpire', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountNeverLogged', Mandatory = $true)]
        [switch]$Users,

        [Parameter(ParameterSetName = 'cAccountInactive', Mandatory = $true)]
        [Parameter(ParameterSetName = 'cAccountNeverExpire', Mandatory = $true)]
        [Parameter(ParameterSetName = 'cPasswordExpired', Mandatory = $true)]
        [Parameter(ParameterSetName = 'cAccountExpiring', Mandatory = $true)]
        [Parameter(ParameterSetName = 'cAccountExpired', Mandatory = $true)]
        [Parameter(ParameterSetName = 'cPasswordNeverExpires', Mandatory = $true)]
        [Parameter(ParameterSetName = 'cAccountDisabled', Mandatory = $true)]
        [Parameter(ParameterSetName = 'cAccountNeverLogged', Mandatory = $true)]
        [switch]$Computers,

        [Parameter(ParameterSetName = 'cAccountNeverLogged', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountNeverLoggedChangePassword', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountNeverLoggedPasswordNeverExpire', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountNeverLogged', Mandatory = $true)]
        [switch]$AccountNeverLogged,

        [Parameter(ParameterSetName = 'uAccountNeverLoggedChangePassword', Mandatory = $true)]
        [switch]$ChangePassword,

        [Parameter(ParameterSetName = 'uAccountNeverLoggedPasswordNeverExpire', Mandatory = $true)]
        [switch]$PasswordNeverExpire,

        [Parameter(ParameterSetName = 'cAccountDisabled', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountDisabled', Mandatory = $true)]
        [switch]$AccountDisabled,

        [Parameter(ParameterSetName = 'cPasswordNeverExpires', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uPasswordNeverExpires', Mandatory = $true)]
        [switch]$PasswordNeverExpires,

        [Parameter(ParameterSetName = 'cAccountExpired', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountExpired', Mandatory = $true)]
        [switch]$AccountExpired,

        [Parameter(ParameterSetName = 'cAccountExpiring', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountExpiring', Mandatory = $true)]
        [switch]$AccountExpiring,

        [Parameter(ParameterSetName = 'cAccountInactive', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountInactive', Mandatory = $true)]
        [switch]$AccountInactive,

        [Parameter(ParameterSetName = 'cAccountInactive')]
        [Parameter(ParameterSetName = 'uAccountInactive')]
        [Parameter(ParameterSetName = 'cAccountExpiring')]
        [Parameter(ParameterSetName = 'uAccountExpiring')]
        [ValidateRange(1, 365)]
        [int]$Days = 30,

        [Parameter(ParameterSetName = 'cPasswordExpired', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uPasswordExpired', Mandatory = $true)]
        [switch]$PasswordExpired,

        [Parameter(ParameterSetName = 'cAccountNeverExpire', Mandatory = $true)]
        [Parameter(ParameterSetName = 'uAccountNeverExpire', Mandatory = $true)]
        [switch]$AccountNeverExpire,

        [Alias('ResultLimit', 'Limit')]
        [ValidateRange(1, 1000)]
        [int]$SizeLimit = 100,

        [Alias('RunAs')]
        [pscredential]
        [System.Management.Automation.Credential()]
        $Credential = [pscredential]::Empty,

        [Alias('Domain')]
        [ValidateScript( { if ($_ -match '^(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,6}$')
                {
                    $true
                }
                else
                {
                    throw "DomainName must be FQDN. Ex: contoso.locale - Hostname like '$_' is not working"
                } })]
        [String]$DomainName,

        [Alias('DomainDN', 'SearchRoot', 'SearchBase')]
        [String]$DomainDistinguishedName = $(([adsisearcher]'').Searchroot.path),

        [Switch]$NoResultLimit


    )

    $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop'


    if ($PSBoundParameters['DomainName'])
    {
        $DomainDistinguishedName = "LDAP://DC=$($DomainName.replace('.', ',DC='))"

        Write-Verbose -Message "Current Domain: $DomainDistinguishedName"

    }
    elseif ($PSBoundParameters['DomainDistinguishedName'])
    {
        if ($DomainDistinguishedName -notlike 'LDAP://*')
        {
            $DomainDistinguishedName = "LDAP://$DomainDistinguishedName"
        }
        Write-Verbose -Message "Different Domain specified: $DomainDistinguishedName"

    }

    $Search.SearchRoot = $DomainDistinguishedName

    if ($PSBoundParameters['Credential'])
    {
        $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password)
        $Search.SearchRoot = $Cred
    }


    write-verbose -Message ('ParameterSetName : {0}' -f $PSCmdlet.ParameterSetName)

    if ($PSBoundParameters['Computers'])
    {
        $type = 'computer'
    }
    else
    {
        $type = 'user'
    }

    switch -wildcard ($PSCmdlet.ParameterSetName)
    {

        '?AccountNeverLogged'
        {
            #Never logged and must change password and not disabled
            $Search.Filter = "(&(objectCategory=$type)(objectClass=$type)(lastLogon=0)(!lastLogonTimestamp=*)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
            break

        }

        '?AccountNeverLoggedChangePassword'
        {
            #Never logged and must change password and not disabled
            $Search.Filter = "(&(objectCategory=$type)(objectClass=$type)(pwdLastSet=0)(lastLogon=0)(!lastlogontime‌​stamp=*)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
            break

        }

        '?AccountNeverLoggedPasswordNeverExpire'
        {

            #Never loged and password never expire and not disabled
            $Search.Filter = "(&(objectCategory=$type)(objectClass=$type)(userAccountControl:1.2.840.113556.1.4.803:=65536)(lastLogon=0)(!lastlogontime‌​stamp=*)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
            break

        }

        '?AccountDisabled'
        {
            #Disabled
            $Search.Filter = "(&(objectCategory=$type)(objectClass=$type)(userAccountControl:1.2.840.113556.1.4.803:=2))"
            break

        }

        '?PasswordNeverExpires'
        {
            #Password never expire and not disabled
            $Search.Filter = "(&(objectCategory=$type)(objectClass=$type)(userAccountControl:1.2.840.113556.1.4.803:=65536)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
            break

        }

        '?AccountExpired'
        {
            #Account expired and not disabled
            $date = (Get-Date).ToFileTime()
            $Search.Filter = "(&(objectCategory=$type)(objectClass=$type)(accountExpires<=$date)(!accountExpires=0)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
            break

        }

        '?AccountExpiring'
        {
            #Account expiring in x days and not disabled
            #Attention : Account set to expire on 22/05/2017, attribute is set to 23/05/2017 00:00 or 22:00

            write-verbose -Message "Show accounts expiring between now and $Days day(s)"

            $Now = Get-Date
            $start = $Now.ToFileTime()
            $end = ($Now.Adddays($Days)).ToFileTime()

            $Search.Filter = "(&(objectCategory=$type)(objectClass=$type)(accountExpires>=$start)(accountExpires<=$end)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
            break

        }

        '?AccountInactive'
        {
            #Account Inactive and not disabled
            #Returns all accounts that have been inactive for more than X days.

            write-verbose -Message "Show inactive accounts for more than $Days day(s)"

            $Now = Get-Date
            $start = ($Now.Adddays( - $Days)).ToFileTime()

            $Search.Filter = "(&(objectCategory=$type)(objectClass=$type)(lastLogonTimestamp<=$start)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
            break

        }

        '?PasswordExpired'
        {
            #PASSWORD Expired and account not disabled
            #Rule : User must be connected at least one time to be in the result

            $Search.Filter = "(&(objectCategory=$type)(objectClass=$type)(pwdLastSet=0)(!(userAccountControl:1.2.840.113556.1.4.803:=65536)(!userAccountControl:1.2.840.113556.1.4.803:=2))(|(lastLogon>=1)(lastLogonTimestamp>=1)))"
            break

        }

        '?AccountNeverExpire'
        {
            #Account never expire and account not disabled
            $Search.Filter = "(&(objectCategory=$type)(objectClass=$type)(accountExpires=0)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
            break

        }

        DEFAULT
        {
            write-verbose -Message 'unknown ParameterSetName'
            return

        }
    }


    if (-not$PSBoundParameters['NoResultLimit'])
    {
        Write-warning -Message "Result is limited to $SizeLimit entries, specify a specific number (1-1000) on the parameter SizeLimit or use -NoResultLimit switch to remove the limit"

        $Search.SizeLimit = $SizeLimit
    }
    else
    {
        Write-Verbose -Message 'Use NoResultLimit switch, all objects will be returned. no limit'

        $Search.PageSize = 10000
    }

    $Search.FindAll()

}
function Set-ADSIUser
{
<#
.SYNOPSIS
    This function modifies an account identified by its display name, sam account name or distinguished name.
 
.DESCRIPTION
    This function modifies an account identified by its display name, sam account name or distinguished name.
 
.PARAMETER Identity
    Specify the Identity of the accounts to modify.
 
    The Identity can either be (in order of resolution attempt):
        A SAM account name
        An object SID
        A distinguished name
 
.PARAMETER Country
    Specify the country name. This parameter sets the co property of a user.
 
.PARAMETER Description
    Specify the description. This parameter sets the description property of a user.
 
.PARAMETER DisplayName
    Specify the display name. This parameter sets the DisplayName property of a user.
 
.PARAMETER Location
    Specify the location name. This parameter sets the l property of a user.
 
.PARAMETER Mail
    Specify the mail address. This parameter sets the mail property of a user.
 
.PARAMETER Manager
    Specify the manager. This parameter sets the manager property of a user.
    The manager must be specified as a SAM account name.
 
.PARAMETER PostalCode
    Specify the postal code name. This parameter sets the postalCode property of a user.
 
.PARAMETER SamAccountName
    Specify the Sam account name. This parameter sets the sAMAccountName property of a user.
 
.PARAMETER UserPrincipalName
    Specify the UserPrincipalName. This parameter sets the UserPrincipalName property of a user.
 
.PARAMETER HomeDrive
    Specify the HomeDrive. This parameter sets the HomeDrive property of a user. This must be a single letter (aka 'U'). This is the drive you want the HomeDirectory to show up as
 
.PARAMETER HomeDirectory
    Specify the HomeDirectory. This parameter sets the HomeDirectory property of a user. This is the directory you want to map to.
 
.PARAMETER TelephoneNumber
    Specify the Telephone number
 
.PARAMETER DomainName
    Specify the Domain Distinguished name
 
.PARAMETER Credential
    Specify alternative Credential
 
.EXAMPLE
    Set-ADSIUSer -Identity micky -UserPrincipalName micky@contoso.com -confirm:$false -SamAccountName mickyballadelli
 
    Changes the UPN and SAM account name of an account without confirmation popup
 
.EXAMPLE
    Set-ADSIUSer -identity micky -Country France
 
    Changes the Country value of the account micky
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High', DefaultParameterSetName = 'Default')]
    param (
        [Parameter(Mandatory = $true)]
        [String]$Identity,

        [Parameter(Mandatory = $false)]
        [string]$Country,

        [Parameter(Mandatory = $false)]
        [string]$Description,

        [Parameter(Mandatory = $false)]
        [string]$DisplayName,

        [Parameter(Mandatory = $false)]
        [string]$Location,

        [Parameter(Mandatory = $false)]
        [string]$Mail,

        [Parameter(Mandatory = $false)]
        [string]$Manager,

        [Parameter(Mandatory = $false)]
        [string]$PostalCode,

        [Parameter(Mandatory = $false)]
        [String]$SamAccountName,

        [Parameter(Mandatory = $false)]
        [String]$TelephoneNumber,

        [Parameter(Mandatory = $false)]
        [string]$UserPrincipalName,

        [Parameter(Mandatory = $false)]         
        [Parameter(Mandatory = $true, ParameterSetName = "HomeDriveHomeDirectory")]
        [string]$HomeDrive,

        [Parameter(Mandatory = $false)] 
        [Parameter(Mandatory = $true, ParameterSetName = "HomeDriveHomeDirectory")]
        [string]$HomeDirectory,

        [Alias("Domain", "DomainDN")]
        [String]$DomainName = $(([adsisearcher]"").Searchroot.path),

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )
    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting
    }
    process
    {
        try
        {
            $DirectoryEntryParams = $ContextSplatting
            $DirectoryEntryParams.remove('ContextType')
            $DirectoryEntry = New-ADSIDirectoryEntry @DirectoryEntryParams

            # Principal Searcher
            $Search = new-object -TypeName System.DirectoryServices.DirectorySearcher
            $Search.SizeLimit = 2
            $Search.SearchRoot = $DirectoryEntry

            # Resolve the Object
            $Search.filter = "(&(objectCategory=person)(objectClass=User)(samaccountname=$Identity))"
            $user = $Search.FindAll()
            if ($user.Count -eq 0)
            {
                $Search.filter = "(&(objectCategory=person)(objectClass=User)(objectsid=$Identity))"
                $user = $Search.FindAll()
            }
            if ($user.Count -eq 0)
            {
                $Search.filter = "(&(objectCategory=person)(objectClass=User)(distinguishedname=$Identity))"
                $user = $Search.FindAll()
            }
            if ($user.Count -eq 0)
            {
                $Search.filter = "(&(objectCategory=person)(objectClass=User)(UserPrincipalName=$Identity))"
                $user = $Search.FindAll()
            }

            if ($user.Count -eq 1)
            {
                $Account = $user.Properties.samaccountname -as [string]
                $adspath = $($user.Properties.adspath -as [string]) -as [ADSI]

                # Country
                if ($Country -ne '')
                {
                    Write-Verbose -Message "[$($Account)] Setting Country value to : $Country"

                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set Country of account $account to $Country"))
                    {
                        if ($PSBoundParameters.ContainsKey('WhatIf'))
                        {
                            Write-Verbose -Message "WhatIf: Setting Country of account $account to $Country" -Verbose:$true
                        }
                        else
                        {
                            $adspath.Put("co", $Country)
                            $adspath.SetInfo()
                        }
                    }
                }

                # Description
                if ($Description -ne '')
                {
                    Write-Verbose -Message "[$($Account)] Setting Description value to : $Description"

                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set Description of account $account to $Description"))
                    {
                        if ($PSBoundParameters.ContainsKey('WhatIf'))
                        {
                            Write-Verbose -Message "WhatIf: Setting Description of account $account to $Description" -Verbose:$true
                        }
                        else
                        {
                            $Adspath.Put("description", $Description)
                            $Adspath.SetInfo()
                        }
                    }
                }

                # DisplayName
                if ($DisplayName -ne '')
                {
                    Write-Verbose -Message "[$($Account)] Setting Country value to : $DisplayName"

                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set DisplayName of account $account to $DisplayName"))
                    {
                        if ($PSBoundParameters.ContainsKey('WhatIf'))
                        {
                            Write-Verbose -Message "WhatIf: Setting DisplayName of account $account to $DisplayName" -Verbose:$true
                        }
                        else
                        {
                            $Adspath.Put("displayName", $DisplayName)
                            $Adspath.SetInfo()
                        }
                    }
                }

                # Location
                if ($Location -ne '')
                {
                    Write-Verbose -Message "[$($Account)] Setting Location value to : $Location"

                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set Location of account $account to $Location"))
                    {
                        if ($PSBoundParameters.ContainsKey('WhatIf'))
                        {
                            Write-Verbose -Message "WhatIf: Setting Location of account $account to $Location" -Verbose:$true
                        }
                        else
                        {
                            $Adspath.Put("l", $Location)
                            $Adspath.SetInfo()
                        }
                    }
                }

                # Mail
                if ($Mail -ne '')
                {
                    Write-Verbose -Message "[$($Account)] Setting Mail value to : $Mail"

                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set Mail of account $account to $Mail"))
                    {
                        if ($PSBoundParameters.ContainsKey('WhatIf'))
                        {
                            Write-Verbose -Message "WhatIf: Setting Mail of account $account to $Mail" -Verbose:$true
                        }
                        else
                        {
                            $Adspath.Put("mail", $Mail)
                            $Adspath.SetInfo()
                        }
                    }
                }

                # Manager
                if ($Manager -ne '')
                {
                    Write-Verbose -Message "[$($Account)] Setting Manager value to : $Manager"

                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set Manager of account $account to $Manager"))
                    {
                        if ($PSBoundParameters.ContainsKey('WhatIf'))
                        {
                            Write-Verbose -Message "WhatIf: Setting Manager of account $account to $Manager" -Verbose:$true
                        }
                        else
                        {
                            $Search.filter = "(&(objectCategory=person)(objectClass=User)(samaccountname=$Manager))"
                            $user = $Search.FindOne()

                            $Adspath.Put("manager", ($user.properties.distinguishedname -as [string]))
                            $Adspath.SetInfo()
                        }
                    }
                }

                # PostalCode
                if ($PostalCode -ne '')
                {
                    Write-Verbose -Message "[$($Account)] Setting Location value to : $PostalCode"

                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set PostalCode of account $account to $PostalCode"))
                    {
                        if ($PSBoundParameters.ContainsKey('WhatIf'))
                        {
                            Write-Verbose -Message "WhatIf: Setting Location of account $account to $PostalCode" -Verbose:$true
                        }
                        else
                        {
                            $Adspath.Put("postalCode", $PostalCode)
                            $Adspath.SetInfo()
                        }
                    }
                }

                # TelephoneNumber
                if ($TelephoneNumber -ne '')
                {
                    Write-Verbose -Message "[$($Account)] Setting SamAccountName value to : $TelephoneNumber"

                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set TelephoneNumber of account $account to $TelephoneNumber"))
                    {
                        if ($PSBoundParameters.ContainsKey('WhatIf'))
                        {
                            Write-Verbose -Message "WhatIf: Setting TelephoneNumber of account $account to $TelephoneNumber" -Verbose:$true
                        }
                        else
                        {
                            $Adspath.Put("telephoneNumber", $TelephoneNumber)
                            $Adspath.SetInfo()
                        }
                    }
                }
                # SAM Account Name
                if ($SamAccountName -ne '')
                {
                    Write-Verbose -Message "[$($Account)] Setting SamAccountName value to : $SamAccountName"

                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set SamAccountName of account $account to $SamAccountName"))
                    {
                        if ($PSBoundParameters.ContainsKey('WhatIf'))
                        {
                            Write-Verbose -Message "WhatIf: Setting SamAccountName of account $account to $SamAccountName" -Verbose:$true
                        }
                        else
                        {
                            $Adspath.Put("sAMAccountName", $SamAccountName)
                            $Adspath.SetInfo()
                        }
                    }
                }

                # UserPrincipalName
                if ($UserPrincipalName -ne '')
                {
                    Write-Verbose -Message "[$($Account)] Setting UPN value to : $UserPrincipalName"

                    if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set UPN of account $account to $UserPrincipalName"))
                    {
                        if ($PSBoundParameters.ContainsKey('WhatIf'))
                        {
                            Write-Verbose -Message "WhatIf: Setting UPN of account $account to $UserPrincipalName" -Verbose:$true
                        }
                        else
                        {
                            $Adspath.Put("UserPrincipalName", $UserPrincipalName)
                            $Adspath.SetInfo()
                        }
                    }
                }

                # HomeDrive
                if ($HomeDrive -ne '')
                {
                    if($HomeDrive.Length -gt 1) {
         
                        $e = New-Object System.Exception "[Set-AdsiUser] HomeDrive must be a single letter!"
                        throw $e

                    } elseif($HomeDirectory -eq '') {

                        $er = New-Object System.Exception "[Set-AdsiUser] HomeDirectory must be use with HomeDrive! HomeDrive is the drive letter, HomeDirectory is the path."
                        throw $er      
                                       
                    } else {
                        Write-Verbose -Message "[$($Account)] Setting HomeDrive value to : $HomeDrive and HomeDirectory to : $HomeDirectory"

                        if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set HomeDrive of account $account to $HomeDrive and HomeDirectory to $HomeDirectory"))
                        {
                            if ($PSBoundParameters.ContainsKey('WhatIf'))
                            {
                                Write-Verbose -Message "WhatIf: Setting HomeDrive of account $account to $HomeDrive and HomeDirectory to $HomeDirectory" -Verbose:$true
                            }
                            else
                            {
                                try {
                                    $Adspath.Put("homeDrive", $HomeDrive)
                                    $adspath.Put("homeDirectory", $HomeDirectory)
                                    $Adspath.SetInfo()
                                } catch {
                                    throw $_
                                }
                            }
                        }
                    }
                }

            }
            elseif ($user.Count -gt 1)
            {
                Write-Warning -Message "[Set-ADSIUser] Identity $identity is not unique"
            }
            elseif ($Search.FindAll().Count -eq 0)
            {
                Write-Warning -Message "[Set-ADSIUser] Account $identity not found"
            }

        }#try
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }#process
    end
    {
        Write-Verbose -Message "[END] Function Set-ADSIUser End."
    }
}
function Set-ADSIUserPassword
{
    <#
.SYNOPSIS
    Function to change a User's password
 
.DESCRIPTION
    Function to change a User's password
 
.PARAMETER Identity
    Specifies the Identity
 
.PARAMETER Credential
    Specifies alternative credential
 
.PARAMETER AccountPassword
    Specifies the new password.
    The object needs to be a System.Security.SecureString.
    You can use something like that:
        $AccountPassword = (read-host -AsSecureString -Prompt "AccountPassword")
 
.PARAMETER DomainName
    Specifies the DomainName to query
    By default it will take the current domain.
 
.EXAMPLE
    Set-ADSIUserPassword -Identity 'testaccount' -AccountPassword (read-host -AsSecureString -Prompt "AccountPassword")
 
    Change the password of the account 'testaccount' to the specified new password
 
.EXAMPLE
    Set-ADSIUserPassword -Identity 'testaccount' -AccountPassword (read-host -AsSecureString -Prompt "AccountPassword") -Credential (Get-Credential)
 
    Change the password of the account 'testaccount' using the credential specified, to the specified new password
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [parameter(Mandatory = $true)]
        $Identity,

        [parameter(Mandatory = $true)]
        [System.Security.SecureString]$AccountPassword,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName)

    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }
    }
    process
    {
        try
        {
            if ($pscmdlet.ShouldProcess("$Identity", "Change Account Password"))
            {
                (Get-ADSIUser -Identity $Identity @ContextSplatting).SetPassword((New-Object -TypeName PSCredential -ArgumentList "user", $AccountPassword).GetNetworkCredential().Password)
            }
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Start-ADSIReplicationConsistencyCheck
{
<#
.SYNOPSIS
    Start-ADSIReplicationConsistencyCheck starts the knowledge consistency checker on a given DC.
 
.DESCRIPTION
    Start-ADSIReplicationConsistencyCheck connects to an Active Directory Domain Controller and starts the KCC to verify if the replication
    topology needs to be optimized.
 
.PARAMETER ComputerName
    Defines the remote computer to connect to.
 
.PARAMETER Credential
    Defines alternate credentials to use. Use Get-Credential to create proper credentials.
 
.EXAMPLE
    Start-ADSIReplicationConsistencyCheck -ComputerName dc1.ad.local
    Connects to remote domain controller dc1.ad.local using current credentials and starts a KCC check.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param ([Parameter(Mandatory = $true)]
        [string]$ComputerName,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )

    if ($ComputerName)
    {
        if ($Credential)
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName, $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        else
        {
            $context = new-object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList "DirectoryServer", $ComputerName
        }
    }

    if ($context)
    {
        Write-Verbose -Message "Connecting to $ComputerName"
        $dc = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($context)
    }

    if ($PSCmdlet.ShouldProcess($dc, "Check Replication Consistency (KCC Check)"))
    {
        if ($dc)
        {
            $dc.CheckReplicationConsistency()
            Write-Verbose -Message "KCC Check started on $($dc.name)"
        }
    }
}
function Test-ADSICredential
{
<#
.SYNOPSIS
    Function to test credential
 
.DESCRIPTION
    Function to test credential
 
.PARAMETER AccountName
    Specifies the AccountName to check
 
.PARAMETER AccountPassword
    Specifies the AccountName's password
 
.PARAMETER Credential
    Specifies the alternative credential to use.
    By default it will use the current user windows credentials.
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current domain.
 
.EXAMPLE
    PS C:\> $Password = read-host -AsSecureString -Prompt "AccountPassword"
    PS C:\> Test-ADSICredential -AccountName 'Xavier' -AccountPassword $Password
 
.EXAMPLE
    PS C:\> $Password = read-host -AsSecureString -Prompt "AccountPassword"
    PS C:\> New-ADSIUser -SamAccountName "fxtest04" -Enabled -AccountPassword $Password -Passthru
 
    # You can test the credential using the following function
    PS C:\> Test-ADSICredential -AccountName "fxtest04" -AccountPassword $Password
 
.OUTPUTS
    System.Boolean
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [OutputType('System.Boolean')]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [Alias("UserName")]
        [string]$AccountName,

        [Parameter(Mandatory)]
        [System.Security.SecureString]$AccountPassword,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName)
    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ ContextType = "Domain" }

        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }

        $Context = New-ADSIPrincipalContext @ContextSplatting
    }
    process
    {
        try
        {
            Write-Verbose -Message "[Test-ADSICredential][PROCESS] Validating $AccountName Credential against $($Context.ConnectedServer)"
            $Context.ValidateCredentials($AccountName, (New-Object -TypeName PSCredential -ArgumentList "user", $AccountPassword).GetNetworkCredential().Password)
        }
        catch
        {
            $pscmdlet.ThrowTerminatingError($_)
        }
    }
}
function Test-ADSIUserIsGroupMember
{
<#
.SYNOPSIS
    This function will check if a domain user is member of a domain group
 
.DESCRIPTION
    This function will check if a domain user is member of a domain group
 
.PARAMETER GroupSamAccountName
    Specifies the Group to query
 
.PARAMETER UserSamAccountName
    Specifies the user account
 
.EXAMPLE
    Test-ADSIUserIsGroupMember -GroupSamAccountName TestGroup -UserSamAccountName Fxcat
 
    This will return $true or $false depending if the user Fxcat is member of TestGroup
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    param (
        $GroupSamAccountName,

        $UserSamAccountName
    )

    $UserInfo = [ADSI]"$((Get-ADSIUser -SamAccountName $UserSamAccountName).AdsPath)"
    $GroupInfo = [ADSI]"$((Get-ADSIGroup -SamAccountName $GroupSamAccountName).AdsPath)"

    #([ADSI]$GroupInfo.ADsPath).IsMember([ADSI]($UserInfo.AdsPath))
    $GroupInfo.IsMember($UserInfo.ADsPath)

}
function Test-ADSIUserIsLockedOut
{
<#
.SYNOPSIS
    Function to test if a User is LockedOut
 
.DESCRIPTION
    Function to test if a User is LockedOut
 
.PARAMETER Identity
    Specifies the Identity
 
.PARAMETER Credential
    Specifies alternative credential
 
.EXAMPLE
    Test-ADSIUserIsLockedOut -Identity 'testaccount'
 
.EXAMPLE
    Test-ADSIUserIsLockedOut -Identity 'testaccount' -Credential (Get-Credential)
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>


    [CmdletBinding()]
    [OutputType('System.Boolean')]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )
    process
    {
        (Get-ADSIUser @PSBoundParameters).IsAccountLockedOut()
    }
}
function Unlock-ADSIUser
{
<#
.SYNOPSIS
    Function to Unlock a User in Active Directory
 
.DESCRIPTION
    Function to Unlock a User in Active Directory
 
.PARAMETER Identity
    Specifies the Identity
 
.PARAMETER Credential
    Specifies alternative credential
 
.EXAMPLE
    Unlock-ADSIUser -Identity 'testaccount'
 
.EXAMPLE
    Unlock-ADSIUser -Identity 'testaccount' -Credential (Get-Credential)
 
.PARAMETER DomainName
    Specifies the alternative Domain where the user should be created
    By default it will use the current domain.
 
.NOTES
    https://github.com/lazywinadmin/ADSIPS
#>

    [CmdletBinding()]
    param ([Parameter(Mandatory)]
        [string]$Identity,

        [Alias("RunAs")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [String]$DomainName)
    begin
    {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement

        # Create Context splatting
        $ContextSplatting = @{ }
        if ($PSBoundParameters['Credential'])
        {
            $ContextSplatting.Credential = $Credential
        }
        if ($PSBoundParameters['DomainName'])
        {
            $ContextSplatting.DomainName = $DomainName
        }
    }
    process
    {
        (Get-ADSIUser -Identity $Identity @ContextSplatting).UnlockAccount()
    }
}

# Try to add necessary assembly during module import - fixes issue where params rely on types within this assembly
# If this assembly was not loaded prior to running Get-ADSIGroup, for example, you were not able to use that function
# If adding this assembly fails, we still allow the user to import the module, but we show them a helpful warning
TRY
{
    Add-Type -AssemblyName System.DirectoryServices.AccountManagement
}
CATCH
{
    Write-Warning "[AdsiPS] Unable to add assembly 'System.DirectoryServices.AccountManagement'.`r`nPlease manually add this assembly into your session or you may encounter issues! `r`n`r`nRun the following command: 'Add-Type -AssemblyName System.DirectoryServices.AccountManagement'"
}