Add delete function into existing stale account PowerShell script

Greeting Experts!  I'm including the existing script that I'm using to clean up my AD environment and now need to tweak it to include the ability to delete accounts that haven't logged in for at least 120 days.  The first part of the script audits the users accounts and if they haven't logged in for 60 days, disables them and moves them to the "Inactive OU".  I need the new portion of the script to just check the "Inactive OU" for accounts to be deleted.  When the account is deleted, I need to have the date logged when the last time the account logged in.  

Thank you for your help Experts!

# Stale user accounts 

Import-Module ActiveDirectory

$logPath = "C:\Tools\Scripts\Stale_Accounts\Logs\Stale_Users_"  #directory to log output
$Date = Get-Date
$TodaysLog = $logPath + $Date.ToString('yyyy_MM-dd_HHmm') + ".log"
$FilterDate = $Date.AddDays(-49)

Function WriteLog($message) {
    $message = (Get-Date).ToShortTimeString() + " - " + $message
    Add-Content -Path $TodaysLog -Value $message
}

$disabledOU = "OU=Users,OU=Inactive,DC=local"

$StaleUsers = Get-ADUser -SearchBase "OU=Depts,DC=local" -Filter * -Properties SAMAccountName, lastLogonTimeStamp |
    Where-Object {[DateTime]::FromFileTime($_.lastLogonTimeStamp) -lt $FilterDate} 

If ($StaleUsers) {
    ForEach ($User in $StaleUsers) {
        try {
            Disable-ADAccount -Identity $User.DistinguishedName -ErrorAction Stop
            WriteLog -Message "Disabled $($User.SAMAccountName)"
        } catch {
            WriteLog -Message "Failed to disable $($User.SAMAccountName): $($_.Exception.Message)"
        }
    
        try {
            Move-ADObject -Identity $User.DistinguishedName -TargetPath $disabledOU -ErrorAction Stop
            WriteLog -Message "Moved $($User.SAMAccountName) from $($User.DistinguishedName)"
        } catch {
            WriteLog -Message "Failed to move $($User.SAMAccountName) to $($disabledOU): $($_.Exception.Message)"
        }
    }
} Else {
	WriteLog -Message "No stale user accounts found."
}

Open in new window

LVL 9
samiam41Asked:
Who is Participating?
 
Dustin SaundersDirector of OperationsCommented:
Something like this should work.
# Stale user accounts 

Import-Module ActiveDirectory

$logPath = "C:\Tools\Scripts\Stale_Accounts\Logs\Stale_Users_"  #directory to log output
$Date = Get-Date
$TodaysLog = $logPath + $Date.ToString('yyyy_MM-dd_HHmm') + ".log"
$FilterDate = $Date.AddDays(-60)
$DelteFilter = $Date.AddDays(-120)

Function WriteLog($message) {
    $message = (Get-Date).ToShortTimeString() + " - " + $message
    Add-Content -Path $TodaysLog -Value $message
}

$disabledOU = "OU=Users,OU=Inactive,DC=local"

$StaleUsers = Get-ADUser -SearchBase "OU=Depts,DC=local" -Filter * -Properties SAMAccountName, lastLogonTimeStamp |
    Where-Object {[DateTime]::FromFileTime($_.lastLogonTimeStamp) -lt $FilterDate} 

If ($StaleUsers) {
    ForEach ($User in $StaleUsers) {
        try {
            Disable-ADAccount -Identity $User.DistinguishedName -ErrorAction Stop
            WriteLog -Message "Disabled $($User.SAMAccountName)"
        } catch {
            WriteLog -Message "Failed to disable $($User.SAMAccountName): $($_.Exception.Message)"
        }
    
        try {
            Move-ADObject -Identity $User.DistinguishedName -TargetPath $disabledOU -ErrorAction Stop
            WriteLog -Message "Moved $($User.SAMAccountName) from $($User.DistinguishedName)"
        } catch {
            WriteLog -Message "Failed to move $($User.SAMAccountName) to $($disabledOU): $($_.Exception.Message)"
        }
    }
} Else {
	WriteLog -Message "No stale user accounts found."
}

$deleteUsers = Get-ADUser -SearchBase $disabledOU -Filter * -Properties SAMAccountName, lastLogonTimeStamp |
    Where-Object {[DateTime]::FromFileTime($_.lastLogonTimeStamp) -lt $DeleteFilter}

if ($deleteUsers){
    foreach($user in $deleteUsers){
        try {
            Remove-ADUser -Identity $user.DistinguishedName -ErrorAction Stop
            WriteLog -Message "Deleted user $($user.SAMAccountName), last logon date was $($user.lastLogonTimeStamp)"
        } catch {
            WriteLog -Message "Failed to delete $($User.SAMAccountName): $($_.Exception.Message)"
        }
    }
}

Open in new window


