-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
826 lines (825 loc) · 121 KB
/
Copy pathindex.html
File metadata and controls
826 lines (825 loc) · 121 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
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
<!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>Available scripts & functions</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>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>PS C:\> Get-BootShutdownEvents |ft</p>
<p class="script-details-text">Level Time Kind Description<br>Info 15/3 6:00:47 WindowsUpdatesInstallStart Installation Started: Windows has started installing the following update: 2026-03 Cumulative Update for Microsoft ...<br>Info 15/3 6:19:54 WindowsUpdatesRestart The process C:\WINDOWS\system32\shutdown.exe (SRV2) has initiated the restart of computer SRV2 ...<br>Info 15/3 6:22:06 NormalShutdown The Event log service was stopped. (id 6006 from EventLog)<br>Info 15/3 6:22:12 NormalShutdown The operating system is shutting down at system time 2026 - 03 - 15T04:22:12.144072600Z. (id 13 from Microsoft-Windows-Kernel-General)<br>Info 15/3 6:22:14 Boot The operating system started at system time 2026 - 03 - 15T04:22:14.500000000Z. (id 12 from Microsoft-Windows-Kernel-General)<br>Info 15/3 6:22:22 Boot The Event log service was started. (id 6005 from EventLog)<br>Info 15/3 6:22:44 WindowsUpdatesRestart The process C:\WINDOWS\servicing\TrustedInstaller.exe (SRV2) has initiated the restart of computer SRV2...<br>Info 15/3 6:22:45 NormalShutdown The Event log service was stopped. (id 6006 from EventLog)<br>Info 15/3 6:22:51 NormalShutdown The operating system is shutting down at system time 2026 - 03 - 15T04:22:51.294803800Z. (id 13 from Microsoft-Windows-Kernel-General)<br>Info 15/3 6:22:53 Boot The operating system started at system time 2026 - 03 - 15T04:22:53.500000000Z. (id 12 from Microsoft-Windows-Kernel-General)<br>Info 15/3 6:22:59 Boot The Event log service was started. (id 6005 from EventLog)<br>Info 15/3 6:23:20 WindowsUpdatesInstallCompl Installation Successful: Windows successfully installed the following update: 2026-03 Cumulative Update for Microsoft ...</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 class="function-entry">
<p class="function-name"><strong>New-ZipFromFolder</strong></p>
<p class="function-synopsis">Creates a zip file from a folder, keeping the folder as the top-level entry and allowing for exclusions.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Packages the source folder into a zip file whose top-level folder name<br>matches the source folder name. Allows for recursive exclusions based<br>on file name, folder name, or relative path.</p>
<p class="script-details-text">Creates a sibling staging folder next to the source folder and removes<br>it before returning. On NTFS volumes, included files are staged as<br>hardlinks unless DontUseHardLinks is set. Hardlinking is super-fast.</p>
<p class="script-details-text">.PARAMETER Exclude<br>Wildcard patterns used to skip files or folders anywhere under the<br>source folder. A pattern can match an item name or a relative path.</p>
<p class="script-details-text">.PARAMETER NoCompression<br>When set, writes the archive without compression.</p>
<p class="script-details-text">.PARAMETER Fast<br>When set, writes the archive with the fastest compression level.</p>
<p class="script-details-text">.PARAMETER DontUseHardLinks<br>When set, stages included files by copying them; otherwise the function<br>uses hardlinks when the source volume supports them.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>New-ZipFromFolder .\App .\App.zip `<br>-Exclude '*.bak','*.tmp','.git'</p>
<p class="script-details-text">Creates the zip while skipping matching files and folders under App.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Delete-OldFiles</strong></p>
<p class="function-synopsis">Deletes old files under a directory tree, with WhatIf support.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Attempts to remove files under Root whose LastWriteTime is older than Cutoff.<br>Use this when you need recursive age-based cleanup with WhatIf support and<br>optional cleanup of child folders left empty by the cleanup.</p>
<p class="script-details-text">Skips reparse points (junctions/symlinks/mount points) so cleanup stays inside<br>the intended tree.</p>
<p class="script-details-text">By default, non-root child folders will be removed when they have no<br>remaining files or child folders after file cleanup. Root is not<br>removed. When LeaveEmptyFolders is set, folders are not removed.</p>
<p class="script-details-text">Deletion failures for individual files or folders are reported as warnings.<br>Other matching files and folders may still be processed. Permission limits,<br>locked files, and enumeration errors can leave matching files or folders in<br>place.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>None. Does not write objects to the pipeline.</p>
<p class="script-details-text">Writes scan and deletion status to the host.<br>Writes warnings for failed file or folder removals.</p>
<p class="script-details-text">.PARAMETER Root<br>Directory tree to clean. The path must identify an existing directory.</p>
<p class="script-details-text">.PARAMETER Cutoff<br>Files with LastWriteTime earlier than this value are candidates for removal.</p>
<p class="script-details-text">.PARAMETER LeaveEmptyFolders<br>When set, only matching files are removed; otherwise, non-root child folders<br>left empty by the cleanup scope may also be removed.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Delete-OldFiles -Root C:\Temp -Cutoff (Get-Date).AddMonths(-6) -WhatIf</p>
<p class="script-details-text">Shows the files and folders that would be removed under C:\Temp.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Delete-OldFiles -Root C:\Temp -Cutoff (Get-Date).AddMonths(-6) `<br>-LeaveEmptyFolders</p>
<p class="script-details-text">Removes old files under C:\Temp but does not remove empty folders.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Get-DiskInfo</strong></p>
<p class="function-synopsis">Gets Win32_LogicalDisk information for one disk.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Popular properties: FreeSpace, Size, VolumeName, DriveType<br>.PARAMETER Disk Disk device ID, for example C:.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Test-NonReparseDirectory</strong></p>
<p class="function-synopsis">Returns true only for real directories, not junctions/symlinks.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">.PARAMETER LiteralPath Directory path to test without wildcard expansion.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Clear-OldTempFiles</strong></p>
<p class="function-synopsis">Deletes old files from common Windows temporary file locations.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Attempts to remove old files from the current process TEMP and TMP<br>locations, the Windows temp location, and detected user temp folders under<br>C:\Users.</p>
<p class="script-details-text">Skips reparse points (junctions/symlinks/mount points) so cleanup stays inside<br>the intended trees.</p>
<p class="script-details-text">Use this as a safer temp cleanup entry point when you need age-based cleanup<br>across the usual system and user temp locations, WhatIf support, and optional<br>cleanup of child folders left empty by the cleanup.</p>
<p class="script-details-text">The cutoff is calculated from the current time and MonthsOld. By default,<br>non-root child folders inside each temp root may also be removed when they<br>have no remaining detected files or child folders after file cleanup. Temp<br>root folders themselves are not removed.</p>
<p class="script-details-text">Deletion failures for individual files or folders are reported as warnings.<br>Other matching files and folders may still be processed. Permission limits,<br>locked files, missing profiles, and enumeration errors can leave matching<br>files or folders in place. Run elevated to cover system and other-user temp<br>locations where required.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>None. Does not write objects to the pipeline.</p>
<p class="script-details-text">Writes cutoff, scan, and deletion status to the host.<br>Writes warnings for failed file or folder removals.</p>
<p class="script-details-text">.PARAMETER MonthsOld<br>Age threshold in months. Files older than the calculated cutoff are<br>candidates for removal.</p>
<p class="script-details-text">.PARAMETER LeaveEmptyFolders<br>When set, only matching files are removed; otherwise, non-root child folders<br>left empty by the cleanup will also be removed.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Clear-OldTempFiles -WhatIf</p>
<p class="script-details-text">Shows the old temp files and folders that would be removed.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Clear-OldTempFiles -MonthsOld 3 -LeaveEmptyFolders</p>
<p class="script-details-text">Removes temp files older than the calculated cutoff and keeps folders.</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>Get-RemoteIpOnLowListeningTcpPort</strong></p>
<p class="function-synopsis">Lists remote IPs observed on local listening TCP ports.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>Produces one psCustomObject per remote IP:<br>RemoteAddress : The remote IP address.<br>LocalPorts : The local listening port or ports observed.<br>States : The TCP state or states observed.<br>ConnectionCount : The number of matching TCP connection rows.</p>
<p class="script-details-text"><span class="detail-directive">.DESCRIPTION</span><br>Reports remote IPs that currently have, or still have visible closed TCP<br>rows for, non-loopback local listening TCP ports below the configured<br>port limit.</p>
<p class="script-details-text">Use this when you need a compact connection inventory by remote IP,<br>rather than raw connection rows. The result is a point-in-time view of<br>the TCP table. Connections removed by Windows before the function runs<br>are not reported.</p>
<p class="script-details-text">By default, loopback, wildcard, and the server's own IP addresses are<br>excluded from remote addresses. This avoids reporting connections from<br>the server to itself.</p>
<p class="script-details-text">.PARAMETER MaxListeningPortExclusive<br>Upper exclusive local listening port limit. Only listening TCP ports<br>below this value are considered.</p>
<p class="script-details-text">.PARAMETER States<br>TCP states to include. Use names accepted by the State property, such<br>as Established, TimeWait, CloseWait, LastAck, FinWait1, and FinWait2.</p>
<p class="script-details-text">.PARAMETER IncludeLocalMachineConnections<br>When set, connections whose remote address is one of the server's own<br>IP addresses may be included. Otherwise, they are excluded.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Get-RemoteIpOnLowListeningTcpPort</p>
<p class="script-details-text">Returns one row per remote IP, with the observed local ports, observed<br>TCP states, and matching connection-row count.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Get-RemoteIpOnLowListeningTcpPort -Verbose</p>
<p class="script-details-text">Returns the same objects and writes the matched connection details to<br>the verbose stream.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Watch-RemoteIpOnLowListeningTcpPort</strong></p>
<p class="function-synopsis">Watches and optionally reports remote IPs observed on local listening TCP ports.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Maintains a cumulative record of remote IPs observed on non-loopback local<br>listening TCP ports below the configured port limit.</p>
<p class="script-details-text">The watch continues until interrupted, for example with Ctrl+C. By default,<br>it runs silently and does not produce success-stream output.</p>
<p class="script-details-text">For each remote IP, the function records the first and last observation time.<br>Local ports and TCP states are accumulated for the lifetime of the stored<br>state.</p>
<p class="script-details-text">When ShowReport is specified, the cumulative report is displayed periodically.<br>Report generation is controlled separately from connection sampling to avoid<br>repeatedly constructing a potentially large formatted report.</p>
<p class="script-details-text">When StateFile is provided, the cumulative state is saved in CLIXML format.<br>If the file already exists, its observations are loaded and collection<br>continues from that state. This allows the watch to be stopped and resumed.</p>
<p class="script-details-text">The parent directory of StateFile is created when necessary. State is first<br>written to a temporary file and then moved over the configured state file to<br>reduce the risk of leaving a partially written file.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>None by default.</p>
<p class="script-details-text">When ShowReport is specified, the function writes a formatted report directly<br>to the host. The report contains:</p>
<p class="script-details-text">RemoteAddress : The remote IP address.<br>LocalPorts : Local listening port or ports observed.<br>States : TCP state or states observed.<br>FirstSeen : First time the remote IP was observed.<br>LastSeen : Most recent time the remote IP was observed.</p>
<p class="script-details-text">The formatted report is host output and is not written to the success-output<br>pipeline.</p>
<p class="script-details-text">.PARAMETER Seconds<br>Number of seconds between TCP connection sampling cycles.</p>
<p class="script-details-text">The default is 10 seconds.</p>
<p class="script-details-text">.PARAMETER MaxListeningPortExclusive<br>Upper exclusive local listening port limit. Only listening TCP ports below<br>this value are considered.</p>
<p class="script-details-text">The default is 49152.</p>
<p class="script-details-text">.PARAMETER States<br>TCP states to include, such as Established, TimeWait, CloseWait, LastAck,<br>FinWait1, and FinWait2.</p>
<p class="script-details-text">.PARAMETER IncludeLocalMachineConnections<br>Includes connections whose remote address is one of the server's own IP<br>addresses.</p>
<p class="script-details-text">By default, loopback, unspecified, and local-machine remote addresses are<br>excluded.</p>
<p class="script-details-text">.PARAMETER StateFile<br>Path to the CLIXML file used to load and save the cumulative observations.</p>
<p class="script-details-text">The file contains structured state, not the formatted on-screen report.</p>
<p class="script-details-text">.PARAMETER SaveIntervalSeconds<br>Minimum number of seconds between writes to StateFile.</p>
<p class="script-details-text">The default is 60 seconds. This parameter has no effect when StateFile is not<br>specified.</p>
<p class="script-details-text">.PARAMETER ShowReport<br>Displays the cumulative report periodically.</p>
<p class="script-details-text">By default, reporting is disabled. Collection and StateFile updates continue<br>normally without this switch.</p>
<p class="script-details-text">.PARAMETER ReportIntervalSeconds<br>Minimum number of seconds between displayed reports when ShowReport is<br>specified.</p>
<p class="script-details-text">The default is 300 seconds. This parameter does not affect the connection<br>sampling interval.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Watch-RemoteIpOnLowListeningTcpPort</p>
<p class="script-details-text">Samples TCP connections every 10 seconds and maintains cumulative observations<br>in memory. It produces no regular report and does not persist the observations.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Watch-RemoteIpOnLowListeningTcpPort `<br>-StateFile C:\IT\log\seen_tcp_connections.clixml</p>
<p class="script-details-text">Samples TCP connections every 10 seconds, loads any existing observations from<br>the CLIXML file, and saves the cumulative state at intervals of at least<br>60 seconds. It does not display the cumulative report.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Watch-RemoteIpOnLowListeningTcpPort `<br>-Seconds 5 `<br>-StateFile C:\Temp\observed-connections.clixml `<br>-ShowReport `<br>-ReportIntervalSeconds 300</p>
<p class="script-details-text">Samples connections every 5 seconds, saves cumulative state to the CLIXML file,<br>and displays the cumulative report no more than once every 5 minutes.</p>
</details>
</div>
<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 'google.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. - If -HostFriendlyName is passed, it is used to refer to the host in all messages. 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>
<p class="function-synopsis">Quotes a string so it can be safely passed as a single argument to a Windows process via CreateProcess, following the exact Win32 command-line parsing rules. It ensures arguments containing spaces, quotes, or trailing backslashes are preserved exactly as intended when reconstructed by the target program. 'simple' => simple 'hello world' => "hello world" 'He said "hello"' => "He said \"hello\"" 'C:\Temp\' => "C:\Temp\\" 'C:\Path With Spaces\' => "C:\Path With Spaces\\" 'C:\X\"Y"\Z\' => "C:\X\\\"Y\\\"\Z\\"</p>
</div>
<div class="function-entry">
<p class="function-name"><strong>Join-Win32CommandLine</strong></p>
<p class="function-synopsis">Constructs a valid Win32 command-line string from a list of arguments.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Iterates through a collection of arguments, applies Win32 escaping to each (via Quote-Win32Arg), and joins them with spaces. This creates a single string safe for use with APIs like System.Diagnostics.Process or CreateProcess.</p>
<p class="script-details-text">['git', 'commit', '-m', 'Msg'] => git commit -m Msg<br>['app.exe', 'C:\Program Files\', '/v'] => app.exe "C:\Program Files\\" /v<br>['echo', '', 'foo'] => echo "" foo</p>
<p class="script-details-text">.PARAMETER ArgumentList<br>The collection of objects (strings) to be joined. Objects are converted to strings before quoting.</p>
</details>
</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-DetachedScriptLocally</strong></p>
<p class="function-synopsis">Runs a PowerShell script in the background as SYSTEM user.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">The script runs elevated as a scheduled task under the local SYSTEM account,<br>and does not depend on the current terminal staying open or the user staying<br>logged-in. Note: SYSTEM has very limited network access. In many environments,<br>it cannot access normal user-mapped drives, user profile locations, remote<br>file shares, SharePoint/OneDrive sync paths owned by a user, or network<br>resources that require the user's credentials.</p>
<p class="script-details-text">.PARAMETER ScriptPath<br>The local path of the .ps1 script to run.</p>
<p class="script-details-text">.PARAMETER ArgumentList<br>Optional arguments to pass to the script.</p>
<p class="script-details-text">.PARAMETER LogPath<br>Optional path for redirected script output. If omitted, a log file is created<br>under the TEMP directory of the caller.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Invoke-DetachedScriptLocally -ScriptPath 'C:\Scripts\Foo.ps1'</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Invoke-DetachedScriptLocally -ScriptPath 'C:\Scripts\Foo.ps1' -ArgumentList 'server01', 'full'</p>
<p class="script-details-text">Runs the script as SYSTEM and passes two arguments.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Invoke-DetachedScriptLocally -ScriptPath 'C:\Scripts\Foo.ps1' -LogPath 'C:\Temp\Foo.log' -Verbose</p>
<p class="script-details-text">Runs the script as SYSTEM and writes verbose messages about cleanup of old idle<br>tasks.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Invoke-DetachedPSScript</strong></p>
<p class="function-synopsis">Executes a PowerShell script locally or on a remote host as a detached process.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">If Computer refers to the local machine, the script is executed through<br>Invoke-DetachedScriptLocally. This means that:<br>the script runs elevated as a scheduled task under the local SYSTEM account,<br>and does not depend on the current terminal staying open or the user staying<br>logged-in. Note: SYSTEM has very limited network access. In many environments,<br>it cannot access normal user-mapped drives, user profile locations, remote<br>file shares, SharePoint/OneDrive sync paths owned by a user, or network<br>resources that require the user's credentials.</p>
<p class="script-details-text">If Computer refers to a remote machine, the script is copied if needed and then<br>started remotely through CIM/WMI using Win32_Process.Create. Again the script<br>does not depend on the current terminal staying open or the user staying<br>logged-in or even the controller-computer being powered-on. This method also<br>avoids leaving a disconnected PowerShell remoting session after execution<br>completes.</p>
<p class="script-details-text">.PARAMETER Script<br>Path to a .ps1 script.</p>
<p class="script-details-text">.PARAMETER ScriptBlock<br>For local execution, it is written to:<br>C:\ProgramData\TempForDetachedScripts\Scripts</p>
<p class="script-details-text">For remote execution, it is written locally first, copied to the remote TEMP<br>directory (old generated remote temp scripts are cleaned up).</p>
<p class="script-details-text">.PARAMETER LogFile<br>Optional log file path. For local execution, if omitted, a log file is created<br>under:<br>C:\ProgramData\TempForDetachedScripts\Logs</p>
<p class="script-details-text">For remote execution, if supplied, Start-Transcript is used on the remote host.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>For local execution, returns task information from Invoke-DetachedScriptLocally<br>plus ComputerName, Status, and ExecutionMode.</p>
<p class="script-details-text">For remote execution, returns a PSCustomObject with:<br>ComputerName, ProcessId, ReturnValue, Status, and ExecutionPath.</p>
</details>
</div>
<div class="function-entry">
<p class="function-name"><strong>Get-ProcessesWithMatchingCommandLine</strong></p>
<p class="function-synopsis">List processes with command lines matching a like expression (e.g. "*myScript.ps1*")</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Get-ProcessesWithMatchingCommandLine "*myScript.ps1*"</p>
</details>
</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>Get-TopRamProcess</strong></p>
<p class="function-synopsis">Returns the processes consuming the most resident physical memory.</p>
<details class="script-details function-details">
<summary>Read more</summary>
<p class="script-details-text">Returns at least MinProcesses processes, ordered by working-set size.</p>
<p class="script-details-text">Processes continue to be included until their combined working sets reach<br>TargetPercentOfUsedRam percent of currently used physical RAM, or until<br>MaxProcesses processes have been included.</p>
<p class="script-details-text">Command lines are excluded by default because retrieving them requires an<br>additional CIM/WMI query. Use IncludeCommandLine when they are required.</p>
<p class="script-details-text">.PARAMETER MinProcesses<br>Minimum number of processes to return.</p>
<p class="script-details-text">The default is 10.</p>
<p class="script-details-text">.PARAMETER MaxProcesses<br>Maximum number of processes to return, even when the requested RAM target<br>has not been reached.</p>
<p class="script-details-text">MaxProcesses cannot be less than MinProcesses.</p>
<p class="script-details-text">The default is 100.</p>
<p class="script-details-text">.PARAMETER TargetPercentOfUsedRam<br>Target percentage of currently used physical RAM to cover with the<br>cumulative working sets of the returned processes.</p>
<p class="script-details-text">This is a target rather than an exact limit. The final process is included<br>in full, so the reported cumulative percentage normally exceeds the<br>requested percentage slightly.</p>
<p class="script-details-text">The default is 25.</p>
<p class="script-details-text">.PARAMETER IncludeCommandLine<br>Retrieves and returns the command line for each selected process.</p>
<p class="script-details-text">This requires an additional CIM/WMI query and therefore adds work on an<br>already stressed system. Command lines may also expose passwords, tokens,<br>connection strings, or other sensitive values.</p>
<p class="script-details-text">Some command lines may be unavailable because of permissions, protected<br>processes, or processes terminating during collection.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>PSCustomObject with the following properties:</p>
<p class="script-details-text">PID<br>Name<br>RAM_MB<br>RAM_PercentOfTotal<br>RAM_PercentOfUsed<br>CumulativeUsedRAMPct<br>CommandLine</p>
<p class="script-details-text">CommandLine is null unless IncludeCommandLine is specified.</p>
<p class="script-details-text"><span class="detail-directive">.NOTES</span><br>RAM usage is based on each process's working set. A working set represents<br>physical pages currently resident for that process, but it can contain<br>shared pages such as DLL code. Summing process working sets can therefore<br>count the same physical pages more than once. CumulativeUsedRAMPct is an<br>approximate diagnostic value and can exceed 100 percent.</p>
<p class="script-details-text">Not all used physical RAM belongs to user-visible processes. Kernel pools,<br>drivers, the file cache, modified pages, memory compression, and other<br>operating-system allocations may consume substantial memory. The requested<br>target may therefore be unreachable even when every process is considered.</p>
<p class="script-details-text">Process state can change during collection. A process may terminate after<br>it is enumerated, and Windows could theoretically reuse its PID before the<br>optional command-line query. Such races cannot be eliminated by a snapshot<br>function.</p>
<p class="script-details-text">The function creates and sorts a small object for every process before<br>selecting the result set. This is normally minor, but its cost increases on<br>systems running unusually large numbers of processes.</p>
<p class="script-details-text">When IncludeCommandLine is used, the additional CIM/WMI query consumes more<br>CPU and memory and may involve provider activity at a time when the system<br>is already under pressure.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Get-TopRamProcess</p>
<p class="script-details-text">Returns at least 10 processes and continues until their combined working<br>sets approximately cover 25 percent of currently used RAM, up to a maximum<br>of 100 processes.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Get-TopRamProcess -MinProcesses 5 -MaxProcesses 50 `<br>-TargetPercentOfUsedRam 40</p>
<p class="script-details-text">Returns at least 5 and at most 50 processes, targeting approximately<br>40 percent of currently used RAM.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Get-TopRamProcess -IncludeCommandLine</p>
<p class="script-details-text">Includes process command lines using an additional CIM/WMI query.</p>
</details>
</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="Invoke-EnableBitLockerForDriveC.ps1">Invoke-EnableBitLockerForDriveC.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="Invoke-EnableBitLockerForDriveC.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for Invoke-EnableBitLockerForDriveC.ps1" aria-label="Copy download command for Invoke-EnableBitLockerForDriveC.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">Requires -Version 5.1<br>Requires -RunAsAdministrator</p>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="Out-PingStats.ps1">Out-PingStats.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="Out-PingStats.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for Out-PingStats.ps1" aria-label="Copy download command for Out-PingStats.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">Continuously pings a host, or a small set of public hosts, and displays live connection-quality statistics.</p>
<details class="script-details">
<summary>Read more</summary>
<p class="script-details-text">Out-PingStats is an interactive terminal monitor for ICMP latency and packet loss.</p>
<p class="script-details-text">When you specify -Target, it continuously pings that host and renders live graphs and summaries for:<br>- recent RTT values<br>- RTT histogram<br>- rolling loss percentage<br>- rolling one-way jitter estimate<br>- rolling RTT 95th percentile</p>
<p class="script-details-text">When you omit -Target, it probes a few well-known Internet hosts in parallel and treats the result as a rough<br>"Internet reachability and quality" indicator rather than a measurement for one exact destination.</p>
<p class="script-details-text">The display updates continuously until you stop it, typically with Ctrl+C.</p>
<p class="script-details-text">This command is intended for human monitoring in a console window. Its primary output is a live screen display,<br>not pipeline-friendly structured objects.</p>
<p class="script-details-text">For best-looking graphs, use a monospace font with good Unicode block-character support. DejaVu Sans Mono works<br>well. Consolas usually forces lower-resolution graph characters.</p>
<p class="script-details-text">.INTERACTIVE CONTROLS<br>While the monitor is running, you can use:<br>Ctrl-H Toggle RTT histogram<br>Ctrl-R Toggle recent-RTT graph<br>Ctrl-L Toggle loss graph<br>Ctrl-J Toggle jitter graph<br>Ctrl-S Toggle graph character set / font mode</p>
<p class="script-details-text">.PARAMETER Target<br>Host name or IP address to probe.</p>
<p class="script-details-text">If omitted, the command monitors general Internet quality by pinging several public hosts in parallel.</p>
<p class="script-details-text">.PARAMETER Title<br>Custom title shown at the top of the screen.</p>
<p class="script-details-text">By default, the title is derived from -Target, or shows a generic Internet-oriented title when -Target is omitted.</p>
<p class="script-details-text">.PARAMETER GraphMax<br>Upper Y-axis limit for RTT graphs.</p>
<p class="script-details-text">By default, the command chooses a sensible value automatically.</p>
<p class="script-details-text">.PARAMETER PingsPerSec<br>Deprecated. Currently has no practical effect.</p>
<p class="script-details-text">.PARAMETER GraphMin<br>Lower Y-axis limit for RTT graphs.</p>
<p class="script-details-text">By default, the command chooses a sensible value automatically.</p>
<p class="script-details-text">.PARAMETER HistBucketsCount<br>Number of buckets to use in the RTT histogram.</p>
<p class="script-details-text">.PARAMETER AggregationSeconds<br>Number of seconds per aggregation period for the slower trend graphs such as loss, jitter, and RTT 95th percentile.</p>
<p class="script-details-text">.PARAMETER HistSamples<br>Number of recent samples to include in the RTT histogram.</p>
<p class="script-details-text">If omitted, the default is at least 100 samples and otherwise about one minute of samples.</p>
<p class="script-details-text">.PARAMETER Visual<br>Reserved legacy parameter. Do not rely on it.</p>
<p class="script-details-text">.PARAMETER DebugMode<br>Enables diagnostic behavior and reduces screen-clearing behavior to help troubleshoot parsing, aggregation,<br>or rendering issues.</p>
<p class="script-details-text">.PARAMETER DebugData<br>Enables extra debug-data collection.</p>
<p class="script-details-text">.PARAMETER HighResFont<br>Controls graph-character mode.</p>
<p class="script-details-text">-1 = auto-detect<br>0 = force low-resolution characters<br>1 = force high-resolution characters</p>
<p class="script-details-text">Use low-resolution mode for terminals or fonts that do not render the Unicode block characters cleanly.</p>
<p class="script-details-text">.PARAMETER UpdateScreenEvery<br>How often, in seconds, the screen is refreshed.</p>
<p class="script-details-text">Lower values make the display more responsive but may increase CPU use.</p>
<p class="script-details-text">.PARAMETER BarGraphSamples<br>How many recent samples to show in the scrolling bar graphs.</p>
<p class="script-details-text">By default, the command derives this from the current console width.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Out-PingStats google.com</p>
<p class="script-details-text">Continuously monitors latency, loss, jitter, and latency distribution to google.com.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Out-PingStats 1.1.1.1 -Title "Cloudflare DNS"</p>
<p class="script-details-text">Monitors 1.1.1.1 and shows a custom title.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Out-PingStats</p>
<p class="script-details-text">Shows a rough live view of general Internet quality by probing several public hosts in parallel.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>Out-PingStats 8.8.8.8 -GraphMin 0 -GraphMax 100 -AggregationSeconds 60</p>
<p class="script-details-text">Monitors 8.8.8.8 with fixed RTT graph limits and 1-minute aggregation windows.</p>
<p class="script-details-text"><span class="detail-directive">.NOTES</span><br>This command starts background jobs and cleans them up when it exits.</p>
<p class="script-details-text">It also writes temporary screen/statistics data files under $env:TEMP.</p>
<p class="script-details-text">Loss, jitter, and percentile values are intended for operational monitoring, not for strict scientific measurement.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>None. This command is designed for interactive console display.</p>
<p class="script-details-text"><span class="detail-directive">.INPUTS</span><br>None. This command does not accept pipeline input.</p>
</details>
</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 ME<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<br>iwr -useb https://ndemou.github.io/scripts/$f -out $p\$f<br>. C:\IT\bin\Start-Copy4Toula.ps1</p>
<p class="script-details-text">Then repeat these steps:<br>1. Copy code from the LLM<br>2. Run `q` in the terminal (it will execute the code _and_ copy back the results)<br>3. Go back to the LLM and paste the output.</p>
<p class="script-details-text">If you run commands directly, or accidentally press ctrl-C you can<br>run `q -CopyOnly` to copy all output since the last time you run either `q`.</p>
<p class="script-details-text">DETAILS<br>The script maintains two transcript files:<br>- `$env:TEMP\TTFtrans-$PID.full.txt` has the cumulative raw history of the full session.<br>- `$env:TEMP\TTFtrans-$PID.txt` has just the most recent chunk of output.</p>
<p class="script-details-text">`q` will copy up to 5000 lines by default. You can change the limit:<br>$global:SctMaxLinesToCopy = 8000</p>
<p class="script-details-text">The output of some rare legacy tools may not appear in the transcript.<br>It's extremely rare though and you can always copy-paste manually.</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>$dir="C:\IT\bin";$f="Test-ForCpuRamDiskStress.ps1"<br>if (-not (test-path $dir\$f)) {<br>"Downloading"; mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f<br>if (-not (Get-ScheduledTask -TaskPath '\enLogic\' -TaskName 'Execute Test-ForCpuRamDiskStress.ps1' -ErrorAction SilentlyContinue)) {<br>$dir="C:\IT\bin";$f="helpers-processes.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f<br>. $dir\$f<br>"Setting up scheduled task on startup"<br>New-ScheduledTaskForPSScript -ScriptPath 'C:\IT\bin\Test-ForCpuRamDiskStress.ps1' `<br>-ScheduleType Startup -TaskPath '\enLogic\' `<br>-ScriptArguments @('-LogDir','c:\it\log','-LogBaseName','CpuRamDiskStress')<br>"Starting task"<br>Start-ScheduledTask -TaskPath '\enLogic\' -TaskName 'Execute Test-ForCpuRamDiskStress.ps1'<br>sleep 2<br>Get-ScheduledTask -TaskPath '\enLogic\' -TaskName 'Execute Test-ForCpuRamDiskStress.ps1'</p>
<p class="script-details-text">if (test-path C:\it\log\CpuRamDiskStress.*) {<br>"Last 20 lines of latest log file"<br>cat (ls C:\it\log\CpuRamDiskStress.*|sort -Property LastWriteTime|select -last 1) | select -last 20<br>} else { echo "ERROR: No logs found: C:\it\log\CpuRamDiskStress.*" }</p>
<p class="script-details-text"><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="update-all-github-ndemou-scripts.ps1">update-all-github-ndemou-scripts.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="update-all-github-ndemou-scripts.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for update-all-github-ndemou-scripts.ps1" aria-label="Copy download command for update-all-github-ndemou-scripts.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">Ensures all ndemou.github.io/scripts are up-to-date.</p>
<details class="script-details">
<summary>Read more</summary>
<p class="script-details-text">Existing files that differ are replaced and a backup is created.<br>Identical files are left unchanged.</p>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="Update-GetHealthCode.ps1">Update-GetHealthCode.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="Update-GetHealthCode.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for Update-GetHealthCode.ps1" aria-label="Copy download command for Update-GetHealthCode.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">Ensures all health-check scripts and PS Modules are installed & up-to-date.</p>
<details class="script-details">
<summary>Read more</summary>
<p class="script-details-text">Missing files are created. Existing files that differ are replaced.<br>Identical files are left unchanged. If this script updates itself,<br>it re-invokes the updated copy. When replacing a file, backup copies<br>are created in the backups directory.<br>Will set PSGallery as Trusted.</p>
<p class="script-details-text">The updater also tracks a small "release marker" for the currently<br>installed code and for the target update source. A release marker is a<br>stable identifier for a specific release source, typically derived from<br>GitHub release metadata or from a manually supplied zip file name plus<br>its content hash. These markers are cached locally and let the updater<br>decide whether the requested update is already installed, whether it can<br>skip re-downloading or reapplying files, and when `-Reinstall` should<br>force the update to run again.</p>
<p class="script-details-text">.PARAMETER Reinstall<br>Forces reinstall even when installed release matches target release.</p>
<p class="script-details-text">.PARAMETER UpdateFromZip<br>Overides the default which is to fetch the latest GitHub release.</p>
<p class="script-details-text">.PARAMETER Version<br>Explicit semantic version to associate with `-UpdateFromZip` when the zip<br>file name does not already embed a version token such as `v4.4.3`.<br>Use `X.Y.Z` or `vX.Y.Z`.</p>
<p class="script-details-text">.PARAMETER ForceRefreshReleaseMetadata<br>Overides the default which is to cache latest-release metadata locally<br>for a few minutes (to avoid querying GitHub on every run).</p>
<p class="script-details-text">.PARAMETER Config<br>Hashtable or PowerShell data file path for customized installs. `Options.InstallDir`<br>sets the install root; `ConfigFiles` writes named .psd1 files under the config folder.</p>
<p class="script-details-text">.PARAMETER SelfRerunCount<br>Internal use only. Tracks the one-time self-rerun pass count.</p>
<p class="script-details-text">.PARAMETER PersistReleaseMarker<br>Internal use only. Carries the resolved release marker across self-rerun.</p>
<p class="script-details-text">.PARAMETER Config<br>Optional installer customization. Pass either a hashtable or the path to a PowerShell data file.<br>The top-level Options branch changes installer behavior. The top-level ConfigFiles branch<br>creates PowerShell data files under the installation config directory.</p>
<p class="script-details-text">.PARAMETER GenerateConfigPsd1<br>Creates a template PowerShell data file named GetComputerHealth.install.psd1 in the current<br>working directory, then exits. Customize that file and pass its path to -Config.</p>
<p class="script-details-text">.PARAMETER ScheduleDailyInvokationAt<br>Creates or updates a daily scheduled task for Invoke-GetComputerHealth.ps1<br>at the specified 24-hour time in HH:mm format, then exits.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>.\Update-GetHealthCode.ps1</p>
<p class="script-details-text">Checks the locally cached latest-release metadata, refreshes it from<br>GitHub when needed, and installs the latest published release when it is<br>newer than the currently installed release marker.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>.\Update-GetHealthCode.ps1 -UpdateFromZip C:\Downloads\GetComputerHealth-v4.4.3.zip</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>.\Update-GetHealthCode.ps1 -ScheduleDailyInvokationAt 07:12</p>
</details>
</li>
<li class="script-item">
<div class="script-header">
<a class="script-link" href="Watch-SeenMacAddresses.ps1">Watch-SeenMacAddresses.ps1</a>
<button class="copy-button" data-copy="$dir="C:\IT\bin";$f="Watch-SeenMacAddresses.ps1";mkdir $dir -force >$null;iwr -useb https://ndemou.github.io/scripts/$f -out $dir\$f" title="Copy download command for Watch-SeenMacAddresses.ps1" aria-label="Copy download command for Watch-SeenMacAddresses.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">Records MAC,IP address pairs observed in the local IPv4 neighbor cache.</p>
<details class="script-details">
<summary>Read more</summary>
<p class="script-details-text">The script keeps a running inventory of observed MAC,IP address pairs, with<br>first seen time and last seen time for each pair.</p>
<p class="script-details-text">It is intended for passive local-link observation. It does not scan the<br>network and should not be used as proof that all devices in a VLAN were<br>found.</p>
<p class="script-details-text">When StateFile is supplied, the output file is created or replaced as a<br>PowerShell CLIXML file. The parent directory is created when needed. If the<br>script stops normally or is interrupted, it attempts one final write. A<br>terminating error can leave the latest observations not written, and may<br>leave a temporary file beside the target file.</p>
<p class="script-details-text">When StateFile is not supplied, the script only prints newly discovered<br>MAC,IP pairs to the screen and keeps the in-memory inventory only for the<br>current run.</p>
<p class="script-details-text"><span class="detail-directive">.OUTPUTS</span><br>Produces no pipeline output.</p>
<p class="script-details-text">Creates or updates a CLIXML file containing:<br>GeneratedAt : Timestamp of the file generation.<br>SeenMACs : List of records, one per MAC,IP address pair.<br>MAC : MAC address.<br>IP : IPv4 address.<br>FirstSeenTimestamp : First time this MAC,IP pair was observed.<br>LastSeenTimestamp : Most recent time this MAC,IP pair was observed.</p>
<p class="script-details-text">.PARAMETER StateFile<br>Optional path of the CLIXML inventory file to create or update. When omitted, no existing state is loaded and no state file is written.</p>
<p class="script-details-text">.PARAMETER PollSeconds<br>Number of seconds between neighbor-cache observations.</p>
<p class="script-details-text">.PARAMETER IdleWriteSeconds<br>Maximum seconds between writes when no new MAC,IP pair has been observed.</p>
<p class="script-details-text">.PARAMETER ActiveWriteSeconds<br>Maximum seconds between writes while new MAC,IP pair observations exist.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>.\Watch-SeenMacAddresses.ps1</p>
<p class="script-details-text">Starts passive observation without loading or writing a state file.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>.\Watch-SeenMacAddresses.ps1 -StateFile 'C:\it\log\seen_MAC_addresses.clixml'</p>
<p class="script-details-text">Starts passive observation and persists observations to the specified CLIXML file.</p>
<p class="script-details-text"><span class="detail-directive">.EXAMPLE</span><br>.\Watch-SeenMacAddresses.ps1 -StateFile 'C:\it\log\seen_MAC_addresses.clixml' -PollSeconds 15 -ActiveWriteSeconds 30</p>
<p class="script-details-text">Polls every 15 seconds and writes changed data at most every 30 seconds.</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>
<p class="footer">Last updated at 2026-06-16 13:12 UTC</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>