Skip to content

Package Delivery

Rod Christiansen edited this page Sep 3, 2026 · 1 revision

Package delivery

StartSet has no transport of its own. It executes what it finds in C:\ProgramData\ManagedState\<payload-directory>; getting files there is somebody else's job — an MSI, a managed-install package, an Intune app, Group Policy, a configuration-management run, or managedstatekeeper add.

This page covers what a delivery mechanism has to get right for StartSet to actually pick the payload up, and documents the packaging convention this repository itself uses.

What StartSet requires of a delivered payload

Requirement Why
The file lands directly in the payload directory Discovery is top level only; subdirectories are never walked
The extension is in allowed_extensions .ps1 .cmd .bat .exe .msi .msix by default; anything else is ignored silently
For an elevated directory, the file's owner is SYSTEM / Administrator / Administrators / a service SID Ownership check; otherwise the payload is skipped with Permission validation failed
The filename sorts where you want it to run Case-insensitive alphabetical within the directory
The filename is stable The run-once ledger keys on the lowercased filename; renaming a *-once payload makes it run again

The first two are where deliveries go wrong. A package that lays down login-every\MyTool\Configure.ps1 produces a payload directory that looks populated and a StartSet run that does nothing and reports nothing missing.

The install_location convention

This repository packages StartSet itself with cimipkg, and the same layout is how you would package a payload for a StartSet directory. A package source directory looks like:

mypackage/
├── build-info.yaml
├── payload/
└── scripts/
    ├── preinstall.ps1
    └── postinstall.ps1

build-info.yaml names the product and, critically, the destination:

product:
  name: StartSet
  version: 2026.03.01.1030
  developer: Windows Admins Open Source
  identifier: com.github.windowsadmins.startset
  description: Automatically process packages and scripts during boot, login, or on demand on Windows.
  architecture: x64
install_location: C:\Program Files\StartSet

The contents of payload/ are laid down at install_location. StartSet's own package puts managedstatekeeper.exe and StartSetService.exe flat inside payload/ with install_location: C:\Program Files\StartSet, and they arrive as C:\Program Files\StartSet\managedstatekeeper.exe.

To deliver a StartSet payload, point install_location at the payload directory and put the script flat in payload/:

product:
  name: DepartmentDriveMap
  version: 2026.03.01.0900
  developer: Example Org
  identifier: com.example.departmentdrivemap
  description: Maps department drives at every user logon.
  architecture: x64
install_location: C:\ProgramData\ManagedState\login-every
departmentdrivemap/
├── build-info.yaml
└── payload/
    └── Map-DepartmentDrives.ps1

That produces C:\ProgramData\ManagedState\login-every\Map-DepartmentDrives.ps1.

The doubled-directory trap

Because the payload tree is laid down at install_location, the leaf directory must appear in exactly one of the two — never both. This layout:

payload/
└── login-every/
    └── Map-DepartmentDrives.ps1

combined with install_location: C:\ProgramData\ManagedState\login-every produces ...\login-every\login-every\Map-DepartmentDrives.ps1. The package installs cleanly, reports success, and the script never runs. Pick one convention:

Convention install_location payload/ contents
Flat the payload directory the script
Nested C:\ProgramData\ManagedState login-every\Map-DepartmentDrives.ps1

When you cannot tell which one a package used, look for the file on a target machine rather than trusting the install status:

Get-ChildItem C:\ProgramData\ManagedState -Recurse -Filter *.ps1 | Select-Object FullName

Pre/post-install scripts

scripts\preinstall.ps1 runs before the payload is laid down, scripts\postinstall.ps1 after. StartSet's own package uses them to stop the service before replacing binaries and to create the directory tree, register the service and start it afterwards.

Write their output to stdout and let the packaging tool capture it — that is the record that gets collected. StartSet's own scripts used to Start-Transcript into a file under the data directory, which diverted the output away from that capture: the file sat where nothing reads it and the session log recorded nothing at all.

Both scripts must exit 0 on success; a non-zero exit fails the install.

Delivering via MSI

Two shapes are possible, and they behave very differently.

An MSI that installs a payload file into C:\ProgramData\ManagedState\login-every and then exits. StartSet picks the file up at the next matching trigger, in the right context. This is usually what you want.

An .msi file placed inside a payload directory. StartSet then treats it as a payload and installs it with msiexec /i … /qn /norestart /l*v. Note that package payloads are not routed through the user session — an MSI in login-every installs as the service account in session 0. Exit codes 0 and 3010 count as success; anything else is a failure with the mapped message. Verbose installer logs go to C:\ProgramData\ManagedState\logs\installs\<name>_<timestamp>.log and are aged out with the same 30-day retention as everything else.

Delivering via Intune

build.ps1 -IntuneWin wraps the MSI (or, failing that, the binaries) with IntuneWinAppUtil.exe into a .intunewin. The same wrapping approach works for a payload package. Nothing in StartSet reads Intune state; from its point of view the file either appeared in the directory or it did not.

Ownership after delivery

For elevated payload directories, remember the ownership check. Files laid down by an installer running as SYSTEM are owned by SYSTEM or Administrators and pass. Files copied in by a standard user, or extracted from an archive by one, are owned by that user and are skipped. If a payload is being skipped for Permission validation failed, check the owner:

Get-Acl 'C:\ProgramData\ManagedState\boot-every\Configure-Firewall.ps1' | Select-Object Owner
icacls 'C:\ProgramData\ManagedState\boot-every\Configure-Firewall.ps1' /setowner "SYSTEM"

Updating a payload that has already run

For *-every payloads, just replace the file.

For *-once payloads, the run-once ledger keys on the lowercased filename and records the SHA256. Replacing the file with different content is enough to make it run again — the checksum no longer matches and the ledger reports it as not executed. Keeping the same filename with the same content will not re-run it, and a delivery that "succeeded" will produce no StartSet activity at all. If you need a byte-identical payload to run again, use an override:

managedstatekeeper add-override Map-DepartmentDrives.ps1

Or clear the ledger entry:

managedstatekeeper remove-override Map-DepartmentDrives.ps1 --clear-runonce --user jsmith

Removing a payload

Deleting the file stops it running. It does not remove the run-once ledger entry, so re-delivering the same filename later will find it already recorded. Use managedstatekeeper remove <name> --type <type> --clear-runonce to do both.

boot-once payloads delete themselves from disk after a successful run, so a redelivery of the same filename with the same content will be blocked by the ledger rather than by the file's presence.

Clone this wiki locally