Stale Resource Cleanup on Windows 10 Devices

Damon Maranya 26 Reputation points
2022-05-24T15:51:34.083+00:00

I have been tasked with finding a way to clean up stale AD account logins and their associated user directories from our fleet of Windows 10 devices. But I am having trouble finding a way to do that.

I can find the local accounts on the device, and I can find the local user accounts get-localuser and I can find the AD accounts using get-aduser. But I neither of those pulls a list of non-local users that have logged into the test device. Just local and AD account respectively.

I can pull all accounts on a device using get-wmiobject but the date format used for the LasUseTime attribute does not appear to be compatible with the output of a get-time command and I can't find anywhere that explains what the format is and how to pull a compatible date. As a result, the code below does not trigger any accounts on the test machine. Despite the fact that some of them have not logged in since last year sometime.

$Users = Get-WmiObject -Class Win32_UserProfile

foreach ($User in $Users)
{

If ($User.LastUseTime -lt (Get-Date).AddDays(-90)) {(Remove-WmiObject -Class Win32_UserProfile -WhatIf)}

}

I can also pull the accounts using get-ciminstance, which uses a compatible date/time format. Running the code above with get and remove-wmiobject replaced with get and remove-ciminstance it completes successfully. But the LastUseTime shows the same date and time for all CIM instance which is todays date and the time that the script was last run. So the date condition in the IF statement is not triggered.

Does anyone have any idea how I might go about doing this?

Windows 10
Windows 10
A Microsoft operating system that runs on personal computers and tablets.
10,541 questions
Active Directory
Active Directory
A set of directory-based technologies included in Windows Server.
5,809 questions
Windows Server PowerShell
Windows Server PowerShell
Windows Server: A family of Microsoft server operating systems that support enterprise-level management, data storage, applications, and communications.PowerShell: A family of Microsoft task automation and configuration management frameworks consisting of a command-line shell and associated scripting language.
5,346 questions
0 comments No comments
{count} votes

Accepted answer
  1. Newbie Jones 1,306 Reputation points
    2022-05-25T16:33:49.057+00:00

2 additional answers

Sort by: Most helpful
  1. Rich Matheisen 44,541 Reputation points
    2022-05-24T19:00:39.85+00:00

    Like this:

    $User.ConvertToDateTime($User.LastUseTime)
    

  2. Rich Matheisen 44,541 Reputation points
    2022-05-26T02:39:59.1+00:00

    This took a bit of digging, but I think I have the way to get the last time a profile was loaded (as opposed to just being modified):

    Get-ChildItem "hklm:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\" |   #Get all local profiles
        ForEach-Object{
            $n = $_.name -replace 'hkey_local_machine','hklm:'
            if ([byte[]]$ba = (Get-ItemProperty $n).sid){   # not every profile has a SID (even though its name may be a SID!)
                $sid = (New-Object System.Security.Principal.SecurityIdentifier($ba, 0)).toString()
                if ((Get-ItemProperty $n).LocalProfileLoadTimeHigh){    # not every profile has been loaded
                    # convert the high/low values to a hex string
                    # convert the hex string to an unsigned 64-bit integer
                    # treat the value a filetime and conver that to a DateTime object
                    [uint64]$ftime = "0X{0:X8}{1:X8}" -f (Get-ItemProperty $n).LocalProfileLoadTimeHigh, (Get-ItemProperty $n).LocalProfileLoadTimelow
                    $LastLoaded = [DateTime]::FromFileTime($ftime)
                    [PSCustomObject]@{
                        SID = $sid
                        LocalProfileLoadTime = $LastLoaded
                    }
                }
            }
        }
    

    That code produces PSCustomObjects that look like this:

    SID                                            LocalProfileLoadTime
    ---                                            --------------------
    S-1-5-21-1671195940-3624339693-1328601057-1001 5/25/2022 9:50:43 AM
    

    The time on my profile (above) matches the time I logged on, not the current time.

    You can use "Get-CimInstance -Class Win32_UserProfile" and compare their SIDs to the SIDs from the above code. If they match, compare the LocalProfileLoadTime to 90-day window and if the result is what you want, pipe the SimInstance to Remove-CimInstance.

    But this still leave open the question of what are you going to do about all the data for that user? Were they using OneDrive? How will you deal with authentication to remove the synchronization before removing the local files (i.e. you don't want to remove the files from OneDrive, just the local copies). What about applications the user may have installed? Etc., etc., etc.