Skip to content

Commit 7d88682

Browse files
imaqsoodclaude
andcommitted
(MODULES-11830) Expand Windows specifics in moduleroot/CLAUDE.md
Replace the 4-bullet Windows stub in the OS-conditional CLAUDE.md template with a full "Windows specifics" section, consolidating the MODULES-11830 artifact into this PR (which now delivers both MODULES-11829 and MODULES-11830). Adds Windows guidance covering: platform basics (Chocolatey/MSI providers, SCM services, backslash/case-insensitive paths, ACL permissions), DSC resource patterns (machine-generated dsc_* types, mangled names, Sensitive() credentials, Test-TargetResource idempotency), PowerShell exec idioms (powershell provider, idempotent onlyif/unless, ruby-pwsh, -EncodedCommand), scheduled_task semantics (taskscheduler_api2, trigger comparison, compatibility), native-extension Ruby gotchas (win32-* gems guarded by microsoft_windows?, Linux CI stubbing), and Windows Litmus/acceptance (real VMs not Docker, WinRM, reboots). Stays inside the existing `<% if has_windows -%>` conditional so it renders only for Windows-supporting modules. Pure ASCII (PDK's diff engine chokes on non-ASCII under a non-UTF-8 locale). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 04a41d4 commit 7d88682

1 file changed

Lines changed: 76 additions & 5 deletions

File tree

moduleroot/CLAUDE.md.erb

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,82 @@ Applies to modules whose primary interface is Bolt tasks (`tasks/`):
236236
<% if has_windows -%>
237237
## Windows specifics
238238

239-
- **Package management**: Chocolatey / MSI / Windows installer providers; not apt/yum.
240-
- **Services**: managed via the Windows SCM (`sc.exe` / the service provider), no systemd.
241-
- **Tasks**: PowerShell tasks are `<name>.ps1`; mind execution policy and quoting.
242-
- **Paths**: Puppet installs under `C:\Program Files\Puppet Labs\Puppet`; use backslash paths and
243-
`windows`-appropriate file resources. Line endings and case-insensitive paths matter in specs.
239+
Windows modules have a meaningfully different surface from Linux. Read these before touching
240+
iis/sqlserver/registry/scheduled_task/dsc-style modules.
241+
242+
### Platform basics
243+
244+
- **Package management**: Chocolatey, MSI, and the Windows installer providers -- not apt/yum. Gate
245+
provider-specific behaviour on `$facts['os']['family'] == 'windows'`.
246+
- **Services**: managed via the Windows SCM (`sc.exe` / the `service` provider), no systemd. Service
247+
names are case-insensitive; account for `LocalSystem`/`NetworkService`/named-account `logon` config.
248+
- **Paths**: Puppet installs under `C:\Program Files\Puppet Labs\Puppet` (ruby at
249+
`...\puppet\bin\ruby.exe`). Use backslash paths in resources and double them in Ruby string
250+
literals. Paths are case-insensitive and `\`-separated; normalise before comparing in providers so
251+
specs stay idempotent. CRLF line endings and BOM handling matter in file specs.
252+
- **Permissions**: there is no POSIX `mode`; model ACLs with the `puppetlabs/acl` module, not
253+
`file { mode => '0644' }`.
254+
255+
### DSC resource patterns
256+
257+
Applies to the generated `puppetlabs/dsc`-family modules (built from PowerShell DSC Resources):
258+
259+
- Types and providers under `lib/puppet/type/dsc_*.rb` and `lib/puppet/provider/dsc_*` are
260+
**machine-generated by the DSC builder -- never hand-edit them**; regenerate from the upstream PS
261+
module instead.
262+
- Resource and property names are mangled: PowerShell `MyResource`/`PropertyName` become
263+
`dsc_myresource` with `dsc_propertyname` parameters (lowercased, `dsc_`-prefixed).
264+
- Embedded CIM instances (e.g. `MSFT_Credential`) are passed as hashes; credentials use
265+
`{ 'user' => ..., 'password' => Sensitive('...') }` and **must** be wrapped in `Sensitive()` so the
266+
value never lands in logs or the catalog in clear text.
267+
- Idempotency is delegated to the resource's `Test-TargetResource`; the provider invokes it through
268+
the persistent PowerShell host (ruby-pwsh). Don't add a parallel `exec` to "fix" drift a DSC
269+
resource already manages.
270+
271+
### PowerShell exec idioms
272+
273+
- Prefer a real type/provider or a DSC resource over shelling out. When you must run PowerShell, use
274+
the `puppetlabs/powershell` `exec` provider: `exec { 'x': provider => 'powershell', ... }`.
275+
- Always make `exec` conditional and idempotent with `onlyif`/`unless` PowerShell that sets a non-zero
276+
exit code on the "needs change" path -- never an unconditional `command`.
277+
- Providers reuse a long-lived PowerShell process via `ruby-pwsh` (`Pwsh::Manager`) for performance;
278+
prefer it over spawning `powershell.exe` per call.
279+
- Quoting cmd/PowerShell nesting is error-prone: prefer here-strings or `-EncodedCommand` (base64) for
280+
non-trivial scripts, and pass `-NonInteractive -ExecutionPolicy Bypass` so runs don't hang or get
281+
blocked by machine policy.
282+
283+
### scheduled_task semantics
284+
285+
Applies to the `puppetlabs/scheduled_task` type:
286+
287+
- Default to the modern `taskscheduler_api2` provider; the legacy `win32_taskscheduler` provider
288+
exists for back-compat only -- don't reintroduce it for new work.
289+
- `trigger` is an array of hashes (`schedule`, `start_time`, `every`, `day_of_week`, ...). Most
290+
idempotency bugs come from trigger comparison: times are interpreted in the target's local timezone
291+
and round-tripped, so assert a second `apply` is a no-op in acceptance.
292+
- Set `compatibility` deliberately when a trigger type needs a newer Task Scheduler schema; a mismatch
293+
silently drops or rewrites triggers.
294+
295+
### Native-extension Ruby gotchas
296+
297+
- Windows providers often depend on native gems (`win32-service`, `win32-dir`, `win32-security`,
298+
`ffi`) that only load on Windows. Guard `require`s and Windows-only code paths with
299+
`Puppet.features.microsoft_windows?` so the file still loads on the Linux CI runner.
300+
- **Unit specs run on Linux** in CI even for Windows modules. Stub the Win32/FFI calls and
301+
`microsoft_windows?` rather than invoking the real API, and assert the *commands generated* and
302+
*state parsed* -- the same rule as any provider spec.
303+
- WinRM/UTF-16, CRLF, and backslash-path handling are common spec foot-guns; normalise in the code,
304+
not the test.
305+
306+
### Litmus / acceptance on Windows
307+
308+
- Windows targets are **not** Docker containers. Litmus provisions real VMs (vmpooler / ABS / cloud),
309+
so the Linux `litmus:provision_list[default]` Docker flow does not apply. Provision explicitly, e.g.
310+
`bundle exec rake 'litmus:provision[vmpooler, windows-2022-x86_64]'`.
311+
- The agent connects over **WinRM**, which is slower than SSH and occasionally flaky -- keep acceptance
312+
manifests tight and idempotent.
313+
- Reboots are real and sometimes required (installers, feature changes); orchestrate them with the
314+
`puppetlabs/reboot` module rather than ad-hoc `exec` restarts.
244315

245316
<% end -%>
246317
## Review policy (outcome-based)

0 commit comments

Comments
 (0)