Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium


Need help with logic for AD Powershell Audit <30 / 30-60 / >90 days inactive users

Posted on 2013-12-03
Medium Priority
Last Modified: 2013-12-05
So I have this script I've been playing with... the goal is to do some basic AD auditing.  I'm using both Microsoft AD cmd-lets and the Quest cmd-lets (I find both have their place).

What I really need to make sure of is my logic for finding users that are inactive for 30 days, then inactive form 30-60 days, then inactive over 90 days.

The script will output:

- New accounts created in last seven days
- Groups modified in last seven days
- 30/30-60/90 and over inactive users
- Report user accounts that have never logged on to their account
- Send an email

Yeah, I know the attach as a text file is somewhat kludgy, but it works for these purposes.

Can someone take a look at the <30 / 30-60 / >90 logic strings and tell me what would work better, I'm not sure my results are accurate or my logic is correct.

# Prepare loading required modules

import-module activedirectory
Add-PSSnapin Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue

# Prepare and output file for mailing

$OutputfilePath = "C:\ADUserAudit-"+([datetime]::Now).tostring("yyyyMMdd")+".txt"

# Accounts created this week

$When = ((Get-Date).AddDays(-7)).Date
$UsersCreatedLastWeek = (Get-ADUser -Filter {whenCreated -ge $When} -Properties whenCreated) | select samaccountname,name,distinguishedname
$UsersCreatedLastWeekTable = ($UsersCreatedLastWeek | format-table) 

# Groups Modified this week

$When = ((Get-Date).AddDays(-7)).Date
$GroupsModifiedLastWeek = (Get-ADGroup -Filter {whenChanged -ge $When} -Properties whenChanged) | select samaccountname,name,distinguishedname 
$GroupsModifiedLastWeekTable =  ($GroupsModifiedLastWeek | format-table)

# List all users in that have not logged on within 
# XXX days in "Active Directory" 
# Get the Current Date 
# Number of Days to check back.   
# Organizational Units to search 

$OUList = "OU=Users,OU=company,DC=com","OU=Users2,OU=company,DC=com"

# Prepare arrays for totals

$30Days = @()
$60Days = @()
$90Days = @()

ForEach ($OU in $OUList) 

    #LastLogon.AddDays() adds x number of days making the users lastlogon x number of days into the future (e.g. Sep 28th somes October 27th...)

    $30Days += (GET-QADUSER -SearchRoot $OU | where { $_.LastLogon.AddDays(30) -lt $CURRENTDATE}) | select samaccountname,Name,DN

    $60Days += (GET-QADUSER -SearchRoot $OU | where { ($_.LastLogon.AddDays(60) -lt $CURRENTDATE -and $_.LastLogon.AddDays(60) -lt ($CURRENTDATE.AddDays(-30))) }) | select samaccountname,Name,DN

    $90Days += (GET-QADUSER -SearchRoot $OU | where { ($_.LastLogon.AddDays(90) -lt $CURRENTDATE -and $_.LastLogon.AddDays(90) -lt ($CURRENTDATE.AddDays(-30))) }) | select samaccountname,Name,DN

$30DaysTable = ($30Days | format-table)
$60DaysTable = ($60Days | format-table)
$90DaysTable = ($90Days | format-table)

# Get users that have never logged in, but use Windows AD Powershell cmdlets as it should look at all domain controllers. Not restricted to OU:

$NeverBeenKissed = get-aduser -f {-not ( lastlogontimestamp -like "*") -and (enabled -eq $true)} | select samaccountname,name,distinguishedname

$NeverBeenKissedTable = $NeverBeenKissed | format-table
# Write to a file

Out-File $OutputfilePath

Add-Content -Path $OutputfilePath "Users Created in the Last Week:"
Add-Content -Path $OutputfilePath " "
$UsersCreatedLastWeekTable | Out-File $OutputfilePath -append
Add-Content -Path $OutputfilePath "Groups Modified in the Last Week:"
Add-Content -Path $OutputfilePath " "
$GroupsModifiedLastWeekTable | Out-File $OutputfilePath -append
Add-Content -Path $OutputfilePath "Users not logged in for 30 days:"
Add-Content -Path $OutputfilePath " "
$30DaysTable | Out-File $OutputfilePath -append
Add-Content -Path $OutputfilePath "Users not logged in for 60-90 days:"
Add-Content -Path $OutputfilePath " "
$60DaysTable | Out-File $OutputfilePath -append
Add-Content -Path $OutputfilePath "Users not logged in for over 90 days:"
Add-Content -Path $OutputfilePath " "
$90DaysTable | Out-File $OutputfilePath -append
Add-Content -Path $OutputfilePath "Users who have NEVER logged in!:"
Add-Content -Path $OutputfilePath " "
$NeverBeenKissedTable | Out-File $OutputfilePath -append

# Send it in email as an attachment, since it's not as simple to port it into an email yet.

$smtpServer = “hub.company.com”

Send-MailMessage -To "person@company.com” -Subject “AD User Audit 30/60/90” -From “PowerShellAudit@company.com” -Body “Current AD User Audit is attached.” -Priority High -SmtpServer $smtpServer -Attachments $OutputfilePath

Open in new window

Question by:gerhardub
  • 3
  • 2
