EnterpriseInstall with an MDM

Deploy with Workspace ONE UEM

Install, configure, detect, repair, upgrade and remove the standalone DefenseClaw enterprise profile with Omnissa Workspace ONE UEM on Windows and macOS.

Template

This recipe is a template, validated by simulating the MDM execution context. It has not been run in a live tenant.

Workspace ONE UEM is an Omnissa product, formerly VMware Workspace ONE UEM (Omnissa documentation). This recipe deploys the standalone profile to Windows and macOS devices with Workspace ONE apps, scripts and sensors, and the MDM kit (the scripts in packaging/mdm). For the contract every recipe follows, such as exit codes, detection and how config and keys are delivered, see Install with an MDM.

OSInstallKeep appliedDetect
WindowsWin32 app from a ZIPA Windows script that runs ensuredetect.ps1 as the app's install-complete script
macOSInternal macOS app (the package)A macOS script that runs ensureA macOS sensor with detect.sh
LinuxLinux App Management installs the packageNot covered: use configuration managementA Linux sensor with detect.sh

The MDM kit scripts accept only their own flags and stop on any other argument. Put every value in a script's settings block or on its command line as shown.

Prerequisites

Verify every artifact against the release's cosign-signed checksums.txt before you upload it; see Install with an MDM.

Windows

How Workspace ONE runs Win32 apps and scripts

