As you know, the concept of auditing in an Active Directory environment, is a key fact of security and it is always wanted to find out what a user has done and where he did it. One of the important task that most of the administrators are dealing these days is to find out where a user has logged on.

Today we will walk through a step-by-step guide in which you will learn how to utilize built-in Event Viewer on domain controllers for your favorite report and specifically, find where a user has logged on.

To make things more understandable, take a look at the attached  image below in which you can find what do we mean by list of computers where a user account has logged on since past days. This picture consider (m.tehrani) user account as an example:

Image1: A list of computers where 'm.tehrani' has logged on


Well everything needs preparation including this script. Because the default setting for event logs are so insufficient and user logon activity generates huge number of events, we are going to increase the size of event logs in order to make enough space for log generation. Although you may have already enabled these settings before, we will cover this here in case there is a need for others people to increase the size of event log.

The default setting of 64 MB for event log might be completely insufficient for this need depending on your environment like number of DCs, number of users and sites and so on. If we want to have a complete list of computers where someone has logged one, we will need far more space in event viewer to be able to keep track of audit logons for several days. Here is an image of default setting of 64 MB:

Image2: Default size of event logs which is 64 MB

So for first step in order to prepare the environment, we will create a GPO which contains the settings for “Event Log Size”. Let’s navigate to “Group Policy Management Console” and create a GPO and name it whatever you like.

Image3: Create a GPO and name it whatever you like

Locate this path “Computer Configuration \ Policies \ Windows Settings \ Security Settings \ Event Log” and change the “Security” event size based on your environments and requirements. If there are no worries in regards with Disk Usage  and performance it is possible to configure settings it at maximum level which is 4 GB.

Image4: Path of group policy settings related to event log size.

The next step is not mandatory if there are no firewall settings on domain controllers, but because we need to be able to query event logs of different domain controllers and possibly different sites, it is a good idea to make sure that “Remote Event Log Monitoring” is enabled through the firewall. It is not possible cover all details about configuring firewall settings, but the image below, will basically show all the settings needed in order to enable “Remote Event Log Monitoring” on your domain controllers. In case of need for further reading on how to enabling "Remote Event Log" through firewall, please refer to "See Also" section and options number 1 and 2.

Image5: Firewall settings in order to enable 'Remote Event Log Monitoring'

Now, link the GPO you just created to “Domain Controllers" OU and wait for some events to get populated. Again it is worth mentioning to say, it all depends on the environment and you can start to query audits right after or wait a couple of days to get populated. Basically the rule of thumb for this setting is, if you like to have logon audits of 10 days before, you have to wait about 10 days after increasing the event log size to get enough events populated.

Running the script

So far we have done the preparation and downloaded the script from this link. Here it comes the actual work. What are we going to do now is to “Dot Source” the script or load in into memory so we can start using the script.

Copy the script to a folder or drive (or place in C:\  to make things simpler!), then open up PowerShell and navigate to the folder or drive which contains the downloaded script.

Now if you type “get” and hit tab key, it will be automatically completed and the name of the script along with its extension will be placed at the line:

Image6: Type 'get' and hit TAB key

Now you have to place the cursor at the beginning of the line and type “.

That’s it, just type a dot (.) following a space ( ) at the beginning of the line and hit Enter. Now the script is loaded into memory.

Image8Insert a dot(.) and a space( ) before the name of the script

Once the enter is tapped, either there will be an error indicating the running of script is disabled because of PowerShell execution policy, or it has no error. In case of error, run a PowerShell console in administrative rights and type the code below in order to allow execution of scripts and then repeat "Dot Sourcing" of the script. You can also refer to "See Also" section and option number 3 to find more about PowerShell execution policy.

Set-ExecutionPolicy RemoteSigned

Code1: Enabling execution of PowerShell scripts 

Now the script is fully loaded, let’s use the actual script and consider (m.tehrani) as an example to find out during the last 15 days, on which workstations he has logged on. Type the name of the username with –User parameter and the time-frame with –Days parameter and hit Enter.

Image9: Run the script with your own parameters.

After running, you will be presented with a progress bar which keeps you informed about the status of script and how long it takes to get completed.

Image10: Script running in progress

Voila! Now you can see that a report has been generated which tells us on which workstations, user (m.tehrani) has logged on during the last 15 days.

Image11: And final results

How it works?

Okay. So far we have utilized the script and gathered the list of computers where our selected user has logged on to. But in this part, we will discuss about how the script actually works under the hood.

Understanding the process

Let’s have a set of diagrams to look at, in order to understand the concepts behind this script.
When we run the script, at the beginning, it checks whether the user account exist or not. It is pretty clear that when the entered user account does not exist, we do not have to run the rest of the scripts.

Image12: Check if user exist or not.

In next step, the necessary variables are calculated and are sent to DC along with filter required to query the events for our selected user. The request is sent to the first DC from the list of domain controllers, and events related to the selected user are queried and saved into a variable.

Image13: Query event logs for selected user.

