ActiveDirectory.psm1

#region Test-ActiveDirectoryGroupMemebership
Function Test-ActiveDirectoryGroupMembership
{
    <#
    .SYNOPSIS
 
    Test if a user is a member of a group.
    .DESCRIPTION
 
    Test if a user is member of a security group.
    .PARAMETER User
 
    The username of the user.
    .PARAMETER Group
 
    The name of the group.
    .EXAMPLE
 
    Test-ADNestedGroupMembership myuser mygroup
    #>


    Param
    (
        [Parameter(Mandatory=$True, Position=1, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [Alias("Username", "SamAccountName")]
        [string[]]$User,

        [Parameter(Mandatory=$True,Position=2)]
        [Alias("Groupname")]
        [string]$Group,

        [switch]$Recurse
    )

    Begin {}

    Process
    {
        # Loop through the users
        Foreach($u in $User)
        {
            # Create a custom object
            $obj = New-Object psobject
            $obj | Add-Member -MemberType NoteProperty -Name User -Value $u
            $obj | Add-Member -MemberType NoteProperty -Name Group -Value $Group

            # Test if the user exists
            try
            {
                $aduser = Get-ADUser $u -ErrorAction Stop
            }
            catch
            {
                Write-Error "Could not find user $u"
                continue
            }

            # Test if the group exists
            try
            {
                $adgroup = Get-ADGroup $Group -ErrorAction Stop
            }
            catch
            {
                Throw "Could not find group $Group"
            }

            $obj | Add-Member NoteProperty "User DN" $aduser.DistinguishedName
            $obj | Add-Member NoteProperty "Group DN" $adgroup.DistinguishedName

            if($Recurse)
            {
                # Recursive query
                if(
                    Get-ADUser `
                        -Filter "memberOf -RecursiveMatch '$($adgroup.DistinguishedName)'" `
                        -SearchBase $($aduser.DistinguishedName)
                )
                {
                    $obj | Add-Member -MemberType NoteProperty -Name Member -Value $True
                }
                else
                {
                    $obj | Add-Member -MemberType NoteProperty -Name Member -Value $false
                }
            }
            else
            {
                # Direct member query
                if(
                    Get-ADUser `
                        -Filter {memberof -eq $adGroup.DistinguishedName -and distinguishedname -eq $aduser.DistinguishedName}
                )
                {
                    $obj | Add-Member -MemberType NoteProperty -Name Member -Value $True
                }
                else
                {
                    $obj | Add-Member -MemberType NoteProperty -Name Member -Value $false
                }
            }

            $obj
        }
    }

    End {}
}
#endregion

#region Test-ActiveDirectoryIsDomainAdmin
Function Test-ActiveDirectoryIsDomainAdmin
{
    <#
    .SYNOPSIS
 
    Test if a user has domain administrator privileges.
    .DESCRIPTION
 
    Test if a user is member of the Domain Admins security group including nested groups.
    .PARAMETER Username
 
    The username of the user.
    .EXAMPLE
 
    Test-ADIsDomainAdmin myuser
    This will return true if the user is a member of the Domain Admins group or false if not.
    #>


    Param
    (
        [Parameter(
            Mandatory=$True,
            Position=0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true)
        ]
        [alias("Username")]
        [string[]]$User
    )

    Begin {}

    Process
    {
        Foreach($u in $User)
        {
            # Get the user
            try
            {
                $aduser = Get-ADUser $u -ErrorAction Stop -Properties SamAccountName
            }
            catch
            {
                Write-Error "Could not find user $u."
            }

            Test-ActiveDirectoryGroupMembership -User $aduser.SamAccountName -Group "Domain Admins" -Recurse
        }
    }

    End {}
}
#endregion

#region Get-ActiveDirectoryUserGroupMembershipHistory
Function Get-ActiveDirectoryUserGroupMembershipHistory
{
    <#
    .SYNOPSIS
 
    Get the group membership history of an account
    .DESCRIPTION
 
    Get the addition history of a user regarding security groups. This cmdlet only displays the groups that the user is member of and only the date when the user was originaly added.
    .PARAMETER Username
 
    The username of the user.
    .PARAMETER Group
 
    The name of the group
    .EXAMPLE
 
    Get-ADUserGroupMembershipHistory -Username myuser -Group mygroup
    This will return the memberhip additions of the myuser group to and from the mygroup group.
    #>


    Param
    (
        [Parameter(Mandatory=$True,Position=0)]
        [string]$Username,
        [Parameter(Mandatory=$False,Position=1)]
        [string]$Group
    )

    # Get the user from Active Directory

    try
    {
        $userobj  = Get-ADUser $username -ErrorAction Stop
    }
    catch
    {
        Throw "Could not find user $Username."
    }
    
    $results = 
        Get-ADUser $userobj.DistinguishedName -Properties memberOf |
            Select-Object -ExpandProperty memberOf |
                ForEach-Object {
                    Get-ADReplicationAttributeMetadata $_ -Server localhost -ShowAllLinkedValues | 
                        Where-Object {`
                            $_.AttributeName -eq 'member' -and 
                            $_.AttributeValue -eq $userobj.DistinguishedName} |
                            Select-Object -Property `
                                @{name="OriginallyAdded";Expression={$_.FirstOriginatingCreateTime}}, `
                                @{name="User";Expression={$username}}, `
                                @{name="Group";Expression={(Get-ADObject -Identity $_.object).Name}}, `
                                @{name="UserDN";Expression={$_.AttributeValue}}, `
                                @{name="GroupDN";Expression={$_.Object}} 
                }
    
    # Narrow down th
    if( $group )
    {
        # Get the group
        try
        {
            $adgroup = Get-AdGroup $Group -ErrorAction Stop
        }
        catch
        {
            Throw "Could not find group $group."
        }

        # Filter the results
        $results |
            Where-Object {$_.groupdn -eq $adgroup.distinguishedname}
    }
    else
    {
        $results
    }
}
#endregion

#region Get-ActiveDirectoryAccountLockEvent
Function Get-ActiveDirectoryAccountLockEvent
{
    <#
    .Synopsis
       Get the account lock events.
    .DESCRIPTION
       Get the account lock events from the security event log of the domain controllers.
    .EXAMPLE
        Get-ActiveDirectoryAccountLockEvent -StartTime $start
        Account Timestamp Domain Controller Caller Computer
        ------- --------- ----------------- ---------------
        cpolydorou 27/5/2017 7:44:43 μμ DC3 WINDOWS10
        cpolydorou 27/5/2017 7:44:43 μμ DC1 WINDOWS10
    #>


    [CmdletBinding()]
    Param
    (
        # The starting date
        [Parameter(Mandatory=$false, 
                   Position=0)]
        [DateTime]
        $StartTime
    )

    # Form the event log query
    $now = [DateTime]::Now

    # If the start time has not been specified, we'll search for events in last hour
    if(-Not $StartTime)
    {
        $StartTime = $now.AddHours(-1)
    }
    $timespan = $now - $StartTime

    $query = @"
        <QueryList>
            <Query Id="0" Path="Security">
                <Select Path="Security">*[System[(EventID=4740) and TimeCreated[timediff(@SystemTime) &lt;= $($timespan.TotalMilliseconds)]]]</Select>
            </Query>
        </QueryList>
"@


    # Import the ActiveDirectory module
    Write-Verbose "Getting Domain Controllers"
    try
    {
        $domainControllers = Get-ADDomainController -Filter * -ErrorAction Stop | % Name
    }
    catch
    {
        throw "Could not get the Domain Controllers."
    }

    # Get the events from all domain controllers
    Write-Verbose "Getting events"
    $events =

    # Search for the relative events on the domain controllers
    Invoke-Command -ComputerName $domainControllers `
                   -ScriptBlock {
                        Param( $q )

                        try
                        {
                            Get-WinEvent -FilterXml $q -ErrorAction Stop
                        }
                        catch
                        {
                            if($_.Exception.Message.StartsWith("No events were found"))
                            {
                                return $null
                            }
                            else
                            {
                                throw $_.Exception
                            }
                        }
                    } -ArgumentList $query

    # Process the events
    Write-Verbose "Processing events" 
    foreach($e in $events)
    {
        if($e -ne $null)
        {
            # Get the account that was locked out
            $startIndex = $e.Message.IndexOf("Account That Was Locked Out:") + 28
            $account = $e.message.substring($startIndex, $e.message.length - $startIndex)
            $startIndex = $account.IndexOf("Account Name:") + 13
            $account = $account.Substring($startIndex, $account.length - $startIndex)
            $endIndex = $account.IndexOf("`r")
            $account = $account.substring(0, $endIndex).Trim()
    
            # Get the caller computer
            $start = $e.Message.IndexOf("Caller Computer Name") + 21
            $caller = $e.message.substring($start, $e.message.length - $start).Trim()

            # Create a custom object
            $obj = New-Object psobject -Property @{
                                                    "Timestamp" = $e.TimeCreated
                                                    "Account" = $account
                                                    "Caller Computer" = $caller
                                                    "Domain Controller" = $e.PSComputerName
                                                }
            $obj
        }
    }
}
#endregion

#region Exports
Export-ModuleMember -Function Test-ActiveDirectoryGroupMembership
Export-ModuleMember -Function Test-ActiveDirectoryIsDomainAdmin
Export-ModuleMember -Function Get-ActiveDirectoryUserGroupMembershipHistory
Export-ModuleMember -Function Get-ActiveDirectoryAccountLockEvent
#endregion