Repository navigation
Cimian Integration
Cimian is the Windows managed-software client that plays the part Munki plays on macOS. csharpDialog can attach to a Cimian run and report it, the way swiftDialog reports a Munki run: one list row per managed install, each row moving from waiting to installing to installed.
This page covers the three flags that do it, what they actually read, and where the integration is brittle.
| Flag | What it does |
|---|---|
--cimian (alias --cimianmonitor) |
Starts the log monitor next to the dialog |
--firstrun |
A preset: topmost on, progress bar on, list on. Marks first-run complete in the registry when the dialog closes |
--autolaunch |
Runs first-run detection and exits 0 without showing anything if this is not a first-run scenario |
--autolaunch has two behaviours depending on how it is passed:
-
As the entire argument list (
dialog --autolaunch) it takes a dedicated code path with a hardcoded configuration: title "Setting up your device", a fixed welcome message, progress bar and list on, topmost, centred, a blue background, and both fullscreen and kiosk mode. Every other flag you might expect to combine with it is ignored on this path because the path is only taken when there are no other arguments. - Alongside other flags it only adds the first-run gate, then overrides title, message, progress, list and topmost with the first-run preset and enables Cimian monitoring.
If you want control over the presentation, use --firstrun --cimian --fullscreen with your
own title and message rather than --autolaunch.
All paths are resolved from the standard folders, not hardcoded drive letters.
| Path | Role |
|---|---|
%ProgramData%\ManagedInstalls\reports\run.log |
Primary log. The client truncates it at the start of every run, so it only ever holds the current run — which is why it is the simplest thing to tail |
%ProgramData%\ManagedInstalls\logs\<yyyy-MM-dd>\<HHmm>\run.log |
Fallback, newest first, used when the report log does not exist yet |
%ProgramData%\ManagedInstalls\manifests\*.yaml |
Scanned for managed_installs: entries to seed the list |
%ProgramFiles%\Cimian\managedsoftwareupdate.exe |
Started if no managedsoftwareupdate process is running |
%ProgramData%\ManagedInstalls\.cimian.bootstrap |
Present for the life of a bootstrap run; a first-run signal |
%ProgramData%\ManagedInstalls\icons\ |
Cimian's icon cache, searched first when a list item's icon is given by basename |
The monitor refuses to start unless it can complete all of the following. If any step
fails, dialog.exe logs a warning, prints one to stdout, and carries on showing an ordinary
dialog — you get a window with no rows in it.
-
First-run gate. Monitoring is only for initial device setup. It requires one of: a
ntuser.datcreated within the last 10 minutes,HKCU\SOFTWARE\Cimian\FirstRun=1, or the bootstrap flag file. - A log to tail. The report log, else the newest session log.
-
A running client. An existing
managedsoftwareupdateprocess, or one it starts. -
A list to show. The union of
managed_installs:names across every cached manifest, de-duplicated case-insensitively, onependingrow each. With no manifests it falls back to a three-name placeholder list, which is a sign something is wrong rather than a feature.
Manifests are read with a plain line scan, not a YAML parser: a line equal to
managed_installs: opens the list, - name lines are collected, and any other key line
closes it. Quoted names are unquoted. Nested or flow-style YAML is not understood.
The monitor matches four case-insensitive regular expressions against each new log line:
| Pattern | Example line | Result |
|---|---|---|
Installing <name>... |
Installing Chrome... |
Row → progress, "Installing..." |
Download progress: <name> <n>% or Installation progress: <name> <n>%
|
Download progress: Chrome 45% |
Per-item progress recorded |
Installation complete: <name> |
Installation complete: Chrome |
Row → success, "Installation complete" |
Error installing: <name> - <message> or Failed installing: …
|
Error installing: Zoom - Access denied |
Row → error, with the message |
This is the integration's weakest point. The monitor is matching on exact English phrasing.
If the client's log wording changes, the tail keeps running and the list simply never moves
off pending. Before blaming the dialog, tail the log yourself and check the wording:
Get-Content "$env:ProgramData\ManagedInstalls\reports\run.log" -Tail 40 -WaitWhen every expected item has reported success the monitor sets progress to 100 with the caption "All installations complete!" and writes the first-run completion markers.
Because the log matching is fragile, the more dependable pattern in production is to skip
--cimian entirely and have your own script call the client per item, writing the result
into the command file. You keep exact control of the row titles and statuses.
$commandFile = Join-Path $env:ProgramData "ManagedNotifications\work\cimian.txt"
$msu = Join-Path $env:ProgramFiles "Cimian\managedsoftwareupdate.exe"
New-Item -ItemType Directory -Path (Split-Path $commandFile) -Force | Out-Null
Remove-Item $commandFile -Force -ErrorAction SilentlyContinue
$proc = Start-Process dialog -PassThru -ArgumentList @(
"--window", "--progress",
"--title", "Installing required software",
"--message", "Your computer is being configured. Please keep this window open.",
"--button1", "Please wait", "--button1disabled", "--hidedefaultkeyboardaction",
"--commandfile", $commandFile
)
Start-Sleep -Seconds 2
$items = "FortiClient", "Teams", "Zoom"
foreach ($item in $items) {
Add-Content $commandFile "listitem: add, title: $item, status: wait, statustext: Queued" -Encoding UTF8
}
$done = 0
foreach ($item in $items) {
Add-Content $commandFile "listitem: update, title: $item, status: pending, statustext: Installing" -Encoding UTF8
Add-Content $commandFile "progresstext: Installing $item" -Encoding UTF8
& $msu --item $item --installonly | Out-Null
if ($LASTEXITCODE -eq 0) {
Add-Content $commandFile "listitem: update, title: $item, status: success, statustext: Installed" -Encoding UTF8
} else {
Add-Content $commandFile "listitem: update, title: $item, status: fail, statustext: Exit $LASTEXITCODE" -Encoding UTF8
}
$done++
Add-Content $commandFile "progress: $([math]::Round($done / $items.Count * 100))" -Encoding UTF8
}
Add-Content $commandFile "message: Setup is complete." -Encoding UTF8
Add-Content $commandFile "button1text: Get started" -Encoding UTF8
Add-Content $commandFile "button1: enable" -Encoding UTF8--firstrun and --autolaunch both consult a detection routine that combines several
signals. Any one of them makes the run "first-run":
| Signal | Threshold |
|---|---|
%USERPROFILE%\ntuser.dat creation time |
Within 10 minutes |
| Profile directory age | Under 15 minutes |
HKCU\...\Explorer\FirstRun = 1
|
— |
A RunOnce value whose name contains FirstLogin, Welcome or Setup
|
— |
HKLM\...\OOBE\OOBECompletedTime |
Within 1 hour |
An oobe, msoobe or CloudExperienceHost process running with a boot under 30 minutes ago |
— |
HKLM\SOFTWARE\Cimian\FirstRun = 1, or InstallDate within 2 hours |
— |
HKCU\SOFTWARE\Cimian\FirstRun = 1
|
— |
A running cimian, managedsoftwareupdate or cimian_bootstrap process started under an hour ago |
— |
%ProgramData%\ManagedInstalls\.cimian.bootstrap exists |
— |
| An ESP marker path exists | — |
The routine also records the device join state (workgroup, domain-joined, Entra-joined, workplace-joined), the current user SID and the last boot time. These are logged and returned but do not change what the dialog shows.
When a first-run dialog closes it writes FirstRun = 0 plus a completion timestamp to
HKCU\SOFTWARE\Cimian, attempts the same in HKLM (silently skipped without admin
rights), and writes %ProgramData%\ManagedInstalls\first_run_completed if that directory
already exists.
Because the signals are broad and time-based, a machine can look like first-run when it is
not — for example any time managedsoftwareupdate happens to be running. Do not rely on
--autolaunch as your only gate for something that must happen exactly once; keep your own
marker.
The dialog must run in the user's own session. A Run key entry under HKCU, or a logon
scheduled task running as the interactive user, both work:
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
csharpDialogFirstRun = "C:\Program Files\csharpDialog\dialog.exe" --autolaunch
An HKLM\...\Run entry works too — that key runs in the session of whoever signs in — but a
SYSTEM-context service or task does not, because it lands in session 0 where nothing is
visible. See Troubleshooting.
List-item icons resolve in this order:
- An
http://orhttps://URL is used as-is (and downloaded to%ProgramData%\csharpDialog\IconCache\when the caching service is used). - A rooted local path that exists is used as-is.
- A bare name is looked for in
%ProgramData%\ManagedInstalls\icons\— Cimian's cache, populated during manifest sync — then in csharpDialog's own cache. - A configured base URL, if any, is joined with the name.
-
_generic.pngin Cimian's icon cache is used as a fallback.
In practice, on a Cimian-managed machine, referencing an icon by the same basename the
package's icon_name uses means the icon is already on disk before the install begins.
One caveat: the list-item control in the shipped window loads an icon only from an
existing absolute file path. It does not resolve bare names or download URLs itself —
that resolution lives in the icon service, which the window does not call. Pass a full path
in the third --listitem field, or resolve the path in your script first.
csharpDialog — MIT licensed — windowsadmins/csharpdialog
Reference
Guides
Internals
Help