Remove-LocalProfilesOlderThan
User Management: Safely deletes local user profiles older than N days
#Requires -Version 5.1
[CmdletBinding(SupportsShouldProcess = $true)]
Param(
[Parameter(Mandatory = $true)]
[int]$Days
)
Process {
try {
if ($Days -le 0) {
throw "Days parameter must be a positive integer."
}
$thresholdDate = (Get-Date).AddDays(-$Days)
Write-Verbose "Threshold date is set to: $thresholdDate"
$profiles = Get-CimInstance -ClassName Win32_UserProfile -ErrorAction Stop
$results = @()
foreach ($profile in $profiles) {
# Skip system special profiles
if ($profile.Special) {
Write-Verbose "Skipping system special profile: $($profile.LocalPath)"
continue
}
# Skip currently loaded/logged-in profiles
if ($profile.Loaded) {
Write-Verbose "Skipping currently loaded profile: $($profile.LocalPath)"
continue
}
# Skip Default or Public folders
if ($profile.LocalPath -like "*\Default" -or $profile.LocalPath -like "*\Public") {
Write-Verbose "Skipping default/public profile: $($profile.LocalPath)"
continue
}
# If LastUseTime is null, fallback to the directory last write time
$lastUsed = $profile.LastUseTime
if ($null -eq $lastUsed -and (Test-Path -Path $profile.LocalPath)) {
$lastUsed = (Get-Item -Path $profile.LocalPath).LastWriteTime
}
if ($null -eq $lastUsed) {
Write-Verbose "Skipping profile with no LastUseTime or path: $($profile.LocalPath)"
continue
}
if ($lastUsed -lt $thresholdDate) {
$status = "Pending Deletion"
if ($PSCmdlet.ShouldProcess($profile.LocalPath, "Delete user profile (SID: $($profile.SID))")) {
try {
# Delete the profile using CIM method (cleanest and official way)
Remove-CimInstance -CimInstance $profile -ErrorAction Stop
$status = "Deleted Successfully"
}
catch {
$status = "Deletion Failed: $_"
Write-Error "Failed to delete profile $($profile.LocalPath): $_"
}
}
$results += [PSCustomObject]@{
SID = $profile.SID
LocalPath = $profile.LocalPath
LastUsed = $lastUsed
Status = $status
}
}
}
if ($results.Count -eq 0) {
Write-Verbose "No inactive profiles found older than $Days days ($thresholdDate)."
} else {
Write-Output $results
}
}
catch {
Write-Error $_
throw
}
}The age threshold in days. Profiles not used for longer than this period will be deleted.