Thanks to Microsoft breaking the "Delete user profiles older than a specified number of days on system restart" GPO and not fixing it after all of these years, I need a script that deletes old user profiles. The thing is that instead of it looking for the modification date of the user profile folder itself, I need it to delete the user profile based on the modification date of the Local folder in the Appdata folder of the user profiles. I noticed that the modification date of the user profile folder might not change for years even if you log in daily, but the local folder does seem to change depending on when you log in.
So, I have this that I grabbed from a spiceworks post made by user cxr-aus.
$useraccounts = Get-ChildItem -path C:\users\ | Where-Object lastwritetime -lt (Get-Date).AddDays(90) | Select-Object Name
$sort = $useraccounts | ForEach-Object {$_.Name}
$removeaccounts = $sort -join "|"
Get-WmiObject -Class Win32_userprofile | Where-Object {($_.LocalPath -match "$removeaccounts") -and (!$_.special)} | Remove-WmiObject -whatif
You would remove the -whatif at the end of the code to get it to remove a user profile. The first problem that I ran into is that I need this to remove multiple user profiles, so the Remove-WmiObject does not work because the Get-WmiObject returns multiple profiles for me, so to fix it to work, I use % { $_.Delete()} instead like the following.
WARNING:Be very careful with the following code as -whatif does not work with it and it might start deleting multiple profiles off of your machine.
$useraccounts = Get-ChildItem -path C:\users\ | Where-Object {$_.lastwritetime -lt (Get-Date).AddDays(90)} | Select-Object Name
Foreach ( $user in $useraccounts) {
$sort = $useraccounts | ForEach-Object {$_.Name}
$removeaccounts = $sort -join "|"
$Username = $removeaccounts.name
Get-WmiObject -Class Win32_userprofile | Where-Object {($_.LocalPath -match "$Username") -and (!$_.special)} | % { $_.Delete()}}
You can see that I did alter some other aspects of the code to try to break it up so that the code runs on one profile at a time. This kind of works as it will start deleting folders based on the Modification date of the user profile folder but the problem is it will delete user profiles that may have been used yesterday, but the modification date of the user profile folder did not change. So what I need the script to do is:
1.Get all of the user profiles folders in the C:\users directory
- Go into the user profile folder and get the modification date of the appdata\local folder.
3.Then return only user profile folders that the appdata\local folder has not been modified in this case for 90 days.
I have tried some things to alter this code that seem to be dead ends. Could Someone help me figure this out?
CodePudding user response:
Alright.
Make sure you test this thouroughly before using in production.
I've added some helpful comments to decipher what you're trying to do inside this code.
$VerbosePreference = 'Continue'
## getting the name of the users
## this returns the filtered results below
$useraccounts = Get-ChildItem -Path $env:HOMEDRIVE\users\ | Where-Object lastwritetime -LT (Get-Date).AddDays(90)
## $useraccounts returns
<#
Directory: /Users
UnixMode User Group LastWriteTime Size Name
-------- ---- ----- ------------- ---- ----
drwxr-x--- jacoby staff 5/2/2022 19:04 1056 jacoby
drwxrwxrwt root wheel 3/26/2022 00:21 128 Shared
#>
## $useraccounts.name returns
<#
PS > $useraccounts.name
jacoby
Shared
#>
## if we use get-childitem | get-member we can see that we have access to a lot of
## properties returned like the .name value
#### we don't need any of this
####$sort = $useraccounts | ForEach-Object {$_.Name}
#####$removeaccounts = $sort -join '|'
## ok let's do something here
## for each account in my list
foreach ($account in $useraccounts) {
## let's write some info so we know what's happening
Write-Verbose -Message ('currently working with {0}' -f $account)
## we want to check moditfication time of folder so we gotta see if it exists
## we want to test c:\users\ the current account \ appdata\local
## $account.FullName gives us c:\users\accountname so we just need to add the rest
$testpath1 = $account.FullName '\appdata\local'
Write-Verbose -Message ('we generated the path {0}' -f $testpath1)
## imma give you some error checking
## this is ugly because it could be there but we can't access it
if ((Test-Path -Path $testpath1 -ErrorAction SilentlyContinue) -eq $true) {
## if the path exists here's what we'll do
## get a count of all the file modified in the last 90 days
$count = (Get-ChildItem -Path $testpath1 `
## -Recurse
## uncomment this if you want recurse or depth
| Where-Object {
$_.LastWriteTime -gt (Get-Date).AddDays(-90)
}
).Count
## now that we have a count we can test if the count is less than than 1 (0)
## that means no files in these folder were modified in the last 90 days
if ($count -lt 1) {
####
##
## this is the area where we can take action on the
## folders/files that have not been modified in the
## last 90 days
## you might delete them or just log them somewbere
##
####
Write-Verbose -Message 'no file modified in the last 90 days'
####
## this is your original deletion pipeline
##Get-WmiObject -Class Win32_userprofile | Where-Object {($_.LocalPath -match "$account") -and (!$_.special)} | Remove-WmiObject -whatif
##i have not tested this. be careful.
####
}
else {
Write-Verbose -Message ('{0} files have been modified in the last 90 days! We do not want delete this.' -f $count)
####
##
## these is the area where we can take action if the
## files/folder HAVE been modified recently
## we would NOT want to delete these files
##
####
}
}
## do some stuff before ending the for each loop
## maybe write our changes somewhere permanent
}
CodePudding user response:
Thanks again, "another victim of the mouse" for the help with the script. I have altered the script a bit for my environment
$VerbosePreference = 'Continue'
$ExcludedUsers="Public","default","defaultuser0","public","administrator"
$path = "$Env:SystemDrive\users"
## getting the name of the users
## this returns the filtered results below
$useraccounts = Get-ChildItem -Path $path -Exclude $ExcludedUsers | Where-Object {$_.lastwritetime -lt (Get-Date).AddDays(-30)}
## $useraccounts returns
<#
Directory: /Users
UnixMode User Group LastWriteTime Size Name
-------- ---- ----- ------------- ---- ----
drwxr-x--- jacoby staff 5/2/2022 19:04 1056 jacoby
drwxrwxrwt root wheel 3/26/2022 00:21 128 Shared
#>
## $useraccounts.name returns
<#
PS > $useraccounts.name
jacoby
Shared
#>
## if we use get-childitem | get-member we can see that we have access to a lot of
## properties returned like the .name value
#### we don't need any of this
####$sort = $useraccounts | ForEach-Object {$_.Name}
#####$removeaccounts = $sort -join '|'
## ok let's do something here
## for each account in my list
foreach ($account in $useraccounts)
{
## let's write some info so we know what's happening
Write-Verbose -Message ('currently working with {0}' -f $account)
## we want to check moditfication time of folder so we gotta see if it exists
## we want to test c:\users\ the current account \ appdata\local
## $account.FullName gives us c:\users\accountname so we just need to add the rest
$testpath1 = $account.FullName '\appdata\local'
Write-Verbose -Message ('we generated the path {0}' -f $testpath1)
## imma give you some error checking
## this is ugly because it could be there but we can't access it
if ((Test-Path -Path $testpath1 -ErrorAction SilentlyContinue) -eq $true)
{
## if the path exists here's what we'll do
## get a count of all the file modified in the last 30 days
$count = (Get-ChildItem -Path $testpath1 | Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-30) }).Count
## now that we have a count we can test if the count is less than than 1 (0)
## that means no files in these folders were modified in the last 30 days
if ($count -lt 1) {
####
##
## this is the area where we can take action on the
## folders/files that have not been modified in the
## last 30 days
## you might delete them or just log them somewbere
##
####
Write-Verbose -Message 'no file modified in the last 30 days'
####
## this is your original deletion pipeline
#Get-WmiObject -Class Win32_userprofile | Where-Object {($_.LocalPath -contains "$account") -and (!$_.special)} | Remove-WmiObject -whatif
##i have not tested this. be careful.
####
}
else {
Write-Verbose -Message ('{0} files have been modified in the last 30 days! We do not want delete this.' -f $count)
####
##
## these is the area where we can take action if the
## files/folder HAVE been modified recently
## we would NOT want to delete these files
##
####
}
}
## do some stuff before ending the for each loop
## maybe write our changes somewhere permanent
}
I first changed $env:HomeDrive to $env:SystemDrive because my environment is set up differently. I also added an $ExcludeUsers so that it does not grab the Administrator or other system user profiles folders. Thanks to the changes you made, Remove-WmiObject works for the multiple profiles in the Users folder, and I was able to delete over 20 profiles by running this script once. For some reason, I cannot figure out, -match no longer works. It does not treat the $account as a full localpath, so I changed it to -contains, and that seems good enough for me. For everyone else, be sure thoroughly test the script before using it. It is a very powerful script.