-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathDeleteDuplicateEndpoints.ps1
More file actions
232 lines (184 loc) · 9.23 KB
/
DeleteDuplicateEndpoints.ps1
File metadata and controls
232 lines (184 loc) · 9.23 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
# Name: DeleteDuplicateEndpoints.ps1
# Description: Script is designed to look for duplicate endpoints in your console and delete all but the newest example of said endpoint.
# Documentation: https://github.com/Action1Corp/PSAction1/
# Use Action1 Roadmap system (https://roadmap.action1.com/) to submit feedback or enhancement requests.
# WARNING: Carefully study the provided scripts and components before using them. Test in your non-production lab first.
# Action1 Public Repository Material
# Subject to TERMS_OF_USE.md
# Provided AS IS
# Use at your own risk
# Review and test before production deployment
# © Action1 Corporation
# Comment out below import/set-action1credentials if not needed, or preformed prior
Install-Module -Name PSAction1 -Force
Set-Action1Credentials -APIKey '<Insert API Key Here>' -Secret '<Insert Secret Here>'
Set-Action1DefaultOrg -Org_ID '<Insert Org_ID here>'
Set-Action1Region -Region '<Enter Region Here>'
try {
Write-Host "[INFO] Starting endpoint processing..."
$endpointsToCheck = @{}
$duplicatedEndpoints = New-Object -TypeName System.Collections.ArrayList
$endpoints = Get-Action1 -Query Endpoints
if (-not $endpoints) {
throw "[ERROR] No any endpoints returned"
}
$endpoints | ForEach-Object {
try {
$currentEndpoint = $_
if (-not $currentEndpoint) {
Write-Host "[WARN] Skipping null endpoint object" -ForegroundColor Yellow
continue
}
$macKey = $currentEndpoint.mac
if (-not $macKey) {
Write-Host "[WARN] Skipping object due to missing MAC for Endpoint: $($currentEndpoint.Id)" -ForegroundColor Yellow
continue
}
$last_seen = $currentEndpoint.last_seen
if (-not $last_seen) {
Write-Host "[WARN] Skipping object due to missing last_seen for Endpoint: $($currentEndpoint.Id)" -ForegroundColor Yellow
continue
}
try {
$parts = $last_seen -split '_'
if ($parts.Count -lt 2) {
throw "Invalid last_seen format for Endpoint: $($currentEndpoint.Id)"
}
$datePart = $parts[0]
$timePart = $parts[1] -replace '-', ':'
$dateString = "$datePart $timePart"
$TimeParsed = [datetime]::MinValue
$format = 'yyyy-MM-dd HH:mm:ss'
if (-not [datetime]::TryParseExact($dateString, $format, $null, [System.Globalization.DateTimeStyles]::None, [ref]$TimeParsed)) {
throw "Failed to parse datetime: $dateString"
}
}
catch {
Write-Host "[ERROR] last_seen Date parsing failed for Endpoint: $($currentEndpoint.Id)"
continue
}
# Ensure MAC key is valid
if ([string]::IsNullOrWhiteSpace($macKey)) {
Write-Host "[WARN] Empty MAC detected, skipping Endpoint $($currentEndpoint.Id) processing." -ForegroundColor Yellow
continue
}
$currentEndpointObject = [PSCustomObject]@{
id = $currentEndpoint.id
name = $currentEndpoint.name
mac = $currentEndpoint.mac
serial = $currentEndpoint.serial
last_seen = $TimeParsed
}
if ($endpointsToCheck.ContainsKey($macKey)) {
$existing = $endpointsToCheck[$macKey]
if (-not $existing -or -not ($existing.last_seen -is [datetime])) {
Write-Host "[WARN] Existing record invalid for MAC $macKey, overwriting..."
$endpointsToCheck[$macKey] = $currentEndpointObject
continue
}
# Newer object found
if ($TimeParsed -gt $existing.last_seen) {
try {
[void]$duplicatedEndpoints.Add($existing)
} catch {
Write-Host "[ERROR] Failed adding existing duplicate for MAC $macKey : $_"
}
$endpointsToCheck[$macKey] = $currentEndpointObject
Write-Host "[INFO] Updated newer endpoint for MAC $macKey"
}
# Older duplicate found
else {
try {
[void]$duplicatedEndpoints.Add($currentEndpointObject)
} catch {
Write-Host "[ERROR] Failed add duplicate for MAC $macKey : $_"
}
Write-Host "[INFO] Found older duplicate for MAC $macKey"
}
}
else {
$endpointsToCheck[$macKey] = $currentEndpointObject
Write-Host "[INFO] Added new endpoint for MAC $macKey"
}
}
catch {
Write-Host "[ERROR] Failed processing Endpoint: $($currentEndpoint.Id). Error: $($_.Exception.Message)" -ForegroundColor Red
}
}
Write-Host "[INFO] Endpoints processing is complete. Total duplicates found: $($duplicatedEndpoints.Count)" -ForegroundColor Green
}
catch {
Write-Host "[FATAL] Script execution failed: $_" -ForegroundColor Red
}
if ($duplicatedEndpoints.Count -ge 1){
$endpointsToDelete = New-Object System.Collections.ArrayList
Write-Host "`n[INFO] The following $($duplicatedEndpoints.Count) duplicated endpoints detected:" -ForegroundColor Cyan
$duplicatedEndpoints | Format-Table id, name, mac, last_seen -AutoSize
$applyToAll = $false
foreach ($endpoint in $duplicatedEndpoints) {
if (-not $endpoint) { continue }
$endpointId = $endpoint.id
if (-not $endpointId) { continue }
Write-Host "`nEndpoint--------------------------------------------------" -ForegroundColor DarkGray
Write-Host "ID : $($endpoint.id)"
Write-Host "Name : $($endpoint.name)"
Write-Host "MAC : $($endpoint.mac)"
Write-Host "Last Seen : $($endpoint.last_seen)"
Write-Host "------------------------------------------------------------" -ForegroundColor DarkGray
if (-not $applyToAll) {
$userInput = Read-Host "Delete this endpoint? (A=Yes to All, Y=Yes, N=No, Enter=Yes)"
}
else {
$userInput = 'y'
}
# Default to Yes if Enter pressed
if ([string]::IsNullOrWhiteSpace($userInput)) { $userInput = 'y' }
switch ($userInput.ToLower()) {
'a' {
$applyToAll = $true
Write-Host "[INFO] 'Yes to All' selected. This one and all remaining endpoints will be deleted." -ForegroundColor Cyan
$null = $endpointsToDelete.Add($endpoint)
Write-Host "[INFO] Marked endpoint ID $endpointId for deletion" -ForegroundColor Yellow
}
'y' {
$null = $endpointsToDelete.Add($endpoint)
Write-Host "[INFO] Marked endpoint ID $endpointId for deletion" -ForegroundColor Yellow
}
'n' {
Write-Host "[INFO] Skipped endpoint ID $endpointId" -ForegroundColor Gray
}
default {
Write-Host "[WARN] Invalid input '$userInput' → defaulting to YES" -ForegroundColor Yellow
$null = $endpointsToDelete.Add($endpoint)
}
}
}
if ($null -ne $endpointsToDelete -and $endpointsToDelete.Count -ge 1){
#Processing safe force delete
Write-Host "`n[INFO] Processing deletion of $($endpointsToDelete.Count) endpoints..." -ForegroundColor Cyan
foreach ($endpoint in $endpointsToDelete) {
$endpointId = $endpoint.id
try {
Write-Host "[INFO] Deleting endpoint with ID: $endpointId" -ForegroundColor Yellow
#No confirmation here because of explicit confirmation above.
$deleteResponse = Update-Action1 -Action 'Delete' -Type 'Endpoint' -Id $endpointId -Force
if($null -ne $deleteResponse){
Write-Host "[SUCCESS] Deleted endpoint with ID: $endpointId" -ForegroundColor Green
}
else{
Write-Host "[WARN] No response returned while deleting endpoint with ID: $endpointId" -ForegroundColor DarkYellow
}
}
catch {
Write-Host "[ERROR] Failed to delete endpoint ID: $endpointId | Error: $($_.Exception.Message)" -ForegroundColor Red
}
}
Write-Host "[INFO] Duplicated Endpoints deleting is complete. Total duplicates removed: $($endpointsToDelete.Count)" -ForegroundColor Green
}
else{
Write-Host "[INFO] No duplicated endpoints of $($duplicatedEndpoints.Count) found were selected to delete. Script execution completed." -ForegroundColor Green
}
}
else{
Write-Host "[INFO] No any duplicated Endpoints found. Script execution completed" -ForegroundColor Yellow
}