Powershell

Logon and Logoff Security Event Viewer Auditing

Update 30th July 2022 – TechNet links no longer work, but updated the script link to a github copy.

Original post:

Logon and Logoff events for a PC running Vista or above are logged to the Security section of Event Viewer. If you’re looking for a particular event at a particular time, you can browse through manually with a bit of filtering in the Event Viewer GUI and find what you need.

On a larger scale though, this doesn’t make sense. If you’re looking at multiple users or multiple events, the task gets tedious very quickly.

Logon and Logoff events on a domain will be logged against the closest domain controller, but unless you’re piping these logs elsewhere (which I briefly talked about here on Tech Target), the DC’s logs will quickly fill up and cycle off. Also, the user may have authenticated against multple DCs, or other scenarios such as an offline laptop user first logging in locally before being on the network.

A PC keeping only it’s own security logs will go back a lot further (over a month hopefully!) so there’s a lot of data to obtain.

There’s an older Microsoft Technet article that covers this briefly called Tracking User Logon Activity Using Logon Events which has some useful information, includoing the Event IDs:

Logon Event ID 4624
Logoff Event ID 4634

Now, you can filter the event viewer to those Event IDs using Event Viewer, but you can’t filter out all the noise around anything authenticating to and from the PC you’re investigating.

One way of doing this is of course, PowerShell.

There are two commands I found for this – Get-EventLog(link now dead) and Get-WinEvent(link now dead). I used Get-Eventlog as it seemed to be a bit easier to get the data I needed…. but I couldn’t get it exactly to work.

Then I read this Technet article – PowerShell Get-WinEvent XML Madness: Getting details from event logs (link now dead) which backed up what I was experiencing, such as “The bad:  All of a sudden reading event logs gets complicated.  The filtering in particular requires some crazy syntax.”

This all started to get too hard, and I couldn’t get my head around the code or get it to work!

Finally, I found someone who’d created a very nice script that did everything I wanted: Security Log Logon/Logoff Event Reporter

The script doesn’t need any parameters to run, just asks for which PC, date range, if you want to only see failed logins (which I don’t for this scenario), and then how to display the information.

pshell

Sometimes it takes a lot of research and time to just use someone else’s script and be done with it :)

Update:
As @GirlGerms pointed out, many people just lock their workstation rather than logging off/on. In that case, these are the two Event IDs:

Workstation Locked Event ID  4800
Workstation Unlocked Event ID  4801

The script I found doesn’t include these, but appears very easy to adjust to see those results too. None of this works if the person doesn’t lock their PC, and never logs off so it’s hardly an all encompassing method.

Line Breaks In Active Directory PowerShell

Quick tip today about line breaks / carriage returns.

If you want to add multi-line values to an Active Directory field – (e.g. the notes field), it’s not plainly obvious as to how you can type this in a PowerShell command.

One way to pass through an ‘enter’ value is by using a line break. This can be achieved by using:

`r`n 

Example:

Get all the users who are in Australia (based on their Country/region field being set to Australia), and update the street address two lines of BEWARE and MONSTERS:

get-aduser -properties * -filter {country -eq “Australia”} | foreach {set-aduser $_ -streetaddress “BEWARE`r`nMONSTERS”}

There are no spaces required, it will just drop the text after `r`n to the next line.

BEWARE

If you want to update the notes field, it’s a bit tricker, as you have to use the “-replace” parameter:

-Replace @{Info=”BEWARE`r`nMONSTERS“}

Password Expiry Notification Script Part 2 – Who’s Expiring?

This is a brief modification to the Password Expiry Notification Script, which seems to be pretty popular.

Amnon Hoppe contacted me from the blog, and passed on a few extra scripts with some other ideas. He has collaborated and adjusted two scripts, to quote him “one to email only the admin/service desk with a list of all users that are about to expire and the other is to do that AND also email the end users in question to warn them to verify/take action to prevent lockout.”

I’ll add them here to share. I haven’t tested these, but have gone over them to make sure nothing malicious is going on.

Accounts2Expire2EndUsers (1)

Accounts2Expire2Admin (1)

Thanks Amnon!

These scripts are a bit more complex, but they’re good examples of what you can do, as well as a different approach from what I took, for a very similar purpose (Those are for account expiry, while mine was for password expiry)

I thought about adjusting the scripts, but after spending some time on it, I thought I’d leave them as is, but take the idea of displaying the users affected, then apply that to my own script. This is similar to the original, but it’s purpose is to just list the users who’s passwords expire in X days or less. This might be handy over a holiday for example, to know who’s going to have their password expire and then contact them to help. You should easily be able to combine the old and new scripts if you want to generate an email with this information on yourself.

Password Expiry List (rename to .ps1)

