psadt-deploy-skill 0.34.2 → 0.36.0
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 +91 -368
- package/package.json +37 -37
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<h1 align="center">PSADT v4 → Intune Deployment Skill</h1>
|
|
2
2
|
|
|
3
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
|
|
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
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
@@ -12,23 +12,40 @@
|
|
|
12
12
|
<img src="https://img.shields.io/badge/Claude%20Code-Skill-d97757?style=flat-square" alt="Claude Code Skill" />
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
-
<p align="center"><sub><a href="#quick-start">Quick start</a> · <a href="#how-it-works">How it works</a> · <a href="#
|
|
15
|
+
<p align="center"><sub><a href="#quick-start">Quick start</a> · <a href="#how-it-works">How it works</a> · <a href="#what-makes-it-different">What makes it different</a> · <a href="#go-deeper">Go deeper</a> · <a href="#security">Security</a> · <a href="#changelog">Changelog</a></sub></p>
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
+
<img width="3200" height="2000" alt="psadt-workflow-v035" src="https://github.com/user-attachments/assets/d9b1c137-3e0a-4a80-a40f-9a234903bb97" />
|
|
20
|
+
|
|
19
21
|
## What is this?
|
|
20
22
|
|
|
21
|
-
A **Claude Code skill
|
|
22
|
-
package, test, troubleshoot and deploy a **PSADT v4.x Intune Win32 app**. You
|
|
23
|
-
skill runs the workflow
|
|
24
|
-
(Install / Uninstall / Repair), pre-flight checks,
|
|
23
|
+
A **Claude Code skill**: a reusable instruction package that teaches the agent how to build,
|
|
24
|
+
package, test, troubleshoot and deploy a **PSADT v4.x Intune Win32 app**. You name the application; the
|
|
25
|
+
skill runs the workflow - intake, research, scaffolding, all three deployment types
|
|
26
|
+
(Install / Uninstall / Repair), pre-flight checks, a SYSTEM test, packaging, the dossier, and the
|
|
25
27
|
optional Graph upload.
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
It loads progressively: the agent sees only the name and description until a task makes it relevant.
|
|
30
|
+
|
|
31
|
+
### What that looks like in practice
|
|
32
|
+
|
|
33
|
+
Three applications, none of them packaged here before, each taken to a `.intunewin` with a GREEN SYSTEM
|
|
34
|
+
gate on the first attempt. The times are the gate itself, read back from each run's `result.json`: all
|
|
35
|
+
five actions as `NT AUTHORITY\SYSTEM` in one throwaway sandbox, with the detection script evaluated
|
|
36
|
+
after every one of them.
|
|
37
|
+
|
|
38
|
+
| Application | Installer engine | SYSTEM gate | VM runs | Verdict |
|
|
39
|
+
|---|---|---|---|---|
|
|
40
|
+
| Audacity 4.0.0 | MSI | **3:11** | 1 | GREEN |
|
|
41
|
+
| draw.io 31.4.5 | NSIS / electron-builder | **4:19** | 1 | GREEN |
|
|
42
|
+
| VS Code 1.138.0 | Inno Setup | **6:12** | 1 | GREEN |
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
Those runs also produced the findings that make the packages correct: draw.io ships an MSI alongside the
|
|
45
|
+
EXE that installs **per-user** and would have vanished into the SYSTEM profile; Audacity regenerates its
|
|
46
|
+
MSI ProductCode on **every build**, so a ProductCode detection rule works exactly once; VS Code's
|
|
47
|
+
uninstaller hands back an exit code while a copy of itself is still deleting. The skill finds that kind of
|
|
48
|
+
thing before it ships, not after a helpdesk ticket.
|
|
32
49
|
|
|
33
50
|
## Quick start
|
|
34
51
|
|
|
@@ -36,344 +53,76 @@ task makes it relevant, then the full body loads on demand.
|
|
|
36
53
|
npx psadt-deploy-skill
|
|
37
54
|
```
|
|
38
55
|
|
|
39
|
-
|
|
40
|
-
everything it can and names the handful of values only you can supply
|
|
41
|
-
|
|
56
|
+
Installs the skill into `~/.claude/skills/psadt-deploy` and runs the setup doctor, which provisions
|
|
57
|
+
everything it can and names the handful of values only you can supply. Then open Claude Code in any folder
|
|
58
|
+
and say what you want:
|
|
42
59
|
|
|
43
|
-
> *"Create the Win32 Intune package for 7-Zip 24.09"*
|
|
60
|
+
> *"Create the Win32 Intune package for 7-Zip 24.09"* - or *"package Notepad++ for Intune"*
|
|
44
61
|
|
|
45
62
|
The skill asks at most **four decision gates** (scope · deployment semantics · SYSTEM-test consent ·
|
|
46
63
|
upload confirmation). Everything else it researches and states as an assumption instead of asking.
|
|
47
64
|
|
|
65
|
+
Details, flags, version pinning and requirements: [`docs/installation.md`](docs/installation.md).
|
|
66
|
+
|
|
48
67
|
## How it works
|
|
49
68
|
|
|
50
|
-
|
|
69
|
+
Thirteen phases, each owned by a script rather than by prose, so a step either passed or did not:
|
|
51
70
|
|
|
52
71
|
| Phase | What happens | Owner |
|
|
53
72
|
|---|---|---|
|
|
54
73
|
| **0** Setup | 13 prerequisite checks, GREEN/YELLOW/RED, `-Fix` provisions | `Initialize-PsadtSkill.ps1` |
|
|
55
|
-
| **1
|
|
56
|
-
| **3** Scaffold | a generator writes launcher + detection + per-run log name + manifest
|
|
74
|
+
| **1-2** Intake + research | blocker questions as clickable options; a local-evidence ladder (installed here? binary here? already written down?) answers what it can, and a research agent is dispatched only per question it leaves open | agent (gates 1-2) |
|
|
75
|
+
| **3** Scaffold | a generator writes launcher + detection + per-run log name + manifest | `New-MsiPackage` · `New-ExePackage` · `New-BrowserExtensionPackage` · `New-WindowsFeaturePackage` · `New-DriverPackage` |
|
|
57
76
|
| **4** Customize | all three hooks filled from the research, helpers in the Extensions module | agent |
|
|
58
|
-
| **5** Pre-flight |
|
|
59
|
-
| **6** SYSTEM test |
|
|
77
|
+
| **5** Pre-flight | 11 checks (encoding, AST parse, v3 cmdlets, structure, detection contract, manifest, log name, driver trust …) → GREEN/RED | `Invoke-PsadtPreflight.ps1` |
|
|
78
|
+
| **6** SYSTEM test | the whole loop in a throwaway Windows Sandbox, every action as **SYSTEM** like the IME does - no elevation, host untouched. **Binding before any upload** | `Invoke-PsadtSandboxTest.ps1` |
|
|
60
79
|
| **7** Package | one command → verified `.intunewin`, named after the app | `Invoke-PsadtPackage.ps1` |
|
|
61
80
|
| **8** Dossier | always, uploaded or not: bilingual self-contained HTML | `New-PsadtReport.ps1` |
|
|
62
81
|
| **9** Upload *(opt-in)* | dry run → confirm → `win32LobApp` via raw Graph | `Invoke-IntuneWin32Upload.ps1` |
|
|
63
82
|
| **10** Assignment *(opt-in)* | create/reuse Entra groups by naming scheme | `Invoke-IntuneAppAssignment.ps1` |
|
|
64
|
-
| **11
|
|
83
|
+
| **11-12** Test + rollout | real devices via a test group, pilot → staged production | agent |
|
|
65
84
|
|
|
66
|
-
**Everything one app knows lives in `<pkg>\psadt-package.json`**
|
|
85
|
+
**Everything one app knows lives in `<pkg>\psadt-package.json`** - identity, the decisions taken at the
|
|
67
86
|
gates, the research findings, every phase's result and the artifacts produced. The generators write it,
|
|
68
87
|
every later phase reads and updates it, and pre-flight fails without it. That is what stops two packages of
|
|
69
88
|
the same app from disagreeing about their own version.
|
|
70
89
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
- **
|
|
84
|
-
|
|
85
|
-
- **
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
- **Switch catalog before the web** — identifies the installer engine from the *binary* (byte signatures in
|
|
97
|
-
the PE overlay, resources and section table, not a filename guess) and serves that engine's documented
|
|
98
|
-
silent / uninstall / log / no-reboot switches from a catalog that ships with the skill. Offline, and it
|
|
99
|
-
reports every stage it checked including the misses. A candidate is still a claim until a run proves it.
|
|
100
|
-
- **Autonomous research** — checks the installed PSADT version against the latest release *and* whether
|
|
101
|
-
commands changed; researches silent install / uninstall / repair switches and known Intune pitfalls.
|
|
102
|
-
- **All three deployment types from the start** — Install, Uninstall *and* Repair, acid-tested, so
|
|
103
|
-
Company-Portal uninstalls actually work.
|
|
104
|
-
- **Pre-flight gate** — encoding/BOM, AST parse, launcher acid test, v3-cmdlet scan, hook structure,
|
|
105
|
-
detection-script contract, the package manifest, the per-run log name and driver trust. GREEN or RED,
|
|
106
|
-
with the failing file named.
|
|
107
|
-
- **Automated SYSTEM test loop** *(opt-in, binding before upload)* — installs, uninstalls and reinstalls as
|
|
108
|
-
the **SYSTEM** account via `Invoke-CommandAs`, mirroring the Intune Management Extension; reads the fresh
|
|
109
|
-
session log and the detection result, and hands back a structured verdict for the fix-and-retry loop.
|
|
110
|
-
Needs an elevated session; belongs on a VM with a snapshot.
|
|
111
|
-
- **Deterministic packaging** — `<outputRoot>\<Vendor>_<App>_<Version>_<Arch>\<same stem>.intunewin`,
|
|
112
|
-
verified after the fact (`Detection.xml`, `SetupFile`, size, SHA256), with the detection script and the
|
|
113
|
-
real logo beside it. It refuses an output folder inside the package, and never deletes a foreign
|
|
114
|
-
`.intunewin` it finds there.
|
|
115
|
-
- **One PSADT log per run** — `<Vendor>_<App>_<Version>_<Arch>_<Install|Uninstall|Repair>_<timestamp>.log`
|
|
116
|
-
instead of every run of every version appending to one unreadable file.
|
|
117
|
-
|
|
118
|
-
### Package types
|
|
119
|
-
|
|
120
|
-
The app's **native installer is always the default**. Everything else is opt-in and only on request:
|
|
121
|
-
|
|
122
|
-
- **MSI / EXE** — the ordinary case, via `New-MsiPackage.ps1` or a hand-filled scaffold.
|
|
123
|
-
- **WinGet** — the full `PSAppDeployToolkit.WinGet` lifecycle: the extension module self-heals into the
|
|
124
|
-
package, the Package ID is discovered with `Find-ADTWinGetPackage`, hooks use `*-ADTWinGet*`
|
|
125
|
-
(`-Scope Machine`). Never selected on its own initiative.
|
|
126
|
-
- **Browser extensions** — force-install via the Edge/Chrome/Firefox policy keys (including the Firefox
|
|
127
|
-
`REG_MULTI_SZ` trap), with selective removal on uninstall.
|
|
128
|
-
- **Windows features** — `Enable-WindowsOptionalFeature` and `Add-WindowsCapability`, offline source or a
|
|
129
|
-
temporary WSUS bypass that is restored afterwards, `EnablePending` handled honestly.
|
|
130
|
-
- **Third-party drivers** — the trust situation is classified *before* anything is built: Microsoft-signed
|
|
131
|
-
installs silently, vendor-signed needs the signer certificate owned in exactly one place, and a
|
|
132
|
-
vendor-signed **kernel** driver is refused because `TrustedPublisher` satisfies the PnP prompt but never
|
|
133
|
-
Code Integrity — it would install and then not load. Unsigned is refused outright, with three honest
|
|
134
|
-
options and no testsigning. Staging is per-INF `pnputil`; uninstall resolves `oemNN.inf` by original name
|
|
135
|
-
instead of a remembered index.
|
|
136
|
-
- **Script-only / remediation packages** — ESP-safe patterns for fix packages with no installer at all.
|
|
137
|
-
|
|
138
|
-
### Deliverables
|
|
139
|
-
|
|
140
|
-
- **HTML dossier — always generated**, uploaded or not. One self-contained file
|
|
141
|
-
(`Intune-Dossier.html`) built from a fixed template, never hand-assembled: the **Intune dossier** (App
|
|
142
|
-
Info, return-code map, detection rule, requirements, assignments, driver trust, and a ready-to-paste
|
|
143
|
-
**Markdown** description for the Company-Portal field) plus a **technical package report** (the three
|
|
144
|
-
hooks, PSADT cmdlets used, pre-flight and SYSTEM-test results, logo and `.intunewin` verification).
|
|
145
|
-
Bilingual with a DE/EN toggle, browser-translatable, logo embedded as a data URI.
|
|
146
|
-
- **Real logo only** — finds and downloads the actual application logo (vendor source or Wikimedia
|
|
147
|
-
Commons), verifies real pixel transparency *and* looks at the image. The PSADT default `AppIcon.png` is
|
|
148
|
-
blocked by hash.
|
|
149
|
-
- **Start Menu only** — creates Start Menu entries and removes stray desktop icons.
|
|
150
|
-
|
|
151
|
-
### Intune
|
|
152
|
-
|
|
153
|
-
- **Access as state, not as a 403** — `Test-PsadtIntuneAccess.ps1` answers before Phase 9 whether the app
|
|
154
|
-
can upload, assign groups or create policies, and for how long the credential lives. Verified / refused /
|
|
155
|
-
**unknown** are three different answers, and an offline check never overwrites what was verified before.
|
|
156
|
-
- **Direct upload via Microsoft Graph** *(opt-in)* — pushes the `.intunewin` as a `win32LobApp` (app +
|
|
157
|
-
logo), self-contained raw Graph, no third-party module. Identity comes from the manifest, so Intune shows
|
|
158
|
-
the same name and version as the artifact and the dossier. Read-only dry run → confirm → upload. Fills the
|
|
159
|
-
whole App-information tab, **never deletes an older version** (new versions coexist, with optional
|
|
160
|
-
supersedence wiring), never auto-assigns categories or notes.
|
|
161
|
-
- **One-time Entra bootstrap** — `New-PsadtEntraApp.ps1` signs in via **WAM**, creates the app, grants and
|
|
162
|
-
admin-consents the roles and stores the credential: a **certificate** (preferred — nothing secret at rest,
|
|
163
|
-
JWT client-assertion auth) or a DPAPI-encrypted client secret. Re-running it is normal: it finds the
|
|
164
|
-
recorded app, merges requested permissions instead of replacing them, and never prompts.
|
|
165
|
-
- **Opt-in group assignment** — creates/reuses Entra security groups by a configured naming scheme and
|
|
166
|
-
assigns Required / Available / Uninstall. Least-privilege (`Group.Create` + `GroupMember.Read.All`),
|
|
167
|
-
dry run → confirm, idempotent, and it never deletes a group or another app's assignment.
|
|
168
|
-
- **Certificate + firewall policies** — Custom OMA-URI profiles for `TrustedPublisher` / `TrustedPeople`
|
|
169
|
-
(the built-in template cannot reach those stores) and settings-catalog firewall-rule policies. Both
|
|
170
|
-
scripts are self-contained deliverables: they can be copied to a test client that has no skill installed.
|
|
171
|
-
|
|
172
|
-
### Operations
|
|
173
|
-
|
|
174
|
-
- **Troubleshooting** — decodes Intune error/HRESULT codes, maps symptoms to root causes, and triages the
|
|
175
|
-
right log (`AppWorkload.log`, the PSADT session log, `setupapi.dev.log` for drivers).
|
|
176
|
-
- **Self-update** — `scripts/Update-PsadtSkill.ps1` compares against GitHub, shows what changed, and
|
|
177
|
-
updates in place on your confirmation (`git pull --ff-only` for a clone, otherwise a branch-zip overwrite
|
|
178
|
-
of tracked files only). Machine-local state is never touched. Say *"psadt update"*.
|
|
179
|
-
- **503 Pester tests** over the helper scripts, including drift guards that fail when the docs and the code
|
|
180
|
-
disagree.
|
|
181
|
-
|
|
182
|
-
## Requirements
|
|
183
|
-
|
|
184
|
-
- Windows with PowerShell 5.1+ / PowerShell 7+
|
|
185
|
-
- For the `npx` installer only: **Node 18+** (the skill itself never needs Node)
|
|
186
|
-
- [PSAppDeployToolkit](https://psappdeploytoolkit.com/) v4.x *(installed/updated automatically from the
|
|
187
|
-
PowerShell Gallery if missing)*
|
|
188
|
-
- [Microsoft Win32 Content Prep Tool](https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool)
|
|
189
|
-
*(provisioned automatically)*
|
|
190
|
-
- For the **SYSTEM test loop**: an **elevated** session; the
|
|
191
|
-
[`Invoke-CommandAs`](https://github.com/mkellerman/Invoke-CommandAs) module is installed automatically
|
|
192
|
-
- For the **direct Intune upload**: an Entra app with the Graph application role
|
|
193
|
-
`DeviceManagementApps.ReadWrite.All` (admin-consented) — created in one run by
|
|
194
|
-
`scripts/New-PsadtEntraApp.ps1` (WAM sign-in as Global Admin / Privileged Role Admin, device-code
|
|
195
|
-
fallback). Check what is actually in place with `scripts/Test-PsadtIntuneAccess.ps1`. Full permission
|
|
196
|
-
matrix and the manual portal route: `references/app-registration.md`.
|
|
197
|
-
- For **Pester tests**: Pester 5+ (`Install-Module Pester -MinimumVersion 5.0 -Scope CurrentUser`)
|
|
198
|
-
- **Optional (recommended): the [superpowers](https://github.com/obra/superpowers) plugin** — if installed,
|
|
199
|
-
the gated research fan-out and the reviewer gate use it. Not required: without it the skill falls back to the
|
|
200
|
-
native Agent tool and `/code-review`, and nothing in the workflow depends on the plugin.
|
|
201
|
-
|
|
202
|
-
## Installation
|
|
203
|
-
|
|
204
|
-
```powershell
|
|
205
|
-
npx psadt-deploy-skill
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Installs the **newest release** into `~/.claude/skills/psadt-deploy` and runs the setup doctor. Flags:
|
|
209
|
-
`--dir <path>` · `--project` (into `./.claude/skills`) · `--ref <tag|branch>` · `--no-setup`. Node 18+ and
|
|
210
|
-
Windows; the installer itself has zero dependencies and the package carries only `bin/` — the skill is
|
|
211
|
-
fetched from GitHub at install time.
|
|
212
|
-
|
|
213
|
-
### Which version you get
|
|
214
|
-
|
|
215
|
-
The default is the newest **release tag**, not `main`. This skill registers an Entra application with
|
|
216
|
-
admin consent and writes to an Intune tenant; installing whatever last landed on `main` is not a
|
|
217
|
-
defensible default for that.
|
|
218
|
-
|
|
219
|
-
```powershell
|
|
220
|
-
npx psadt-deploy-skill # newest release (default)
|
|
221
|
-
npx psadt-deploy-skill --ref v0.26.7 # pin an exact release
|
|
222
|
-
npx psadt-deploy-skill --ref main # the development branch, deliberately
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
**For managed environments:** pin a tag, read the diff between it and the next one before moving, then
|
|
226
|
-
lift the pin. Releases are tagged `vX.Y.Z` and match the [Changelog](#changelog); tags exist from
|
|
227
|
-
**v0.24.0** onward — earlier versions predate the current history and cannot be tagged retroactively.
|
|
228
|
-
|
|
229
|
-
Re-running the installer updates an existing installation, and so does saying *"psadt update"* to Claude
|
|
230
|
-
Code. What counts as an update depends on what you installed: on a **pinned release** it is the next
|
|
231
|
-
release tag — unreleased work on `main` is deliberately invisible, because that is what pinning means. On
|
|
232
|
-
a **branch** installation it is the next commit, as before. Either way the update overwrites tracked
|
|
233
|
-
repository files only; `config.json`, `secret.dpapi` and `tools/` are never touched.
|
|
234
|
-
|
|
235
|
-
**Or clone it yourself** — the repo root *is* the skill folder:
|
|
236
|
-
|
|
237
|
-
```powershell
|
|
238
|
-
git clone https://github.com/pt1987/claude-code-psadt-skill.git "$env:USERPROFILE\.claude\skills\psadt-deploy"
|
|
239
|
-
pwsh "$env:USERPROFILE\.claude\skills\psadt-deploy\scripts\Initialize-PsadtSkill.ps1" -Fix
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
`npx skills add pt1987/claude-code-psadt-skill` works too, since `SKILL.md` sits in the repository root.
|
|
243
|
-
|
|
244
|
-
No git on the machine? The installer falls back to the GitHub tarball and Windows' own `tar.exe`, so the
|
|
245
|
-
one-liner still works — including with `--ref <tag>`, which is the combination a locked-down machine
|
|
246
|
-
actually needs.
|
|
247
|
-
|
|
248
|
-
The skill activates automatically when you ask Claude Code to build an Intune package, or when you work in
|
|
249
|
-
a folder containing `Invoke-AppDeployToolkit.ps1`.
|
|
250
|
-
|
|
251
|
-
### What is deliberately not in the skill frontmatter
|
|
252
|
-
|
|
253
|
-
`SKILL.md` declares `name`, `description` and `license`, and nothing else. The omissions are choices, not
|
|
254
|
-
oversights:
|
|
255
|
-
|
|
256
|
-
- **`paths`** would look like the right way to express "activates in a folder containing
|
|
257
|
-
`Invoke-AppDeployToolkit.ps1`". It is the opposite: the field *limits* activation to files matching the
|
|
258
|
-
globs. Setting it would switch the skill off for the most common request there is — packaging an app in
|
|
259
|
-
an empty folder, where `Invoke-AppDeployToolkit.ps1` does not exist yet because Phase 3 is what creates
|
|
260
|
-
it. The folder case is covered by the last sentence of the description instead.
|
|
261
|
-
- **`allowed-tools`** grants tools up front; it does not restrict them. For a skill that installs software
|
|
262
|
-
as SYSTEM and writes to a tenant, being asked per call is the point. See [`SECURITY.md`](SECURITY.md).
|
|
263
|
-
- **`metadata.version`** is ignored by Claude Code, and the version already lives in `CHANGELOG.md`,
|
|
264
|
-
`package.json` (kept in sync by a test) and on the website. A fourth place to forget on release day, for
|
|
265
|
-
no behaviour, is not worth it.
|
|
266
|
-
- **`shell`** only matters for `!` command injection in `SKILL.md`, which this skill does not use — and a
|
|
267
|
-
failing `!` command aborts the *entire* skill invocation, so an `Initialize-PsadtSkill` call wired up that
|
|
268
|
-
way would be a single point of failure for every packaging request.
|
|
269
|
-
- **`context: fork` / `agent`** would isolate the skill in a subagent. It orchestrates its own sub-agents
|
|
270
|
-
and needs the main context to hold the decision gates.
|
|
271
|
-
|
|
272
|
-
## First-run setup
|
|
273
|
-
|
|
274
|
-
`scripts/Initialize-PsadtSkill.ps1` (also reachable by saying *"psadt setup"* / *"psadt doctor"*) checks
|
|
275
|
-
every prerequisite in one pass and reports **GREEN / YELLOW / RED**. Every line comes with a concrete fix
|
|
276
|
-
hint, and `-Fix` applies the ones that need no decision (module installs, the tool download, the
|
|
277
|
-
`language.*` defaults, `paths.intuneWinAppUtil`, and migrating a pre-0.19 config). It is idempotent — run it
|
|
278
|
-
as often as you like.
|
|
279
|
-
|
|
280
|
-
Only four values genuinely need you; the doctor lists them in `.Missing` and takes them via `-Set`:
|
|
281
|
-
|
|
282
|
-
```powershell
|
|
283
|
-
pwsh scripts/Initialize-PsadtSkill.ps1 -Fix -Set @{
|
|
284
|
-
'paths.packageRoot' = 'D:\Pakete'; 'paths.outputRoot' = 'D:\Intune'
|
|
285
|
-
'author.person' = 'Pat Taubert'; 'author.company' = 'PHAT Consulting'
|
|
286
|
-
}
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
| Setting | Purpose |
|
|
90
|
+
## What makes it different
|
|
91
|
+
|
|
92
|
+
- **The installer engine is read from the binary**, not guessed from a filename - byte signatures in the
|
|
93
|
+
PE overlay, resources and section table - and 19 engines' documented silent switches ship with the skill,
|
|
94
|
+
offline. A candidate is still a claim until a run proves it.
|
|
95
|
+
- **The SYSTEM test is real.** Every action runs as `NT AUTHORITY\SYSTEM` through a scheduled task in a
|
|
96
|
+
throwaway Windows Sandbox - the same context the Intune Management Extension uses - and the verdict is
|
|
97
|
+
keyed on the detection script, the same rule Intune evaluates. No elevation on your machine, and the
|
|
98
|
+
machine is never modified.
|
|
99
|
+
- **An uninstall has to prove it removed something.** Inno Setup and NSIS uninstallers return an exit code
|
|
100
|
+
while a copy of themselves is still deleting; generated hooks wait for the application to disappear and
|
|
101
|
+
fail loudly if it does not.
|
|
102
|
+
- **Nothing is deleted that you did not ask to delete** - not an older Intune app version, not a foreign
|
|
103
|
+
`.intunewin`, not a group, not another app's assignment.
|
|
104
|
+
- **A dossier is produced every time**, uploaded or not: one self-contained bilingual HTML file with the
|
|
105
|
+
return-code map, the detection rule, the hooks, the test results - and a ready-to-paste Company-Portal
|
|
106
|
+
description.
|
|
107
|
+
- **657 Pester tests**, including drift guards that fail when the documentation and the code disagree -
|
|
108
|
+
one of them reads the published landing page and compares its figures against this repository.
|
|
109
|
+
|
|
110
|
+
## Go deeper
|
|
111
|
+
|
|
112
|
+
| | |
|
|
290
113
|
|---|---|
|
|
291
|
-
|
|
|
292
|
-
| `
|
|
293
|
-
| `
|
|
294
|
-
| `
|
|
295
|
-
| `
|
|
296
|
-
| `
|
|
297
|
-
|
|
298
|
-
### Where the setup is stored
|
|
299
|
-
|
|
300
|
-
`config.json`, `secret.dpapi` and `tools/` live in the **config home** — `%LOCALAPPDATA%\psadt-deploy\`,
|
|
301
|
-
overridable with `$env:PSADT_DEPLOY_HOME` — **not** in the skill folder, so they survive a `git pull`, a
|
|
302
|
-
re-clone and a re-install. They are machine-local and never committed. A `config.json` from a pre-0.19
|
|
303
|
-
install (beside `scripts/`) keeps working read-only; the doctor flags it and `-Fix` migrates it, renaming
|
|
304
|
-
the originals to `*.migrated` rather than deleting anything.
|
|
305
|
-
|
|
306
|
-
> DPAPI is bound to the Windows user profile: a re-installed OS invalidates a stored client secret. The
|
|
307
|
-
> doctor and `Test-PsadtIntuneAccess.ps1` both say so, and the fix is one `New-PsadtEntraApp.ps1` run.
|
|
308
|
-
|
|
309
|
-
## Project structure
|
|
310
|
-
|
|
311
|
-
```
|
|
312
|
-
psadt-deploy/
|
|
313
|
-
├─ SKILL.md · README.md · CHANGELOG.md · SECURITY.md · LICENSE
|
|
314
|
-
├─ package.json · bin/install.mjs the npx installer (Node 18+, zero dependencies)
|
|
315
|
-
├─ scripts/
|
|
316
|
-
│ │ setup + config
|
|
317
|
-
│ ├─ Initialize-PsadtSkill.ps1 setup doctor (Phase 0, GREEN/YELLOW/RED, -Fix/-Set)
|
|
318
|
-
│ ├─ Get-PsadtConfig.ps1 config read + config-home resolver
|
|
319
|
-
│ ├─ Set-PsadtConfig.ps1 config write (deep merge, DPAPI secret, -Remove)
|
|
320
|
-
│ ├─ Get-PsadtModule.ps1 PSADT module (self-heal)
|
|
321
|
-
│ ├─ Get-IntuneWinAppUtil.ps1 content-prep tool (self-heal)
|
|
322
|
-
│ ├─ Get-WinGetModule.ps1 WinGet extension (opt-in)
|
|
323
|
-
│ ├─ Update-PsadtSkill.ps1 self-update from GitHub
|
|
324
|
-
│ │ per-package truth
|
|
325
|
-
│ ├─ Get-PsadtPackageManifest.ps1 manifest read (+ the artifact stem)
|
|
326
|
-
│ ├─ Set-PsadtPackageManifest.ps1 manifest write (merge / append)
|
|
327
|
-
│ │ package generators
|
|
328
|
-
│ ├─ New-MsiPackage.ps1 MSI packages
|
|
329
|
-
│ ├─ New-BrowserExtensionPackage.ps1 browser-extension force-install (opt-in)
|
|
330
|
-
│ ├─ New-WindowsFeaturePackage.ps1 optional features / capabilities (opt-in)
|
|
331
|
-
│ ├─ New-DriverPackage.ps1 driver packages, pnputil staging (opt-in)
|
|
332
|
-
│ ├─ Get-DriverSignatureInfo.ps1 driver trust classifier (signed? kernel? deployable?)
|
|
333
|
-
│ │ gates + deliverables
|
|
334
|
-
│ ├─ Invoke-PsadtPreflight.ps1 pre-flight GREEN/RED gate (Phase 5, 10 checks)
|
|
335
|
-
│ ├─ Invoke-PsadtSystemTest.ps1 SYSTEM test (Phase 6)
|
|
336
|
-
│ ├─ Invoke-PsadtPackage.ps1 build the .intunewin (Phase 7, named + verified)
|
|
337
|
-
│ ├─ New-PsadtReport.ps1 HTML dossier (Phase 8, always)
|
|
338
|
-
│ │ intune / graph
|
|
339
|
-
│ ├─ New-PsadtEntraApp.ps1 Entra app bootstrap (WAM)
|
|
340
|
-
│ ├─ Get-GraphToken.ps1 app-only Graph token (cert / DPAPI)
|
|
341
|
-
│ ├─ Test-PsadtIntuneAccess.ps1 access verdict (roles, capabilities, expiry)
|
|
342
|
-
│ ├─ Invoke-IntuneWin32Upload.ps1 direct upload (Phase 9)
|
|
343
|
-
│ ├─ Invoke-IntuneAppAssignment.ps1 group assignment (Phase 10, opt-in)
|
|
344
|
-
│ ├─ New-IntuneTrustedCertPolicy.ps1 Custom OMA-URI cert policy (self-contained)
|
|
345
|
-
│ ├─ New-IntuneFirewallPolicy.ps1 firewall-rule policy (self-contained)
|
|
346
|
-
│ ├─ _GraphCommon.ps1 shared Graph helpers (retry, errors, token roles)
|
|
347
|
-
│ └─ _GraphInteractive.ps1 shared WAM sign-in
|
|
348
|
-
├─ references/
|
|
349
|
-
│ ├─ README.md the reference map (label -> file)
|
|
350
|
-
│ ├─ phases-0-6.md · phases-7-12.md the twelve phases
|
|
351
|
-
│ ├─ appendix-a-errors.md … -q-drivers.md one file per appendix
|
|
352
|
-
│ ├─ switch-catalog/ engine defaults + JSON schema (App. L.0)
|
|
353
|
-
│ ├─ Report-Template.html the fixed dossier template
|
|
354
|
-
│ └─ app-registration.md THE Graph permission matrix + manual portal route
|
|
355
|
-
└─ tests/ Pester suite, 552 tests
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
Machine-local state lives outside the skill folder:
|
|
359
|
-
|
|
360
|
-
```
|
|
361
|
-
%LOCALAPPDATA%\psadt-deploy\ ($env:PSADT_DEPLOY_HOME overrides)
|
|
362
|
-
├─ config.json settings incl. the optional intune.* block
|
|
363
|
-
├─ secret.dpapi DPAPI client secret (only without cert auth)
|
|
364
|
-
└─ tools/ IntuneWinAppUtil.exe + WinGet module
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
And per package, next to `Invoke-AppDeployToolkit.ps1`:
|
|
368
|
-
|
|
369
|
-
```
|
|
370
|
-
psadt-package.json identity · gate decisions · research · results · artifacts
|
|
371
|
-
```
|
|
114
|
+
| [**Website**](https://pt1987.github.io/claude-code-psadt-skill/) | the phase-by-phase walkthrough, the pre-flight checks, and the trap in each of the 19 installer engines |
|
|
115
|
+
| [`docs/installation.md`](docs/installation.md) | install flags, version pinning, requirements, manual clone |
|
|
116
|
+
| [`docs/features.md`](docs/features.md) | the complete feature list, package type by package type |
|
|
117
|
+
| [`docs/setup-and-structure.md`](docs/setup-and-structure.md) | first-run setup, config home, full project structure |
|
|
118
|
+
| [`SKILL.md`](SKILL.md) | the control plane the agent actually reads |
|
|
119
|
+
| [`references/README.md`](references/README.md) | the reference map: phases 0-12 and appendices A-Q |
|
|
120
|
+
| [`SECURITY.md`](SECURITY.md) | the risk surface and the control covering each part of it |
|
|
372
121
|
|
|
373
122
|
## Status
|
|
374
123
|
|
|
375
124
|
In active use for the full build → package → test → dossier workflow, with the direct Graph upload
|
|
376
|
-
verified against a live tenant. The helper scripts are covered by
|
|
125
|
+
verified against a live tenant. The helper scripts are covered by 657 Pester tests.
|
|
377
126
|
|
|
378
127
|
One open point, honestly: **the driver `pnputil` exit-code semantics are documented, not verified here.**
|
|
379
128
|
`0` / `259` / `3010` and the two `0xE...` failures come from Microsoft's documentation; confirming them
|
|
@@ -384,38 +133,27 @@ still open.
|
|
|
384
133
|
|
|
385
134
|
This skill installs software as SYSTEM, researches on the open web, and writes to an Intune tenant
|
|
386
135
|
through an Entra app with admin consent. [`SECURITY.md`](SECURITY.md) states that risk surface next to
|
|
387
|
-
the control that already covers each part of it
|
|
388
|
-
|
|
389
|
-
the config home outside the skill folder, and the self-containment rule for anything that ships to a
|
|
390
|
-
test client. Each control names the file that implements it and the test that enforces it, so a review
|
|
391
|
-
can check the claims rather than take them.
|
|
136
|
+
the control that already covers each part of it, and each control names the file that implements it and
|
|
137
|
+
the test that enforces it - so a review can check the claims rather than take them.
|
|
392
138
|
|
|
393
|
-
Two deliberate non-features
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
[`references/research-trust.md`](references/research-trust.md).
|
|
139
|
+
Two deliberate non-features: the skill does **not** declare `allowed-tools` (that field pre-approves
|
|
140
|
+
tools, it does not restrict them), and content fetched during research is treated as data, never as
|
|
141
|
+
instructions - see [`references/research-trust.md`](references/research-trust.md).
|
|
397
142
|
|
|
398
143
|
## Roadmap
|
|
399
144
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
to push the per-app artifacts (`.intunewin`, dossier, detection, logo) to a Git repo instead of, or in
|
|
404
|
-
addition to, a local folder — versioned and shareable. Will need **Git LFS** for large `.intunewin` files
|
|
405
|
-
(GitHub's 100 MB per-file limit).
|
|
406
|
-
|
|
407
|
-
Have a request? Open an issue.
|
|
145
|
+
**Sync finished packages to a GitHub repo** - a setup option (`output.target` = `local` / `git` / `both`)
|
|
146
|
+
to push the per-app artifacts to a Git repo instead of, or in addition to, a local folder. Will need
|
|
147
|
+
**Git LFS** for large `.intunewin` files. Have a request? Open an issue.
|
|
408
148
|
|
|
409
149
|
## Contributing
|
|
410
150
|
|
|
411
|
-
Issues and pull requests are welcome. Keep `SKILL.md`, the references and the docs in **English
|
|
412
|
-
non-English content is the generated end-user output
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
pre-flight fails on non-ASCII without a BOM), and anything that lands in a package's output folder must be
|
|
418
|
-
**self-contained** — it gets copied to test clients that have no skill installed.
|
|
151
|
+
Issues and pull requests are welcome. Keep `SKILL.md`, the references and the docs in **English** - the
|
|
152
|
+
only non-English content is the generated end-user output, whose language follows `language.dossier`
|
|
153
|
+
(default German). Two conventions worth knowing before you send a patch: generated `.ps1` content is
|
|
154
|
+
**7-bit ASCII** (pre-flight fails on non-ASCII without a BOM), and anything that lands in a package's
|
|
155
|
+
output folder must be **self-contained**, because it gets copied to test clients that have no skill
|
|
156
|
+
installed.
|
|
419
157
|
|
|
420
158
|
## License
|
|
421
159
|
|
|
@@ -430,25 +168,10 @@ pre-flight fails on non-ASCII without a BOM), and anything that lands in a packa
|
|
|
430
168
|
|
|
431
169
|
## Changelog
|
|
432
170
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
second copy to keep in sync.
|
|
436
|
-
|
|
437
|
-
### 0.34.2 - 2026-09-16
|
|
438
|
-
- **Added: `tests/SiteFigures.Tests.ps1`** - the landing page's stat tiles are now derived from the
|
|
439
|
-
repository and compared, instead of being hand-maintained and unchecked. A reader found the page
|
|
440
|
-
claiming 19 installer engines in the tile and "1 of 14" in the Phase 2 step right below it; the
|
|
441
|
-
script count had also been one behind since 0.33.0. index.html lives on gh-pages, so the suite had
|
|
442
|
-
never seen it. The guard reads it out of that ref, skips itself when the ref is absent, and the
|
|
443
|
-
workflow fetches it so CI checks it for real. Both stale figures fixed. Suite 624 -> 631.
|
|
444
|
-
|
|
445
|
-
### 0.34.1 - 2026-09-16
|
|
446
|
-
- **Changed: SKILL.md now names `-TrustedPublisherCert` at Phase 6**, with `-Scenarios` for iteration and
|
|
447
|
-
`STOP.txt` for cancelling. 0.34.0 documented the certificate in phase 6.1 only, because the control
|
|
448
|
-
plane had 55 bytes of headroom against its 5000-token budget - the wrong trade for a failure that is
|
|
449
|
-
expensive and silent, since an agent that never opens 6.1 repeats the 25-minute timeout blind. Room was
|
|
450
|
-
made the way the budget test prescribes: `rule:author-version-changelog` and `rule:start-menu-only`
|
|
451
|
-
moved to `references/conventions.md`, which already carried their full text and which SKILL.md routes
|
|
452
|
-
to. Both are editorial rules - their failure costs a doc edit or a stray desktop icon, not a
|
|
453
|
-
deployment. Control plane 17348 / 17500 bytes, headroom 55 -> 152. Suite 624, unchanged.
|
|
171
|
+
**[CHANGELOG.md](CHANGELOG.md)** carries the complete history, every release since 0.1.0, and nothing is
|
|
172
|
+
ever removed from it.
|
|
454
173
|
|
|
174
|
+
Latest: **0.36.0 - The store that had a reader, a rank and no writer.** The verified-switch store has
|
|
175
|
+
been read since 0.30.0 and was never written by anything, so every run started from a blank slate. A
|
|
176
|
+
GREEN full sandbox gate now records the proven switch, keyed by the installer hash, and the next
|
|
177
|
+
package of that file gets it at `verified` confidence instead of paying for the research again.
|
package/package.json
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "psadt-deploy-skill",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Installer for the psadt-deploy Claude Code skill: build, test and deploy PSADT v4.x Intune Win32 packages.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"psadt",
|
|
7
|
-
"psappdeploytoolkit",
|
|
8
|
-
"intune",
|
|
9
|
-
"win32",
|
|
10
|
-
"claude-code",
|
|
11
|
-
"claude-skill",
|
|
12
|
-
"packaging"
|
|
13
|
-
],
|
|
14
|
-
"homepage": "https://github.com/pt1987/claude-code-psadt-skill#readme",
|
|
15
|
-
"bugs": {
|
|
16
|
-
"url": "https://github.com/pt1987/claude-code-psadt-skill/issues"
|
|
17
|
-
},
|
|
18
|
-
"repository": {
|
|
19
|
-
"type": "git",
|
|
20
|
-
"url": "git+https://github.com/pt1987/claude-code-psadt-skill.git"
|
|
21
|
-
},
|
|
22
|
-
"license": "MIT",
|
|
23
|
-
"author": "Patrick Taubert, PHAT Consulting GmbH",
|
|
24
|
-
"type": "module",
|
|
25
|
-
"bin": {
|
|
26
|
-
"psadt-deploy-skill": "bin/install.mjs"
|
|
27
|
-
},
|
|
28
|
-
"files": [
|
|
29
|
-
"bin"
|
|
30
|
-
],
|
|
31
|
-
"engines": {
|
|
32
|
-
"node": ">=18"
|
|
33
|
-
},
|
|
34
|
-
"os": [
|
|
35
|
-
"win32"
|
|
36
|
-
]
|
|
37
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "psadt-deploy-skill",
|
|
3
|
+
"version": "0.36.0",
|
|
4
|
+
"description": "Installer for the psadt-deploy Claude Code skill: build, test and deploy PSADT v4.x Intune Win32 packages.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"psadt",
|
|
7
|
+
"psappdeploytoolkit",
|
|
8
|
+
"intune",
|
|
9
|
+
"win32",
|
|
10
|
+
"claude-code",
|
|
11
|
+
"claude-skill",
|
|
12
|
+
"packaging"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/pt1987/claude-code-psadt-skill#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/pt1987/claude-code-psadt-skill/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/pt1987/claude-code-psadt-skill.git"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Patrick Taubert, PHAT Consulting GmbH",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"bin": {
|
|
26
|
+
"psadt-deploy-skill": "bin/install.mjs"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"bin"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"os": [
|
|
35
|
+
"win32"
|
|
36
|
+
]
|
|
37
|
+
}
|