Skip to content

Cimian Integration

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

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.

The flags

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.

What the monitor reads

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

Startup sequence

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.

  1. First-run gate. Monitoring is only for initial device setup. It requires one of: a ntuser.dat created within the last 10 minutes, HKCU\SOFTWARE\Cimian\FirstRun = 1, or the bootstrap flag file.
  2. A log to tail. The report log, else the newest session log.
  3. A running client. An existing managedsoftwareupdate process, or one it starts.
  4. A list to show. The union of managed_installs: names across every cached manifest, de-duplicated case-insensitively, one pending row 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.

Log patterns the monitor recognises

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 -Wait

When 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.

The reliable alternative: drive it yourself

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

First-run detection

--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.

Launching it at first logon

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.

Icons

List-item icons resolve in this order:

  1. An http:// or https:// URL is used as-is (and downloaded to %ProgramData%\csharpDialog\IconCache\ when the caching service is used).
  2. A rooted local path that exists is used as-is.
  3. A bare name is looked for in %ProgramData%\ManagedInstalls\icons\ — Cimian's cache, populated during manifest sync — then in csharpDialog's own cache.
  4. A configured base URL, if any, is joined with the name.
  5. _generic.png in 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.

Clone this wiki locally