BehaviorWorkspace ONE UEMSource
IdentityRun as System installs for the device.Win32 installation behavior
ContentA ZIP needs an EXE or MSI inside it to run a PowerShell script.Upload and configure Win32 files
Success and rebootOne Installer Success Exit Code and one Installer Reboot Exit Code.Upload and configure Win32 files
Retry and timeoutRetry Count, Retry Interval and Install Timeout. The defaults are 3 retries, 5 minutes and 60 minutes.Win32 installation behavior
Install completeDefining criteria, or a custom script with a Success Exit code.Upload and configure Win32 files
ScriptsPowerShell, in the user or system context, with a chosen architecture (32-bit, 64-bit or the device's) and a timeout in seconds. Exit 0 shows Executed, any other code Failed.Windows scripts
VariablesA script's Variables tab holds static values, including secrets, and the script reads them as $env:<key>.Windows scripts

Build the content

On an administrator workstation with PowerShell 7.4 or later, run the kit's packager without -IntuneWinAppUtil. Despite its name, the content it builds does not depend on Intune:

./packaging/mdm/intune/windows/New-DefenseClawIntunePackage.ps1 `
  -SetupPath .\DefenseClawSetup-Enterprise-Standalone-x64.exe `
  -Sha256 <SHA-256 from the cosign-verified checksums.txt> `
  -ConfigPath .\config.yaml `
  -OutputDirectory .\ws1-defenseclaw-1.4.0 `
  -ProductVersion 1.4.0
Compress-Archive -Path .\ws1-defenseclaw-1.4.0\content\* -DestinationPath .\DefenseClaw-1.4.0.zip

The content folder holds the Setup, config.yaml, the Install-DefenseClawIntune.ps1 launcher and intune-package.json. The launcher re-checks the SHA-256 pins in intune-package.json on the device, then starts DefenseClawSetup-Enterprise-Standalone-x64.exe /ensure JSON=1 with an absolute CONFIG= path, because Setup refuses relative paths. It runs in 32- or 64-bit Windows PowerShell 5.1. The packager refuses a config that contains an inline api_key.

Setup also refuses a config file that a non-administrator can change, or that sits in a folder a non-administrator can replace. If the device's app cache does not meet that, Setup stops with exit code 1603 and names the file.

Add PowerShell 7 as a dependency

The standalone lifecycle runs on PowerShell 7 from the x64 MSI and refuses without it. Upload PowerShell-7.x.y-win-x64.msi in Resources > Apps

Native Apps > Internal > ADD > Application File, and answer Yes to Is this a dependency file. Then select it under Files > App Dependencies of the DefenseClaw app (Upload and configure Win32 files). The kit recommends PowerShell 7.4 or later.

Add the app

Upload the ZIP in Resources > Apps > Native Apps > Internal > ADD > Application File, answer No to the dependency question, and set:

TabSettingValue
DetailsSupported Processor Architecture64-bit
FilesApp DependenciesThe PowerShell 7 dependency
FilesApp Uninstall ProcessUse Custom Script, with packaging/mdm/windows/uninstall.ps1 (see Uninstall on Windows)
Deployment Options > How To InstallRun asSystem
Install Commandpowershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File .\Install-DefenseClawIntune.ps1
Admin PrivilegesYes
Device RestartDo Not Restart
Retry Count, Retry Interval3, 5 minutes
Install Timeout60 minutes
Installer Reboot Exit Code3010
Installer Success Exit Code0
Deployment Options > When To Call Install CompleteIdentify Application ByUsing Custom Script
Script TypePowerShell
Command to Run the Scriptpowershell.exe -NoProfile -ExecutionPolicy Bypass -File .\detect.ps1 -MinimumVersion 1.4.0
Custom Script Filepackaging/mdm/windows/detect.ps1
Success Exit0

detect.ps1 reads the protected enterprise marker through the 64-bit registry view, so it works in a 32- or 64-bit engine. It exits 0 and prints DefenseClaw Enterprise <version> when the standalone deployment is installed at that version or newer, and exits 1 otherwise.

The first install needs the config in the content: /ensure without one exits 1639.

Keep it healthy

Add a Windows script in Resources > Scripts > Add > Windows (Windows scripts):

SettingValue
LanguagePowerShell
Execution ContextSystem
Timeout900 seconds or more
Codepackaging/mdm/intune/windows/Remediate-Fix.ps1

Assign it to the devices with a recurring trigger. The script runs enterprise windows ensure --profile standalone --json, which repairs from the installed payload and does nothing on a healthy device. It prints one short line and exits 0 when the device is healthy, repaired, or has no deployment yet. Otherwise it exits with the lifecycle's code, so the device's Scripts tab shows Failed. On unsigned (hash-pinned) releases the script's ensure keeps the marker's hash_pinned trust mode, so detect.ps1 and uninstall.ps1 keep accepting the unsigned CLI.

Deliver the AI Defense key on Windows

The key is optional. Without it the local policy engine decides alone. Never put it in the ZIP, in config.yaml or in the script body.

Add a second Windows script, System context, with the key as a variable named AIDEFENSE_KEY on its Variables tab. The script sends the value on standard input, never on a command line:

# Stores the Cisco AI Defense key from the Workspace ONE script variable
# AIDEFENSE_KEY. The value travels on standard input, never on a command line.
$hklm = [Microsoft.Win32.RegistryKey]::OpenBaseKey(
    [Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64)
$marker = $hklm.OpenSubKey('SOFTWARE\Cisco\DefenseClaw\Enterprise')
if ($null -eq $marker) { throw 'DefenseClaw is not installed yet' }
$cli = Join-Path ([string]$marker.GetValue('InstallRoot')) 'bin\defenseclaw.exe'
$null = $env:AIDEFENSE_KEY | & $cli enterprise secret set --name ai-defense-api-key --from-stdin --json 2>&1
exit $LASTEXITCODE

enterprise secret set needs an installed standalone deployment and an elevated context, which System provides. Each run stores the key and restarts the gateway, so run the script once, and again only when you rotate the key. Workspace ONE stores the value, so limit who can edit the script. Then set enterprise.inspection.ai_defense.enabled: true and credential: ai-defense-api-key in config.yaml. See AI Defense key.

Upgrade, roll back and change the config on Windows

TaskWhat to do
UpgradeBuild new content for the new release, add it as a new version of the app, and raise -MinimumVersion in the install-complete command. /ensure upgrades in place, keeps the state, and applies the config in the new content.
Roll backensure refuses to downgrade on Windows. Follow Roll back.
Change the configBuild new content with the new config.yaml and add it as a new version. /ensure validates and applies it, and rolls back if applying fails.

Uninstall on Windows

uninstall.ps1 finds the installed CLI through the protected registry marker and runs enterprise windows uninstall --profile standalone --json. That stops and deletes the services, removes DefenseClaw's hooks and machine-policy entries, and removes the Add/Remove Programs entry and the marker. It works in 32- or 64-bit Windows PowerShell 5.1, prints a no-op result and exits 0 when nothing is installed. Pass -Purge to also remove managed state.

Windows logs

WhereWhat
Workspace ONE: the app's Details View, and the device's Scripts tabInstallation status and reason codes, and script status (Upload and configure Win32 files)
%WINDIR%\Logs\DefenseClaw\enterprise-lifecycle.logEvery lifecycle result, one JSON line per run
DefenseClaw event log, source DefenseClaw Lifecycle (legacy copy: Application log, source DefenseClaw Enterprise)Installed, upgraded, repaired, failed, busy and refused events

Windows exit codes

CodeMeaningIn Workspace ONE
0Success, or nothing to doSuccess (Installer Success Exit Code)
1603The action failed and rolled back, the config does not validate, Setup is not elevated, or a non-administrator can change the config fileFailure; retried up to Retry Count. Read errors[].code in the lifecycle log.
1618Another lifecycle run holds the lockFailure for this attempt; the retry usually succeeds
1639Invalid arguments, such as a first install without a config, an unknown Setup property, or a relative or environment-expanded CONFIG= pathFailure; fix the content, because a retry gives the same result
3010Reserved; Setup does not return it todayReboot (Installer Reboot Exit Code)

See Exit codes and Lifecycle error codes.

macOS

How Workspace ONE runs macOS apps, scripts and sensors

BehaviorWorkspace ONE UEMSource
Internal appsUpload the .pkg with a metadata .plist made by the Workspace ONE Admin Assistant.Deploy internal macOS applications
App scriptsWritten in Bash. A pre-install script that exits non-zero skips the install. A post-install failure is recorded as installed with warnings.Deploy internal macOS applications
ScriptsBash, Python 3 or Zsh, in the user or system context, with a timeout in seconds. Exit 0 shows Executed, any other code Failed. Triggers include Run Periodically every 4, 6, 8 or 12 hours.macOS scripts
SensorsBash, Python 3 or Zsh, in the system or user context. The whole output is the value.macOS sensors
VariablesScripts and sensors read variables by name, such as $myvariable. Sensor variables are encrypted at rest and in transit.macOS scripts, macOS sensors

Install the package

Upload the verified defenseclaw-enterprise-<version>-darwin-arm64.pkg in Resources > Apps > Native Apps > Internal > ADD > Application File, with the metadata file from the Workspace ONE Admin Assistant (Deploy internal macOS applications). On the app's Scripts settings:

SettingValue
Pre-Install ScriptThe stage script below
Uninstall MethodUninstall script, with packaging/mdm/macos/uninstall.sh (change its first line to #!/bin/bash)

The pre-install script puts your config at the layout path /opt/cisco/defenseclaw/etc/config.yaml before the package installs, so the package's own ensure --from-package applies it on the first install. It never overwrites an existing config:

#!/bin/bash
# DefenseClaw: put the administrator config in place before a first install.
set -eu
PATH=/usr/bin:/bin:/usr/sbin:/sbin
layout=/opt/cisco/defenseclaw/etc
[ -e "$layout/config.yaml" ] && exit 0
umask 022
mkdir -p "$layout"
tmp=$(mktemp "$layout/.config.yaml.XXXXXX")
cat >"$tmp" <<'DEFENSECLAW_CONFIG'
config_version: 8
deployment_mode: managed_enterprise
data_dir: /opt/cisco/defenseclaw/runtime
policy_dir: /opt/cisco/defenseclaw/etc/policies
enterprise:
  profile: standalone
gateway:
  api_bind: 127.0.0.1
  api_port: 18970
guardrail:
  enabled: true
  mode: observe
  connectors:
    claudecode: {}
    codex: {}
DEFENSECLAW_CONFIG
chown root:wheel "$tmp"
chmod 0640 "$tmp"
mv -f "$tmp" "$layout/config.yaml"

Replace the YAML between the markers with your config. Never put the Cisco AI Defense key in it.

The package's postinstall step runs enterprise macos ensure --from-package and fails the install if the lifecycle fails, after the lifecycle has rolled back. The result is kept in /opt/cisco/defenseclaw/lifecycle/last-package-result.json.

Keep it applied

Add a script in Resources > Scripting > Scripts > Add > macOS, with Language Bash, Execution Context System and a Timeout of 1800 seconds. For the Code, paste defenseclaw-enterprise.sh, leave the source settings empty, and put the same YAML between the markers of its inline config:

DC_ACTION=ensure

dc_inline_config() {
    cat <<'DEFENSECLAW_CONFIG'
config_version: 8
deployment_mode: managed_enterprise
data_dir: /opt/cisco/defenseclaw/runtime
policy_dir: /opt/cisco/defenseclaw/etc/policies
enterprise:
  profile: standalone
gateway:
  api_bind: 127.0.0.1
  api_port: 18970
guardrail:
  enabled: true
  mode: observe
  connectors:
    claudecode: {}
    codex: {}
DEFENSECLAW_CONFIG
}

Assign it with Run Periodically (for example every 12 hours). Each run, the wrapper stages the inline config in a root-only folder and runs enterprise macos ensure with it: it applies a changed config, repairs drift, or does nothing. It prints one lifecycle result document and exits with the lifecycle's code. On a device where the package is not installed yet it exits 1 with mdm_not_installed.

Detect on macOS

Add a sensor in Resources > Scripting > Sensors > Add > macOS (macOS sensors):

SettingValue
Namedefenseclaw_status (a lowercase letter first, then letters, digits and underscores)
LanguageBash
Execution ContextSystem
Response Data TypeString
Codedetect.sh, with the settings below
DC_MIN_VERSION="1.4.0"
DC_REQUIRE_HEALTHY=1
DC_FORMAT="value"

The sensor always exits 0 and reports the installed version, not-installed, outdated or unhealthy.

Deliver the AI Defense key on macOS

Add a second macOS script, System context, with the key as a variable named AIDEFENSE_KEY. printf is a shell built-in, so the value never appears on a command line:

#!/bin/bash
# Stores the Cisco AI Defense key from the Workspace ONE variable AIDEFENSE_KEY.
set -eu
printf '%s' "$AIDEFENSE_KEY" | /opt/cisco/defenseclaw/bin/defenseclaw-gateway enterprise secret set --name ai-defense-api-key --from-stdin --json

enterprise secret set stores the key and runs ensure. Storing the same key again changes nothing, so a periodic trigger is safe. It fails until the package is installed. Workspace ONE stores the value, so limit who can edit the script. Then set enterprise.inspection.ai_defense.enabled: true and credential: ai-defense-api-key in the config.

Upgrade, roll back and change the config on macOS

TaskWhat to do
UpgradeUpload the new package as a new version of the app, and raise DC_MIN_VERSION in the sensor. The package's ensure --from-package upgrades in place and keeps the config and state.
Roll backFollow Roll back.
Change the configEdit the inline config in the ensure script. The next run applies it; the lifecycle validates it first and rolls back if applying fails.

macOS logs and exit codes

WhereWhat
Workspace ONE: the device's Scripts and Sensors tabsScript status and logs, and sensor values
/Library/Logs/Cisco/DefenseClaw/mdm-wrapper.logEach run of defenseclaw-enterprise.sh and uninstall.sh that gets past its argument checks, with its result (root, 0600). detect.sh does not log.
/opt/cisco/defenseclaw/lifecycle/last-package-result.jsonThe package's own ensure result
/Library/Logs/Cisco/DefenseClaw/lifecycle.log, verify.logThe automatic apply and daily verify jobs
CodeMeaningIn Workspace ONE
0Success, or nothing to doExecuted
1The action failed and rolled back, or the wrapper refused an inputFailed; read errors[].code in the script log
2Invalid settings, such as a missing or malformed value (mdm_invalid_arguments). A config that does not validate exits 1 (config_invalid).Failed; fix the settings block
75Another lifecycle run or the installer holds a lockFailed for this run; the next periodic run retries

Linux

Workspace ONE's Linux App Management installs, removes and versions .deb and .rpm files (Linux App Management), and Linux sensors run Bash or Python 3 in the system or current-user context (Linux sensors). Workspace ONE's Linux Custom Configuration profile can also run configuration code (Omnissa lists Puppet, Bash, Python 3 and Ansible, Linux profiles), which can stage the config and run ensure like the configuration-management recipes. This recipe does not cover that route, and never put the AI Defense key in a profile:

  • Without a config at /etc/defenseclaw/config.yaml, the package installs the default config, which protects no agent. Stage the config and the key with your configuration-management tool.
  • For detection, add a Linux sensor with detect.sh from packaging/mdm/linux, System context, and DC_FORMAT="value" in its settings block.
  • The package's postinstall step never fails the package transaction. Check the sensor, or /var/lib/defenseclaw-enterprise/last-package-result.json.