0 votes

I'm trying to generate a custom report based off the return values of a PowerShell script. I've tried looking over the tutorial docs and SDK, but I can't seem to piece it all together.

The function below accepts a given user's samAccountName, then searches the AD Security logs to find the date/time and source host that's causing lockouts on their account.

Overall, I want to provide a search box for an admin to lookup a given user in Adaxes, then pass that as a parameter value to the function. At the end, I want a report that displays the infromation that's being returned from PowerShell (i.e. Username, Source Host, Date/Time of Event).

function Get-LockoutBlame
{
    [CmdletBinding(DefaultParameterSetName = 'Filtered')]
    [OutputType('System.Diagnostics.Eventing.Reader.EventLogRecord')]
    param
    (
        [Parameter(Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string] $UserName,

        [Parameter(ParameterSetName = 'Filtered')]
        [ValidateNotNullOrEmpty()]
        [int] $PastHours = 1,

        [Parameter(ParameterSetName = 'Unfiltered')]
        [switch] $All,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $ComputerName = ((Get-ADDomainController -Discover -Service PrimaryDC).HostName)
    )

    process
    {
        try
        {
            $filter = '*[System[EventID=4740'

            if (!$all)
            {
                $PastMilliseconds = $PastHours * 3600000
                $filter += " and TimeCreated[timediff(@SystemTime) <= $PastMilliseconds]]"
            }
            else
            {
                $filter += ']'
            }

            if ($username)
            {
                $filter += " and EventData[Data[@Name='TargetUserName']='$UserName']]"
            }
            else
            {
                $filter += ']'
            }

            $Events = Get-WinEvent -ComputerName $ComputerName -LogName Security -FilterXPath $filter
            $Events | Select-Object TimeCreated,
            @{Name = 'User Name'; Expression = { $_.Properties[0].Value } },
            @{Name = 'Source Host'; Expression = { $_.Properties[1].Value } }
        }
        catch
        {
            Throw $_.Exception
        }
    }
}
by (60 points)
0

Hello Joshua,

As far as we understood, you need to get a user lockout information and display it in Adaxes. If so, for us to suggest a solution, please, specify the following:

  • What information should be displayed?
  • What should be displayed if no records were found in the AD Security log?
0

The function returns an object that displays as a table in the console. It should show TimeCreated (DateTime), UserName (string), and the Source Host (string) that triggered the lockout.

TimeCreated         User Name Source Host
-----------         --------- -----------
9/7/2021 8:33:12 AM USERNAME   \\COMPUTERNAME
9/7/2021 6:11:54 AM USERNAME   \\COMPUTERNAME

If no lockouts exist for a given user, it returns an exception message.

Get-WinEvent : No events were found that match the specified selection criteria.

If I can have the report to return "No events were found that match the specified selection criteria," that would work, otherwise, I'm fine with the report returning a blank page.

1 Answer

0 votes
by (11.0k points)

Hello Joshua,

Thank you for specifying. To achieve the desired a custom command executing a PowerShell script can be used. The command must be executed on the user account whose lockout information you need to view. The information will be output into the command execution log. To create the custom command:

  1. Launch Adaxes Administration console.
  2. In the Console Tree, right-click your service node.
  3. In the context menu, navigate to New and click Custom Command. New.custom.command.png
  4. On step 2 of the Create Custom Command wizard, select the User object type. User.object.type.png
  5. Click Next twice.
  6. Click Add an action.
  7. Select Run a program or PowerShell script.
  8. Paste the below script into the Script field.
function Get-LockoutBlame
{
    [CmdletBinding(DefaultParameterSetName = 'Filtered')]
    [OutputType('System.Diagnostics.Eventing.Reader.EventLogRecord')]
    param
    (
        [Parameter(Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string] $UserName,

        [Parameter(ParameterSetName = 'Filtered')]
        [ValidateNotNullOrEmpty()]
        [int] $PastHours = 1,

        [Parameter(ParameterSetName = 'Unfiltered')]
        [switch] $All,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $ComputerName = ((Get-ADDomainController -Discover -Service PrimaryDC).HostName)
    )

    process
    {
        try
        {
            $filter = '*[System[EventID=4740'

            if (!$all)
            {
                $PastMilliseconds = $PastHours * 3600000
                $filter += " and TimeCreated[timediff(@SystemTime) <= $PastMilliseconds]]"
            }
            else
            {
                $filter += ']'
            }

            if ($username)
            {
                $filter += " and EventData[Data[@Name='TargetUserName']='$UserName']]"
            }
            else
            {
                $filter += ']'
            }

            $Events = Get-WinEvent -ComputerName $ComputerName -LogName Security -FilterXPath $filter -ErrorAction Stop
            $Events | Select-Object TimeCreated,
            @{Name = 'User Name'; Expression = { $_.Properties[0].Value } },
            @{Name = 'Source Host'; Expression = { $_.Properties[1].Value } }
        }
        catch
        {
            $Context.LogMessage($_.Exception.Message, "Information")
        }
    }
}

$records = Get-LockoutBlame "%sAMAccountName%"

foreach ($record in $records)
{
    $Context.LogMessage("Time created: " + $record.TimeCreated + " Source host: " + $record."Source Host", "Information")
}
  1. Specify a description for the script and click OK.

  2. Click Next and finish creating the custom command.

0

Thanks for the reply, I didn't think of going the route of using a custom command. I'll try it out to see how it functions compared to utilizing a report.

With regards to the report, I managed to get it working and have it return the information I want. I also discovered that when returning a full AD object, as is the case with DirectorySearcher or BindToObjectByDN, it allows the end user to execute relevant right-click options on the user account, vs. just having the ability to export the report when returning a simple custom object.

image.png

function Get-LockoutBlame
{
    [CmdletBinding(DefaultParameterSetName = 'Filtered')]
    [OutputType('System.Diagnostics.Eventing.Reader.EventLogRecord')]
    param
    (
        [Parameter(Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string] $UserName,

        [Parameter(ParameterSetName = 'Filtered')]
        [ValidateNotNullOrEmpty()]
        [int] $PastHours = 1,

        [Parameter(ParameterSetName = 'Unfiltered')]
        [switch] $All,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $ComputerName = ((Get-ADDomainController -Discover -Service PrimaryDC).HostName)
    )

    process
    {
        try
        {
            $filter = '*[System[EventID=4740'

            if (!$all)
            {
                $PastMilliseconds = $PastHours * 3600000
                $filter += " and TimeCreated[timediff(@SystemTime) <= $PastMilliseconds]]"
            }
            else
            {
                $filter += ']'
            }

            if ($username)
            {
                $filter += " and EventData[Data[@Name='TargetUserName']='$UserName']]"
            }
            else
            {
                $filter += ']'
            }

            $Events = Get-WinEvent -ComputerName $ComputerName -LogName Security -FilterXPath $filter

            $columnLockoutTime = "{6ebb67ec-d4a7-444e-9a64-3e703a87634c}" 
            $columnSourceHost = "{29768609-7d15-4cea-a2f5-40a47fc78442}"



            # Add item to report 
            $Events | ForEach-Object {

                $columnValues = @{ }
                $columnValues.Add($columnLockoutTime, $_.TimeCreated)
                $columnValues.Add($columnSourceHost, $_.Properties[1].Value)

                $Context.Items.Add($ADObj, $columnValues)
            }


        }
        catch
        {
            Throw $_.Exception
        }
    }
}

$ADSearchDN = $Context.DirectorySearcher.AdsPath.DN
$ADObj = $Context.BindToObjectByDN($ADSearchDN) 
$LockedOutUser = $ADObj.Get("SamAccountName")

Get-LockoutBlame -UserName $LockedOutUser

Related questions

0 votes
1 answer

Aiming to go passwordless, this is a must-have

asked Aug 30, 2023 by JM (20 points)
0 votes
1 answer

I've been attempting to run the "Management History" report located in Reports -&gt; All Reports -&gt; Miscellaneous-&gt;Logging based on this script in the repository. ... to using the ADSI Adaxes functionality and I'm not sure where to begin troubleshooting.

asked Aug 15, 2023 by awooten (40 points)
0 votes
1 answer

Hey guys, First time Adaxes user, and let me say, we absolutely love the product! Quick question though.... As a part of our account de-provisioning process, we ... exact same code in a regular powershell window with no problem. Any thoughts? Any alternatives?

asked Sep 6, 2012 by sco.robinso (20 points)
0 votes
1 answer

Hallo, I'm trying to build a function that will have two parameters $Containrer and $Filter $filter - is a LDAP filer that serach some specific objects $Containrer - is a ... is treated by powershell as a hash table, is it possible to workoroud it somhow?

asked Aug 12, 2015 by axmaster (510 points)
0 votes
1 answer

We have some dynamic groups with roughly 1800 members. Get-AdmGroup returns the member property OK for small groups, but for these large groups it returns null ... by calling Get-AdmGroupMember for those groups? Thanks, Randy Lindsey Colorado Springs Utilities

asked Aug 1, 2013 by rlindsey (20 points)
3,326 questions
3,026 answers
7,727 comments
544,680 users