-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSetup-UserProfileCleanupTask.ps1
188 lines (173 loc) · 10.7 KB
/
Setup-UserProfileCleanupTask.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
Remove-Variable * -ErrorAction SilentlyContinue; Remove-Module *; $error.Clear();
function Remove-UserProfiles
{
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$DeleteProfileOlderInDays,
[Parameter(Mandatory = $true)]
[string]$WorkingFolder
)
#This will log the script and how it goes
Start-Transcript -Path "C:\WIndows\Temp\Setup-UserProfileCleanupTask.log"
#Gets the current date
$TodayDate = Get-Date
#
##
###
#START - CREATE SCHEDULED TASK to record logon time stamps
#Initially just put the script into the C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp folder but if a user didn't logon daily it wouldn't be accurate. For example user logons, never reboots, and stays logged in for weeks.
#Adjusted the get-scheduledtasks check below as when run on some computers, it was getting "The System Cannot Find the file specified", expanding it out with the where-object solved this.
$ScheduledTaskCheck = Get-ScheduledTask -TaskPath "\" -erroraction SilentlyContinue | where-object { ($_.TaskName -match "User Logon Task - Write User Last Logon Date") }
If ($ScheduledTaskCheck -ne $null)
{
Write-Host "Scheduled Task is present, no need to create."
}
Else
{
Write-Host "scheduled task is not present, will create"
#This help me get it to run for any user they may logon
#https://www.reddit.com/r/PowerShell/comments/qc469s/create_a_scheduled_task_to_run_as_logged_on_user/
$TaskName1 = "User Logon Task - Write User Last Logon Date"
$Description1 = "This scheduled task will run at logon and continuously every 60 minutes for any logged in user. It will write todays date to c:\Users\username\LastLogonDate.txt"
$taskAction1 = New-ScheduledTaskAction -Execute "wscript.exe" -Argument "WriteLastLogonDate.vbs //B //Nologo" -WorkingDirectory "$WorkingFolder"
$taskTrigger1 = New-ScheduledTaskTrigger -AtLogOn
$taskTrigger2 = New-ScheduledTaskTrigger -RepetitionDuration (New-TimeSpan -Days (365 * 20)) -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 60) -Once
$TaskSettings1 = New-ScheduledTaskSettingsSet -StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$TaskPrinciple = New-ScheduledTaskPrincipal -GroupId "Users"
Register-ScheduledTask -TaskName $taskName1 -Action $taskAction1 -Trigger $taskTrigger1, $taskTrigger2 -Description $description1 -Settings $TaskSettings1 -Principal $TaskPrinciple
}
#END - CREATE SCHEDULED TASK to record logon time stamps
###
##
#
#Performs Checks to ensure that all the pieces are in place, before allowing the script to run.
$verifyPS1 = Test-Path -Type Leaf "$WorkingFolder\WriteLastLogonDate.ps1"
$verifyVBS = Test-Path -Type Leaf "$WorkingFolder\WriteLastLogonDate.vbs"
$verifyScheduledTask = Get-ScheduledTask -TaskPath "\" -erroraction SilentlyContinue | where-object { ($_.TaskName -match "User Logon Task - Write User Last Logon Date") }
If (($verifyPS1 -eq $true) -and ($verifyVBS -eq $true) -and ($verifyScheduledTask -ne $null))
{
Write-Host "The WriteLastLogonDate.vbs, WriteLastLogonDate.ps1, and the Scheduled Tasks all exist."
#START - SECTION FOR LOGGING PURPOSES ONLY
#This give a list of all accounts after process is run
Write-Host "---------------------------------------------------"
Write-Host "LIST OF USERS ACCOUNTS ON COMPUTER (BEFORE CLEANUP)"
Write-Host "---------------------------------------------------"
#Below will retrieve all user profile entries in the registry minus the special accounts of SYSTEM, NetworkService, and LocalService. It will also exclude those have empty localpath (ex. C:\Users\username), It will also avoid profiles that part of serviceprofiles (ex. SQL)
$listofProfilesLoggingOnly = Get-CimInstance -Class Win32_UserProfile | Where-Object { ($_.Special -ne $true) -and ($_.localpath -ne $null) -and ($_.localpath -notlike "*WINDOWS\ServiceProfiles*") }
#Retrieves a list of profiles that have a null localpath, this may mean they are corrupt
$listofNULLProfilesLoggingOnly = Get-CimInstance -Class Win32_UserProfile | Where-Object { ($_.localpath -eq $null) }
Foreach ($p in $listofProfilesLoggingOnly)
{
$usernameLoggingOnly = $($p.localpath).Replace("C:\Users\", "")
Write-Host $usernameLoggingOnly
}
Foreach ($p in $listofNULLProfilesLoggingOnly)
{
Write-Host "NULL Profile Path Detected: The SID $($p.sid) doesn't have a profile path defined. It may be corrupt. Check C:\Users for usernames not displaying on the list above to identify."
}
#END - SECTION FOR LOGGING PURPOSES ONLY
#START - USER PROFILE CHECKING AND REMOVAL
Write-Host "---------------------------------------------------------------------------------------------------------"
Write-Host "USER PROFILE CHECKING AND REMOVAL --- Will remove profiles older than $DeleteProfileOlderInDays days."
Write-Host "---------------------------------------------------------------------------------------------------------"
#Below will retrieve all user profile entries in the registry minus the special accounts of SYSTEM, NetworkService, and LocalService. It will also exclude those have empty localpath (ex. C:\Users\username), It will also avoid profiles that part of serviceprofiles (ex. SQL)
$ListOfUsersProfiles = Get-CimInstance -Class Win32_UserProfile | Where-Object { ($_.Special -ne $true) -and ($_.localpath -ne $null) -and ($_.localpath -notlike "*WINDOWS\ServiceProfiles*")}
#Goes through each user account
foreach ($user in $ListOfUsersProfiles)
{
$username = $($user.localpath).Replace("C:\Users\", "")
#
##
#START - WRITE THE LastLogonDate.txt FILE IF NOT PRESENT
<#
Due to there being no sure way of determine profile age, there is another script that runs for all users at logon that writes a file to the user profile called LastLogonDate.txt
The below will make sure any existing profiles on the machine have one generated based off the ntuser.dat file if it is present. Some service accounts,
or accounts that never fully logon won't be removed by this script as they will not have a ntuser.dat file. This is to ensure old stale accounts will eventually be removed once
the LastLogonDate.txt doesn't get written to again.
#>
$testUserProfilepath = Test-Path -Type Container "C:\Users\$username"
$testUserLogonFile = Test-Path -Type Leaf "C:\Users\$username\LastLogonDate.txt"
If (($testUserProfilepath -eq $true) -and ($testUserLogonFile -eq $false))
{
#Because the user is missing the LastLogonDate.txt file it will generate one with the LastAccessDate associated with the User USRCLASS.DAT File.
#This is only designed to run once per profile, once a LastLogonDate.txt file exists this will never run again.
$USRClassDat = Get-Item "C:\Users\$Username\AppData\Local\Microsoft\Windows\UsrClass.dat" -Force
"$(Get-Date -Date $USRClassDat.LastWriteTime -uformat "%Y/%m/%d")" | Out-File -FilePath "C:\Users\$username\LastLogonDate.txt" -Encoding ascii
}
#END - WRITE THE LastLogonDate.txt FILE IF NOT PRESENT
##
#
#Checks if the file below exists as it is used to determine last used time, if it doesn't exist nothing happens.
IF ((Test-Path -Type Leaf C:\Users\$username\LastLogonDate.txt) -eq $true)
{
#Retrieve the date from the file and convert to date
$LastLogonDate = Get-Content C:\Users\$username\LastLogonDate.txt | Get-Date
#checks is the last logon is less than today date minus age variable
If ($LastLogonDate -lt $($TodayDate.AddDays(-$DeleteProfileOlderInDays)))
{
# ---------->>> ACCOUNT EXCLUSIONS HERE <<<----------
#Exclusions are listed here, to ensure they are not processed. YOU CAN ADJUST THE IF STATEMENT AS NEEDED. There are not case sensitive. If using a partial username to filter out use the -notlike one.
#Recommend that you keep "Default" account in this list.
If ($username -ne "Default")
{
Write-Host "USER REMOVED $username : Last used on $LastLogonDate --- Profile is older than $DeleteProfileOlderInDays days, will be removed."
Write-Host "USER REMOVED $username : Win32_UserProfile removal proccess STARTED."
#Will attempt to delete the User Profile using the Win32_UserProfile method
Get-CimInstance -Class Win32_UserProfile | Where-Object { $_.SID -eq $user.sid } | Remove-CimInstance
Write-Host "USER REMOVED $username : Win32_UserProfile removal proccess FINISHED."
}
else
{
Write-Host "USER ON EXCLUSION LIST $username : Profile is older than $DeleteProfileOlderInDays days but is excluded"
}
}
else
{
"USER EXCLUDED $username : Last used on $LastLogonDate ---- Profile has been used in the last $DeleteProfileOlderInDays days."
}
}
$username = $null
$LastLogonDate = $null
$testUserProfilepath = $null
$testUserLogonFile = $null
}
#END - PROFILE PARSING AND REMOVAL
#START - SECTION FOR LOGGING PURPOSES ONLY
#This give a list of all accounts after process is run
Write-Host "---------------------------------------------------"
Write-Host "LIST OF USERS ACCOUNTS ON COMPUTER (AFTER CLEANUP)"
Write-Host "---------------------------------------------------"
#Below will retrieve all user profile entries in the registry minus the special accounts of SYSTEM, NetworkService, and LocalService. It will also exclude those have empty localpath (ex. C:\Users\username), It will also avoid profiles that part of serviceprofiles (ex. SQL)
$listofProfilesLoggingOnly = Get-CimInstance -Class Win32_UserProfile | Where-Object { ($_.Special -ne $true) -and ($_.localpath -ne $null) -and ($_.localpath -notlike "*WINDOWS\ServiceProfiles*")}
#Retrieves a list of profiles that have a null localpath, this may mean they are corrupt
$listofNULLProfilesLoggingOnly = Get-CimInstance -Class Win32_UserProfile | Where-Object { ($_.localpath -eq $null) }
Foreach ($p in $listofProfilesLoggingOnly)
{
$usernameLoggingOnly = $($p.localpath).Replace("C:\Users\", "")
Write-Host $usernameLoggingOnly
}
Foreach ($p in $listofNULLProfilesLoggingOnly)
{
Write-Host "NULL Profile Path Detected: The SID $($p.sid) doesn't have a profile path defined. It may be corrupt. Check C:\Users for usernames not displaying on the list above to identify."
}
#END - SECTION FOR LOGGING PURPOSES ONLY
}
else
{
Write-Host "One of the WriteLastLogonDate.vbs, WriteLastLogonDate.ps1, and/or the Scheduled Tasks DO NOT exist."
}
Stop-Transcript
}
$CdPath = "$($PSScriptRoot)"
$CdPathInfo=[System.Uri]$CdPath
if($CdPathInfo.IsUnc){
Push-Location "$($CdPathInfo.OriginalString)"
$CurrentDirectory = Get-Location
} else {
Push-Location "$($PSScriptRoot)"
$CurrentDirectory = Get-Location
}
"$($CurrentDirectory)\Update-NtUserDat.ps1"
Remove-UserProfiles -DeleteProfileOlderInDays 90 -WorkingFolder "$($CurrentDirectory.ProviderPath)"
Pop-Location