psadt-deploy-skill 0.26.7 → 0.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +973 -865
- package/bin/install.mjs +201 -125
- package/package.json +37 -37
package/README.md
CHANGED
|
@@ -1,865 +1,973 @@
|
|
|
1
|
-
<h1 align="center">PSADT v4 → Intune Deployment Skill</h1>
|
|
2
|
-
|
|
3
|
-
<p align="center">
|
|
4
|
-
<em>A Claude Code skill that drives the full lifecycle of a PowerShell App Deployment Toolkit (PSADT) v4.x Intune Win32 package — from first conversation to a tested, upload-ready <code>.intunewin</code>.</em>
|
|
5
|
-
</p>
|
|
6
|
-
|
|
7
|
-
<p align="center">
|
|
8
|
-
<a href="
|
|
9
|
-
<img src="https://img.shields.io/badge/
|
|
10
|
-
<img src="https://img.shields.io/badge/
|
|
11
|
-
<img src="https://img.shields.io/badge/
|
|
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
|
-
the
|
|
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
|
-
- For the **
|
|
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
|
-
one
|
|
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
|
-
### 0.
|
|
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
|
-
### 0.
|
|
631
|
-
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
`New-
|
|
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
|
-
### 0.
|
|
720
|
-
- **
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
### 0.
|
|
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
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
- **
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
-
|
|
848
|
-
|
|
849
|
-
### 0.
|
|
850
|
-
- **
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
(
|
|
861
|
-
-
|
|
862
|
-
|
|
863
|
-
-
|
|
864
|
-
|
|
865
|
-
|
|
1
|
+
<h1 align="center">PSADT v4 → Intune Deployment Skill</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<em>A Claude Code skill that drives the full lifecycle of a PowerShell App Deployment Toolkit (PSADT) v4.x Intune Win32 package — from first conversation to a tested, upload-ready <code>.intunewin</code>.</em>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/pt1987/claude-code-psadt-skill/actions/workflows/tests.yml"><img src="https://github.com/pt1987/claude-code-psadt-skill/actions/workflows/tests.yml/badge.svg" alt="tests" /></a>
|
|
9
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" alt="License: MIT" /></a>
|
|
10
|
+
<img src="https://img.shields.io/badge/PSADT-v4.x-0a7bbb?style=flat-square" alt="PSADT v4.x" />
|
|
11
|
+
<img src="https://img.shields.io/badge/Platform-Windows-0078d6?style=flat-square&logo=windows&logoColor=white" alt="Windows" />
|
|
12
|
+
<img src="https://img.shields.io/badge/Claude%20Code-Skill-d97757?style=flat-square" alt="Claude Code Skill" />
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center"><sub><a href="#quick-start">Quick start</a> · <a href="#how-it-works">How it works</a> · <a href="#features">Features</a> · <a href="#first-run-setup">Setup</a> · <a href="#security">Security</a> · <a href="#roadmap">Roadmap</a> · <a href="#changelog">Changelog</a></sub></p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What is this?
|
|
20
|
+
|
|
21
|
+
A **Claude Code skill** (not a plugin): a reusable instruction package that teaches the agent how to build,
|
|
22
|
+
package, test, troubleshoot and deploy a **PSADT v4.x Intune Win32 app**. You describe the application; the
|
|
23
|
+
skill runs the workflow — intake, web research, scaffolding, all three deployment types
|
|
24
|
+
(Install / Uninstall / Repair), pre-flight checks, the SYSTEM test, packaging, the dossier, and the
|
|
25
|
+
optional Graph upload.
|
|
26
|
+
|
|
27
|
+
A skill is a folder with a `SKILL.md` (YAML frontmatter + Markdown instructions), here bundled with
|
|
28
|
+
`scripts/` and `references/`. It loads progressively: the agent sees only the name and description until a
|
|
29
|
+
task makes it relevant, then the full body loads on demand.
|
|
30
|
+
|
|
31
|
+
<img width="1024" height="254" alt="image" src="https://github.com/user-attachments/assets/7c7931ba-dcae-4476-a648-11115eceb3b5" />
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
npx psadt-deploy-skill
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
That installs the skill into `~/.claude/skills/psadt-deploy` and runs the setup doctor, which provisions
|
|
40
|
+
everything it can and names the handful of values only you can supply (see
|
|
41
|
+
[First-run setup](#first-run-setup)). Then open Claude Code in any folder and say what you want:
|
|
42
|
+
|
|
43
|
+
> *"Create the Win32 Intune package for 7-Zip 24.09"* — or *"package Notepad++ for Intune"*
|
|
44
|
+
|
|
45
|
+
The skill asks at most **four decision gates** (scope · deployment semantics · SYSTEM-test consent ·
|
|
46
|
+
upload confirmation). Everything else it researches and states as an assumption instead of asking.
|
|
47
|
+
|
|
48
|
+
## How it works
|
|
49
|
+
|
|
50
|
+
Twelve phases, each owned by a script rather than by prose, so a step either passed or did not:
|
|
51
|
+
|
|
52
|
+
| Phase | What happens | Owner |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| **0** Setup | 13 prerequisite checks, GREEN/YELLOW/RED, `-Fix` provisions | `Initialize-PsadtSkill.ps1` |
|
|
55
|
+
| **1–2** Intake + research | blocker questions as clickable options; parallel research of version, silent switches, Intune pitfalls | agent (gates 1–2) |
|
|
56
|
+
| **3** Scaffold | a generator writes launcher + detection + per-run log name + manifest; `New-ADTTemplate` only when none fits | `New-MsiPackage` · `New-BrowserExtensionPackage` · `New-WindowsFeaturePackage` · `New-DriverPackage` |
|
|
57
|
+
| **4** Customize | all three hooks filled from the research, helpers in the Extensions module | agent |
|
|
58
|
+
| **5** Pre-flight | 10 checks (encoding, AST parse, v3 cmdlets, structure, detection contract, manifest, log name, driver trust …) → GREEN/RED | `Invoke-PsadtPreflight.ps1` |
|
|
59
|
+
| **6** SYSTEM test | installs/uninstalls as **SYSTEM** like the IME does; **binding before any upload** | `Invoke-PsadtSystemTest.ps1` |
|
|
60
|
+
| **7** Package | one command → verified `.intunewin`, named after the app | `Invoke-PsadtPackage.ps1` |
|
|
61
|
+
| **8** Dossier | always, uploaded or not: bilingual self-contained HTML | `New-PsadtReport.ps1` |
|
|
62
|
+
| **9** Upload *(opt-in)* | dry run → confirm → `win32LobApp` via raw Graph | `Invoke-IntuneWin32Upload.ps1` |
|
|
63
|
+
| **10** Assignment *(opt-in)* | create/reuse Entra groups by naming scheme | `Invoke-IntuneAppAssignment.ps1` |
|
|
64
|
+
| **11–12** Test + rollout | DEV-VM cycles, test group, pilot → staged production | agent |
|
|
65
|
+
|
|
66
|
+
**Everything one app knows lives in `<pkg>\psadt-package.json`** — identity, the decisions taken at the
|
|
67
|
+
gates, the research findings, every phase's result and the artifacts produced. The generators write it,
|
|
68
|
+
every later phase reads and updates it, and pre-flight fails without it. That is what stops two packages of
|
|
69
|
+
the same app from disagreeing about their own version.
|
|
70
|
+
|
|
71
|
+
Depth lives in `references/` (phases 0–12 + appendices A–Q, one file per domain — see
|
|
72
|
+
`references/README.md`); `SKILL.md` stays the
|
|
73
|
+
control plane.
|
|
74
|
+
|
|
75
|
+
## Features
|
|
76
|
+
|
|
77
|
+
### Setup and prerequisites
|
|
78
|
+
|
|
79
|
+
- **Setup doctor** — one idempotent script checks PowerShell 7, Windows PowerShell 5.1, elevation, git,
|
|
80
|
+
PSAppDeployToolkit, the content-prep tool, `Invoke-CommandAs`, Pester, the config, a legacy config, the
|
|
81
|
+
skill tree, a pending update and the Intune credentials. Each line carries a concrete fix; `-Fix` applies
|
|
82
|
+
the ones that need no decision.
|
|
83
|
+
- **Self-healing prerequisites** — installs the PSAppDeployToolkit module from the PowerShell Gallery and
|
|
84
|
+
downloads `IntuneWinAppUtil.exe`, keeping both current against their official sources.
|
|
85
|
+
- **Config outside the skill folder** — `config.json`, `secret.dpapi` and `tools/` live in
|
|
86
|
+
`%LOCALAPPDATA%\psadt-deploy\` (override: `$env:PSADT_DEPLOY_HOME`), so a `git pull`, a re-clone or a
|
|
87
|
+
re-install can no longer take your setup with it. A pre-0.19 config keeps working and is migrated on
|
|
88
|
+
request, never deleted.
|
|
89
|
+
- **One-line install** — `npx psadt-deploy-skill` (Node 18+, zero dependencies) does clone-or-update plus
|
|
90
|
+
the doctor run in one step.
|
|
91
|
+
|
|
92
|
+
### Build and verify
|
|
93
|
+
|
|
94
|
+
- **Guided intake** — the blocker questions up front as clickable options, pre-filled with researched
|
|
95
|
+
defaults (app, latest version, installer type, package type).
|
|
96
|
+
- **Autonomous research** — checks the installed PSADT version against the latest release *and* whether
|
|
97
|
+
commands changed; researches silent install / uninstall / repair switches and known Intune pitfalls.
|
|
98
|
+
- **All three deployment types from the start** — Install, Uninstall *and* Repair, acid-tested, so
|
|
99
|
+
Company-Portal uninstalls actually work.
|
|
100
|
+
- **Pre-flight gate** — encoding/BOM, AST parse, launcher acid test, v3-cmdlet scan, hook structure,
|
|
101
|
+
detection-script contract, the package manifest, the per-run log name and driver trust. GREEN or RED,
|
|
102
|
+
with the failing file named.
|
|
103
|
+
- **Automated SYSTEM test loop** *(opt-in, binding before upload)* — installs, uninstalls and reinstalls as
|
|
104
|
+
the **SYSTEM** account via `Invoke-CommandAs`, mirroring the Intune Management Extension; reads the fresh
|
|
105
|
+
session log and the detection result, and hands back a structured verdict for the fix-and-retry loop.
|
|
106
|
+
Needs an elevated session; belongs on a VM with a snapshot.
|
|
107
|
+
- **Deterministic packaging** — `<outputRoot>\<Vendor>_<App>_<Version>_<Arch>\<same stem>.intunewin`,
|
|
108
|
+
verified after the fact (`Detection.xml`, `SetupFile`, size, SHA256), with the detection script and the
|
|
109
|
+
real logo beside it. It refuses an output folder inside the package, and never deletes a foreign
|
|
110
|
+
`.intunewin` it finds there.
|
|
111
|
+
- **One PSADT log per run** — `<Vendor>_<App>_<Version>_<Arch>_<Install|Uninstall|Repair>_<timestamp>.log`
|
|
112
|
+
instead of every run of every version appending to one unreadable file.
|
|
113
|
+
|
|
114
|
+
### Package types
|
|
115
|
+
|
|
116
|
+
The app's **native installer is always the default**. Everything else is opt-in and only on request:
|
|
117
|
+
|
|
118
|
+
- **MSI / EXE** — the ordinary case, via `New-MsiPackage.ps1` or a hand-filled scaffold.
|
|
119
|
+
- **WinGet** — the full `PSAppDeployToolkit.WinGet` lifecycle: the extension module self-heals into the
|
|
120
|
+
package, the Package ID is discovered with `Find-ADTWinGetPackage`, hooks use `*-ADTWinGet*`
|
|
121
|
+
(`-Scope Machine`). Never selected on its own initiative.
|
|
122
|
+
- **Browser extensions** — force-install via the Edge/Chrome/Firefox policy keys (including the Firefox
|
|
123
|
+
`REG_MULTI_SZ` trap), with selective removal on uninstall.
|
|
124
|
+
- **Windows features** — `Enable-WindowsOptionalFeature` and `Add-WindowsCapability`, offline source or a
|
|
125
|
+
temporary WSUS bypass that is restored afterwards, `EnablePending` handled honestly.
|
|
126
|
+
- **Third-party drivers** — the trust situation is classified *before* anything is built: Microsoft-signed
|
|
127
|
+
installs silently, vendor-signed needs the signer certificate owned in exactly one place, and a
|
|
128
|
+
vendor-signed **kernel** driver is refused because `TrustedPublisher` satisfies the PnP prompt but never
|
|
129
|
+
Code Integrity — it would install and then not load. Unsigned is refused outright, with three honest
|
|
130
|
+
options and no testsigning. Staging is per-INF `pnputil`; uninstall resolves `oemNN.inf` by original name
|
|
131
|
+
instead of a remembered index.
|
|
132
|
+
- **Script-only / remediation packages** — ESP-safe patterns for fix packages with no installer at all.
|
|
133
|
+
|
|
134
|
+
### Deliverables
|
|
135
|
+
|
|
136
|
+
- **HTML dossier — always generated**, uploaded or not. One self-contained file
|
|
137
|
+
(`Intune-Dossier.html`) built from a fixed template, never hand-assembled: the **Intune dossier** (App
|
|
138
|
+
Info, return-code map, detection rule, requirements, assignments, driver trust, and a ready-to-paste
|
|
139
|
+
**Markdown** description for the Company-Portal field) plus a **technical package report** (the three
|
|
140
|
+
hooks, PSADT cmdlets used, pre-flight and SYSTEM-test results, logo and `.intunewin` verification).
|
|
141
|
+
Bilingual with a DE/EN toggle, browser-translatable, logo embedded as a data URI.
|
|
142
|
+
- **Real logo only** — finds and downloads the actual application logo (vendor source or Wikimedia
|
|
143
|
+
Commons), verifies real pixel transparency *and* looks at the image. The PSADT default `AppIcon.png` is
|
|
144
|
+
blocked by hash.
|
|
145
|
+
- **Start Menu only** — creates Start Menu entries and removes stray desktop icons.
|
|
146
|
+
|
|
147
|
+
### Intune
|
|
148
|
+
|
|
149
|
+
- **Access as state, not as a 403** — `Test-PsadtIntuneAccess.ps1` answers before Phase 9 whether the app
|
|
150
|
+
can upload, assign groups or create policies, and for how long the credential lives. Verified / refused /
|
|
151
|
+
**unknown** are three different answers, and an offline check never overwrites what was verified before.
|
|
152
|
+
- **Direct upload via Microsoft Graph** *(opt-in)* — pushes the `.intunewin` as a `win32LobApp` (app +
|
|
153
|
+
logo), self-contained raw Graph, no third-party module. Identity comes from the manifest, so Intune shows
|
|
154
|
+
the same name and version as the artifact and the dossier. Read-only dry run → confirm → upload. Fills the
|
|
155
|
+
whole App-information tab, **never deletes an older version** (new versions coexist, with optional
|
|
156
|
+
supersedence wiring), never auto-assigns categories or notes.
|
|
157
|
+
- **One-time Entra bootstrap** — `New-PsadtEntraApp.ps1` signs in via **WAM**, creates the app, grants and
|
|
158
|
+
admin-consents the roles and stores the credential: a **certificate** (preferred — nothing secret at rest,
|
|
159
|
+
JWT client-assertion auth) or a DPAPI-encrypted client secret. Re-running it is normal: it finds the
|
|
160
|
+
recorded app, merges requested permissions instead of replacing them, and never prompts.
|
|
161
|
+
- **Opt-in group assignment** — creates/reuses Entra security groups by a configured naming scheme and
|
|
162
|
+
assigns Required / Available / Uninstall. Least-privilege (`Group.Create` + `GroupMember.Read.All`),
|
|
163
|
+
dry run → confirm, idempotent, and it never deletes a group or another app's assignment.
|
|
164
|
+
- **Certificate + firewall policies** — Custom OMA-URI profiles for `TrustedPublisher` / `TrustedPeople`
|
|
165
|
+
(the built-in template cannot reach those stores) and settings-catalog firewall-rule policies. Both
|
|
166
|
+
scripts are self-contained deliverables: they can be copied to a test client that has no skill installed.
|
|
167
|
+
|
|
168
|
+
### Operations
|
|
169
|
+
|
|
170
|
+
- **Troubleshooting** — decodes Intune error/HRESULT codes, maps symptoms to root causes, and triages the
|
|
171
|
+
right log (`AppWorkload.log`, the PSADT session log, `setupapi.dev.log` for drivers).
|
|
172
|
+
- **Self-update** — `scripts/Update-PsadtSkill.ps1` compares against GitHub, shows what changed, and
|
|
173
|
+
updates in place on your confirmation (`git pull --ff-only` for a clone, otherwise a branch-zip overwrite
|
|
174
|
+
of tracked files only). Machine-local state is never touched. Say *"psadt update"*.
|
|
175
|
+
- **441 Pester tests** over the helper scripts, including drift guards that fail when the docs and the code
|
|
176
|
+
disagree.
|
|
177
|
+
|
|
178
|
+
## Requirements
|
|
179
|
+
|
|
180
|
+
- Windows with PowerShell 5.1+ / PowerShell 7+
|
|
181
|
+
- For the `npx` installer only: **Node 18+** (the skill itself never needs Node)
|
|
182
|
+
- [PSAppDeployToolkit](https://psappdeploytoolkit.com/) v4.x *(installed/updated automatically from the
|
|
183
|
+
PowerShell Gallery if missing)*
|
|
184
|
+
- [Microsoft Win32 Content Prep Tool](https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool)
|
|
185
|
+
*(provisioned automatically)*
|
|
186
|
+
- For the **SYSTEM test loop**: an **elevated** session; the
|
|
187
|
+
[`Invoke-CommandAs`](https://github.com/mkellerman/Invoke-CommandAs) module is installed automatically
|
|
188
|
+
- For the **direct Intune upload**: an Entra app with the Graph application role
|
|
189
|
+
`DeviceManagementApps.ReadWrite.All` (admin-consented) — created in one run by
|
|
190
|
+
`scripts/New-PsadtEntraApp.ps1` (WAM sign-in as Global Admin / Privileged Role Admin, device-code
|
|
191
|
+
fallback). Check what is actually in place with `scripts/Test-PsadtIntuneAccess.ps1`. Full permission
|
|
192
|
+
matrix and the manual portal route: `references/app-registration.md`.
|
|
193
|
+
- For **Pester tests**: Pester 5+ (`Install-Module Pester -MinimumVersion 5.0 -Scope CurrentUser`)
|
|
194
|
+
- **Optional (recommended): the [superpowers](https://github.com/obra/superpowers) plugin** — if installed,
|
|
195
|
+
the research fan-out and the reviewer gate use it. Not required: without it the skill falls back to the
|
|
196
|
+
native Agent tool and `/code-review`, and nothing in the workflow depends on the plugin.
|
|
197
|
+
|
|
198
|
+
## Installation
|
|
199
|
+
|
|
200
|
+
```powershell
|
|
201
|
+
npx psadt-deploy-skill
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Installs the **newest release** into `~/.claude/skills/psadt-deploy` and runs the setup doctor. Flags:
|
|
205
|
+
`--dir <path>` · `--project` (into `./.claude/skills`) · `--ref <tag|branch>` · `--no-setup`. Node 18+ and
|
|
206
|
+
Windows; the installer itself has zero dependencies and the package carries only `bin/` — the skill is
|
|
207
|
+
fetched from GitHub at install time.
|
|
208
|
+
|
|
209
|
+
### Which version you get
|
|
210
|
+
|
|
211
|
+
The default is the newest **release tag**, not `main`. This skill registers an Entra application with
|
|
212
|
+
admin consent and writes to an Intune tenant; installing whatever last landed on `main` is not a
|
|
213
|
+
defensible default for that.
|
|
214
|
+
|
|
215
|
+
```powershell
|
|
216
|
+
npx psadt-deploy-skill # newest release (default)
|
|
217
|
+
npx psadt-deploy-skill --ref v0.26.7 # pin an exact release
|
|
218
|
+
npx psadt-deploy-skill --ref main # the development branch, deliberately
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**For managed environments:** pin a tag, read the diff between it and the next one before moving, then
|
|
222
|
+
lift the pin. Releases are tagged `vX.Y.Z` and match the [Changelog](#changelog); tags exist from
|
|
223
|
+
**v0.24.0** onward — earlier versions predate the current history and cannot be tagged retroactively.
|
|
224
|
+
|
|
225
|
+
Re-running the installer updates an existing installation, and so does saying *"psadt update"* to Claude
|
|
226
|
+
Code. What counts as an update depends on what you installed: on a **pinned release** it is the next
|
|
227
|
+
release tag — unreleased work on `main` is deliberately invisible, because that is what pinning means. On
|
|
228
|
+
a **branch** installation it is the next commit, as before. Either way the update overwrites tracked
|
|
229
|
+
repository files only; `config.json`, `secret.dpapi` and `tools/` are never touched.
|
|
230
|
+
|
|
231
|
+
**Or clone it yourself** — the repo root *is* the skill folder:
|
|
232
|
+
|
|
233
|
+
```powershell
|
|
234
|
+
git clone https://github.com/pt1987/claude-code-psadt-skill.git "$env:USERPROFILE\.claude\skills\psadt-deploy"
|
|
235
|
+
pwsh "$env:USERPROFILE\.claude\skills\psadt-deploy\scripts\Initialize-PsadtSkill.ps1" -Fix
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`npx skills add pt1987/claude-code-psadt-skill` works too, since `SKILL.md` sits in the repository root.
|
|
239
|
+
|
|
240
|
+
No git on the machine? The installer falls back to the GitHub tarball and Windows' own `tar.exe`, so the
|
|
241
|
+
one-liner still works — including with `--ref <tag>`, which is the combination a locked-down machine
|
|
242
|
+
actually needs.
|
|
243
|
+
|
|
244
|
+
The skill activates automatically when you ask Claude Code to build an Intune package, or when you work in
|
|
245
|
+
a folder containing `Invoke-AppDeployToolkit.ps1`.
|
|
246
|
+
|
|
247
|
+
### What is deliberately not in the skill frontmatter
|
|
248
|
+
|
|
249
|
+
`SKILL.md` declares `name`, `description` and `license`, and nothing else. The omissions are choices, not
|
|
250
|
+
oversights:
|
|
251
|
+
|
|
252
|
+
- **`paths`** would look like the right way to express "activates in a folder containing
|
|
253
|
+
`Invoke-AppDeployToolkit.ps1`". It is the opposite: the field *limits* activation to files matching the
|
|
254
|
+
globs. Setting it would switch the skill off for the most common request there is — packaging an app in
|
|
255
|
+
an empty folder, where `Invoke-AppDeployToolkit.ps1` does not exist yet because Phase 3 is what creates
|
|
256
|
+
it. The folder case is covered by the last sentence of the description instead.
|
|
257
|
+
- **`allowed-tools`** grants tools up front; it does not restrict them. For a skill that installs software
|
|
258
|
+
as SYSTEM and writes to a tenant, being asked per call is the point. See [`SECURITY.md`](SECURITY.md).
|
|
259
|
+
- **`metadata.version`** is ignored by Claude Code, and the version already lives in `CHANGELOG.md`,
|
|
260
|
+
`package.json` (kept in sync by a test) and on the website. A fourth place to forget on release day, for
|
|
261
|
+
no behaviour, is not worth it.
|
|
262
|
+
- **`shell`** only matters for `!` command injection in `SKILL.md`, which this skill does not use — and a
|
|
263
|
+
failing `!` command aborts the *entire* skill invocation, so an `Initialize-PsadtSkill` call wired up that
|
|
264
|
+
way would be a single point of failure for every packaging request.
|
|
265
|
+
- **`context: fork` / `agent`** would isolate the skill in a subagent. It orchestrates its own sub-agents
|
|
266
|
+
and needs the main context to hold the decision gates.
|
|
267
|
+
|
|
268
|
+
## First-run setup
|
|
269
|
+
|
|
270
|
+
`scripts/Initialize-PsadtSkill.ps1` (also reachable by saying *"psadt setup"* / *"psadt doctor"*) checks
|
|
271
|
+
every prerequisite in one pass and reports **GREEN / YELLOW / RED**. Every line comes with a concrete fix
|
|
272
|
+
hint, and `-Fix` applies the ones that need no decision (module installs, the tool download, the
|
|
273
|
+
`language.*` defaults, `paths.intuneWinAppUtil`, and migrating a pre-0.19 config). It is idempotent — run it
|
|
274
|
+
as often as you like.
|
|
275
|
+
|
|
276
|
+
Only four values genuinely need you; the doctor lists them in `.Missing` and takes them via `-Set`:
|
|
277
|
+
|
|
278
|
+
```powershell
|
|
279
|
+
pwsh scripts/Initialize-PsadtSkill.ps1 -Fix -Set @{
|
|
280
|
+
'paths.packageRoot' = 'D:\Pakete'; 'paths.outputRoot' = 'D:\Intune'
|
|
281
|
+
'author.person' = 'Pat Taubert'; 'author.company' = 'PHAT Consulting'
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
| Setting | Purpose |
|
|
286
|
+
|---|---|
|
|
287
|
+
| `paths.packageRoot` / `outputRoot` | Where packages are built and where artifacts are written |
|
|
288
|
+
| `paths.intuneWinAppUtil` | Content-prep tool location — filled by `-Fix` |
|
|
289
|
+
| `language.script` / `dossier` | Script language (EN) vs. dossier language (DE for the Company Portal) — filled by `-Fix` |
|
|
290
|
+
| `author.person` / `company` | Stamped into every package's `AppScriptAuthor` |
|
|
291
|
+
| `intune.*` *(optional)* | Direct upload: tenant/client, credential reference, verified roles — written by `New-PsadtEntraApp.ps1` |
|
|
292
|
+
| `intune.groups.*` *(optional)* | Opt-in group assignment (`enabled` / `create` / `membershipType` / `naming`) — guide Appendix M |
|
|
293
|
+
|
|
294
|
+
### Where the setup is stored
|
|
295
|
+
|
|
296
|
+
`config.json`, `secret.dpapi` and `tools/` live in the **config home** — `%LOCALAPPDATA%\psadt-deploy\`,
|
|
297
|
+
overridable with `$env:PSADT_DEPLOY_HOME` — **not** in the skill folder, so they survive a `git pull`, a
|
|
298
|
+
re-clone and a re-install. They are machine-local and never committed. A `config.json` from a pre-0.19
|
|
299
|
+
install (beside `scripts/`) keeps working read-only; the doctor flags it and `-Fix` migrates it, renaming
|
|
300
|
+
the originals to `*.migrated` rather than deleting anything.
|
|
301
|
+
|
|
302
|
+
> DPAPI is bound to the Windows user profile: a re-installed OS invalidates a stored client secret. The
|
|
303
|
+
> doctor and `Test-PsadtIntuneAccess.ps1` both say so, and the fix is one `New-PsadtEntraApp.ps1` run.
|
|
304
|
+
|
|
305
|
+
## Project structure
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
psadt-deploy/
|
|
309
|
+
├─ SKILL.md · README.md · CHANGELOG.md · SECURITY.md · LICENSE
|
|
310
|
+
├─ package.json · bin/install.mjs the npx installer (Node 18+, zero dependencies)
|
|
311
|
+
├─ scripts/
|
|
312
|
+
│ │ setup + config
|
|
313
|
+
│ ├─ Initialize-PsadtSkill.ps1 setup doctor (Phase 0, GREEN/YELLOW/RED, -Fix/-Set)
|
|
314
|
+
│ ├─ Get-PsadtConfig.ps1 config read + config-home resolver
|
|
315
|
+
│ ├─ Set-PsadtConfig.ps1 config write (deep merge, DPAPI secret, -Remove)
|
|
316
|
+
│ ├─ Get-PsadtModule.ps1 PSADT module (self-heal)
|
|
317
|
+
│ ├─ Get-IntuneWinAppUtil.ps1 content-prep tool (self-heal)
|
|
318
|
+
│ ├─ Get-WinGetModule.ps1 WinGet extension (opt-in)
|
|
319
|
+
│ ├─ Update-PsadtSkill.ps1 self-update from GitHub
|
|
320
|
+
│ │ per-package truth
|
|
321
|
+
│ ├─ Get-PsadtPackageManifest.ps1 manifest read (+ the artifact stem)
|
|
322
|
+
│ ├─ Set-PsadtPackageManifest.ps1 manifest write (merge / append)
|
|
323
|
+
│ │ package generators
|
|
324
|
+
│ ├─ New-MsiPackage.ps1 MSI packages
|
|
325
|
+
│ ├─ New-BrowserExtensionPackage.ps1 browser-extension force-install (opt-in)
|
|
326
|
+
│ ├─ New-WindowsFeaturePackage.ps1 optional features / capabilities (opt-in)
|
|
327
|
+
│ ├─ New-DriverPackage.ps1 driver packages, pnputil staging (opt-in)
|
|
328
|
+
│ ├─ Get-DriverSignatureInfo.ps1 driver trust classifier (signed? kernel? deployable?)
|
|
329
|
+
│ │ gates + deliverables
|
|
330
|
+
│ ├─ Invoke-PsadtPreflight.ps1 pre-flight GREEN/RED gate (Phase 5, 10 checks)
|
|
331
|
+
│ ├─ Invoke-PsadtSystemTest.ps1 SYSTEM test (Phase 6)
|
|
332
|
+
│ ├─ Invoke-PsadtPackage.ps1 build the .intunewin (Phase 7, named + verified)
|
|
333
|
+
│ ├─ New-PsadtReport.ps1 HTML dossier (Phase 8, always)
|
|
334
|
+
│ │ intune / graph
|
|
335
|
+
│ ├─ New-PsadtEntraApp.ps1 Entra app bootstrap (WAM)
|
|
336
|
+
│ ├─ Get-GraphToken.ps1 app-only Graph token (cert / DPAPI)
|
|
337
|
+
│ ├─ Test-PsadtIntuneAccess.ps1 access verdict (roles, capabilities, expiry)
|
|
338
|
+
│ ├─ Invoke-IntuneWin32Upload.ps1 direct upload (Phase 9)
|
|
339
|
+
│ ├─ Invoke-IntuneAppAssignment.ps1 group assignment (Phase 10, opt-in)
|
|
340
|
+
│ ├─ New-IntuneTrustedCertPolicy.ps1 Custom OMA-URI cert policy (self-contained)
|
|
341
|
+
│ ├─ New-IntuneFirewallPolicy.ps1 firewall-rule policy (self-contained)
|
|
342
|
+
│ ├─ _GraphCommon.ps1 shared Graph helpers (retry, errors, token roles)
|
|
343
|
+
│ └─ _GraphInteractive.ps1 shared WAM sign-in
|
|
344
|
+
├─ references/
|
|
345
|
+
│ ├─ README.md the reference map (label -> file)
|
|
346
|
+
│ ├─ phases-0-6.md · phases-7-12.md the twelve phases
|
|
347
|
+
│ ├─ appendix-a-errors.md … -q-drivers.md one file per appendix
|
|
348
|
+
│ ├─ Report-Template.html the fixed dossier template
|
|
349
|
+
│ └─ app-registration.md THE Graph permission matrix + manual portal route
|
|
350
|
+
└─ tests/ Pester suite, 441 tests
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Machine-local state lives outside the skill folder:
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
%LOCALAPPDATA%\psadt-deploy\ ($env:PSADT_DEPLOY_HOME overrides)
|
|
357
|
+
├─ config.json settings incl. the optional intune.* block
|
|
358
|
+
├─ secret.dpapi DPAPI client secret (only without cert auth)
|
|
359
|
+
└─ tools/ IntuneWinAppUtil.exe + WinGet module
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
And per package, next to `Invoke-AppDeployToolkit.ps1`:
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
psadt-package.json identity · gate decisions · research · results · artifacts
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Status
|
|
369
|
+
|
|
370
|
+
In active use for the full build → package → test → dossier workflow, with the direct Graph upload
|
|
371
|
+
verified against a live tenant. The helper scripts are covered by 441 Pester tests.
|
|
372
|
+
|
|
373
|
+
One open point, honestly: **the driver `pnputil` exit-code semantics are documented, not verified here.**
|
|
374
|
+
`0` / `259` / `3010` and the two `0xE...` failures come from Microsoft's documentation; confirming them
|
|
375
|
+
against `setupapi.dev.log` on a DEV VM with a real vendor-signed and a real Microsoft-signed driver is
|
|
376
|
+
still open.
|
|
377
|
+
|
|
378
|
+
## Security
|
|
379
|
+
|
|
380
|
+
This skill installs software as SYSTEM, researches on the open web, and writes to an Intune tenant
|
|
381
|
+
through an Entra app with admin consent. [`SECURITY.md`](SECURITY.md) states that risk surface next to
|
|
382
|
+
the control that already covers each part of it — the dry-run-before-execute rule, the three-valued
|
|
383
|
+
access check, never-delete, role assertion before the first write, certificate before DPAPI secret,
|
|
384
|
+
the config home outside the skill folder, and the self-containment rule for anything that ships to a
|
|
385
|
+
test client. Each control names the file that implements it and the test that enforces it, so a review
|
|
386
|
+
can check the claims rather than take them.
|
|
387
|
+
|
|
388
|
+
Two deliberate non-features are explained there as well: the skill does **not** declare
|
|
389
|
+
`allowed-tools` (that field pre-approves tools, it does not restrict them), and content fetched during
|
|
390
|
+
research is treated as data, never as instructions — see
|
|
391
|
+
[`references/research-trust.md`](references/research-trust.md).
|
|
392
|
+
|
|
393
|
+
## Roadmap
|
|
394
|
+
|
|
395
|
+
Designed and waiting to be built:
|
|
396
|
+
|
|
397
|
+
- **Sync finished packages to a GitHub repo** — a setup option (`output.target` = `local` / `git` / `both`)
|
|
398
|
+
to push the per-app artifacts (`.intunewin`, dossier, detection, logo) to a Git repo instead of, or in
|
|
399
|
+
addition to, a local folder — versioned and shareable. Will need **Git LFS** for large `.intunewin` files
|
|
400
|
+
(GitHub's 100 MB per-file limit).
|
|
401
|
+
|
|
402
|
+
Have a request? Open an issue.
|
|
403
|
+
|
|
404
|
+
## Contributing
|
|
405
|
+
|
|
406
|
+
Issues and pull requests are welcome. Keep `SKILL.md`, the references and the docs in **English**. The only
|
|
407
|
+
non-English content is the generated end-user output (the Intune dossier and the Company-Portal app
|
|
408
|
+
description), whose language follows the `language.dossier` config value — **default German**, but
|
|
409
|
+
configurable per machine.
|
|
410
|
+
|
|
411
|
+
Two conventions worth knowing before you send a patch: generated `.ps1` content is **7-bit ASCII** (the
|
|
412
|
+
pre-flight fails on non-ASCII without a BOM), and anything that lands in a package's output folder must be
|
|
413
|
+
**self-contained** — it gets copied to test clients that have no skill installed.
|
|
414
|
+
|
|
415
|
+
## License
|
|
416
|
+
|
|
417
|
+
[MIT](LICENSE) © Patrick Taubert, PHAT Consulting GmbH
|
|
418
|
+
|
|
419
|
+
## Acknowledgements
|
|
420
|
+
|
|
421
|
+
- [PSAppDeployToolkit](https://github.com/PSAppDeployToolkit/PSAppDeployToolkit)
|
|
422
|
+
- [Microsoft Win32 Content Prep Tool](https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool)
|
|
423
|
+
- [`Invoke-CommandAs`](https://github.com/mkellerman/Invoke-CommandAs)
|
|
424
|
+
- README structure inspired by [ComposioHQ/awesome-claude-skills](https://github.com/ComposioHQ/awesome-claude-skills)
|
|
425
|
+
|
|
426
|
+
## Changelog
|
|
427
|
+
|
|
428
|
+
Recent releases below; the complete history is in **[CHANGELOG.md](CHANGELOG.md)** (nothing is ever removed
|
|
429
|
+
from either).
|
|
430
|
+
|
|
431
|
+
### 0.27.1 - 10.09.2026
|
|
432
|
+
- **Fixed: das Dossier erfand Fakten über das Paket und druckte sie als Aussage.** Beim Paketieren zweier
|
|
433
|
+
echter Apps zum Test von 0.27.0 aufgefallen. Ohne `-Metadata` behauptete der Report `_Beschreibung folgt._`
|
|
434
|
+
als Company-Portal-Text — der wird unverändert nach Intune übernommen und liest sich wie ein fertiges Feld —
|
|
435
|
+
sowie `Start-ADTMsiProcess` und „Nutzerdaten bleiben erhalten" als Hook-Inhalt, für welches Paket auch immer
|
|
436
|
+
gerade berichtet wurde. Ein WinMerge-Paket, das ausschließlich `Start-ADTProcess` mit Inno-Switches aufruft,
|
|
437
|
+
wurde so viermal mit einem Cmdlet beschrieben, das es nie verwendet. Nichts kennzeichnete das als Vermutung.
|
|
438
|
+
- **Hooks und Cmdlet-Liste werden jetzt per AST aus dem echten Launcher gelesen**, je `*-ADTDeployment`-Funktion.
|
|
439
|
+
Kein Launcher vorhanden: „nicht ermittelbar" statt einer plausiblen Liste. Ein explizites `-Metadata` gewinnt
|
|
440
|
+
weiterhin.
|
|
441
|
+
- **Eine fehlende Beschreibung warnt und wird sichtbar markiert — und wird bei `decisions.upload = true`
|
|
442
|
+
verweigert**, in derselben Form wie das bestehende SYSTEM-Test-Gate. `-AllowMissingDescription` ist der
|
|
443
|
+
bewusste, sichtbare Ausweg.
|
|
444
|
+
- **Der Header-Status wird aus den Belegen abgeleitet** statt auf „Upload-bereit · getestet" zu defaulten.
|
|
445
|
+
- **SKILL.md Phase 8** sagt jetzt, dass die Beschreibung Pflicht ist; vorher stand dort nur, wie sie zu
|
|
446
|
+
formatieren wäre. Suite 474 → 484.
|
|
447
|
+
### 0.27.0 - 10.09.2026
|
|
448
|
+
- **Fixed: after auto-compaction, half of SKILL.md was gone.** Claude Code re-attaches only the **first
|
|
449
|
+
5000 tokens** of an invoked skill after a summary. SKILL.md was ~10 10900, so the cut fell at line 198 — the
|
|
450
|
+
middle of Phase 2. In exactly the sessions long enough to compact, the skill lost Phases 3–12, the whole
|
|
451
|
+
troubleshooting table, every anti-pattern and the reference map. The fix is **ordering, not size**: the
|
|
452
|
+
operating mode, the four gates, the conventions and Phases 0–6 now sit ahead of the cut, and Phase 6 ends
|
|
453
|
+
at byte 17 457 of a 17 500-byte budget — with a test that fails if it ever crosses back.
|
|
454
|
+
- **Fixed: `--ref <tag>` returned HTTP 404 on the tarball route.** The installer built
|
|
455
|
+
`tar.gz/refs/heads/<ref>`, and `refs/heads` only resolves *branches* — so pinning worked with git and failed
|
|
456
|
+
on exactly the machines without it, which are the ones most likely to need a pinned release.
|
|
457
|
+
`Update-PsadtSkill.ps1` had the same latent bug in its archive path.
|
|
458
|
+
- **Fixed: a failed install exited 127 instead of 1**, with a libuv assertion after the error message. A
|
|
459
|
+
mistyped `--ref` printed a correct explanation and then looked like a crash.
|
|
460
|
+
- **Changed: the default install is the newest release tag, not `main`.** This skill registers an Entra app
|
|
461
|
+
with admin consent and writes to a tenant; installing whatever last landed on a branch is not a defensible
|
|
462
|
+
default for that. `--ref main` and `--ref v0.27.0` both remain. The update check now distinguishes a
|
|
463
|
+
release-pinned installation (counts *releases* behind) from one following a branch.
|
|
464
|
+
- **Changed: the 2942-line deployment guide is now nineteen files**, one per domain, with
|
|
465
|
+
`references/README.md` as the map. Section numbering is unchanged, so every "App. L.1" and "Phase 6.2"
|
|
466
|
+
still resolves.
|
|
467
|
+
- **Changed (behaviour): Phase 11 no longer re-runs Phase 6.** Phase 6 answers *does the package work* (the
|
|
468
|
+
gate, in a throwaway Sandbox); Phase 11 answers *does the delivery work* (Intune test group, real device,
|
|
469
|
+
`AppWorkload.log`). A package that passes 6 and fails 11 now tells you something.
|
|
470
|
+
- **Changed: `"update skill"` is gone from the description** — un-namespaced, it made this skill answer for
|
|
471
|
+
every other updatable skill on the machine. `"psadt update"` stays.
|
|
472
|
+
- **Added `SECURITY.md`** — the risk surface next to the control that covers each part of it, each naming the
|
|
473
|
+
file and the test that implement it.
|
|
474
|
+
- **Added: researched content is data, never instructions** (`references/research-trust.md`) — Phase 2
|
|
475
|
+
researches on the open web and the result runs as SYSTEM. The install4j case generalised.
|
|
476
|
+
- **Added three guards and CI**: rule anchors proving no binding rule was lost in the move, a two-directional
|
|
477
|
+
doc cross-reference check that also reads `scripts/`, a context-budget test, and the suite on a clean
|
|
478
|
+
Windows runner for every push. Suite 441 → 474.
|
|
479
|
+
- **Added `evals/`** — 20 trigger and behaviour cases. Not yet run: `claude plugin eval` is in early access.
|
|
480
|
+
### 0.26.7 - 08.09.2026
|
|
481
|
+
- **Fixed: the control plane did not know MSIX exists.** L.8/L.9 landed in 0.26.5, but Gate 1's package-type list
|
|
482
|
+
never mentioned MSIX — so a `.msix` fell through to "native installer (default)", the one route that is wrong for
|
|
483
|
+
it, and nothing pointed at the new appendix. Gate 1 now carries **MSIX/AppX** with the decision attached: Intune
|
|
484
|
+
takes it **natively as a line-of-business app**, so the default is to use that and *not* build a PSADT package;
|
|
485
|
+
the wrapper is the exception, and L.8 is read first when it is used.
|
|
486
|
+
- **Added `tests/SKILL.Tests.ps1`**, a coherence guard: every appendix SKILL.md points at must exist, and the Gate 1
|
|
487
|
+
decision must actually route MSIX. Research that the router cannot reach is dead weight — now a test failure.
|
|
488
|
+
### 0.26.6 - 08.09.2026
|
|
489
|
+
- **Fixed: the sandbox host timeout force-killed the viewer, then told you to close the window.** A timed-out run
|
|
490
|
+
was torn down like a finished one. That **orphans `vmmemWindowsSandbox`** — the Hyper-V compute service owns it,
|
|
491
|
+
so the host cannot kill it, and it keeps the work folder locked until the next run throws on it — and it removes
|
|
492
|
+
the only window through which the guest could still be shut down cleanly. The script's own comments name this
|
|
493
|
+
mechanism as the reason the *guest* must shut itself down; the timeout path did it anyway. A timeout now touches
|
|
494
|
+
nothing and explains what to close and which knob to raise.
|
|
495
|
+
- **Fixed: the warning claimed 60 seconds while the code waited 180** — a literal duplicating a parameter default.
|
|
496
|
+
One declared value now feeds both.
|
|
497
|
+
- **Appendix G finding 6 corrected**: "no recovery path" is now scoped to a run killed from *outside* the script.
|
|
498
|
+
### 0.26.5 - 08.09.2026
|
|
499
|
+
- **L.8, new: MSIX/AppX**, verified against Microsoft Learn and against the live cmdlets. MSIX is a two-step
|
|
500
|
+
model — machine-wide **staging**, then **per-user registration at logon** — and three traps follow from it:
|
|
501
|
+
**`Add-AppxPackage` as SYSTEM registers the app for the SYSTEM account and reports success** (nobody can launch
|
|
502
|
+
it), **`Get-AppxPackage` is the wrong detection cmdlet** (finds nothing right after provisioning, so Intune
|
|
503
|
+
reinstalls forever), and **de-provisioning does not remove the app from existing users** — a complete uninstall
|
|
504
|
+
needs `Remove-AppxProvisionedPackage` *and* `Remove-AppxPackage -AllUsers`. Plus signing: the certificate
|
|
505
|
+
Subject must equal the manifest Publisher (so a vendor MSIX cannot just be re-signed), self-signed certs belong
|
|
506
|
+
in `LocalMachine\TrustedPeople`, and missing timestamping is what makes an expired certificate break installs.
|
|
507
|
+
And the decision that comes first: **usually don't wrap MSIX in PSADT** — Intune takes it natively.
|
|
508
|
+
- **L.9, new: App-V is not end of life.** The client and sequencer are **no longer deprecated** (fixed extended
|
|
509
|
+
support, no end date, no extra cost); only the **server components** end in **April 2026**. Also: `-Global` is
|
|
510
|
+
the device-context switch, and a package in use goes *pending* — a global task applies only after a restart.
|
|
511
|
+
- **The MSIX row in L.2 was misleading and is rewritten**; L.2 gains an App-V row.
|
|
512
|
+
### 0.26.4 - 08.09.2026
|
|
513
|
+
- **L.7, new: Inno Setup and NSIS**, verified against vendor documentation. `/VERYSILENT` **reboots the
|
|
514
|
+
machine by itself** when a restart is needed — so `/NORESTART` is mandatory, and `/RESTARTEXITCODE=3010`
|
|
515
|
+
turns the fact into the code Intune already understands. And `Uninstall.exe /S` in NSIS **returns before
|
|
516
|
+
the uninstall has finished** (it copies itself to temp and detaches), so any verification afterwards races
|
|
517
|
+
it — `_?=<installdir>` makes it synchronous, must be last, must not be quoted.
|
|
518
|
+
- **Advanced Installer wired into L.1 and L.2**, not just the new L.6 — with a measured fingerprint
|
|
519
|
+
(`AI_*` custom actions, `aicustact.dll`, `OLDPRODUCTS;AI_NEWERPRODUCTFOUND`).
|
|
520
|
+
### 0.26.3 - 08.09.2026
|
|
521
|
+
- **Appendix L gains three sections**, researched against vendor documentation instead of written from
|
|
522
|
+
memory: **MSP patches** (the full `/p` matrix, why `/i` and `/p` never combine, and that exit 1642 means
|
|
523
|
+
*either* "not installed" *or* "wrong patch revision" — so it must not be treated as a blanket success),
|
|
524
|
+
**WiX Burn bundles** (BundleProviderKey rather than a ProductCode for detection; `/layout` is a Burn
|
|
525
|
+
action but the bootstrapper decides whether it can be narrowed — the ADK refuses, so an offline layout is
|
|
526
|
+
always the whole kit), and **Advanced Installer projects** (the `.aip` CLI, a fresh ProductCode per build
|
|
527
|
+
changing the package identity every time, and the undocumented behaviour that relative paths resolve
|
|
528
|
+
against the .aip's own location — moving a project between drives breaks it silently).
|
|
529
|
+
### 0.26.2 - 08.09.2026
|
|
530
|
+
- **Fixed: a WHQL driver pack turned the pre-flight red.** `Get-DriverSignatureInfo.ps1` did not strip the
|
|
531
|
+
inline comment that WHQL packs write after `CatalogFile=` (`; for WHQL certified`), so the catalog never
|
|
532
|
+
resolved and validly signed drivers classified as `Unsigned` — 6 of 70 INF in a Dell WinPE set, all of
|
|
533
|
+
whose catalogs were Authenticode-valid and Microsoft-signed. An unquoted `;` now ends the value.
|
|
534
|
+
- **Fixed: `New-MsiPackage.ps1` was the sixth script with the `-File` binder trap.** `-ProcessesToClose
|
|
535
|
+
'a','b'` arrived as one element, so the scaffold closed no processes at all — silently, while reporting
|
|
536
|
+
success. 0.25.1 had fixed the other five and missed the generator.
|
|
537
|
+
- **Documented: seven findings from a three-package dependency chain** (Appendix G) and four new
|
|
538
|
+
anti-patterns (Appendix B), including `msiexec /a` against bundled payload, the `-Include` trap, 8.3
|
|
539
|
+
short names in path comparisons, and what a service's `%TEMP%` really is on Windows 11.
|
|
540
|
+
### 0.26.1 - 06.09.2026
|
|
541
|
+
- **Fixed: a syntax error in the template silently killed the whole dossier script.** A `
|
|
542
|
+
` inside a JS
|
|
543
|
+
string literal became a real newline; JavaScript then discards the entire `<script>` block, so the
|
|
544
|
+
language toggle, all copy buttons and the condensing sticky header died at once — while all 419 tests
|
|
545
|
+
stayed green, because they only assert on rendered HTML. `tests/Report-Template.Tests.ps1` now runs
|
|
546
|
+
`node --check` on the script block plus a node-free quote-balance check, both proven against the
|
|
547
|
+
reintroduced bug.
|
|
548
|
+
- **Changed: every table copies by clicking the row**, replacing the hover icon. Key/value tables give the
|
|
549
|
+
value, return codes the code, assignments the group name, the SYSTEM-test table the whole row. Clicks on
|
|
550
|
+
a row's own controls (the foldable detection script) and text selections are left alone. The Markdown
|
|
551
|
+
description keeps its own copy button.
|
|
552
|
+
|
|
553
|
+
### 0.26.0 - 06.09.2026
|
|
554
|
+
- **Fixed: the dossier could name a return-code type Intune does not have.** Graph accepts exactly
|
|
555
|
+
`success`, `softReboot`, `hardReboot`, `retry`, `failed` — there is no `ignored`, yet "Ignored" reached a
|
|
556
|
+
real dossier because the report rendered a caller-supplied table without validating anything (`Label` was
|
|
557
|
+
free text, `Cls` was interpolated raw into a `class` attribute). `Label` and `Cls` are now derived from
|
|
558
|
+
`Type` through a closed switch and cannot be supplied at all; an invalid type throws.
|
|
559
|
+
- **Return codes are sorted and copyable.** Sorted by type in Appendix F.4 order, then numerically — the
|
|
560
|
+
order the table is verified against and typed into the portal. Each code cell has a copy icon and the
|
|
561
|
+
section a "copy table" button.
|
|
562
|
+
- **New `Get-PsadtReturnCodes.ps1`: one source of truth for the dossier AND the upload.** Previously two
|
|
563
|
+
independent literals that agreed by coincidence. `Invoke-IntuneWin32Upload.ps1` gained `-ReturnCodes`, so
|
|
564
|
+
installer-specific codes finally reach the app instead of only the document, and a new manifest key
|
|
565
|
+
`research.returnCodes` records them once for both.
|
|
566
|
+
- 419 tests; six new regression guards, each verified to fail on its reintroduced bug.
|
|
567
|
+
|
|
568
|
+
### 0.25.2 - 06.09.2026
|
|
569
|
+
- **Fixed: a sandbox run left an error dialog on the desktop.** The guest shuts itself down at the end, the
|
|
570
|
+
RDP-style client loses its session and shows a connection-lost box. The host now disposes of the viewer as
|
|
571
|
+
soon as the run reports in. The tempting fix — let the host kill the VM instead of shutting the guest down
|
|
572
|
+
— was measured and is wrong: it orphans the `vmmemWindowsSandbox` worker, which then holds the work folder
|
|
573
|
+
open for minutes and cannot be killed. Guest shuts down, host closes the window, and the wait is on the VM
|
|
574
|
+
worker rather than on the client processes.
|
|
575
|
+
|
|
576
|
+
### 0.25.1 - 06.09.2026
|
|
577
|
+
- **Fixed: array parameters broke under the `-File` binder.** `pwsh script.ps1 -Intents a,b` passes the
|
|
578
|
+
whole string as ONE element, so `Invoke-IntuneAppAssignment.ps1` rejected a perfectly valid comma-separated
|
|
579
|
+
intent list with a ValidateSet error naming a value nobody typed. It now splits the list itself and still
|
|
580
|
+
rejects a real typo by name. `Invoke-PsadtSandboxTest.ps1 -Paths*` had the same trap with a worse outcome —
|
|
581
|
+
only the first path was asserted and the run still reported GREEN. Six regression tests; suite at 382.
|
|
582
|
+
|
|
583
|
+
### 0.25.0 - 05.09.2026
|
|
584
|
+
- **New: `Get-PsadtMsiFacts.ps1` reads an MSI in one pass** — identity, signature, SHA256, features with
|
|
585
|
+
component counts, *decoded* upgrade flags, shortcuts, directories, file versions, registry rows and the
|
|
586
|
+
Icon table. For an MSI this probe **is** the research: it is what reveals an auto-updater sitting in its
|
|
587
|
+
own feature (so `ADDLOCAL` replaces post-install cleanup), a `DesktopFeature` you must not install, and
|
|
588
|
+
`MigrateFeatures` on the upgrade row — the flag that means `ADDLOCAL` alone is not enough. Five
|
|
589
|
+
regression guards, each verified to fail on its reintroduced bug.
|
|
590
|
+
- **Fixed: the sandbox test left litter and put the evidence in the wrong place.** It kept one work folder
|
|
591
|
+
per app under the config home forever, and `result.json` plus the PSADT logs lived there with the
|
|
592
|
+
manifest pointing into a profile directory. Evidence now lands in
|
|
593
|
+
`<outputRoot>\<Stem>\SandboxTest\` beside the dossier — not in the package folder, which is what
|
|
594
|
+
IntuneWinAppUtil packs — the logs are recorded in `artifacts.logs[]`, and the scratch is removed.
|
|
595
|
+
`-KeepWorkFolder` keeps it, and a failed run keeps it automatically.
|
|
596
|
+
- **Fixed: the test suite wrote into the real config home.** It now redirects `PSADT_DEPLOY_HOME` to a temp
|
|
597
|
+
directory, with a test that asserts it.
|
|
598
|
+
- **Measured:** PuTTY 0.85 packaged with these lessons applied took **13 min 45 s end to end** — versus 75
|
|
599
|
+
minutes for the Notepad++ run that produced them. The largest single factor: the sandbox test costs zero
|
|
600
|
+
wall-clock time when started as soon as pre-flight goes GREEN, because packaging and the dossier do not
|
|
601
|
+
depend on its verdict.
|
|
602
|
+
|
|
603
|
+
### 0.24.0 - 05.09.2026
|
|
604
|
+
- **New: `Invoke-PsadtSandboxTest.ps1` runs the whole Phase 6 loop in a throwaway Windows Sandbox** —
|
|
605
|
+
Install, detection, Uninstall, detection, Reinstall, Repair, final Uninstall, every action as
|
|
606
|
+
`NT AUTHORITY\SYSTEM` via a scheduled task. **No elevation on the host, and the host is never touched**,
|
|
607
|
+
so the SYSTEM test stops being the step that gets deferred to "a DEV VM later". Every action starts from
|
|
608
|
+
a machine that has never seen the app, so a pass cannot be an artefact of the previous run. The verdict
|
|
609
|
+
is keyed on the detection script; package-specific facts go in via `-PathsPresentAfterInstall` /
|
|
610
|
+
`-PathsAbsentAfterInstall` / `-PathsAbsentAfterUninstall`. Verified end to end on Notepad++ 8.9.8 x64:
|
|
611
|
+
GREEN, all seven steps, 5 min 58 s.
|
|
612
|
+
- **Why it is a script and not a snippet:** a hand-rolled version hit three bugs that each destroyed a full
|
|
613
|
+
VM run and each present as a timeout or a null-reference minutes after launch — `echo %ERRORLEVEL%>file`
|
|
614
|
+
silently becoming the `0>` stdin redirection (empty file, never a number), file existence read as
|
|
615
|
+
completion, and `[string]$null` still being `$null` in Windows PowerShell 5.1 (an empty detection output
|
|
616
|
+
is the *normal* result, so the harness crashed because the package was clean). All three now have
|
|
617
|
+
regression guards that were each verified to fail on the reintroduced bug.
|
|
618
|
+
- Phase 6 in SKILL.md and the guide now lead with the sandbox route; the per-action route stays for apps a
|
|
619
|
+
VM cannot host. New anti-patterns cover hand-rolled harnesses, debugging through a long job instead of a
|
|
620
|
+
two-second local check, and "speeding up" the test by disabling Defender or dropping Uninstall/Repair.
|
|
621
|
+
|
|
622
|
+
### 0.23.1 - 04.09.2026
|
|
623
|
+
- **Fixed: two generators were broken since 0.21.0.** `New-BrowserExtensionPackage.ps1` and
|
|
624
|
+
`New-WindowsFeaturePackage.ps1` died at run time — the per-run `LogName` change had wedged a statement
|
|
625
|
+
into the template `Replace(...)` chain, which PowerShell parses as a property assignment with an empty
|
|
626
|
+
name. Valid syntax, so neither the parse check nor the source-inspecting tests could see it; it surfaced
|
|
627
|
+
the first time the whole chain ran against a real package. A guard in all four generator test suites now
|
|
628
|
+
fails on a broken chain, and the guard itself was verified against a broken and a good sample.
|
|
629
|
+
|
|
630
|
+
### 0.23.0 - 04.09.2026
|
|
631
|
+
- **`npx psadt-deploy-skill`.** One line installs or updates the skill into `~/.claude/skills/psadt-deploy`
|
|
632
|
+
and runs the setup doctor. Flags `--dir`, `--project`, `--ref`, `--no-setup`. Cloning to exactly the right
|
|
633
|
+
path by hand was the first thing a new user could get wrong.
|
|
634
|
+
- **Zero dependencies** (Node 18's `fetch` + the `tar.exe` Windows ships). Three acquisition routes:
|
|
635
|
+
update an existing clone, else `git clone --depth 1`, else the branch tarball — because plenty of managed
|
|
636
|
+
machines have no git. The npm package ships only `bin/`; the skill comes from GitHub at install time, so
|
|
637
|
+
a new skill version needs no republish.
|
|
638
|
+
- **The installer never writes the config itself** — it spawns `Set-PsadtConfig.ps1` and
|
|
639
|
+
`Initialize-PsadtSkill.ps1 -Fix`, because a second config-home implementation in JavaScript would drift
|
|
640
|
+
from `Get-PsadtConfig`. `Update-PsadtSkill.ps1` now tracks `package.json` and `bin/` so the archive
|
|
641
|
+
update route stops dropping them. Suite 307 → 326 tests.
|
|
642
|
+
|
|
643
|
+
### 0.22.0 - 04.09.2026
|
|
644
|
+
- **Third-party drivers.** New `scripts/Get-DriverSignatureInfo.ps1` classifies a driver folder before
|
|
645
|
+
anything is built — Microsoft-signed / vendor-signed / unsigned — by checking the **catalog** signature
|
|
646
|
+
rather than the `.sys` (a dual-signed `.sys` reports only its primary signature). New
|
|
647
|
+
`scripts/New-DriverPackage.ps1` builds the package: pnputil staging per INF, uninstall that resolves
|
|
648
|
+
`oemNN.inf` by original name instead of a remembered index, `Get-WindowsDriver` detection.
|
|
649
|
+
- **The rule that saves the most time:** a vendor-signed *kernel* driver is RED, not a warning. With Secure
|
|
650
|
+
Boot on, only Microsoft Dev-Portal-signed kernel drivers load — importing the signer certificate removes
|
|
651
|
+
the "install device software?" prompt but does nothing for Code Integrity, so the driver installs and
|
|
652
|
+
then never loads. Unsigned drivers are refused outright, with three honest options and no testsigning.
|
|
653
|
+
- Pre-flight gained a `DriverTrust` check that fires for **any** package shipping an `.inf` (a vendor
|
|
654
|
+
installer staging a driver is the case nobody declares), the dossier has a driver-trust row, and
|
|
655
|
+
`New-IntuneTrustedCertPolicy.ps1` is now fully self-contained like the firewall script.
|
|
656
|
+
- New guide **Appendix Q** with the decision tree, pnputil exit codes and the installer-bundled-driver
|
|
657
|
+
case. Suite 251 → 307 tests.
|
|
658
|
+
|
|
659
|
+
### 0.21.0 - 04.09.2026
|
|
660
|
+
- **Every package gets a manifest** (`psadt-package.json`): identity, gate decisions, research findings,
|
|
661
|
+
every phase result and the produced artifacts, in one file per app. The generators write it, every phase
|
|
662
|
+
script reads and updates it — so an app's version, name and test evidence stop living in someone's head.
|
|
663
|
+
- **The `.intunewin` is named after the app.** New: `<outputRoot>\<Vendor>_<App>_<Version>_<Arch>\<same
|
|
664
|
+
stem>.intunewin`, produced by the new `scripts/Invoke-PsadtPackage.ps1`. Until now every package came out
|
|
665
|
+
as `Invoke-AppDeployToolkit.intunewin` — that name reached Intune, and concurrent uploads collided in one
|
|
666
|
+
shared temp folder. The new script also verifies the archive before calling it a deliverable and refuses
|
|
667
|
+
an output folder inside the package (which made the tool pack its own previous output).
|
|
668
|
+
- **One PSADT log per run** instead of one ever-growing file:
|
|
669
|
+
`<Vendor>_<App>_<Version>_<Arch>_<Install|Uninstall|Repair>_<timestamp>.log`. PSADT appends to a fixed
|
|
670
|
+
name by default, so by the third attempt a failed install was unreadable.
|
|
671
|
+
- **Pre-flight gained two checks:** a missing or incomplete manifest is RED, a pre-0.21 launcher without a
|
|
672
|
+
per-run log name is a WARN. Report and upload both take their identity from the manifest.
|
|
673
|
+
- Guide: the missing `Phase 6 / 9 / 10` sections now exist, and Appendix E is numbered by phase.
|
|
674
|
+
Suite 173 → 251 tests.
|
|
675
|
+
|
|
676
|
+
### 0.20.0 - 04.09.2026
|
|
677
|
+
- **Intune access is state, not a 403.** New `scripts/Test-PsadtIntuneAccess.ps1` answers *before* Phase 9
|
|
678
|
+
whether the configured app can actually upload, assign groups or create policies — and for how long the
|
|
679
|
+
credential lives. `TokenOk` and every capability are three-valued: verified / refused / **unknown**,
|
|
680
|
+
because Graph tokens are opaque by contract and "we could not tell" is not "not permitted". An offline run
|
|
681
|
+
never overwrites what was verified earlier.
|
|
682
|
+
- **The scripts assert the role they need before their first write** instead of discovering it from a 403
|
|
683
|
+
mid-upload. Group assignment requires *both* group roles and names the missing half.
|
|
684
|
+
- **`New-PsadtEntraApp.ps1` is re-runnable.** It finds the app by the recorded `clientId`, **merges**
|
|
685
|
+
requested permissions instead of replacing them (a run without `-IncludeConfigurationManagement` used to
|
|
686
|
+
silently revoke that role), never prompts, persists what it learned, and only sets `uploadEnabled` once
|
|
687
|
+
consent is really in place. Older client secrets are counted, never deleted.
|
|
688
|
+
- **Auth failures say what to do:** expired/invalid client secret, unknown app or tenant, Conditional Access
|
|
689
|
+
block — and an undecryptable DPAPI secret now explains that DPAPI is bound to the Windows user profile.
|
|
690
|
+
- `references/app-registration.md` is now the single permission matrix (app roles → capabilities → how to
|
|
691
|
+
grant), referenced from the guide instead of duplicated. Suite 128 → 173 tests.
|
|
692
|
+
|
|
693
|
+
### 0.19.0 - 04.09.2026
|
|
694
|
+
- **Setup doctor: `scripts/Initialize-PsadtSkill.ps1`.** One idempotent script replaces the Phase 0 prose
|
|
695
|
+
wizard and reports GREEN/YELLOW/RED over 13 prerequisite checks, each with a concrete fix hint. `-Fix`
|
|
696
|
+
installs the modules, downloads the content-prep tool, fills the EN/DE + tool-path defaults and migrates an
|
|
697
|
+
old setup; `-Set @{...}` persists your values first; `-Json` / `-JsonPath` for other tooling. `.Missing`
|
|
698
|
+
lists only the four values a human has to supply (`paths.packageRoot`, `paths.outputRoot`, `author.person`,
|
|
699
|
+
`author.company`) — never a key the doctor could fill itself.
|
|
700
|
+
- **Config, secret and tools moved to a per-user config home** (`%LOCALAPPDATA%\psadt-deploy\`, override
|
|
701
|
+
`$env:PSADT_DEPLOY_HOME`) instead of the skill folder, so a `git pull`, re-clone or re-install no longer
|
|
702
|
+
takes the whole setup with it — and scripts started from an output folder still find their config.
|
|
703
|
+
`Get-PsadtConfig.ps1` is the single resolver and now returns `.Home` / `.DefaultHome` / `.LegacyInUse`. An
|
|
704
|
+
old config beside `scripts/` keeps working read-only until `-Fix` migrates it (originals renamed
|
|
705
|
+
`*.migrated`, nothing deleted).
|
|
706
|
+
- **`Set-PsadtConfig.ps1 -Remove`** deletes dotted keys, so switching credential type can clean up the
|
|
707
|
+
stale one. **Fixed:** `New-PsadtEntraApp.ps1` reported `<skill>\config.json` even when the config lived
|
|
708
|
+
elsewhere. Suite 120 → 128 tests.
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
<details>
|
|
712
|
+
<summary><strong>Earlier releases (0.18.1 and older)</strong></summary>
|
|
713
|
+
|
|
714
|
+
### 0.18.1 - 03.09.2026
|
|
715
|
+
- **Upload: `-MaxRunTimeMinutes`.** `Invoke-IntuneWin32Upload.ps1` can now set
|
|
716
|
+
`installExperience.maxRunTimeInMinutes` (1–1440); `0` (default) omits the field and keeps the service default
|
|
717
|
+
of 60 min. Raise it for long-running installs (OS in-place upgrades, large suites) so the IME does not kill them.
|
|
718
|
+
|
|
719
|
+
### 0.18.0 - 01.07.2026
|
|
720
|
+
- **HanseMerkur corporate design + editorial report redesign.** The dossier/report template is re-themed to the
|
|
721
|
+
HanseMerkur CD (green brand family on a light mint canvas; Metric font stack with Segoe fallback and no
|
|
722
|
+
web-font fetch → no CORS console errors on a local `file://` open) and relaid out as an "editorial
|
|
723
|
+
data-report": flat hairline sections, auto-numbered headings (`01…13`), an at-a-glance KPI band under the hero
|
|
724
|
+
(version · pre-flight · min OS · arch), and a wider 1600px layout. The detection script is folded behind a
|
|
725
|
+
collapsed `<details>` (the rule summary stays visible). German report text now uses real umlauts. Fixed the
|
|
726
|
+
sticky-header flicker (Chrome/Edge scroll-anchoring vs. the condensing hero → `overflow-anchor: none`,
|
|
727
|
+
Playwright-verified) and removed the redundant hero status pill.
|
|
728
|
+
|
|
729
|
+
### 0.17.0 - 01.07.2026
|
|
730
|
+
- **install4j fingerprint + behavioral silent-switch verification.** Appendix L.1 now recognises install4j
|
|
731
|
+
(Java) installers (`com/install4j/runtime`, `exe4j`, `i4jparams.conf`, bundled `jre\`) and records that `/S`
|
|
732
|
+
is NOT its switch (it hangs on the language dialog) — the unattended switch is `-q`, run elevated. New BINDING
|
|
733
|
+
rule: a single string match is a hint, not proof; confirm the engine by its definitive fingerprint AND run the
|
|
734
|
+
silent switch once (timeout+kill, expect exit 0, no dialog) before packaging. Appendix L.3 adds the
|
|
735
|
+
trademark-sign gotcha (`Name(R)` breaks `-match 'Name'` → tolerant regex); Appendix B adds anti-patterns 13–15.
|
|
736
|
+
(Driven by an Aperio install4j installer misidentified as NSIS, where `/S` hung on the language dialog.)
|
|
737
|
+
|
|
738
|
+
### 0.16.0 - 29.06.2026
|
|
739
|
+
- **Dossier auto-sync convention (BINDING)** + report header layout fix. Any change to the package scripts
|
|
740
|
+
(launcher, Extensions, detection, version/changelog, return codes, re-packaging) now requires regenerating
|
|
741
|
+
`Intune-Dossier.html` in the same pass; a stale dossier is a defect. The `.pill-lg` status badge caps at 230px
|
|
742
|
+
and wraps so a long status no longer overlaps the hero title.
|
|
743
|
+
|
|
744
|
+
### 0.15.2 - 15.06.2026
|
|
745
|
+
- **Follow-up doc fix.** A contradiction sweep after 0.15.1 caught one more stale "Phase 7.5" in
|
|
746
|
+
`New-PsadtReport.ps1` help (upload is Phase 9); corrected. No other live stale references remain.
|
|
747
|
+
|
|
748
|
+
### 0.15.1 - 15.06.2026
|
|
749
|
+
- **Generator hardening from a self-review (correctness + security).** All three generators now single-quote-escape
|
|
750
|
+
values embedded in `$adtSession` literals, so an apostrophe in the App name/vendor/author (e.g. "Bob's App",
|
|
751
|
+
"L'Oreal") no longer produces an unparseable package; the MSI `-AdditionalArgumentList` / `ProcessesToClose`
|
|
752
|
+
literals are escaped too (also closing a SYSTEM code-injection path). Detection exit-code drift fixed:
|
|
753
|
+
`New-MsiPackage.ps1` + the WinGet example now `exit 0` for "not installed" (a non-zero exit reads as a detection
|
|
754
|
+
error), and a new **pre-flight Detection check** WARNs on a non-zero exit in `Detect*.ps1`. The WSUS bypass in
|
|
755
|
+
`New-WindowsFeaturePackage.ps1` now saves all prior state before writing and runs inside the `try/finally`, so a
|
|
756
|
+
partial failure can't leave `UseWUServer=0` permanently. Added input guards (`$Name` path-traversal, `__TOKEN__`
|
|
757
|
+
leak), MSI `-Author` config fallback + `-InstallerPath` validation. Stale refs fixed (SKILL.md "A-M"->"A-P",
|
|
758
|
+
`New-PsadtEntraApp.ps1` "Phase 7.5"->"Phase 9").
|
|
759
|
+
|
|
760
|
+
### 0.15.0 - 15.06.2026
|
|
761
|
+
- **Windows-feature packages (optional features + capabilities / FoD).** New `scripts/New-WindowsFeaturePackage.ps1`
|
|
762
|
+
— one-call generator that enables Windows **Optional Features** (`Enable-WindowsOptionalFeature`: NetFx3,
|
|
763
|
+
Hyper-V, WSL, TelnetClient, …) and **Capabilities / Features on Demand** (`Add-WindowsCapability`: RSAT.*,
|
|
764
|
+
OpenSSH, …) from one typed list, multiple per package. Uninstall reverts (disable/remove); Repair re-enables
|
|
765
|
+
(idempotent). Reboot surfaces **3010** via `$adtSession.SetExitCode(3010)` (`-NoRestart`); detection treats
|
|
766
|
+
`EnablePending` as not-yet-done. Content comes from a bundled `-Source` (offline SxS) else Windows Update
|
|
767
|
+
behind a **temporary** WSUS bypass (`RepairContentServerSource=2`, `UseWUServer=0`) whose exact prior state is
|
|
768
|
+
restored. Guide **Appendix P**, SKILL.md Gate-1/anti-patterns/ref-lookup. Pre-flight GREEN; helper logic
|
|
769
|
+
verified against an in-memory registry sim; ASCII-clean.
|
|
770
|
+
|
|
771
|
+
### 0.14.0 - 15.06.2026
|
|
772
|
+
- **Browser-extension force-install packages (Edge / Chrome / Firefox).** New
|
|
773
|
+
`scripts/New-BrowserExtensionPackage.ps1` — one-call generator for force-installing browser extensions via
|
|
774
|
+
enterprise **policy registry keys** (policy-only, no installer, ESP-safe). Multiple extensions per package.
|
|
775
|
+
Chromium helper computes the **next free `ExtensionInstallForcelist` index** (never hard-codes `1`), dedupes by
|
|
776
|
+
ID and removes only its own entry (coexistence); Firefox merges into the single `ExtensionSettings` JSON written
|
|
777
|
+
as **`REG_MULTI_SZ`** (single-line `REG_SZ` is silently ignored — Mozilla bug 1750233). Guide **Appendix O**,
|
|
778
|
+
SKILL.md Gate-1/anti-patterns/ref-lookup. Pre-flight GREEN; helpers validated against a scratch registry hive;
|
|
779
|
+
ASCII-clean.
|
|
780
|
+
|
|
781
|
+
### 0.13.1 - 15.06.2026
|
|
782
|
+
- **Firewall policy body fixed against the live template (verified 201).** `New-IntuneFirewallPolicy.ps1`
|
|
783
|
+
produced a body Graph rejected (400). Corrected via the **msgraph skill** (not guessed): the group id needs
|
|
784
|
+
the `{firewallrulename}` token, the program path is the direct child `..._app_filepath`, action values are
|
|
785
|
+
`_action_type_1`/`_0`, and a template-based policy requires `settingInstanceTemplateReference` per instance +
|
|
786
|
+
`settingValueTemplateReference` per simple/choice value (profiles collection: instance ref only — a per-value
|
|
787
|
+
ref is a duplicate). Confirmed by a live **201 Create**; tests assert the references. Mirrored into the
|
|
788
|
+
MxManagementCenter Output deliverable.
|
|
789
|
+
|
|
790
|
+
### 0.13.0 - 15.06.2026
|
|
791
|
+
- **Self-contained firewall deliverable (copy-to-client safe).** `scripts/New-IntuneFirewallPolicy.ps1` is now
|
|
792
|
+
fully self-contained — no dot-sourcing of `_GraphCommon`/`_GraphInteractive`, no skill path; WAM sign-in +
|
|
793
|
+
policy body builder + console helpers are embedded. It runs on a test client that does **not** have the skill
|
|
794
|
+
installed (`-Interactive` WAM, or `-GraphToken`). Fixes the "Skill script not found … -SkillRoot" failure when
|
|
795
|
+
the deliverable was copied to another machine. New **binding SKILL.md convention "Self-contained deliverables"**
|
|
796
|
+
+ a Pester test that enforces it (no dot-source / no skill path / embeds WAM). Test-first per writing-skills.
|
|
797
|
+
|
|
798
|
+
### 0.12.0 - 15.06.2026
|
|
799
|
+
- **Interactive WAM sign-in for the Intune policy scripts.** `New-IntuneFirewallPolicy.ps1` and
|
|
800
|
+
`New-IntuneTrustedCertPolicy.ps1` gain `-Interactive` (+ `-TenantId`): delegated sign-in via **WAM**
|
|
801
|
+
(Windows Web Account Manager) when there is no app registration — **no device code**. The WAM machinery
|
|
802
|
+
(`Initialize-MsalBroker` / `Get-WamToken` / new `Get-InteractiveGraphToken`) was extracted from
|
|
803
|
+
`New-PsadtEntraApp.ps1` into a shared `scripts/_GraphInteractive.ps1` (one implementation, no copy-paste
|
|
804
|
+
drift); the bootstrap now consumes it. The MxManagementCenter firewall deliverable became a thin wrapper
|
|
805
|
+
over the generic script.
|
|
806
|
+
|
|
807
|
+
### 0.11.0 - 15.06.2026
|
|
808
|
+
- **Intune firewall-rules policy + app config-management permission.** New `scripts/New-IntuneFirewallPolicy.ps1`
|
|
809
|
+
(Endpoint Security "Windows Firewall Rules" policy, one program-scoped rule; dry-run / `-Execute` / manual
|
|
810
|
+
portal fallback) + Pester test. Suppresses the first-run Windows Firewall prompt for apps that listen inbound.
|
|
811
|
+
New `New-PsadtEntraApp.ps1 -IncludeConfigurationManagement` consents `DeviceManagementConfiguration.ReadWrite.All`
|
|
812
|
+
(needed by the firewall and trusted-cert policies for `-Execute`).
|
|
813
|
+
|
|
814
|
+
### 0.10.0 - 15.06.2026
|
|
815
|
+
- **Certificate store deployment (driver-trust / TrustedPublisher).** New `scripts/New-IntuneTrustedCertPolicy.ps1` —
|
|
816
|
+
a Custom OMA-URI profile that places a certificate into a Windows machine store via the
|
|
817
|
+
`RootCATrustedCertificates` CSP (the policy-based way to suppress the Windows "install device software?"
|
|
818
|
+
driver-trust prompt). Guide Appendix N, dossier "Treiber-Zertifikat" row, SKILL.md convention + Pester test.
|
|
819
|
+
|
|
820
|
+
### 0.9.2 - 12.06.2026
|
|
821
|
+
- **Reconciled a diverged install copy back into the repo.** Fixed `Invoke-PsadtSystemTest.ps1` crashing at
|
|
822
|
+
param binding under the WinPS 5.1 re-exec (`$SkillRoot` default is now fail-safe, so the SYSTEM Install/
|
|
823
|
+
Uninstall gate works on a pwsh-7 host). Added per-field copy buttons + a `file://`-safe clipboard to the
|
|
824
|
+
HTML dossier (`Report-Template.html`, token set unchanged). Added `scripts/New-MsiPackage.ps1` (reusable
|
|
825
|
+
MSI package generator, `$PSScriptRoot`-relative, ASCII-only) + its Pester test.
|
|
826
|
+
|
|
827
|
+
### 0.9.1 - 12.06.2026
|
|
828
|
+
- **Applicability/portability drift cleanup** (docs + instructions; no script logic changed). Removed the
|
|
829
|
+
phantom `test.maxIterations` / `test.endState` config keys from SKILL.md Phase 6 (the cap is a hard count of
|
|
830
|
+
5 the orchestrator owns); fixed stale "Phase 7.5" -> **Phase 9** in SKILL.md + `app-registration.md`; fixed
|
|
831
|
+
the README project tree (report **Phase 8**, added the 3 missing scripts `Invoke-PsadtPreflight`,
|
|
832
|
+
`Invoke-IntuneAppAssignment`, `_GraphCommon`).
|
|
833
|
+
- **`superpowers` downgraded from hard `REQUIRED` to optional/preferred.** The Researcher/Reviewer roles now
|
|
834
|
+
prefer `superpowers:*` if installed and otherwise fall back to the native Agent tool / `/code-review`; the
|
|
835
|
+
Requirements list documents it as an optional (recommended) enhancement. The workflow no longer depends on it.
|
|
836
|
+
|
|
837
|
+
### 0.9.0 - 11.06.2026
|
|
838
|
+
- **Audit cleanup** (quality / content / applicability+compatibility / tests). Highlights: shared
|
|
839
|
+
`scripts/_GraphCommon.ps1` (de-duplicates the 3 Graph scripts) + **28 new Pester tests** for the previously
|
|
840
|
+
untested upload/assignment/Entra-app/token scripts (suite now 74); fixed an `Invoke-WithRetry` precedence
|
|
841
|
+
bug (retried every error 6x) and PS7-fragile throttling reads; the HTML report no longer shows synthetic
|
|
842
|
+
"passed" rows when no pre-flight/SYSTEM-test results are supplied (neutral "not run") and derives the PSADT
|
|
843
|
+
version from the installed module; GUID `ValidatePattern` on the MSI codes.
|
|
844
|
+
- **Phase numbering unified** across SKILL.md + guide into one integer scheme **0-12**; every Phase/Appendix
|
|
845
|
+
cross-reference re-verified. Anti-pattern list trimmed; SYSTEM-test prerequisites, `/beta` drift caveat,
|
|
846
|
+
rollback step, logging convention, and config help added. Guide `$adtSession` template `1.0.0`->`0.1` +
|
|
847
|
+
author-from-config; intro appendix index -> A-M.
|
|
848
|
+
|
|
849
|
+
### 0.8.1 - 11.06.2026
|
|
850
|
+
- **Docs consistency.** Fixed a stale cross-reference in SKILL.md (the guide range said **Appendix A-J** but
|
|
851
|
+
the guide now runs through **M** — K/L were added in 0.7.0 and M in 0.8.0 without updating it). Restored the
|
|
852
|
+
**0.5.3** entry that was missing from this README changelog mirror (it was present in `CHANGELOG.md`).
|
|
853
|
+
|
|
854
|
+
### 0.8.0 - 11.06.2026
|
|
855
|
+
- **Opt-in Entra group assignment, wired end-to-end.** New Phase 10 + `Invoke-IntuneAppAssignment.ps1`:
|
|
856
|
+
create/reuse Entra security groups by a configured naming scheme (`intune.groups`) and assign the uploaded
|
|
857
|
+
app (Required / Available / Uninstall). Read-only dry-run → confirm → execute; idempotent; never deletes a
|
|
858
|
+
group or another app's assignment; ambiguous/duplicate names skipped. Least-privilege roles
|
|
859
|
+
(`Group.Create` + `GroupMember.Read.All`) via `New-PsadtEntraApp.ps1 -IncludeGroupManagement`. Full
|
|
860
|
+
reference in **guide Appendix M** (config schema, naming tokens, version-independent default vs `%version%`
|
|
861
|
+
opt-in, permission model).
|
|
862
|
+
- **Upload min-OS fix.** `-MinWindowsRelease` is now a `ValidateSet` of backend-accepted release IDs
|
|
863
|
+
(`1607..2004`) — `21H2`/`22H2` are server-rejected and used to kill the upload mid-flight with a Graph
|
|
864
|
+
`BadRequest`. Fails fast at param binding instead; set a higher minimum in the portal. Guide **H.11**.
|
|
865
|
+
|
|
866
|
+
### 0.7.5 - 10.06.2026
|
|
867
|
+
- **Honest exit codes + detection (correctness fix).** Removed the dangerous "always `exit 0`" guidance from
|
|
868
|
+
guide Appendix K — a blanket `exit 0` (or a detection tag written in a `finally`) reports GREEN on failure.
|
|
869
|
+
New **K.7**: the exit code reflects whether the fix could RUN (couldn't-run -> non-zero; the 64-bit relaunch
|
|
870
|
+
propagates the child's exit code), detection reflects the real END-STATE (tag only on success), with a
|
|
871
|
+
per-package decision table; "never block enrollment" is now an explicit ESP-assignment + return-code-mapping
|
|
872
|
+
choice, not a masked exit code. SKILL.md anti-pattern added.
|
|
873
|
+
|
|
874
|
+
### 0.7.0 - 10.06.2026
|
|
875
|
+
- **Value-adding extensions.** New `scripts/Invoke-PsadtPreflight.ps1` turns the Phase-5 Reviewer gate into one
|
|
876
|
+
deterministic `GREEN/RED` tool (encoding/parse/v3-scan/top-level/structure/GUID-to-`-FilePath`), with a Pester
|
|
877
|
+
suite. New guide **Appendix K** (script-only remediation / fix packages, ESP-safe — the debloat/Cisco pattern)
|
|
878
|
+
and **Appendix L** (installer technologies + silent switches, a lookup consulted before web research). Expanded
|
|
879
|
+
error-code catalogue (MSI 1603/1605/1619/1638/1639…, PSADT 60001/60008 + ranges) in guide Appendix A and the
|
|
880
|
+
SKILL.md troubleshooting table. SKILL.md rewired to point at the pre-flight script and the new appendices.
|
|
881
|
+
|
|
882
|
+
### 0.6.2 - 10.06.2026
|
|
883
|
+
- **Audit & harden.** Agent-based audit + source-level verification (discarded ~8 false positives). Fixes: the
|
|
884
|
+
guide's broken `New-ADTTemplate` "Extended scaffold" (passing app metadata params that v4.1.x rejects);
|
|
885
|
+
the Graph uploader leaving its extracted work dir (with the AES keys in `Detection.xml`) in `%TEMP%` (now
|
|
886
|
+
`try/finally` cleanup); the report `Notes` default `·` double-escape; the fallback initials-SVG logo
|
|
887
|
+
now XML-escapes + base64-encodes (no markup injection, with a regression test). Robustness: `Invoke-Graph`
|
|
888
|
+
429/5xx retry with `Retry-After`; malformed-`config.json` safety in four scripts; WinGet 2-byte header read;
|
|
889
|
+
symmetric temp cleanup in self-update. Graph request shapes left untouched.
|
|
890
|
+
|
|
891
|
+
### 0.6.1 - 10.06.2026
|
|
892
|
+
- **Report header flicker fixed** (`references/Report-Template.html`): at widths where the content height met
|
|
893
|
+
the viewport edge, the vertical scrollbar toggled on/off and the header's `vw`-based `clamp()` padding and
|
|
894
|
+
`h1` font-size reflowed on every toggle — a wild flicker loop. Reserving the scrollbar gutter
|
|
895
|
+
(`html { overflow-y: scroll; scrollbar-gutter: stable; }`) keeps the width constant and breaks the loop.
|
|
896
|
+
|
|
897
|
+
### 0.6.0 - 10.06.2026
|
|
898
|
+
- **SKILL.md slimmed to a control plane** (733 → 244 lines, ~67% fewer tokens) via progressive disclosure:
|
|
899
|
+
the long inline code moved into the reference guide (new **Appendix I** WinGet + **Appendix J** app-logo),
|
|
900
|
+
loaded on demand. Intake restructured into **4 decision gates** (researchable facts become stated
|
|
901
|
+
assumptions, not questions); explicit **sub-agent roles** (Researcher×3 / Builder / Reviewer) with GREEN
|
|
902
|
+
handoff gates; a single **blockade protocol** for errors. No binding rule or behaviour dropped.
|
|
903
|
+
|
|
904
|
+
### 0.5.3 - 09.06.2026
|
|
905
|
+
- **Guide code-fences are now English/ASCII.** Anglicized every German comment, string literal and placeholder
|
|
906
|
+
living **inside** PowerShell/text code fences in the guide (and the inline-code placeholders in the Appendix
|
|
907
|
+
F.1 table) — snippets get copied verbatim into deployment scripts, where the binding rule is English + 7-bit
|
|
908
|
+
ASCII. German explanatory **prose** and the **F.2 Company-Portal dossier template** deliberately stay German
|
|
909
|
+
(legitimate `language.dossier` end-user text). No script/tooling code changed.
|
|
910
|
+
|
|
911
|
+
### 0.5.2 - 08.06.2026
|
|
912
|
+
- **HTML package report is now always generated** (upload or not) by `scripts/New-PsadtReport.ps1` from the
|
|
913
|
+
fixed template `references/Report-Template.html`. One self-contained, **bilingual (DE/EN toggle)** document
|
|
914
|
+
combining the Intune dossier + a technical package report; Fluent-2 styled, sticky shrink header, logo
|
|
915
|
+
embedded as a data URI, description preview rendered from its Markdown source. New Pester test
|
|
916
|
+
`tests/New-PsadtReport.Tests.ps1`.
|
|
917
|
+
|
|
918
|
+
### 0.5.1 - 06.06.2026
|
|
919
|
+
- Self-update now decides by **commit** (git `HEAD` vs `origin/main`, or the GitHub commits-API sha vs a
|
|
920
|
+
recorded `tooling.skillCommit`) instead of the CHANGELOG version — no more CDN lag / circular version reads.
|
|
921
|
+
- README project-structure tree compacted so it renders without horizontal scroll.
|
|
922
|
+
|
|
923
|
+
### 0.5.0 - 06.06.2026
|
|
924
|
+
- **Skill self-update** — `scripts/Update-PsadtSkill.ps1` checks GitHub for a newer version, shows what's new,
|
|
925
|
+
and updates in place on confirmation (`git pull` for a clone, else branch-zip overwrite of tracked files
|
|
926
|
+
only; `config.json` / `secret.dpapi` / `tools/` preserved). Triggers: *"update skill"*, *"/update-skill"*,
|
|
927
|
+
*"psadt update"*.
|
|
928
|
+
|
|
929
|
+
### 0.4.0 - 06.06.2026
|
|
930
|
+
- **WinGet packaging support** (strictly opt-in, never the default) + **certificate-based auth** for the Phase 9
|
|
931
|
+
upload (no secret at rest) + MSI icon-table logo fallback + device-code first-poll fix. Contributed by
|
|
932
|
+
**@joakim-i** (PR #4), reviewed and hardened before merge. See [CHANGELOG.md](CHANGELOG.md).
|
|
933
|
+
|
|
934
|
+
### 0.3.2 - 06.06.2026
|
|
935
|
+
- **Test-before-upload is now a binding gate:** Install + Uninstall must pass the Phase 6 SYSTEM test before
|
|
936
|
+
any Phase 9 upload. If it can't be run (no elevation / VM), stop before upload and hand back the command.
|
|
937
|
+
|
|
938
|
+
### 0.3.1 - 06.06.2026
|
|
939
|
+
- `Invoke-IntuneWin32Upload.ps1` gains **`-DetectionScriptPath`** (PowerShell-script detection rule) for
|
|
940
|
+
EXE / non-MSI installers without a ProductCode (e.g. Vivaldi). Verified live by uploading Vivaldi 8.0.4033.44.
|
|
941
|
+
- Lesson: a *detection* script rule accepts only `ruleType,enforceSignatureCheck,runAs32Bit,scriptContent`
|
|
942
|
+
(guide Appendix H.2).
|
|
943
|
+
|
|
944
|
+
### 0.3.0 - 06.06.2026
|
|
945
|
+
- **Direct upload** (`scripts/Invoke-IntuneWin32Upload.ps1`, Phase 9): self-contained raw-Graph
|
|
946
|
+
`win32LobApp` upload (parse `.intunewin` → token → probe → idempotency → create/update → content → SAS
|
|
947
|
+
block-blob upload via HttpClient → commit → activate → categories → supersedence). Read-only dry-run by
|
|
948
|
+
default; `-Execute` to write.
|
|
949
|
+
- **WAM Entra-app bootstrap** (`scripts/New-PsadtEntraApp.ps1`): interactive Windows-broker sign-in (device
|
|
950
|
+
code fallback), creates the app + admin consent + secret, DPAPI-stored.
|
|
951
|
+
- **App-only token helper** (`scripts/Get-GraphToken.ps1`).
|
|
952
|
+
- **Coexistence-safe versioning:** never deletes an older version; new versions coexist; optional
|
|
953
|
+
supersedence wiring. **Logo guard:** refuses the PSADT default `AppIcon.png`. Fills the full
|
|
954
|
+
App-information tab; never auto-assigns category/notes/groups.
|
|
955
|
+
- Fixed the Repair `-FilePath`→`-ProductCode` example; reference guide gains **Appendix H**.
|
|
956
|
+
|
|
957
|
+
### 0.2.0 - 05.06.2026
|
|
958
|
+
- **Automated SYSTEM test loop** (`scripts/Invoke-PsadtSystemTest.ps1`, Phase 6): install → uninstall →
|
|
959
|
+
reinstall the package as the SYSTEM account via `Invoke-CommandAs`, with agent-driven auto-fix until
|
|
960
|
+
green or a max-iteration cap. Opt-in; elevated session required.
|
|
961
|
+
- Phase 8 now prefers `Invoke-CommandAs -AsSystem` for SYSTEM-context testing (PsExec kept as a fallback).
|
|
962
|
+
|
|
963
|
+
### 0.1.0 - 04.06.2026
|
|
964
|
+
- Initial release: guided PSADT v4 → Intune Win32 lifecycle (intake, autonomous research, scaffolding, all
|
|
965
|
+
three deployment types, pre-flight checks, packaging, dossier + logo, guided testing, troubleshooting).
|
|
966
|
+
- First-run setup writing a machine-local `config.json` (paths, language, author).
|
|
967
|
+
- Self-healing prerequisites: PSAppDeployToolkit module (PSGallery) and `IntuneWinAppUtil.exe`
|
|
968
|
+
(auto-download + version check).
|
|
969
|
+
- HTML dossier document with a Markdown app-description block (the Intune description field is
|
|
970
|
+
Markdown-only).
|
|
971
|
+
- English skill + reference guide; MIT licensed.
|
|
972
|
+
|
|
973
|
+
</details>
|