Note I changed the $FilterDate to -60, it was set to -49 (which is 7 weeks rather than 60 days).

Note also I made an edit, so please reload for up to date code.  Had the wrong filter date in the bottom block.
0
 
Dustin SaundersDirector of OperationsCommented:
*Just a note, i edited the above block so please refresh to up to date version.
0
 
Jason CrawfordTransport NinjaCommented:
Note that, in it's current form, the $StaleUses variable will include any user that has never logged in along with users that haven't logged in in the last 49 days.  I changed your script to exclude users that have never logged in:

# Stale user accounts 

Import-Module ActiveDirectory

$logPath = "C:\Tools\Scripts\Stale_Accounts\Logs\Stale_Users_"  #directory to log output
$Date = Get-Date
$TodaysLog = $logPath + $Date.ToString('yyyy_MM-dd_HHmm') + ".log"
$FilterDate = $Date.AddDays(-49)

Function WriteLog($message) {
    $message = (Get-Date).ToShortTimeString() + " - " + $message
    Add-Content -Path $TodaysLog -Value $message
}

$disabledOU = "OU=Users,OU=Inactive,DC=local"

$StaleUsers = Get-ADUser -SearchBase "OU=Depts,DC=local" -Filter * -Properties SAMAccountName, lastLogonTimeStamp |
    Where-Object {[DateTime]::FromFileTime($_.lastLogonTimeStamp) -lt $FilterDate -and $_.LastLogonDate -ne $null} 

If ($StaleUsers) {
    ForEach ($User in $StaleUsers) {
        try {
            Disable-ADAccount -Identity $User.DistinguishedName -ErrorAction Stop
            WriteLog -Message "Disabled $($User.SAMAccountName)"
        } catch {
            WriteLog -Message "Failed to disable $($User.SAMAccountName): $($_.Exception.Message)"
        }
    
        try {
            Move-ADObject -Identity $User.DistinguishedName -TargetPath $disabledOU -ErrorAction Stop
            WriteLog -Message "Moved $($User.SAMAccountName) from $($User.DistinguishedName)"
        } catch {
            WriteLog -Message "Failed to move $($User.SAMAccountName) to $($disabledOU): $($_.Exception.Message)"
        }
    }
} Else {
	WriteLog -Message "No stale user accounts found."
}

foreach ($120user in (Get-ADUser -SearchBase 'OU=Users,OU=Inactive,DC=local' -Filter * -Properties SAMAccountName, lastLogonTimeStamp, LastLogonDate | Where-Object {[DateTime]::FromFileTime($_.lastLogonTimeStamp) -lt $120FilterDate -and $_.LastLogonDate -ne $null})) {
  $120LastLogon = $120user | Select-Object @{Name="LastLogonTimeStamp";Expression={([datetime]::FromFileTime($_.LastLogonTimeStamp))}}
  WriteLog -Message $120LastLogon
  Remove-ADUser $120user.samaccountname -WhatIf
}

Open in new window

For the users that haven't logged in within 120 days, I added a -WhatIf to ensure logging is appropriate.  If all is good just remove the -WhatIf in the last foreach loop.  Note that I am not in a position to perform a dry run so if you encounter any errors just let me know and I'll address as needed.
0
How do you know if your security is working?

Protecting your business doesn’t have to mean sifting through endless alerts and notifications. With WatchGuard Total Security Suite, you can feel confident that your business is secure, meaning you can get back to the things that have been sitting on your to-do list.

 
samiam41Author Commented:
I appreciate the suggestions experts.  With both scripts, I wasn't able to get a user account that I know is very stale to be deleted.  I'm trying to check the lastlogon date now of that user account so I know if it is just my bad memory of the accounts age or if I need to troubleshoot the script.
0
 
samiam41Author Commented:
Much like the "no stale user accounts found" entry added to the log file, can I add a "no accounts to delete" entry?  @Jason Crawford, how can I do this?  @Dustin Saunders, I got it to work for your script.
0
 
samiam41Author Commented:
LastLogonDate for the two accounts:

UserA- 11/1/2012 2:20:44 PM
UserB- 11/16/2016 3:01:05 PM

Neither account is being deleted (which should be).
0
 
Dustin SaundersDirector of OperationsCommented:
Ah, you might need to add
-Confirm:$false

Open in new window

to the end to the Remove-ADUser line.

You can add:
Else {
	WriteLog -Message "No accounts to delete."
}

Open in new window

to the end of the:
if ($deleteUsers) { }

Open in new window

code block to log if there are no accounts to delete.
0
 
samiam41Author Commented:
Like this? >> Remove-ADUser -Identity $user.DistinguishedName  -Confirm:$false -ErrorAction Stop

I added that line and get (no accounts to delete) in the log file with neither of those accounts deleted.

