-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRemove-LocalUserProfiles.psm1
More file actions
241 lines (205 loc) · 7.38 KB
/
Remove-LocalUserProfiles.psm1
File metadata and controls
241 lines (205 loc) · 7.38 KB
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# Documentation home: https://github.com/engrit-illinois/Remove-LocalUserProfiles
# By mseng3
function Remove-LocalUserProfiles {
param(
# Delete profiles that are older than the given number of days
[Parameter(Mandatory=$true)]
[int]$DeleteProfilesOlderThan,
[switch]$UseMinsInsteadOfDays,
# Timeout gracefully via self-regulation because MECM Run Scripts feature will timeout ungracefully at 60 mins
# Recommended to make this a few minutes less than the expected ungraceful timeout
[Parameter(Mandatory=$true)]
[int]$TimeoutMins,
# Comma-separated list of NetIDs
[string]$ExcludeUsers,
# A starting lowball (i.e. minimum) estimate for how long it will take to delete a single profile
# This will become more accurate once we clock the actual deletions
[int]$DeletionTimeEstimateMins = 1,
[string]$Log = "c:\engrit\logs\Remove-LocalUserProfiles_$(Get-Date -Format `"yyyy-MM-dd_HH-mm-ss`").log",
[string]$TSVersion = "unspecified",
[switch]$Loud
)
$SCRIPT_VERSION = "v1.7"
function log($msg) {
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss:ffff"
$msg = "[$ts] $msg"
if($Loud) {
Write-Host $msg
}
if(!(Test-Path -PathType leaf -Path $Log)) {
New-Item -ItemType File -Force -Path $Log | Out-Null
}
$msg | Out-File $Log -Append
}
function Quit($msg) {
log "Quitting with message: `"$msg`"."
$quitTime = Get-Date
$runTime = New-TimeSpan -Start $startTime -End $quitTime
log "Total runtime: `"$runTime`""
Write-Output $msg
exit
}
$startTime = Get-Date
$endTime = $startTime.AddMinutes($TimeoutMins)
$timedOut = $false
$profilesCount = 0
$profilesAttempted = 0
$profilesDeleted = 0
$profilesFailed = 0
log "Script version: `"$SCRIPT_VERSION`""
log "TS version: `"$TSVersion`""
log "-DeleteProfilesOlderThan: `"$DeleteProfilesOlderThan`""
log "-ExcludeUsers: `"$ExcludeUsers`""
if($DeleteProfilesOlderThan -lt 0) {
Quit "-DeleteProfilesOlderThan value is less than 0!"
}
else {
$oldestDate = (Get-Date).AddDays(-$DeleteProfilesOlderThan)
if($UseMinsInsteadOfDays) { $oldestDate = (Get-Date).AddMinutes(-$DeleteProfilesOlderThan) }
log "oldestDate = $oldestDate"
log "Getting profiles..."
try {
#$profiles = Get-WMIObject -ClassName "Win32_UserProfile"
$profiles = Get-CIMInstance -ClassName "Win32_UserProfile" -OperationTimeoutSec 300
}
catch {
log ($_ | ConvertTo-Json | Out-String)
Quit "Failed to retrieve profiles with Get-CIMInstance!"
}
if(!$profiles) {
Quit "Profiles found is null!"
}
else {
$count = @($profiles).count
$profilesCount = $count
if($count -lt 1) {
Quit "Zero profiles found!"
}
else {
log " Found $count profiles."
log "Filtering profiles to those older than $DeleteProfilesOlderThan days..."
$profiles = $profiles | Where { $_.LastUseTime -le $oldestDate }
$count = @($profiles).count
if($count -lt 1) {
Quit "Zero profiles older than $DeleteProfilesOlderThan days old were found."
}
else {
log " $count profiles remain."
log "Filtering out system profiles..."
$profiles = $profiles | Where { $_.LocalPath -notlike "*$env:SystemRoot*" }
$count = @($profiles).count
if($count -lt 1) {
Quit "Zero non-system profiles older than $DeleteProfilesOlderThan days old were found."
}
else {
log " $count profiles remain."
if($ExcludeUsers) {
log "-ExcludeUsers was specified: `"$ExcludeUsers`""
$users = $ExcludeUsers.Split(",")
$users = $users.Replace("`"","")
log " users: $users"
log " Filtering out excluded users..."
foreach($user in $users) {
log " Filtering out user: `"$user`"..."
$profiles = $profiles | Where { $_.LocalPath -notlike "*$user*" }
}
$count = @($profiles).count
if($count -lt 1) {
Quit "Zero non-system, non-excluded profiles older than $DeleteProfilesOlderThan days old were found."
}
else {
log " $count profiles remain."
}
}
else {
log "No -ExcludeUsers were specified."
}
log "Deleting remaining profiles..."
$profiles = $profiles | Sort LocalPath
$profilesAttempted = @($profiles).count
# Keep track of how much time it takes roughly to delete a profile
# Start out with a lowball value (since we have plenty of time), and
# update the estimate every time a profile takes longer to delete.
$estDeleteTime = New-TimeSpan -Minutes $DeletionTimeEstimateMins
log " Initial estimated deletion time: `"$estDeleteTime`""
foreach($profile in $profiles) {
log " Processing profile: `"$($profile.LocalPath)`"..."
# Check that there's at least enough time to delete one more profile
# before the configured timeout occurs
log " Current estimated deletion time: `"$estDeleteTime`""
$currentTime = Get-Date
$timeLeft = New-TimeSpan -Start $currentTime -End $endTime
log " Time left before timeout: `"$timeLeft`""
if($timeLeft -lt $estDeleteTime) {
log " Looks like there's probably not enough time for any more deletions."
$timedOut = $true
break
}
else {
log " Looks like there's probably enough time for at least one more deletion."
log " Deleting profile..."
$startDeleteTime = Get-Date
$startDeleteDeletions = $profilesDeleted
try {
# Delete() method works with Get-WMIObject, but not with Get-CIMInstance
# https://www.reddit.com/r/PowerShell/comments/7qu9dg/inconsistent_results_with_calling_win32/
#$profile.Delete()
$profile | Remove-CIMInstance
log " Profile deleted."
$profilesDeleted += 1
}
catch {
log " Failed to delete profile."
log ($_ | ConvertTo-Json | Out-String)
$profilesFailed += 1
}
$endDeleteTime = Get-Date
$deleteTime = New-TimeSpan -Start $startDeleteTime -End $endDeleteTime
if($profilesDeleted -gt $startDeleteDeletions) {
log " Time taken to delete: `"$deleteTime`""
if($deleteTime -gt $estDeleteTime) {
log " Deletion took longer than current estimate. Updating to `"$deleteTime`"."
$estDeleteTime = $deleteTime
}
}
}
}
log "Done deleting profiles."
}
}
}
}
}
log "Profiles total: $profilesCount"
log "Filtered profiles targeted for deletion: $profilesAttempted"
log "Targeted profiles successfully deleted: $profilesDeleted"
log "Targeted profiles failed to delete: $profilesFailed"
if($profilesFailed -lt 1) {
if($timedOut) {
Quit "Timeout reached. Before that, all targeted profiles were deleted successfully."
}
else {
Quit "All targeted profiles were deleted successfully."
}
}
else {
if($profilesFailed -eq $profilesAttempted) {
if($timedOut) {
Quit "Timeout reached. Before that, all targeted profiles failed to delete!"
}
else {
Quit "All targeted profiles failed to delete!"
}
}
else {
if($timedOut) {
Quit "Timeout reached. Before that, some, but not all targeted profiles failed to delete."
}
else {
Quit "Some, but not all targeted profiles failed to delete."
}
}
}
Quit "Unknown result."
log "EOF"
}