-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
500 lines (498 loc) · 70.6 KB
/
index.html
File metadata and controls
500 lines (498 loc) · 70.6 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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Nick's Scripts</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<main class="card">
<h1>Nick's Scripts</h1>
<p>PowerShell and shell scripts published via GitHub Pages.</p>
<h2>Available downloads</h2>
<ul class="script-list">
<li class="script-item">
<div class="script-header">
<a class="script-link" href="Clear-DiskJunk.ps1">Clear-DiskJunk.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="Clear-DiskJunk.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for Clear-DiskJunk.ps1" aria-label="Copy download command for Clear-DiskJunk.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">Deletes selected junk files and directories from a copied disk tree, with conservative defaults and full WhatIf/Confirm support.</p>
<details class="script-details">
<summary>Read more</summary>
<p class="script-details-text">Clear-DiskJunk scans a target path and removes only the junk categories you explicitly allow.</p>
<p class="script-details-text">By default it runs in conservative mode and targets:<br>- browser-caches<br>- safe-temp-files</p>
<p class="script-details-text">Conservative mode avoids deleting generic directory names such as cache, temp, log, or logs unless you explicitly include<br>DANGEROUS-broad-temp-cache-logs.</p>
<p class="script-details-text">The function is designed for very large trees. It streams matches as they are found instead of accumulating all results in memory.<br>Use -PassThru to emit one result object per matched item. Without -PassThru it emits only a final summary object.</p>
<p class="script-details-text">safe-temp-files deletes:<br>- *.tmp files older than 30 days<br>- Office temp files (~$*.??? and ~$*.????)</p>
<p class="script-details-text">DANGEROUS-broad-temp-cache-logs expands matching to broad generic temp/cache/log locations and broad *.log / *.tmp file cleanup.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Clear-DiskJunk -Path D:\MountedDisk -WhatIf</p>
<p class="script-details-text">Shows what would be deleted using the conservative default categories.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Clear-DiskJunk -Path D:\MountedDisk -IncludeCategory browser-caches,DANGEROUS-broad-temp-cache-logs -Confirm</p>
<p class="script-details-text">Prompts before deleting and includes the dangerous broad cleanup category.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Clear-DiskJunk -Path D:\MountedDisk -ExcludePath 'D:\MountedDisk\Users\Nick\AppData\Local\Google\*' -PassThru |<br>Export-Csv C:\temp\diskjunk-results.csv -NoTypeInformation</p>
<p class="script-details-text">Streams one result object per matched item and saves them.</p>
<p class="script-details-text">.PARAMETER Path<br>Root path to scan.</p>
<p class="script-details-text">.PARAMETER IncludeCategory<br>Cleanup categories to include.</p>
<p class="script-details-text">Allowed values:<br>- browser-caches<br>- safe-temp-files<br>- DANGEROUS-broad-temp-cache-logs</p>
<p class="script-details-text">Default:<br>- browser-caches<br>- safe-temp-files</p>
<p class="script-details-text">.PARAMETER ExcludePath<br>Paths or wildcard patterns to exclude. Matches are tested against both full Windows paths and root-relative forward-slash paths.</p>
<p class="script-details-text">.PARAMETER TmpOlderThanDays<br>Age threshold for *.tmp deletion in the safe-temp-files category. Default is 30.</p>
<p class="script-details-text">.PARAMETER PassThru<br>Emits one object per matched item as it is processed. Useful for logging and large-scale runs.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>Without -PassThru:<br>A summary object.</p>
<p class="script-details-text">With -PassThru:<br>One result object per matched item, followed by a final summary object.</p>
<p class="script-details-text"><span class="detail-directive">.NOTES</span><br>- Supports -WhatIf and -Confirm.<br>- Intended primarily for copies of Windows disks, not live system cleanup.<br>- Broad cleanup can remove data you may later want for troubleshooting or forensics.</p>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="Get-BootShutdownEvents.ps1">Get-BootShutdownEvents.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="Get-BootShutdownEvents.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for Get-BootShutdownEvents.ps1" aria-label="Copy download command for Get-BootShutdownEvents.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">Shows a compact, admin-friendly timeline of recent boots, shutdowns,<br>crashes, and update-related restart signals from the System log.</p>
<details class="script-details">
<summary>Read more</summary>
<p class="script-details-text">Use this for first-pass reboot and shutdown triage. It helps answer<br>whether a recent restart was clean, unexpected, crash-related, planned<br>by an administrator or process, or associated with Windows Updates.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>Produces one PSCustomObject per matching event:<br>Level : Event level text.<br>Time : Event time.<br>Kind : Event category. One of:<br>Boot, NormalShutdown, PlannedShutdownOrRestart,<br>WindowsUpdatesInstallStarted, WindowsUpdatesInstallCompleted,<br>WindowsUpdatesRestartRequired, WindowsUpdatesRestart<br>BugCheck, CrashDump, AbnormalShutdown,<br>UnexpectedShutdownFollowup, Other<br>Description : First line of the event message with source metadata<br>appended as "(id <n> from <provider>)".</p>
<p class="script-details-text">Intentionally excludes noise about Microsoft Defender updates.</p>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="helpers-DCs.ps1">helpers-DCs.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="helpers-DCs.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for helpers-DCs.ps1" aria-label="Copy download command for helpers-DCs.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">A collection of helper functions for Domain Controllers</p>
<details class="script-details">
<summary>Read more</summary>
<div class="function-panel">
<div class="function-entry">
<p class="function-name"><strong>Search-ADUserAnyProperty</strong></p>
<p class="function-synopsis">Search Active Directory users by a pattern across multiple common attributes.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">This function wraps Get-ADUser with a wide LDAP filter that checks a user-supplied<br>pattern against multiple commonly used identifier attributes (e.g. sAMAccountName,<br>userPrincipalName, displayName, cn, givenName, sn, mail, proxyAddresses).<br>It's intended as a convenient "search anywhere that matters" for finding users when<br>you only know part of a name, email, alias, or login.</p>
<p class="script-details-text">Results include useful profile fields: Display Name, Department, Job Title, State,<br>Company, OU (derived from DistinguishedName), and all proxyAddresses flattened into<br>a comma-separated list. Optionally you can also search phone fields and/or restrict<br>the search scope with -SearchBase.</p>
<p class="script-details-text">.PARAMETER Pattern<br>The text pattern to search for (wildcards are automatically added at both ends).</p>
<p class="script-details-text">.PARAMETER SearchBase<br>Optional LDAP distinguished name to scope the search.</p>
<p class="script-details-text">.PARAMETER IncludePhones<br>If specified, phone-related attributes are included in the search (makes the search a bit slower)</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br># Find any user whose name, alias, or email contains "nick"<br>Search-ADUserAnyProperty -Pattern 'nick'</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br># Search within a specific OU, also matching phone numbers<br>Search-ADUserAnyProperty -Pattern '2103' -IncludePhones -SearchBase 'OU=Athens,OU=Users,DC=corp,DC=example,DC=com'</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br># Export results to CSV<br>Search-ADUserAnyProperty -Pattern 'nick' |<br>Export-Csv C:\temp\ad-search.csv -NoTypeInformation -Encoding UTF8</p>
</details>
</div>
</div>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="helpers-files.ps1">helpers-files.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="helpers-files.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for helpers-files.ps1" aria-label="Copy download command for helpers-files.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">A collection of helper functions for handling files</p>
<details class="script-details">
<summary>Read more</summary>
<div class="function-panel">
<div class="function-entry">
<p class="function-name"><strong>Get-HardLinks</strong></p>
<p class="function-synopsis">Lists files in the current directory that have >1 hardlink, and shows all link paths</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Get-HardLinks|Format-List</p>
</details>
</div>
</div>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="helpers-networking.ps1">helpers-networking.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="helpers-networking.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for helpers-networking.ps1" aria-label="Copy download command for helpers-networking.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">A collection of helper functions for Networking</p>
<details class="script-details">
<summary>Read more</summary>
<div class="function-panel">
<div class="function-entry">
<p class="function-name"><strong>Test-IpReachability</strong></p>
<p class="function-synopsis">Probe point-in-time ICMP reachability for one or more IP targets.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Sends one or more ICMP echo attempts to each target and returns one output<br>object per unique target IP.</p>
<p class="script-details-text">When a ping attempt cannot be performed due to an internal runtime error,<br>the target result's lastStatus is set to a string beginning with<br>"PROGRAM EXCEPTION:".</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>[pscustomobject]<br>One object per unique target IP with properties:<br>- ip (string): The target IP string as provided/normalized.<br>- responded (bool): $true if any attempt succeeded; otherwise $false.<br>- attempts (int): Number of attempts made for that target.<br>- respondedOnAttempt (Nullable[int]): Attempt number of first success;<br>otherwise $null.<br>- rttMs (Nullable[long]): Round-trip time in milliseconds for the<br>successful response; otherwise $null.<br>- lastStatus (string): Final status observed for the target. For runtime<br>errors, begins with "PROGRAM EXCEPTION:".</p>
<p class="script-details-text">.PARAMETER Ip<br>One or more target IPs to probe.</p>
<p class="script-details-text">Accepts:<br>- A single value convertible to string.<br>- An enumerable of values convertible to string.<br>- A single string containing multiple targets separated by commas and/or<br>whitespace.</p>
<p class="script-details-text">.PARAMETER Retry<br>Number of additional attempts per target after the first attempt.</p>
<p class="script-details-text">.PARAMETER TimeoutMs<br>Timeout in milliseconds for each attempt.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Test-IpReachability -Ip '10.1.11.50,10.1.11.55 10.1.11.56' `<br>-Retry 1 -TimeoutMs 500 -Verbose</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Test-TcpPort</strong></p>
<p class="function-synopsis">Quickly tests whether a TCP connection can be established.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Tests TCP connectivity from the current machine to the specified target<br>and ports, and returns one result object per requested port.</p>
<p class="script-details-text">The target MAY be specified as an IP address or a hostname. If name<br>resolution fails, the function throws a terminating error.</p>
<p class="script-details-text">Ports are accepted in multiple input forms.</p>
<p class="script-details-text">The -TimeoutMs value is a single overall time budget (in milliseconds) for<br>the entire batch of ports, not a per-port timeout.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>System.Management.Automation.PSCustomObject</p>
<p class="script-details-text">One object per normalized port, with these properties:<br>- port (int) The TCP port tested.<br>- open (bool) $true if a connection was established; otherwise $false.<br>- detail (string) "connected" on success; otherwise an error identifier<br>or "timeout" if the overall time budget was reached.</p>
<p class="script-details-text">Objects are emitted in ascending port order.</p>
<p class="script-details-text">.PARAMETER Target<br>Target host to test.</p>
<p class="script-details-text">.PARAMETER Ports<br>Ports to test. Accepts:<br>- a single integer<br>- an array of integers<br>- a string containing one or more port numbers<br>- an enumerable of values convertible to integers</p>
<p class="script-details-text">.PARAMETER TimeoutMs<br>Overall time budget for the entire batch, in milliseconds. Defaults to<br>200. When the budget is exhausted, remaining ports are reported as<br>"timeout".</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Test-TcpPort -Target '10.1.11.1' -Ports 80,443,4444</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Test-TcpPort -Target 'timesheet-gr.forvismazars.com' -Ports '80,443,4444' `<br>-TimeoutMs 1000</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Test-NetConnectivityToHost</strong></p>
<p class="function-synopsis">Test-NetConnectivityToHost validates that basic network reachability to a target host matches an explicit expectation profile. What it checks - ICMP echo (ping): verifies whether the host responds to pings or not. - TCP ports (optional): - OpenPorts: ports that are expected to accept a TCP connection. - ClosedPorts: ports that are expected to refuse or time out (treated as CLOSED/FILTERED). Output / side effects - Outputs discrepancies using Write-Warning "[<level>] <message>" (<level> can be pass or failure). - If -ReturnTrueFalse is used, the function returns $true/$false and emits no warnings. Notes / interpretation - A TCP port is considered OPEN only if a TCP connect completes successfully within the timeout window. - A TCP port is considered CLOSED/FILTERED if the connect fails or does not complete within the timeout. - If OpenPorts/ClosedPorts are omitted, only the ping expectation is validated. - If -SkipPing is used, only port expectations are validated. Example Test-NetConnectivityToHost -TargetHost 10.30.0.2 -RespondsToPing:$true -OpenPorts @(53,88,135,389,445) -ClosedPorts @(22,3389) -PortTimeoutMs 1000 Example (ports only) Test-NetConnectivityToHost -TargetHost 10.30.0.2 -SkipPing -OpenPorts @(443) -PortTimeoutMs 1000 Example (boolean result only) Test-NetConnectivityToHost -TargetHost 10.30.0.2 -RespondsToPing:$true -ReturnTrueFalse</p>
</div>
<div class="function-entry">
<p class="function-name"><strong>Split-IpByReachability</strong></p>
<p class="function-synopsis">Splits input IPs into Alive vs NotAlive based on whether they respond to pings</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Runs Test-IpReachability for the provided targets and returns a single object<br>containing two string arrays:<br>- AliveIps: IPs that responded ($true)<br>- DeadIps: IPs that did not respond ($false) or hit errors/timeouts</p>
<p class="script-details-text"><span class="detail-directive">.INPUTS</span><br>Same accepted shapes as Test-IpReachability -Ip.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>[pscustomobject] with:<br>- AliveIps ([string[]])<br>- DeadIps ([string[]])<br>- Results ([pscustomobject[]]) raw per-IP results (handy for lastStatus/rtt)</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Test-NetConnectivityToNetwork</strong></p>
<p class="function-synopsis">Assesses reachability of a network by pinging a list of hosts that are known to reply.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Given a human-friendly network description (e.g. "10.11.x.y/16") and a list of<br>IP addresses that are expected to respond to ICMP, this function probes them<br>(using Split-IpByReachability) and outputs the results using:<br>Write-Warning "[<level>] ..."<br>(<level> is one of pass, notice, failure)</p>
<p class="script-details-text">If -ReturnListOfAliveHosts is used, the function does not emit warnings and<br>instead returns the list of responsive hosts.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Test-ShareLikelyUp</strong></p>
<p class="function-synopsis">QUICKLY tests whether the host of a UNC share is LIKELY reachable over SMB.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">This is a FAST reachability test, not a definitive share-access test. A positive result means the host likely has SMB available. It does not prove that the share exists or that the current user has access to it.</p>
<p class="script-details-text">Optionally verifies that at least one configured DNS server falls within an expected CIDR range (prefer to pass it so that you don't spend time on failed DNS resolutions), resolves the host to IPv4 and/or IPv6 addresses, and tests whether any resolved address accepts a TCP connection on port 445 within a short timeout. Supports hostnames, IPv4 UNC hosts, and Windows IPv6-literal UNC hosts.</p>
<p class="script-details-text">.PARAMETER SharePath<br>UNC share path whose host will be tested.</p>
<p class="script-details-text">.PARAMETER DnsCidrs<br>Optional CIDR ranges. When specified, at least one configured DNS server must fall within one of these ranges or the test returns a negative result.</p>
<p class="script-details-text">.PARAMETER TcpTimeoutMs<br>For the connection test to TCP port 445 (SMB).</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>A PSCustomObject with the test outcome and discovered details.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Test-ShareLikelyUp -SharePath '\\server01\share'</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Test-ShareLikelyUp -SharePath '\\192.168.1.2\foo'</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Test-ShareLikelyUp -SharePath '\\server01.contoso.local\share' -DnsCidrs '10.30.0.0/16'</p>
</details>
</div>
</div>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="helpers-processes.ps1">helpers-processes.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="helpers-processes.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for helpers-processes.ps1" aria-label="Copy download command for helpers-processes.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">A collection of helper functions for Processes & Tasks</p>
<details class="script-details">
<summary>Read more</summary>
<div class="function-panel">
<div class="function-entry">
<p class="function-name"><strong>Quote-Win32Arg</strong></p>
</div>
<div class="function-entry">
<p class="function-name"><strong>Join-Win32CommandLine</strong></p>
</div>
<div class="function-entry">
<p class="function-name"><strong>New-ScheduledTaskForPSScript</strong></p>
<p class="function-synopsis">Registers a Windows Scheduled Task that runs a PowerShell script as SYSTEM.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Creates or updates a scheduled task that runs the script specified by<br>-ScriptPath using Windows PowerShell (powershell.exe).</p>
<p class="script-details-text">The task is registered under -TaskPath and -TaskName (a default name is<br>chosen when -TaskName is not provided). If a task with the same name<br>already exists at that path, it is replaced.</p>
<p class="script-details-text">The task runs as the built-in SYSTEM account with highest run level. Task<br>settings limit concurrent executions by ignoring new starts while an<br>instance is already running, and apply -ExecutionTimeLimit to each run.</p>
<p class="script-details-text">Depending on the script path and provided arguments, the function MAY<br>create a .cmd wrapper file in the script's folder and configure the task<br>to execute that wrapper instead of invoking powershell.exe directly.</p>
<p class="script-details-text">If -ScheduleType is Manual, the task is created without a trigger and<br>will only run when started manually (or by other tooling).</p>
<p class="script-details-text">Supports -WhatIf and -Confirm. If confirmation is declined (or -WhatIf is<br>used), no task is registered and no wrapper file is created.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>Microsoft.Management.Infrastructure.CimInstance<br>A scheduled task object returned by Register-ScheduledTask when the task<br>is registered. If ShouldProcess declines the action, no output is<br>produced.</p>
<p class="script-details-text">.PARAMETER ScriptPath<br>Path to an existing PowerShell script file. The path MUST exist or the<br>function throws before making changes.</p>
<p class="script-details-text">.PARAMETER ScheduleType<br>Selects how (or whether) the task is triggered.</p>
<p class="script-details-text">Valid values:<br>- Startup: runs at system startup.<br>- Daily: runs daily at -Time.<br>- Weekly: runs weekly on -Day at -Time.<br>- Hourly: repeats every hour starting shortly after creation time.<br>- EveryMinute: repeats every minute starting shortly after creation time.<br>- Manual: no trigger is created.</p>
<p class="script-details-text">.PARAMETER Time<br>Time of day in HH:mm (24-hour) format. Required for Daily and Weekly.</p>
<p class="script-details-text">.PARAMETER Day<br>One or more weekdays. Required for Weekly.</p>
<p class="script-details-text">.PARAMETER TaskPath<br>Scheduled task folder path in Task Scheduler (for example '\enLogic\').<br>Defaults to '\enLogic\'.</p>
<p class="script-details-text">.PARAMETER TaskName<br>Scheduled task name. If omitted, a name is derived from the script file<br>name.</p>
<p class="script-details-text">.PARAMETER ScriptArguments<br>Argument values passed to the script. Provide either -ScriptArguments or<br>-RawArgumentsAvoidMe, not both.</p>
<p class="script-details-text">Elements MAY be $null. How the script receives $null depends on the<br>invocation mode chosen by the function.</p>
<p class="script-details-text">.PARAMETER RawArgumentsAvoidMe<br>A raw argument string appended to the invocation. Intended for advanced<br>cases. Provide either -ScriptArguments or -RawArgumentsAvoidMe, not both.</p>
<p class="script-details-text">.PARAMETER ExecutionTimeLimit<br>Maximum runtime allowed for each task invocation. Defaults to 2 hours.</p>
<p class="script-details-text">.PARAMETER StartItNow<br>If set, the function attempts to start the task immediately after it is<br>registered. If starting fails, the task remains created and an error is<br>written.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>New-ScheduledTaskForPSScript -ScriptPath 'C:\Ops\Health.ps1' `<br>-ScheduleType Startup -TaskPath '\enLogic\'</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>New-ScheduledTaskForPSScript -ScriptPath 'C:\Ops\Report.ps1' `<br>-ScheduleType Daily -Time '02:30' -TaskName 'Daily Report' `<br>-ScriptArguments @('Full','EU')</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>New-ScheduledTaskForPSScript -ScriptPath 'C:\Ops\Cleanup.ps1' `<br>-ScheduleType Weekly -Day Monday,Thursday -Time '03:00' `<br>-ExecutionTimeLimit (New-TimeSpan -Minutes 30) -StartItNow</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>New-ScheduledTaskForPSScript -ScriptPath 'C:\Ops\OnDemand.ps1' `<br>-ScheduleType Manual -TaskName 'Run On Demand'</p>
<p class="script-details-text"><span class="detail-directive">.NOTES</span><br>Registers the task to run as SYSTEM with highest privileges, and sets<br>MultipleInstances to IgnoreNew.</p>
<p class="script-details-text">For Hourly and EveryMinute schedules, the first run is anchored to a<br>start time shortly after the function is invoked (not aligned to clock<br>boundaries).</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Invoke-DetachedPSScript</strong></p>
<p class="function-synopsis">Executes a PowerShell script on a remote host as a detached process without leaving a disconnected session after execution completes.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">The script or script block is executed on the remote host. Unlike<br>`Invoke-Command -InDisconnectedSession` once the execution terminates<br>it does not leave a disconnected session on the target.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>Produces a PSCustomObject representing the process start result:<br>ComputerName : The target host name.<br>ProcessId : The ID of the newly created remote process.<br>ReturnValue : The system code from the process creation attempt.<br>Status : The outcome of the initiation (e.g., "Success").<br>ExecutionPath : The path to the script being executed on the target.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Get-ProcessesWithMatchingCommandLine</strong></p>
<p class="function-synopsis">.DESCRIPTION List processes with command lines matching a like expression (e.g. "*myScript.ps1*") .EXAMPLE Get-ProcessesWithMatchingCommandLine "*myScript.ps1*"</p>
</div>
<div class="function-entry">
<p class="function-name"><strong>Test-ExeFound</strong></p>
</div>
</div>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="helpers-text-files.ps1">helpers-text-files.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="helpers-text-files.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for helpers-text-files.ps1" aria-label="Copy download command for helpers-text-files.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">A collection of helper functions for handling text files</p>
<details class="script-details">
<summary>Read more</summary>
<div class="function-panel">
<div class="function-entry">
<p class="function-name"><strong>Edit-TextFile</strong></p>
<p class="function-synopsis">Searches and replaces text in files while maintaining the existing text encoding (but will switch ASCII to UTF8 if replacement is unicode).</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Performs an in-place regex (or literal) search/replace on a text file,<br>while preserving the file's actual encoding. For _really_ hard cases it<br>may mistake the encoding. In such cases it may fail to find the pattern<br>and/or change the encoding of the input file.</p>
<p class="script-details-text">You may pass wildcards like "*.txt" to -File.</p>
<p class="script-details-text">To apply one substitution use `-Patern 'a' -Replacement 'b'`. To apply<br>multiple substitutions use `-ReplaceMap` (see examples).</p>
<p class="script-details-text">Without -Literal considers Pattern(s) to be a regex pattern.</p>
<p class="script-details-text">By default keeps a backup with .bak extension. To skip the Backup<br>use -Backup "". To change the extension use -Backup "ext".</p>
<p class="script-details-text">If the sampled bytes are pure 7-bit ASCII and no BOM is present, the<br>function by default assumes a UTF8 encoding (even though ASCII is also<br>valid). This is intentional: it allows replacements to introduce Unicode<br>without changing the reported encoding unexpectedly. Use -DontPreferUTF8<br>to force ASCII encoding.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>Produces a psCustomObject for each evaluated file:<br>File = The file path<br>Changed = True/False<br>EncodingStr = Human readable encoding.<br>EncodingObj = The output of Get-TextFileEncoding<br>Details = Human readable outcome. E.g.:<br>"Changes made"<br>"Pattern(s) not found"<br>"Skipped too big file ..."<br>"Ignored empty file"<br>"No files matched pattern"</p>
<p class="script-details-text">.PARAMETER MaxFileSize<br>Files larger than these many bytes are ignored.</p>
<p class="script-details-text">.PARAMETER PreferISOEncodings<br>When set, ISO encodings are selected when multiple encodings represent<br>the text equivalently; otherwise, Windows encodings are selected.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Edit-TextFile -file .\ansi.txt -Pattern "foo" -Replacement="_FOO_"</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Edit-TextFile -file .\ansi.txt -Pattern "foo?" -Replacement="_FOO_?" -Literal</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Edit-TextFile -file .\ansi.txt -ReplaceMap ([ordered]@{"foo"="_FOO_"; "bar"="_BAR_"})</p>
<p class="script-details-text"><span class="detail-directive">.NOTES</span><br>The operation writes modified content to temporary storage before<br>replacing the target file. Original files are backed up prior to the<br>modification. If a terminating error occurs during the file swap, the<br>target file might remain in its prior state and intermediate temporary<br>files might remain on the storage volume.</p>
<p class="script-details-text">Files exceeding the configured maximum size limit or containing no<br>data are skipped. Substitutions are evaluated sequentially.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Get-NewlineStyle</strong></p>
<p class="function-synopsis">Detect newline style in a decoded string. Returns 'CRLF','LF','CR','Mixed','None','NotChecked'.</p>
</div>
<div class="function-entry">
<p class="function-name"><strong>Get-TextFileEncoding</strong></p>
<p class="function-synopsis">Detect (if possible) or guess the encoding of Text Files.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Uses uchardet (CLI) and simple BOM/ASCII checks.<br>Returns an object like this:<br>File : C:\tempansi.txt<br>Type : NON-ASCII-TEXT<br>BOMBytes : {}<br>UCharDetEncoding : ISO-8859-1<br>EncodingDescription : CP1252<br>DotNetEncodingObj : System.Text.SBCSCodePageEncoding<br>NewlineStyle :<br>BytesRead : 22<br>UCharDetTimeMs : 0<br>TotalTimeMs : 55</p>
<p class="script-details-text"><span class="detail-directive">.PARAMETER</span><br>-DontPreferUTF8: If the sampled bytes are pure 7-bit ASCII and no BOM<br>is present, the function returns UTF-8 by default (even though ASCII<br>is also valid). This is intentional: it allows later writes/replacements<br>to introduce Unicode without changing the reported encoding unexpectedly.<br>Use -DontPreferUTF8 to return ASCII instead.<br>In other words: The default UTF-8 return value is not a strict<br>"encoding detection" result; it is a compatibility policy for downstream<br>editing workflows.<br>-PreferISOEncodings: by default we return Windows encodings instead of ISO ones<br>WHEN BOTH PRODUCE THE SAME TEXT. This switch overides that behavior.</p>
<p class="script-details-text"><span class="detail-directive">.NOTES</span><br>- Will fail for pathological files (e.g. BOM indicates UTF-8<br>but file is UTF-16).<br>- It consumes RAM to read the file's bytes, possible twice.<br>(That's why -MaxBytes is by default 512KB)<br>- It may fail in very hard cases like:<br>- Files larger than 512KB that appear as ASCII in the first<br>-MaxBytes and then have some ANSI or UTF-8 bytes.<br>- Files with ambiguous ASCII encodings (e.g. ISO-8859-1 /<br>CP1252)<br>- Respects -ErrorAction by emitting non-terminating errors.<br>- By default (without -PreferISOEncodings) it will assume a file<br>is encoded with a Windows(CP) encoding if it can be either<br>an ISO encoding (e.g. ISO-8859-1) or a windows one (e.g. CP1251)<br>(Since a lot of these encodings are very similar, a file with plenty<br>of text but none of the few characters that are encoded<br>differently between the ISO/CP encodings can be encoded with both giving<br>exactly the same bytes)</p>
<p class="script-details-text">For reference these are the default encoding for Notepad,<br>PS5 & PS7 per windows version:</p>
<p class="script-details-text">| | PS 5.1 | PS 5.1 | PS 7 either<br>Operating System | Notepad | > a.txt | Out-File | > or Out-File<br>2016 (1607 LTSC) | ANSI | ANSI | UTF-16 LE*| UTF-8*<br>2019 (1809 LTSC) | ANSI | ANSI | UTF-16 LE*| UTF-8*<br>2022 (21H2 LTSC) | UTF-8* | ANSI | UTF-16 LE*| UTF-8*<br>2025 (24H2 LTSC) | UTF-8* | ANSI | UTF-16 LE*| UTF-8*<br>*: UTF-8 always without BOM, UTF-16 always with BOM</p>
<p class="script-details-text">TODO:<br>Offer help on how to install uchardet if not found.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Remove-TypographyUnicodeFromTextFile</strong></p>
<p class="function-synopsis">Substitutes Unicode typography characters with ASCII characters in one or more target text files. Mimics the interface of Edit-TextFile.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Modifies the specified file or files (if you use wildcards like *.txt)<br>in place. Useful for eliminating unnecessary Unicode typography from code.</p>
<p class="script-details-text">You may pass wildcards like "*.ps1" to -File.</p>
<p class="script-details-text">By default keeps a backup with .bak extension. To skip the Backup<br>use -Backup "". To change the extension use -Backup "ext".</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>Produces a psCustomObject for each evaluated file:<br>File = The file path<br>Changed = True/False<br>EncodingStr = Human readable encoding.<br>EncodingObj = The output of Get-TextFileEncoding<br>Details = Human readable outcome. E.g.:<br>"Changes made"<br>"Pattern(s) not found"<br>"Skipped too big file ..."<br>"Ignored empty file"<br>"No files matched pattern"</p>
<p class="script-details-text">.PARAMETER MaxFileSize<br>Files larger than these many bytes are ignored.</p>
<p class="script-details-text">.PARAMETER PreferISOEncodings<br>When set, ISO encodings are selected when multiple encodings represent<br>the text equivalently; otherwise, Windows encodings are selected.</p>
<p class="script-details-text"><span class="detail-directive">.NOTES</span><br>Why we have to do the changes in three batches instead of all together:<br>By default, PowerShell hashtables (@{} and [ordered]@{}) use culture-sensitive<br>linguistic comparison.<br>Because 0x200B, 0x200C, and 0x200D are invisible formatting characters,<br>the linguistic comparer gives them a sorting weight of zero. Therefore,<br>PowerShell evaluates them as the exact same string and throws a "Duplicate keys"<br>error when building the hashtable.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Replace-FileBytesSafely</strong></p>
<p class="function-synopsis">Replaces a file's contents with specified bytes, optionally retaining a backup.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Behavior and guarantees:<br>- Writes the new content to -TmpPath first, then attempts to replace -ResolvedPath with it.<br>- Primary path uses [IO.File]::Replace(), which is the best option on local NTFS:<br>* Readers see either the old or the new file, not a partially-written file.<br>* A backup at -BakPath is created/overwritten as part of the replace.<br>- If Replace() fails (common on non-NTFS volumes, SMB shares with varying semantics, or transient locks<br>e.g. AV/OneDrive/indexing), it falls back to Copy-Item + Move-Item with best-effort rollback:<br>* This fallback is NOT atomic. It is provided for compatibility and "works in more places".<br>* If the move fails after the backup copy, the function attempts to restore from -BakPath.</p>
<p class="script-details-text">Backup semantics:<br>- -BakPath is always used as the safety copy when swapping.<br>- If -UserBackup is $true, -BakPath is considered caller-visible and is preserved.<br>- If -UserBackup is $false, -BakPath is a transient safety backup and may be deleted on success.</p>
<p class="script-details-text">Cleanup:<br>- Always attempts to delete -TmpPath in a finally block.<br>- Does not promise preservation of metadata/streams in the fallback path (ACLs, ADS, timestamps, etc.)<br>beyond what the underlying filesystem/provider naturally keeps.</p>
<p class="script-details-text">.PARAMETER ResolvedPath<br>The existing target file to be replaced (must be on the same volume as -TmpPath for best behavior).</p>
<p class="script-details-text">.PARAMETER TmpPath<br>A temp file path in the same directory as the target (recommended) containing the new bytes to commit.</p>
<p class="script-details-text">.PARAMETER BakPath<br>Path for the backup copy used during the swap. May be a user-requested backup (kept) or a transient one.</p>
<p class="script-details-text">.PARAMETER UserBackup<br>Indicates whether -BakPath is user-requested (keep it) or internal/transient (delete on success).</p>
<p class="script-details-text">.PARAMETER Bytes<br>The final bytes to be written/committed to the target file.</p>
<p class="script-details-text"><span class="detail-directive">.NOTES</span><br>- Intended for "in-place update" workflows: generate full new content, then commit in one swap.<br>- For OneDrive/SMB scenarios, transient failures are normal; callers may want a small retry policy<br>around the Replace() stage (if not implemented inside this function).</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Read-FirstBytes</strong></p>
<p class="function-synopsis">Safe(shared) read of up to Count bytes from the start of a file.</p>
</div>
<div class="function-entry">
<p class="function-name"><strong>Test-ExeFound</strong></p>
<p class="function-synopsis">Tests whether an executable can be resolved either as a full path or via PATH/PATHEXT.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Accepts either:<br>- A rooted path (e.g. C:\Tools\uchardet or C:\Tools\uchardet.exe), in which case it checks existence and,<br>if no extension was given, also tries common executable extensions (.exe/.cmd/.bat/.com).<br>- A bare command name (e.g. uchardet), in which case it resolves it the same way PowerShell would when<br>launching a process (Get-Command + PATHEXT).<br>Returns $true if the executable can be found, otherwise $false.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Resolve-FileFromPath</strong></p>
<p class="function-synopsis">Resolve a path to exactly one existing FileSystem file ([IO.FileInfo]) or return $null.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Accepts a path (or FileInfo/DirectoryInfo) and enforces strict "one real file" semantics:<br>- Must exist<br>- Must be FileSystem provider<br>- Must not be a directory<br>- If wildcards are used, they must match exactly one item</p>
<p class="script-details-text">On failure, emits a tagged _Write-FunctionError ([RFFP-*]) including both the original input and (when available)<br>the resolved full path, then returns $null (caller decides whether to stop via -ErrorAction).</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>System.IO.FileInfo or $null.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Test-BufferIsValidUtf8</strong></p>
<p class="function-synopsis">Validates that a byte[] buffer is UTF-8, while intentionally tolerating an incomplete final UTF-8 sequence.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Returns $true if the buffer contains no invalid UTF-8 sequences in its body.</p>
</details>
</div>
</div>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="Install-PrepLapCode.ps1">Install-PrepLapCode.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="Install-PrepLapCode.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for Install-PrepLapCode.ps1" aria-label="Copy download command for Install-PrepLapCode.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">Only relevant to mazars (Install/Update mazars-prepare-laptop-code)</p>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="Run-DismSfc.ps1">Run-DismSfc.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="Run-DismSfc.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for Run-DismSfc.ps1" aria-label="Copy download command for Run-DismSfc.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">Safe* and automatic DISM + SFC repairs made easy.</p>
<details class="script-details">
<summary>Read more</summary>
<p class="script-details-text">Runs CHKDSK, DISM(CheckHealth/RestoreHealth) and SFC with preflight checks, concise console output, and logging.<br>Designed to minimize risk* and be thourough.</p>
<p class="script-details-text">*: REGARDING SAFETY</p>
<p class="script-details-text">This script is safe to run on Windows installations with no weird customizations,<br>Don't use it on boxes with OEM-customized, but still WRP-protected components,<br>or older apps that replace protected system binaries.</p>
<p class="script-details-text">.PARAMETER Source<br>Optional, one or more DISM sources, e.g. 'WIM:D:\sources\install.wim:1','ESD:E:\sources\install.esd:6'.</p>
<p class="script-details-text">.PARAMETER ScratchDirectory<br>Optional scratch directory for servicing if supported (offloads staging from C:).</p>
<p class="script-details-text">.PARAMETER MinFreeSystemGB<br>Minimum free GB on system drive before running heavy servicing (default 4GB).</p>
<p class="script-details-text">.PARAMETER MinFreeScratchGB<br>Minimum free GB on ScratchDirectory if provided. (default 4GB)</p>
<p class="script-details-text">.PARAMETER LimitAccess<br>Use only -Source and avoid Windows Update (WU/WSUS). Use this along with -Source. PREFER TO AVOID THIS OPTION.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>.\Run-DismSfc.ps1 -Source 'WIM:D:\sources\install.wim:1'</p>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="Start-Copy4Toula.ps1">Start-Copy4Toula.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="Start-Copy4Toula.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for Start-Copy4Toula.ps1" aria-label="Copy download command for Start-Copy4Toula.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">Minimize copy-pasting and typing while using an LLM like Toula-the-fixer to troubleshoot issues.</p>
<details class="script-details">
<summary>Read more</summary>
<p class="script-details-text">HOW TO USE<br>Begin by opening a terminal and dot-sourcing this script. E.g.:</p>
<p class="script-details-text">$p="C:\IT\bin";$f="Start-Copy4Toula.ps1";mkdir $p -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $p\$f; . $p\$f</p>
<p class="script-details-text">Then repeat these steps:<br>1. Copy code from the LLM<br>2. Run `q` in the terminal<br>3. Go back to the LLM and click to paste the clipboard.</p>
<p class="script-details-text">Every time you run `q` it:<br>- Reads code from the clipboard.<br>- Executes it.<br>- Automatically copies the output back to the clipboard.</p>
<p class="script-details-text">It will copy up to 3000 lines by default. Change like this:<br>$global:SctMaxLinesToCopy = 4000</p>
<p class="script-details-text">If you run commands directly (without `q`) use `ccc` to copy all output<br>since the last time you run either `q` or `ccc`.</p>
<p class="script-details-text">UNDER THE HOOD<br>The script maintains two transcript files per PowerShell process:<br>1. `$env:TEMP\Toula-the-fixer-$PID.txt`<br>Stores the most recent chunk of output.<br>2. `$env:TEMP\Toula-the-fixer-$PID.full.txt`<br>Stores the cumulative raw history for the current session.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>. .\Start-Copy4Toula.ps1<br>q<br>q</p>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="Test-ForCpuRamDiskStress.ps1">Test-ForCpuRamDiskStress.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="Test-ForCpuRamDiskStress.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for Test-ForCpuRamDiskStress.ps1" aria-label="Copy download command for Test-ForCpuRamDiskStress.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">Prints a detailed warning every time it finds RAM, CPU or Disks are stressed</p>
<details class="script-details">
<summary>Read more</summary>
<p class="script-details-text">There are two modes of operation: monitoring and log file analysis.</p>
<p class="script-details-text">In monitor mode (the default) it prints a detailed warning every<br>time it finds RAM, CPU or Disks are stressed.</p>
<p class="script-details-text">In monitor mode these switches may be used.<br>-MonitorVolumes: Will also monitor IO stress of Volumes<br>-DontMonitorDisks: Will NOT monitor IO stress of disks</p>
<p class="script-details-text">In log analysis (invoked if you supply a -LogFile) it creates a<br>summary report based on the contents of the log file.</p>
<p class="script-details-text">In log analysis mode these arguments may be used.<br>-Granularity<br>-LogsToIgnoreRegex</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Download & Setup to always run on startup<br>$bin="C:\IT\bin";$log="C:\it\log"<br>$f="Test-ForCpuRamDiskStress.ps1";mkdir $bin -force >$null;mkdir $log -force > $null<br>iwr -useb https://ndemou.github.io/scripts/$f -OutFile $bin\$f</p>
<p class="script-details-text">$f="helpers-processes.ps1"; iwr -useb https://ndemou.github.io/scripts/$f -OutFile $bin\$f; . $bin\$f<br>New-ScheduledTaskForPSScript -ScriptPath "$bin\Test-ForCpuRamDiskStress.ps1" -ScheduleType Startup -ScriptArguments "-LogDir",$log<br>Start-ScheduledTask -TaskPath '\enLogic\' -TaskName 'Execute Test-ForCpuRamDiskStress.ps1'<br><span class="detail-directive">.EXAMPLE</span><br>Get statistics for a particular date<br>& $bin\Test-ForCpuRamDiskStress.ps1 -Granularity 10m -LogFile C:\it\log\CpuRamDiskStress.2026-01-14.log</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>If you want to run only once:<br>& C:\IT\bin\Test-ForCpuRamDiskStress.ps1 -LogDir c:\it\log -LogBaseName 'CpuRamDiskStress'</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>What you see if memory is under pressure.<br>C:\IT\bin\Test-ForCpuRamDiskStress.ps1 | Tee-Object "C:\it\temp\CpuRamDiskStress.log"</p>
<p class="script-details-text">20:12:47 HIGH <HOSTNAME> Reasons:<br>- Available memory minimum 0,5% is below the 5% threshold while average Page Reads/sec is 319, above the 150 threshold. Low headroom with active hard faults indicates real stress.<br>- High classification gate satisfied: Available memory(%) minimum 0,5 is below the 10% gate threshold.<br>Measurements:<br>- Time window: 18 secs (9 samples)<br>- Available memory: average 68,3%, minimum 0,5%<br>- Committed memory: average 31,9%, maximum 67,0%<br>- Paging file usage: maximum 73%<br>- Page Reads/sec (hard faults): average 319, maximum 1.789<br>- Page Writes/sec: average 23, maximum 203<br>- Transition Faults/sec: average 1.281, maximum 6.797<br>- Disk read latency: average 0,0 ms<br>- Disk write latency: average 0,0 ms<br>- Disk queue length: average 7,3<br>- Standby cache memory: Normal 58 MB, Reserve 222 MB, Core 0 MB<br>- File cache memory: 4 MB<br>- Modified page list: 134 MB<br>- Compressed memory: 0 MB</p>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="WindowsUpdatesHelper.ps1">WindowsUpdatesHelper.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="WindowsUpdatesHelper.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for WindowsUpdatesHelper.ps1" aria-label="Copy download command for WindowsUpdatesHelper.ps1"><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M5 2.75A1.75 1.75 0 0 1 6.75 1h5.5A1.75 1.75 0 0 1 14 2.75v6.5A1.75 1.75 0 0 1 12.25 11h-5.5A1.75 1.75 0 0 1 5 9.25zm1.75-.25a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-6.5a.25.25 0 0 0-.25-.25z"></path><path d="M2 5.75C2 4.784 2.784 4 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h5.5a.25.25 0 0 0 .25-.25v-.5a.75.75 0 0 1 1.5 0v.5A1.75 1.75 0 0 1 9.25 14h-5.5A1.75 1.75 0 0 1 2 12.25z"></path></svg> </button>
</div>
<p class="script-synopsis">Installs Windows Updates using the supported WUA COM API, with optional download, controlled reboot (now or scheduled), and detailed logging.</p>
<details class="script-details">
<summary>Read more</summary>
<p class="script-details-text">SPECIAL SHOW UPDATE HISTORY MODE</p>
<p class="script-details-text">If -ShowHistory is used, the script does NOT install/download/reboot.<br>Instead it queries Windows Update history (same source as the GUI "View update history"),<br>optionally filtered, and then exits.</p>
<p class="script-details-text">NORMAL INSTALL UPDATES MODE</p>
<p class="script-details-text">By default(without -ShowHistory) it installs Windows updates like this:<br>- Installs already downloaded updates if any.<br>- With -Download, will also download all required updates.<br>- With -Reboot, will reboot after installation if needed (or regardless with -RebootAnyway).</p>
<p class="script-details-text">Batches "normal" updates together; installs "exclusive" updates one-by-one; accepts EULAs.</p>
<p class="script-details-text"># Regarding Robustness<br>- Service start is retried up to 3x with exponential delays (5s, 10s, 20s) before failing.<br>- Pending reboot detection uses WU `RebootRequired` and CBS `RebootPending`.<br>- Post-download refresh search has a catch/fallback: if the refresh search throws, proceeds using the initial search results (with a warning).<br>- Uses only supported, inbox components (no extra modules, no `UsoClient`, PS 5.1-safe syntax).<br>- Reboot is first tried without /f and after a few minutes with /f.</p>
<p class="script-details-text">If an update (e.g. Servicing Stack) installs and immediately requires a reboot; subsequent updates will fail with 0x80240030 until reboot. If such failures are detected and the script was run with -Reboot or -RebootAnyway,<br>the script will ensure continuation of installations after the reboot like this:<br>- Create a temporary scheduled task that runs this script again<br>at startup with -XXX_ResumeAfterReboot.<br>- On that second automated run with -XXX_ResumeAfterReboot: The startup<br>task is removed and the script continues as normal (install + maybe reboot).</p>
<p class="script-details-text"># Logging<br>Logs everything to `WindowsUpdateHelper-YYYY-MM-DD.log`<br>1. If C:\IT\LOG exists, log is created there.<br>2. Else if C:\IT\LOGS exists, there.<br>3. Else in the system temp folder.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>The fastest way to bring a new installation up-to-date:<br>**CAUTION**: WILL REBOOT WITHOUT WAITING / WITHOUT ASKING<br>Download & first run<br>Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted -Force<br>$p="C:\it\bin";mkdir $p -force >$null<br>$f="WindowsUpdatesHelper.ps1";iwr -useb https://wiki.enlogic.gr/pub/KnowledgeBase/PublicFiles/$f -OutFile $p\$f<br>& C:\it\bin\WindowsUpdatesHelper.ps1 -Download -Reboot</p>
<p class="script-details-text">2nd Run (REPEAT UNTIL YOU GET 0 Updates Found)<br>& c:\it\bin\WindowsUpdatesHelper.ps1 -Download -Reboot -Interactive<br>It will also install updates that MAY ask you to accept EULAs or make choices<br><span class="detail-directive">.EXAMPLE</span><br>Display the logs from the last 10 executions of this script<br>& c:\it\bin\WindowsUpdatesHelper.ps1 -ListRecentLogs | %{cat $_.fullname|sls -NotMatch '^(Machine|Host Application|Process ID|Log file|Configuration Name|Username|End time|[A-Z][a-z]*(Versions?|Edition)): '}</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>**CAUTION**: An unfortunate side-effect of this script is that the updates<br>it installs are not visible in the GUI (Settings -> Windows Updates).<br>The only way to view them:<br>& c:\it\bin\WindowsUpdatesHelper.ps1 -ShowHistory | ft<br>You may add -IncludeAV if you want to also view (the very frequent) Antivirus udpates</p>
<p class="script-details-text">.PARAMETER Download<br>Perform online scan and download applicable updates that are not yet downloaded, then proceed to install everything downloaded.</p>
<p class="script-details-text">.PARAMETER Reboot<br>After installation, reboot only if updates require it (or regardless if -RebootAnyway is also used).</p>
<p class="script-details-text">.PARAMETER Interactive<br>If specified, the script will include updates flagged as InstallationBehavior.CanRequestUserInput<br>(Like some drivers/firmware that show GUI prompts and wait for user actions -- e.g. clicking "Accept").<br>Without this switch, such updates are skipped.<br>Alias: -CanRequestUserInput</p>
<p class="script-details-text">.PARAMETER RebootAnyway<br>Reboot regardless of whether updates require it (implies -Reboot).</p>
<p class="script-details-text">.PARAMETER XXX_ResumeAfterReboot<br>DO NOT USE THIS SWITCH. It is used INTERNALLY to continue installations after a reboot.</p>
<p class="script-details-text">.PARAMETER AbortReboot<br>Abort a reboot initiated by this script</p>
<p class="script-details-text">.PARAMETER XXX_RebootNow<br>DO NOT USE THIS SWITCH. It is used INTERNALLY to force a reboot.</p>
<p class="script-details-text">.PARAMETER XXX_RebootArmedAt<br>DO NOT USE THIS SWITCH. It is used INTERNALLY to avoid rebooting if a reboot already occured after this time.</p>
<p class="script-details-text">.PARAMETER ShowHistory<br>List Windows Update history (no install/download/reboot) and exit.</p>
<p class="script-details-text">.PARAMETER MaxResults<br>(ShowHistory mode) Maximum number of matching history entries to output. Default: 30. Use 0 to return all matches found (within MaxScanEntries).</p>
<p class="script-details-text">.PARAMETER LastDays<br>(ShowHistory mode) Only include history entries from the last N days.</p>
<p class="script-details-text">.PARAMETER IncludeAV<br>(ShowHistory mode) Include KB2267602 (Defender definitions) entries (excluded by default).</p>
<p class="script-details-text">.PARAMETER MaxScanEntries<br>(ShowHistory mode) Safety cap: maximum number of history rows to scan. Default: 10000.</p>
<p class="script-details-text">.PARAMETER ListRecentLogs<br>List this script's log files and exit. Returns FileInfo objects sorted by LastWriteTime (oldest -> newest; most recent last).<br>By default it returns the most recent 10 log files.</p>
<p class="script-details-text">.PARAMETER ListAll<br>Used only with -ListRecentLogs. If specified, returns all log files that can be found (instead of only the most recent 10).</p>
<p class="script-details-text">.PARAMETER InstallOptional<br>Installs an optional update (needs the update ID)</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Install any already downloaded updates and reboot if needed:<br>.\WindowsUpdatesHelper.ps1 -Reboot</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Download, Install and if needed reboot:<br>.\WindowsUpdatesHelper.ps1 -Download -Reboot</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Download, Install and reboot regardless of whether updates require it:<br>.\WindowsUpdatesHelper.ps1 -Download -Reboot -RebootAnyway</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Show latest Windows Update history entries:<br>.\WindowsUpdatesHelper.ps1 -ShowHistory</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Show latest Windows Update history entries, include AV updates:<br>.\WindowsUpdatesHelper.ps1 -ShowHistory -LastDays 14 -IncludeAV</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>List the most recent 10 log files created by this script (most recent last):<br>.\WindowsUpdatesHelper.ps1 -ListRecentLogs</p>
</details>
</li>
</ul>
<p class="footer">
Source repository:
<a href="https://github.com/ndemou/scripts">github.com/ndemou/scripts</a>
</p>
</main>
<script>
document.addEventListener("click", async (event) => {
const button = event.target.closest(".copy-button");
if (!button) return;
const text = button.getAttribute("data-copy");
if (!text) return;
try {
await navigator.clipboard.writeText(text);
button.classList.add("copied");
setTimeout(() => button.classList.remove("copied"), 1200);
} catch {
button.classList.add("copy-failed");
setTimeout(() => button.classList.remove("copy-failed"), 1200);
}
});
</script>
</body>
</html>