- Use PowerShell splatting and PSBoundParameters to pass parameters - Wed, Nov 9 2022
- Using PowerShell with $PSStyle - Mon, Jan 24 2022
- Clean up user profiles with PowerShell - Mon, Jun 9 2014
#requires -version 3.0 #Remove-UserProfile.ps1 [cmdletbinding(SupportsShouldProcess)] Param( [Parameter(Position=0)] [ValidateNotNullorEmpty()] [int]$Days=30 ) Start-Transcript -Path C:\ProfileCleanup.txt -Append Write-Warning "Filtering for user profiles older than $Days days" Get-CimInstance win32_userprofile -Verbose | Where {$_.LastUseTime -lt $(Get-Date).Date.AddDays(-$days)} | Remove-CimInstance -Verbose Stop-Transcript
The script searches for all profiles using the Win32_UserProfile WMI class that have not been used in X number of days, where the default is 30. I’m not using a WMI filter because I’d have to create a filter using a date in the DMTF which is more work than I care to do. There shouldn’t be that many profiles so using Where-Object is acceptable in this case and definitely easier. Any profiles that meet the requirement will be removed using Remove-CimInstance.
I also added code to create a transcript file so I’d have a way of tracking what happened at startup. Be aware that if you test the script in the PowerShell ISE you will get an error since that host does not support transcription. Also, if you use –WhatIf to test you’ll also get an error on the Stop-Transcript line since transcription will never have been started. Technically, I don’t need the Stop-Transcript command since transcription will end as soon as the PowerShell session ends, but I wanted to be thorough. If you try this script, feel free to comment out the last line.
Setup
As before, I created a GPO but this time navigated to Computer Configuration – Policies – Windows Settings – Scripts and double-clicked on Startup.
Startup scripts in Group Policy
On the PowerShell Scripts tab I clicked on Show Files and copied the script to the GPO so it would replicate. Then I could add the script and set a parameter value.
Add PowerShell script to startup scripts
The script has a default value of 30 but in the screenshot I am setting it to 45 days. Click OK a few times to save the policy. Make sure it is linked and enabled to an organizational unit and reboot a test computer running Windows 7 or later.
Testing
The best way to test this is with a virtual machine that has a few profiles. You may need to wait a few days to “age” them. Then modify the GPO to adjust the number of days to meet your test age. Take a snapshot of the virtual machine before rebooting so that you can restore and test again if necessary. Or you can revise the script to filter for a specific user profile. Depending on your GPO configuration, you might not see the transcript file if you logon immediately. But give it a few minutes. If you still don’t see anything, then check the System and Group Policy Operational event logs. You also might want to simply run the script manually to see what happens. Again, having a snapshot to roll back will be valuable. Finally, don’t forget to take replication into account if you are making changes to the script or parameter values. You may also want to run gpupdate on the desktop prior to rebooting as well.
Editing tip
Once you set up the policy using the Group Policy management console, you can skip the GUI for revising the script or parameters. While, using the GUI is probably the recommended approach, at least for testing purposes you can access the script and it’s configuration through Windows Explorer. Go to \\yourdomain\sysvol\yourdomain\policies.
Access Group Policy startup script in Windows Explorer
I sorted on Date Modified to find my policy which I’ve highlighted in the screenshot above. Open up the folder and navigate to the Machine\Scripts\Startup.
Startup folder of the policy
You should be able to see the script. You can edit it directly or copy a new version to this folder and let it replicate. Parameter settings are stored elsewhere. Go back up to the Script folder. You may need to enable Explorer to show hidden files. But when you do, you should get something like in the screenshot below.
pssscripts.ini for PowerShell scripts
The pssscripts.ini file is for PowerShell scripts. The scripts.ini is for traditional scripts. You can edit the ini directly in Notepad.
Parameter settings of the PowerSgell startup script
Here you can see my parameter value of 45. If I want to change it, I can do so here, save the file and let it replicate to my domain controllers. This technique will also work for user scripts. But if you are more comfortable using the GUI, then by all means continue to use the Group Policy management console.
Summary
Once you know how to use PowerShell and can write a basic script, you can take advantage of Group Policy and add a whole new level of administration. I love this “set it and forget” approach, although as with any Group Policy setting, be sure to document and test it thoroughly. Now, what sort of tasks do you want to automate for users and computers using PowerShell and Group Policy?
Nice script, when you remove the WMI instance, does it remove the profile directory in the c:\users too?
In my testing it deleted the directory as well, but please test in a non-production environment to verify.
Will this leave local accounts intact?
This cleans up profiles regardless of whether they belong to local or domain accounts. You can always adjust your WMI query to ignore profiles belonging to local accounts. As long as the account is relatively active it shouldn’t matter. Personally, even if it is a local account, if the profile is 1 year old I’d just assume see it gone.
Well, I guess you would want to leave the profiles of local administrators untouched…
Again, if the local admin account has not logged on in a while I have no problem wiping the profile. But you could filter them out.
Get-CimInstance win32_userprofile -filter “NOT localpath like ‘%Administrator%'”
Get-CimInstance win32_userprofile -Verbose | Where {($_.LastUseTime -lt $(Get-Date).Date.
AddDays(-$days)) -and ($_.Special -ne $true) -and ($_.LocalPath -ne “C:\Users\Administrator”)}
I modified mine a bit to exclude special profiles, and the local administrator profile.
Thanks for the example!
I believe this would be even more useful if it were a logoff script for privileged accounts – leaving behind cached passwords for admin-level accounts on workstations is bad juju. Any way to accomplish that?
Kurt
I don’t know offhand of a way to clear cached passwords. I’d look to Group Policy to disable password caching.
Unfortunately, the only GP that I know of (to prevent password caching) targets the computer, and not user accounts, so would affect all user accounts on the machine. I wonder if setting this up as a scheduled task (perhaps once a day), would be a decent approach.
Kurt
The Group Policy “Delete User Profiles Older than a Specified Number of Days on System Restart” could address the exact need (not to destroy the PowerShell playing around – which could be usefull too):
http://blogs.technet.com/b/askperf/archive/2009/11/03/just-me-and-my-profile-part-2.aspx
Hi,
nice script, is it possible to generate a log where I can see the names not the SID of the profiles which were removed?
Thanks
You would need to add some code to convert the SID to a friendly name. You could use WMI
[wmi]”\\Win81-ent-01\root\cimv2:Win32_SID.SID=’$SID'”
or CIM
Get-WSManInstance -ResourceURI “wmicimv2/win32_sid” -SelectorSet @{SID=”$sid”} -ComputerName $computername
Any reason this would not work on a Windows 10 machine?
I have no reason to think it wouldn’t but you’ll have to test.
I like this script, however, when I tried it, it did remove the profiles, but didn’t completely remove the folders. I had to manually remove the folders. I frequently get the error message that the folder is not empty. The powershell error was: Remove-CimInstance : The directory is not empty. How can we get the script to completely remove the directory? Thanks, Butch
You could modify the script to use a Try/Catch block. In the Catch block add code to remove the folder path with -force. Or modify the script to first delete all files in the path and then remove the instance. If you go that route I’d also use Try/Catch so that if there is an error removing any files, you don’t try to remove the profile.
Good morning Early Bird! Would you be so kind as to provide an example? I’m sort of a newby when it comes to using Powershell. Thanks a bunch
Hello Jeff
I want to help
How to remove folder InDesign on folder roaming user with powershell script or batch file
example username : lim.ping
\\hpz230-003\c$\Users\lim.ping\AppData\Roaming\Adobe\InDesign
Thxs
I created this small function, it doesn't delete users with specific names, like sql, .net.
Doesn't work in windows 2003…
Even better than my previous comment, please check my git repository for that funcion but improved :):
https://github.com/p0w3rb0y/PSfunctions/blob/master/remove-userprofile.ps1
Thanks for the script! Everything works except the empty folder is left behind. I am testing the script as my Tech admin and it leaves the user folders behind. Think it is a permissions issue because when I click on the user folder for the first time it states "You don't currently have permissions to access this folder. Click Continue to permanently get access to this folder" Once I click continue I can access this. So I want to assume if this script is run from GPO it will run as an domain admin or am I completely off with this?
Thanks!
Hello
is it possible to makes exception to exclude profile not to be deleted.
thanks