windowspowershellprofileuser-management

Delete user profiles based on the date of the Appdata\Local folder


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

  1. 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?


Solution

  • 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
    }