if ($deleteUsers){
    foreach($user in $deleteUsers){
        try {
            Remove-ADUser -Identity $user.DistinguishedName -Confirm:$false -ErrorAction Stop 
            WriteLog -Message "Deleted user $($user.SAMAccountName), last logon date was $($user.lastLogonTimeStamp)"
        } catch {
            WriteLog -Message "Failed to delete $($User.SAMAccountName): $($_.Exception.Message)"
        }
    }
} Else {
	WriteLog -Message "No accounts to delete."
}

Open in new window

0
 
samiam41Author Commented:
Figured it out.  Line #42 of Dustin Sanders script
 Where-Object {[DateTime]::FromFileTime($_.lastLogonTimeStamp) -lt $DeleteFilter}

Open in new window

, I had to change the $DeleteFilter to $FilterDate.
0
 
samiam41Author Commented:
Final product

# Stale user accounts 

Import-Module ActiveDirectory

$logPath = "C:\Tools\Logs\_Stale_Accounts\Stale_Users_"  #directory to log output
$Date = Get-Date
$TodaysLog = $logPath + $Date.ToString('yyyy_MM-dd_HHmm') + ".log"
$FilterDate = $Date.AddDays(-60)
$DelteFilter = $Date.AddDays(-120)

Function WriteLog($message) {
    $message = (Get-Date).ToShortTimeString() + " - " + $message
    Add-Content -Path $TodaysLog -Value $message
}

$disabledOU = "OU=Test,OU=Inactive,DC=local"

$StaleUsers = Get-ADUser -SearchBase "OU=Test,OU=Inactive,DC=local" -Filter * -Properties SAMAccountName, lastLogonTimeStamp |
    Where-Object {[DateTime]::FromFileTime($_.lastLogonTimeStamp) -lt $FilterDate} 

If ($StaleUsers) {
    ForEach ($User in $StaleUsers) {
        try {
            Disable-ADAccount -Identity $User.DistinguishedName -ErrorAction Stop
            WriteLog -Message "Disabled $($User.SAMAccountName)"
        } catch {
            WriteLog -Message "Failed to disable $($User.SAMAccountName): $($_.Exception.Message)"
        }
    
        try {
            Move-ADObject -Identity $User.DistinguishedName -TargetPath $disabledOU -ErrorAction Stop
            WriteLog -Message "Moved $($User.SAMAccountName) from $($User.DistinguishedName)"
        } catch {
            WriteLog -Message "Failed to move $($User.SAMAccountName) to $($disabledOU): $($_.Exception.Message)"
        }
    }
} Else {
	WriteLog -Message "No stale user accounts found."
}

$deleteUsers = Get-ADUser -SearchBase $disabledOU -Filter * -Properties SAMAccountName, lastLogonTimeStamp, LastLogonDate |
    Where-Object {[DateTime]::FromFileTime($_.lastLogonTimeStamp) -lt $FilterDate}

if ($deleteUsers){
    ForEach ($user in $deleteUsers){
        try {
            Remove-ADUser -Identity $user.DistinguishedName -Confirm:$false -ErrorAction Stop
            WriteLog -Message "Deleted user $($user.SAMAccountName), last logon date was $($user.lastLogonDate)"
        } catch {
            WriteLog -Message "Failed to delete $($User.SAMAccountName): $($_.Exception.Message)"
        }
    }
} Else {
	WriteLog -Message "No accounts matched the criteria to be deleted."
}

Open in new window

0
 
samiam41Author Commented:
Thanks Dustin for the help, time and guidance on this one!  This script ended up being a pretty useful and power resource for my environment.  I'm sure others will find it equally beneficial as well.
0
 
samiam41Author Commented:
Good morning Experts.  I closed this question out without giving enough thought to the new user concern brought up by Jason Crawford.  Well, as soon as I took this script out of the testing world and ran it in the prod world, I wiped out two new user accounts the helpdesk had just created.  With that lesson learned just 10 minutes in, I immediately modified the script.  Thus, Jason deserves some credit in the end product.  I've asked the mods to modify the points so that Jason gets 200 and Dustin gets 800.  I believe that to be only fair.  Thanks again Experts.
0
 
samiam41Author Commented:
Sorry for the confusion experts but I wanted to make sure everyone received credit for the contribution to the final, solid product.  Your time and input was much appreciated and I look forward to working with you all again.
1
 
samiam41Author Commented:
@Dustin, in line 9 in the script of the answer I accepted, $DelteFilter is spelled wrong which is why I had to take it out to make the script work.  Took me awhile to figure it out but that's the reason if anyone else can't make it work.  Happy Holidays!
1
 
Dustin SaundersDirector of OperationsCommented:
Thanks for circling back around and posting an update.
1
 
samiam41Author Commented:
I figured you took the time and energy to work with me and find a solution that I (and all those after me) could use.  It's a solid solution and I'm sure others will find it equally helpful.  I didn't want them to bail on it because of that one part.
1
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.