Using Uninstall-WindowsFeature in a Worklet

The Uninstall-WindowsFeature cmdlet removes Windows Server roles and features. It comes from the ServerManager module, which is only available in 64-bit PowerShell, and Automox runs scripts in a 32-bit PowerShell context. Running it directly in a Worklet fails with a message that the term is not recognized.

The supported approach is to wrap your code in a script block and invoke it through the 64-bit PowerShell. This article covers that pattern, plus the two things that most often go wrong with it: exit codes not propagating, and output never reaching the Activity Log.

The pattern

$scriptBlock = {
    Uninstall-WindowsFeature -Name "FeatureName"
}

& "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" `
    -ExecutionPolicy Bypass -NoProfile -NonInteractive -Command $scriptBlock

exit $LASTEXITCODE
  1. Replace FeatureName with the feature you want to remove.
  2. Save the Worklet and assign it to a test group before deploying broadly.

Propagate the exit code

This is the most common mistake with this pattern. Assigning the invocation to a variable, as in $exitCode = & "...powershell.exe" ..., does not capture an exit code. The call operator returns whatever the command wrote to output, so that variable ends up holding text, not a status.

The 64-bit process's real exit code lands in $LASTEXITCODE after the call returns. The Worklet reports the exit code of the outer 32-bit script, so unless you end with exit $LASTEXITCODE the Worklet reports success regardless of what the inner script did.

This matters most in Evaluation Code. An evaluation that never propagates a non-zero code always reports the device compliant, so the remediation never runs and nothing appears to happen:

$scriptBlock = {
    $feature = Get-WindowsFeature -Name "FeatureName"
    if ($feature.Installed) {
        Write-Output "Feature is installed. Remediation required."
        exit 2
    }
    Write-Output "Feature is not installed."
    exit 0
}

& "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" `
    -ExecutionPolicy Bypass -NoProfile -NonInteractive -Command $scriptBlock

exit $LASTEXITCODE

Use a non-zero value such as 2 rather than 1. An exit 1 from Worklet script content frequently surfaces in logs as Exit Code 124, COMMAND TIMED OUT, even when the script ran to completion, which sends you looking for a timeout that did not occur.

Getting output into the Activity Log

Output from a separately invoked 64-bit PowerShell process is not captured automatically, because the agent is running in a 32-bit context. Anything the inner script writes is lost unless it is allowed to flow back out of the call.

Two things to avoid:

  • Do not assign the invocation to a variable. That swallows the output instead of printing it.
  • Do not use -WindowStyle Hidden. It is unnecessary for a process with no interactive session and contributes to output not surfacing.

Write progress with Write-Output inside the script block, and let the call print. If you would rather capture and then emit it, assign to a variable and write it out explicitly:

$result = & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" `
    -ExecutionPolicy Bypass -NoProfile -NonInteractive -Command $scriptBlock
$code = $LASTEXITCODE

Write-Output $result
exit $code

Capture $LASTEXITCODE into a variable immediately, before running anything else, since later commands overwrite it. For more on output visibility see Why Isn't Output Showing in the Activity Log for My Worklets.

Server features versus client optional features

The ServerManager cmdlets, including Get-WindowsFeature, Install-WindowsFeature and Uninstall-WindowsFeature, are available on Windows Server. They do not exist on Windows client editions, so targeting workstations with this Worklet returns a message that the term is not recognized no matter how the 64-bit invocation is configured.

On Windows 10 and Windows 11, use the optional-features cmdlets or DISM instead, inside the same script block wrapper:

Disable-WindowsOptionalFeature -Online -FeatureName "FeatureName" -NoRestart

Feature names differ between the two sets, so confirm the correct name for the platform you are targeting rather than reusing a server role name. Scope the Worklet to the appropriate device group so it is not attempted on both.

Restarts

Removing a role or feature commonly requires a restart to finish. Check the result rather than assuming, and let the policy handle the restart rather than rebooting from inside the script:

$result = Uninstall-WindowsFeature -Name "FeatureName"
Write-Output "Success: $($result.Success)  RestartNeeded: $($result.RestartNeeded)"

If a restart is required, use the Worklet policy's automatic restart setting so the reboot is handled with your notification and scheduling configuration.

Troubleshooting

  • Term is not recognized. Either the code is not running through the 64-bit invocation, or the device is a Windows client edition where these cmdlets do not exist.
  • Worklet reports success but nothing changed. The exit code is not being propagated. Confirm the script ends with exit $LASTEXITCODE.
  • No output in the Activity Log. The output is being assigned to a variable and never written. See the section above.
  • Access to the path is denied, referencing a path under Program Files (x86)\Automox. Endpoint protection or application control is blocking execution from the agent's working directory. Trust-list Automox rather than modifying the script. See Globally Trust-listing Automox Through EPP Application Control.
  • Works when run manually, fails as a Worklet. Reproduce it in the same context Automox uses, 32-bit and as SYSTEM, before changing the logic. See How to Test a PowerShell Script Locally for Worklets.

Related articles

Was this article helpful?
0 out of 0 found this helpful