LVL 40

Expert Comment

ID: 39694056
You can try with Search-ADAccount to search the inactive accounts.. for example..

$30  = Search-ADAccount -UsersOnly -AccountInactive -TimeSpan 30
$3060 = Search-ADAccount -UsersOnly -AccountInactive -TimeSpan 60 | ?{($30 | Select -ExpandProperty Samaccountname) -notcontains $_.Samaccountname}
$90 = Search-ADAccount -UsersOnly -AccountInactive -TimeSpan 90

Open in new window

LVL 41

Accepted Solution

footech earned 2000 total points
ID: 39694175
What I really need to make sure of is my logic for finding users that are inactive for 30 days, then inactive form 30-60 days, then inactive over 90 days
This says to me - find the users;
 - who haven't logged in in the last 30 days, so last logon date must be greater than 30
 - whose last logon was between 30 and 60 days ago
 - whose last logon was greater than 90 days ago

The first two are pretty much the same, the second just has an additional limit placed on it.  And what about last logon between 60 and 90 days ago?

Your script logic is pretty convoluted (and a little different than my understanding of what you want).  The first will get users whose last logon was greater then 30 days ago.  The second will get users whose last logon was between 30 and 60 days ago.  The third will get users whose last logon was between 30 (I think you meant 60 here) and 90 days ago.

In addition to Search-ADAccount you could also do something like the following:
$lt30 = @()
$30Days = @()
$60Days = @()
$90Days = @()

$now = Get-Date
ForEach ($OU in $OUList) 
        Get-ADUser -filter * -Properties LastLogonDate -SearchBase $OU | ForEach `
        If ( $_.LastLogonDate -gt $now.AddDays(-30) )
        { $lt30 += $_ | Select samaccountname,Name,DistinguishedName }
        ElseIf ( $_.LastLogonDate -lt $now.AddDays(-30) -and $_.LastLogonDate -gt $now.AddDays(-60) )
        { #another way to do the same thing
          $30Days += New-Object PsObject -Property @{ samaccountname = $_.samaccountname; Name = $_.name; DN = $_.DistinguishedName }
        ElseIf ( $_.LastLogonDate -lt $now.AddDays(-60) -and $_.LastLogonDate -gt $now.AddDays(-90) )
        { $60Days += $_ | Select samaccountname,Name,DistinguishedName }
        ElseIf ( $_.LastLogonDate -lt $now.AddDays(-90) )
        { $90Days += $_ | Select samaccountname,Name,DistinguishedName }

Open in new window


Author Comment

ID: 39694411
Folks ,

You rock.... Let me plug these in and play in the AM... I'll get back to you tomorrow!
Veeam Disaster Recovery in Microsoft Azure

Veeam PN for Microsoft Azure is a FREE solution designed to simplify and automate the setup of a DR site in Microsoft Azure using lightweight software-defined networking. It reduces the complexity of VPN deployments and is designed for businesses of ALL sizes.


Author Comment

ID: 39695312
Interestingly, the Search-ADAccount cmdlet gave me false results.  It was odd... $30 and $90 were exactly the same, $60 had zero results.... which makes sense in hindsight.

FooTech: Your understanding of what I really needed worked great.  The arrays literally break out the users from time as I needed, and then allowed me to break it out in a text based report that ended with 7/7/30/60/90 and "never been kissed." -grin-

Thanks for that... you've just saved me a ton of time, and I learned something from both of you.  I need to brush up on my Powershell logic... and revisit the cmdlets.

I'll open another ticket if either of your want 500 points toward figuring out how to format it in HTML and send that in an email! Let me know!


Author Closing Comment

ID: 39695317
Fantastic level of help... I can't thank you enough... and I'm a hardass.
LVL 41

Expert Comment

ID: 39699387
Glad I could help.

One thing to keep in mind if you want to explore the use of Search-ADAccount, when specifying a timespan to indicate days you should enclose the number in quotes.  So
Search-ADAccount -UsersOnly -AccountInactive -TimeSpan 30
would have a timespan of 30 ticks, and
Search-ADAccount -UsersOnly -AccountInactive -TimeSpan "30"
would have a timespan of 30 days.  You could also write it like
Search-ADAccount -UsersOnly -AccountInactive -TimeSpan 30.0:0
When written like this the quotes are optional.

Featured Post

Evaluating UTMs? Here's what you need to know!

Evaluating a UTM appliance and vendor can prove to be an overwhelming exercise.  How can you make sure that you're getting the security that your organization needs without breaking the bank? Check out our UTM Buyer's Guide for more information on what you should be looking for!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Measuring Server's processing rate with a simple powershell command. The differences in processing rate also was recorded in different use-cases, when a server in free and busy states.
I’m willing to make a bet that your organization stores sensitive data in your Windows File Servers; files and folders that you really don’t want making it into the wrong hands.
This tutorial will walk an individual through the steps necessary to install and configure the Windows Server Backup Utility. Directly connect an external storage device such as a USB drive, or CD\DVD burner: If the device is a USB drive, ensure i…
There are cases when e.g. an IT administrator wants to have full access and view into selected mailboxes on Exchange server, directly from his own email account in Outlook or Outlook Web Access. This proves useful when for example administrator want…

581 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question