> ## Documentation Index
> Fetch the complete documentation index at: https://university.triggr-app.com/llms.txt
> Use this file to discover all available pages before exploring further.

# On-prem scripts

> Write PowerShell in Studio, run it on a client's network through the desktop agent from a workflow, and read what it returns.

On-Prem > Scripts holds the PowerShell your workflows run on a client's machine through the [desktop agent](/desktop-agent/overview). The client needs an [installed agent](/desktop-agent/install) showing Online.

<Frame caption="On-prem scripts: writing a script, running it from a workflow through Globex's agent, and reading what it returned (1:28).">
  <video controls preload="none" playsInline className="w-full aspect-video rounded-xl" poster="/images/videos/studio-on-prem-scripts.jpg" src="https://mintcdn.com/triggr/GxrCQFDFNLmEMyJ1/images/videos/studio-on-prem-scripts.mp4?fit=max&auto=format&n=GxrCQFDFNLmEMyJ1&q=85&s=323366421b296c112af6f694276f3c5f" data-path="images/videos/studio-on-prem-scripts.mp4" />
</Frame>

<Frame>
  <img src="https://mintcdn.com/triggr/XztmSfsoAUpshfIO/images/studio/on-prem/scripts.png?fit=max&auto=format&n=XztmSfsoAUpshfIO&q=85&s=74f397ba6e77ce85b8e00d50e28da81a" alt="The Scripts page, Manage your PowerShell scripts, with Refresh and New Script buttons, a Search scripts box and one script, Docs sample – Unlock AD account, with Edit and delete actions." width="2368" height="772" data-path="images/studio/on-prem/scripts.png" />
</Frame>

## Example: unlock an AD account

The script unlocks a user in a client's Active Directory and reports their last logon.