In the following steps, the list of events is saved and the process of extracting valuable information from the gathered events will be started. Basically in this phase, the important data such as, domain, name of DC, time of logon, workstation of logon, are extracted and placed into a table variable for a better look.

Image14: List of event logs is gathered.

Image15: Process gathered event logs to extract valuable info.

At the final step, the script goes through the next DC and start repeating the process.

Image16: Continue the script for next DC.

Once the final DC is contacted and the information is gathered, the data which will be a list of computers, will be presented to us.

Explaining the script

In order to have a better view and understanding, the script is divided into 4 sections which we will cover them right as we speak. As you can see, this script is a function and each function in PowerShell contains 4 main blocks. These 4 main blocks are Param, Begin, Process and End. Take a look at the image below:

Image17: Four main blocks of the function

These 4 main blocks are covered comprehensively in sections below.

Param section 

Param section actually contains the parameters which are needed to run the script. As you can see in image below, we have 3 parameters as follow:

Image18: Parameters of this script.

  • User: This is the main and mandatory parameter. You need to enter the name of user account which you like to run the query against. Through out this Wiki, we considered (m.tehrani) as our example.
  • Server: In this parameter we define the name of target domain controllers. However if you do not enter any name, all domain controllers will be considered to fill this parameter. This is not a mandatory parameter.
  • Days: This parameter is also not a mandatory parameter. If you like to get audit list of our selected user account in last X days, this is the parameter which you have to fill. If you are interested to find out, to which workstations a user has logged on in last 10 days, feed this parameter with 10.

Begin section

Right after param section, we have Begin section. It is recommended to place the variables and general information at the Begin section and avoid over populating the main part of the script. 

The image below represent the first part of the main section.

Image19: Defining variables in main section

Here are the descriptions of the most important lines in the image above:

  • Line 54:  The result are stored in this variable. By result we mean the list of computers and other information which will be presented at the end of script:
  • Line 57: This variable is called the exclusion list. As you can see, this is an array which contains the account ‘krbtgt’ and the user itself. Since Kerberos logs need heavy clean up, and every time a ticket is generated for the user, the ‘krbtgt’ account and the user itself are the source requester, we want to exclude them from the final result.
  • Line 59: The $UPN variable is important because, in events, we need to remove the UPN from the event table and just extract the user name. 
  • Lines 60, 61, 62: These are the lines which are related to the Days parameter. We need to calculate the current data and past date in order to create the duration format of our query. The time format should be in a form which is understandable for XML filtering.

Image20: Creating the XML filter needed by Event Viewer.

  • Lines 66 to 75: Here the main filter for querying event log is created. This filter will be sent to each domain controller to fetch information. As you can see in these lines, there are some variables which are previously discussed like $UPN, $User.

Process section

If we want to summarize the Process section, it can be done using the image below.

Image21: Process section in a glance

However, as you can see at lines 84 and 85, the Foreach command is closed so we can cover it in next image.  

  • Line 83: Checks if the user exist or not. If it exists, it continues the script and follow the line 84 and 85, if not, it shows an error (line 136) and exit the script.
  • Lines 84, 85: These lines are the building block of the script. We cover them in next image.

Image22: Querying event viewer at line 97.

This image belongs to Foreach command which runs every time per DC to query data.

  • Line 86: The variable $Events, holds the events which are queried from each DC. So we need to clear it every time we move to next DC in order to avoid mixing the events together.
  • Lines 89, 90: Why do we run these lines? Good question! We have to extract the user name alone from the event, so in order to do that, first we have to calculate the UPN length and increment it by 1, because in UPN we have an @ symbol too.
  • Line 97: This line will fetch all necessary events from the DC and place that in $Events variable. Since this variable is an array, we will use it later to extract required information which is covered in next image.

Image23 Processing events and save them to a table

The events are gathered for our first DC, as next step we have to extract valuable information from each event and save them to an object, and then we move to next event. 

  • Lines 111 to 117: These lines will extract valuable information from each event and store them in appropriate variables. 
  • Lines 120 to 130: Once the needed information are gathered from each single event, we need to place them in a nice, clean table.

End Section

This section will show the final result. 

Image24: Showing final result

  • Lines 143 to 145: Here and for the last time, the result is filtered against our exclusion list which we covered them before. This final filtering is done to have a clean result without unnecessary information.
  • Line 146: And for last line, we Format-Table the result to show our final report.


In this guide we learned how to utilize the basic event viewer and combine it with our PowerShell codes to find out the list of workstations where a user has logged on. This script queries all domain controllers in a domain and find the result and parse all them to a readable format so you can clearly see where he\she has logged on.

Do not forget that in order to run this script, there are some requirements which should be met before running as we explained them in “Preparation” section.

Feel free to edit the code in a way you like and utilize it in your environment.

See Also

  1. Enable Remote Management (server side)
  2. Unable to Access Event Viewer on a Remote Computer?
  3. Using the Set-ExecutionPolicy Cmdlet