Here’s the script, which if you compare to the original is very similar. Instead of generating an email, we’re creating a list of users that have the password expiry due on X days or less (represented by the -le comparison operator, rather than -eq, a list is here). The list is then echo’d out to the console.

#################################################
# Please Configure the following variables….
# expireindays1 = How many days maximum the password has to expire (e.g. 7 will be up to 7 days)
$expireindays1 = 7
#################################################

#Get Users From AD who are enabled
Import-Module ActiveDirectory
$users = get-aduser -filter * -Properties enabled, passwordneverexpires, passwordexpired, emailaddress, passwordlastset |where {$_.Enabled -eq “True”} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }

$ListOfNames = @()
foreach ($user in $users)
{
$Name = (Get-ADUser $user | foreach { $_.Name})
$emailaddress = $user.emailaddress
$passwordSetDate = (get-aduser $user -properties passwordlastset | foreach { $_.PasswordLastSet })
$PasswordPol = (Get-AduserResultantPasswordPolicy $user)
# Check for Fine Grained Password
if (($PasswordPol) -ne $null)
{
$maxPasswordAge = ($PasswordPol).MaxPasswordAge
}

else
{
$maxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
}

$expireson = $passwordsetdate + $maxPasswordAge
$today = (get-date)
$daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days

if ($daystoexpire -le $expireindays1)
{
$ListOfNames += $Name

}

}
echo $listofnames

Script to Change Multiple Out Of Office Messages

This is probably an inefficient script, but I needed to create it as a once off to change the wording of any user with an Out Of Office message. It will go through every account, and change any instance of swearword and replace it with censored (no, this isn’t what I needed to use it for!).

This is using the Exchange Management Shell:

$data = get-mailbox -resultsize unlimited
foreach ($user in $data){
$currentreply = Get-MailboxAutoReplyConfiguration $user.alias
$newreply = $currentreply.internalmessage -replace “swearword”, “censored”
Set-MailboxAutoReplyConfiguration $user.Alias -InternalMessage $newreply -ExternalMessage $newreply
}

You can also use this to find any accounts with an Out Of Office message containing a certain word or phrase:

$data = get-mailbox -resultsize unlimited
foreach ($user in $data){
Get-MailboxAutoReplyConfiguration $user.alias | Where-Object {$_.internalmessage -like “*swearword1 swearword2*”}
}

Used on Exchange 2010, but should work on 2007 and 2013 also. You may notice the results of InternalMessage and ExternalMessage are hard to read, as by default they’ll show the HTML coding – you may need to factor this into any searches or replaces you perform.

Updating Active Directory from a CSV

Scenario:

You’ve been asked to populate everyone’s Active Directory job title. The payroll system is correct, and they’re able to export you a list of usernames and correct job titles. All you need to do is get that into AD.

Solution:

You could do this manually of course, but that’s no fun and a waste of time. This is one of those scenarios where you’ll hopefully think ‘PowerShell can do this!’ and possibly wonder how. That’s what I did anyway, so set out to make it work.

Here’s a fake example of the data I was working with, in a file called fake.csv:

EMPLOYEENAME,JOBTITLE
AFOWLER,IT OPERATIONS MANAGER
RSOLE,JANITOR

Tip: If you open a csv file in Excel it is a bit easier to read.

From this data, we want to match the EMPLOYEENAME to the correct AD account, then update the Job Title field from the JOBTITLE entry of the csv file.

A script that will do this is:

Import-module ActiveDirectory
$data = import-csv -path c:\fake.csv
foreach ($user in $data){
Get-ADUser -Filter “SamAccountName -eq ‘$($user.employeename)'” | Set-ADUser -Replace @{title = “$($user.JobTitle)”}
}

So, what’s happening here? It can take a bit to get your head around especially if you’re not used to programming (like me), so I’ll try to explain it:

Import-module ActiveDirectory
Importing the ActiveDirectory module so the Get-ADUser command works. If you can’t load the module, install RSAT (Remote Server Administration Tools) which includes the AD module.

$data = import-csv -path c:\fake.csv
This is setting the $data variable to memory, which will contain all the contents of the fake.csv file.

foreach ($user in $data){
This is saying ‘for each line of information from the $data variable (which is the csv file), map that to $user and do the following”

Get-ADUser -Filter “SamAccountName -eq ‘$($user.employeename)'” | Set-ADUser -Replace @{title = “$($user.Job Title)”}
This is getting any AD User where their SamAccountName matches the employeename column of the $user variable (which is the current line of information from the csv at time of processing). Then with the pipe | it will use the result to then Set the AD User’s title field (where the job title goes) to the Job Title part of our $user variable. This command will run twice, because there are two lines for ‘foreach’ to process.

}
This is closing off the command which each ‘foreach’ command runs.

 

I hope that explains it enough so you’re able to manipulate the script to your own requirements.