<Steps>
  <Step title="Write the script">
    Click **New Script**, name it, paste the script and click **Create Script**.

    ```powershell theme={null}
    $ErrorActionPreference = 'Stop'
    Import-Module ActiveDirectory

    $user = Get-ADUser -Identity '{{Username}}' -Properties LockedOut, LastLogonDate
    $wasLocked = $user.LockedOut
    if ($wasLocked) { Unlock-ADAccount -Identity $user }

    $lastLogon = 'Never'
    if ($user.LastLogonDate) { $lastLogon = $user.LastLogonDate.ToString('yyyy-MM-dd HH:mm') }

    [ordered]@{ username = $user.SamAccountName; wasLocked = $wasLocked; lastLogonDate = $lastLogon } | ConvertTo-Json -Compress
    ```

    <Frame>
      <img src="https://mintcdn.com/triggr/XztmSfsoAUpshfIO/images/studio/on-prem/script-editor.png?fit=max&auto=format&n=XztmSfsoAUpshfIO&q=85&s=42d65913b68c4a0f2c7c226fb08d4ada" alt="The Create New Script dialog with the name Docs sample – Unlock AD account and the unlock script in the PowerShell Script Editor, with Cancel and Create Script buttons." width="2736" height="1030" data-path="images/studio/on-prem/script-editor.png" />
    </Frame>

    `{{Username}}` is a parameter Triggr fills in before the script runs; `$ErrorActionPreference = 'Stop'` makes any error fail the run, and the last line returns the result as one line of JSON.
  </Step>

  <Step title="Run it from a workflow">
    Add **Execute PowerShell (Timeout)** from On Prem and rename it `Unlock Account` with the pencil (it starts as Execute Powershell With Timeout). Choose the script, fill in Parameters and set Client ID (or leave it empty to use the client an earlier Client step set).

    <Frame>
      <img src="https://mintcdn.com/triggr/XztmSfsoAUpshfIO/images/studio/on-prem/execute-powershell-step.png?fit=max&auto=format&n=XztmSfsoAUpshfIO&q=85&s=a91f914cc9542e42a4e9cb2daef122e9" width="384" alt="The Unlock Account step: Script Docs sample – Unlock AD account, Parameters with Username set to ctx.params.username with the json filter, Timeout (seconds) 120 and Client ID set to ctx.params.clientId." data-path="images/studio/on-prem/execute-powershell-step.png" />
    </Frame>

    Each key in Parameters fills the placeholder with the same name; `| json` keeps quotes in form or webhook values from breaking the JSON (see [Build JSON](/studio/workflows/expressions#build-json)).
  </Step>

  <Step title="Handle each outcome">
    Connect the Completed, Failed and Timed Out branches; Timed Out means no agent reported back in time, usually because it's offline.

    <Frame>
      <img src="https://mintcdn.com/triggr/XztmSfsoAUpshfIO/images/studio/on-prem/unlock-account-workflow.png?fit=max&auto=format&n=XztmSfsoAUpshfIO&q=85&s=6af6e33361eb778383973842939a827e" alt="A workflow canvas: Trigger, then Unlock Account with three branches labelled Completed, Failed and Timed Out, leading to Read Result then Done, Script Failed, and Agent Offline." width="2160" height="1440" data-path="images/studio/on-prem/unlock-account-workflow.png" />
    </Frame>
  </Step>

  <Step title="Read the result">
    The script's output arrives as text in `output`. On the Completed branch, add a Code step named `Read Result` that parses it, so later steps can use `{{ ctx.steps['Read Result'].lastLogonDate }}`.

    ```javascript theme={null}
    return JSON.parse(ctx.steps['Unlock Account'].output);
    ```

    <Frame>
      <img src="https://mintcdn.com/triggr/XztmSfsoAUpshfIO/images/studio/on-prem/read-result-step.png?fit=max&auto=format&n=XztmSfsoAUpshfIO&q=85&s=bbe55de8161a909a66260e9bb6e1d3f3" width="384" alt="The Read Result Code step with JavaScript return JSON.parse(ctx.steps['Unlock Account'].output);" data-path="images/studio/on-prem/read-result-step.png" />
    </Frame>
  </Step>

  <Step title="Test it">
    Save, click **Manual Trigger** and enter a real user and the client's ID from its page in [Clients](/studio/clients/clients-and-variables). The run waits on Unlock Account until the agent reports back; click the step to see its output.

    <Frame>
      <img src="https://mintcdn.com/triggr/XztmSfsoAUpshfIO/images/studio/on-prem/manual-trigger-unlock.png?fit=max&auto=format&n=XztmSfsoAUpshfIO&q=85&s=27b1cf3316a0434dd7eabfb2cea7ab76" width="560" alt="The Trigger Workflow dialog with JSON giving a clientId of 11111111-2222-3333-4444-555555555555 and username jane.doe, and Cancel and Trigger Workflow buttons." data-path="images/studio/on-prem/manual-trigger-unlock.png" />
    </Frame>
  </Step>
</Steps>

## What the step returns

| Field | Holds |
| - | - |
| `status` | `completed`, `failed` or `timed_out` |
| `output` | Everything the script wrote, as text |
| `error` | Why it failed, such as `PowerShell script failed with exit code 1` |
| `completed`, `timedOut` | `true` or `false` |
| `executionId` | The run's ID in On-Prem > Executions |

The plain Execute PowerShell step has no deadline or branches: it waits for an agent, then carries on whether the script worked or not, so check `{{ ctx.steps['Step name'].completed }}` in a Condition step. Check Agents Online tests an agent is up first ([On Prem steps](/studio/workflows/steps#on-prem)).

## How scripts run

* The agent stops a script after 10 minutes, whatever the step's timeout, and the step fails.
* Scripts run in PowerShell 7 if it's installed, otherwise Windows PowerShell 5.1, under the agent service's account (Local System unless you changed it).
* Any online agent for the client can run the script, so install the modules you need on each.
* A placeholder becomes the value's text exactly. Keep it in single quotes and check form input first: a `'` in the value ends the string early.

## Executions

**On-Prem > Executions** lists every run with its script, client and status; a run is Pending until an agent picks it up. Runs that timed out show under All Statuses. Click View for its parameters, output and error.

<Frame>
  <img src="https://mintcdn.com/triggr/XztmSfsoAUpshfIO/images/studio/on-prem/executions.png?fit=max&auto=format&n=XztmSfsoAUpshfIO&q=85&s=e393ba77c0ddaf1428b822eeb25cdd78" alt="The PowerShell Executions page, View and manage PowerShell script executions, with a Search executions box, an All Statuses filter and Script, Client, Status, Created and Actions columns." width="2368" height="842" data-path="images/studio/on-prem/executions.png" />
</Frame>

<Warning>
  Parameters show in full on the execution's page and in the workflow run, so don't pass passwords to scripts: generate them on the machine.
</Warning>

## FAQ

<AccordionGroup>
  <Accordion title="Can I run a script without a workflow?">
    No. Make a small workflow with an Execute PowerShell step and run it with Manual Trigger.
  </Accordion>

  <Accordion title="Do I need a param() block?">
    No. Write `{{Name}}` wherever a value goes. A placeholder with no matching key in Parameters stays as it is.
  </Accordion>

  <Accordion title="A number I passed arrives as 30.000000">
    Numbers in Parameters are written with six decimal places. Pass them as text, such as `"Days": "30"`.
  </Accordion>

  <Accordion title="My Code step can't parse the output">
    `output` includes `Write-Host` lines and warnings. Make the JSON line the only output: remove `Write-Host` and pipe other output to `Out-Null`.
  </Accordion>

  <Accordion title="Can I change a script without breaking workflows?">
    Saving a script changes it for every workflow that uses it from the next run; there's no version history, and deleting one doesn't check which workflows use it.
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="“the on-prem agent did not report a result before the step timeout — check that it's online”">
    No agent for that client reported back in time. Check the client's agent is Online ([Install the agent](/desktop-agent/install)).
  </Accordion>

  <Accordion title="“PowerShell script failed with exit code 1”">
    The script hit an error or ran past 10 minutes. `output` holds PowerShell's error message.
  </Accordion>

  <Accordion title="“client not found in context” or “no client set in the workflow context and no clientId configured on the step”">
    The run has no client. Set Client ID, or start the workflow with a [Client step](/studio/workflows/steps#client) such as Set Client By ID.
  </Accordion>

  <Accordion title="“parameters must be valid JSON”">
    A filled-in value has quotes in it. Add `| json` to the template: `{ "Username": {{ ctx.params.username | json }} }`.
  </Accordion>

  <Accordion title="“The specified module 'ActiveDirectory' was not loaded”">
    The agent's machine lacks the ActiveDirectory module. Install the RSAT Active Directory tools there, or install the agent on a domain controller.
  </Accordion>
</AccordionGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.