---
title: "PowerShell (.ps1) Scripting"
order: 4
---

**PowerShell** is the standard for automating configuration on Windows servers, and Otter was designed to seamlessly integrate with it  -- whether that means running your existing scripts across dozens of servers, leveraging scripts built by the community, or a custom combination of both.

While you definitely don't need to be a scripting guru to use Otter, learning how to incorporate scripts into Otter will enable you to automate virtually any aspect of Windows, at the click of a button.

:::(Internal) (TODO)
This pattern isn't great, and this should link to better tutorials perhaps. also, we need prereqs, since this seems to mostly work on Windows.

## Prereqs
 - Windows server v ??
 - PowerShell v??
 - Permissions?
:::

## Adding Existing Scripts to Otter

The easiest way to add your existing scripts to Otter is by navigating to Scripts > Add Script, and then creating or uploading your scripts. You can also add scripts and assets directly to a Git repository that you've configured as a [Git-based raft](/docs/otter/scripting-in-otter/otter-rafts-and-git-storage), and Otter will use your repository instead.

## Running PowerShell Scripts on Servers

Otter can create a GUI for your scripts so that it becomes easy to run complex PowerShell scripts by defining script parameters that include descriptions, help text, dropdown, or checkbox inputs, and more.

The best way to do this is creating a [Job Template](/docs/otter/orchestration-server-automation/otter-jobs-templates) that runs your script against target servers. You can also use an [ad-hoc Job](/docs/otter/orchestration-server-automation/otter-jobs-templates#running-a-script-without-job-templates) to run the script without a template. 

See the [Create a GUI for Scripts with Input Forms](/docs/otter/orchestration-server-automation/otter-create-a-gui-for-scripts-with-input-forms) for step-by-step guide on this in Otter.

::: (Info) (Note:)
By default, PowerShell will prefer using Windows PowerShell 5.1 when available.  Starting with v2.4.0 of the Scripting extensions, this functionality can be changed to prefer PowerShell Core by setting the configuration value `$PreferWindowsPowerShell` to `false`.  This can be set globally at the extension/system level or scoped to a server, environment, job, etc...

If you are executing PowerShell from OtterScript, all the PowerShell operations have a `PreferWindowsPowerShell` parameter that can be set also.  This parameter will default to use `$PreferWindowsPowerShell`.
:::

## Using OtterScript to Orchestration Scripts

Once you've added PowerShell Scripts to Otter, you can use an [OtterScript Orchestration](/docs/otter/scripting-in-otter/otter-otterscript-and-operations) to run those scripts on different servers in a complex manner, whether that means targeting servers sequentially, in parallel, or even with branching and iterating (looping) logic.

Although OtterScript is based on programming logic, you do not need to be a coder to use OtterScript, which offers both a text editor and a low-code visual editor.

### Using PSCall to Run PowerShell

You can call your scripts using the [PSCall Operation](/docs/otter/reference/operations/powershell/pscall). If you use PowerShell's Comment-based Help, you'll get descriptions for script arguments when editing in visual mode, similar to running the script using a Job.

```OtterScript
pscall CreateUserInDirectory(
  domain: hdars.local,
  user: $UserName
);

pscall GiveUserPermissions(
  domain: hdars.local,
  user: $UserName
);
```

:::(Internal) (TODO)
screenshot of PSCall in Visual Editor showing parameters
:::


## Collecting & Verifying with PowerShell

Otter can use your existing PowerShell scripts to verify server configuration by using a special operation called `PSVerify`. This leverages PowerShell's Comment-based Help and adds "Augmented-Help Blocks" to help instruct Otter on how to verify desired configuration.

See [Compliance as Code with PowerShell & PSVerify](/docs/otter/collecting-verifying-configuration/otter-compliance-as-code-with-powershell-psverify) to learn more.

### PowerShell Script Asset
```PowerShell
<# 
.PARAMETER HotFixID
ID of the HotFix to check, such as "KB4562830"

.AHCONFIGTYPE
HotFix

.AHCONFIGKEY 
$HotFixID
#>
param ([string]$HotFixID)

if (!(Get-HotFix -Id  $HotFixID) 2> $null){
    Write-Warning "HotFixID $HotFixID is not installed"
    return $false;
}
return $true;
```

### OtterScript Desired Configuration
```OtterScript
PSVerify verify-hotfix-installed
(
  HotFixID: KB4532938
);

PSVerify verify-hotfix-installed
(
  HotFixID: KB4483843
);
```

## Remedating Drift with PowerShell

In addition to verifying the configuration of your servers, Otter can use your existing PowerShell scripts to reconfigure your servers into your desired state using an operation called `PSEnsure`.

This is a bit different than PowerShell's Desired State Configuration (DSC), and it's a simpler alternative that lets you use basic PowerShell commands.

See the [Remediating Drift with PowerShell & PSEnsure](/docs/otter/drift-remediation-configuration-as-code/otter-remediating-drift-with-powershell-psensure) to learn more.

### PowerShell Script ASset
```PowerShell
<# 
.AHEXECMODE 
$ExecutionMode
#>

if ($ExecutionMode = "Collect") {
    if (-not(Get-Hotfix -Id $KB)) {
        Write-Information "HotFixID KB4532938 is not installed."
        return $false
    } 
    else {
        Write-Information "HotFixID KB4532938 is installed."
        return $true
        }
    } 
elseif ($ExecutionMode = "Configure") { 
    if (-not(Get-Hotfix -Id $KB)) {
        Start-Process -FilePath "wusa.exe" -ArgumentList "KB4532938.msu /quiet /norestart" -Wait 
    } 
    else {
        Write-Information "HotFixID KB4532938 is already installed."
    }
}
```
## Using PowerShellDSC with OtterScript
[PowerShell Desired State Configuration (DSC)](https://msdn.microsoft.com/en-us/powershell/dsc/overview) offers [quite a few open-source modules](https://github.com/powershell/) that you can use to configure different features of Windows Servers. With [PSDsc](/docs/otter/reference/operations/powershell/psdsc), you can invoke DSC Resources in nearly the same manner as a native DSC statement.

```
PSDsc xWebAppPool (
  Name: AccountsAppPool,
  Ensure: present
);
```

These declarative-style statements are essentially *Ensure Operations*, and thus will collect, report, and remediate drift just like the [Built-in Operations](/docs/otter/reference/operations).


## Inline Execution of PowerShell

Through [PSExec](/docs/otter/reference/operations/powershell/psexec) and [Swim Strings](/docs/executionengine/otterscript/strings-and-literals), you can run scripts text directly from your OtterScript.

Otter will seamlessly replace variables within this script string, so in the example below `$ApplicationName` might be defined as a [Configuration Variable](/docs/otter/scripting-in-otter/otter-configuration-variables).

### PSExec Example

```OtterScript
psexec >>
  # delete all but the latest 3 logs in the log directory
  Get-ChildItem "E:\Site\Logs\$ApplicationName" |
     Sort-Object $.CreatedDate -descending |
     Select-Object -skip 3 |
     Remove-Item
>>;
```

### Evaluating PowerShell Literals 

OtterScript is not a general-purpose programming language, and thus doesn't have built-in support for things like arithmetic. You could, of course, write a [custom variable function](/docs/executionengine/overview/statements-and-blocks/otter-execution-engine-runtime-variables#variable-functions) to support this, but [PSEval](/docs/otter/reference/functions/powershell/pseval) is much easier:

For example, if you wanted to convert the value stored in one variable (`$minutes`) to milliseconds. You could PSEval the simple expression `$minutes * 60 * 1000`:

```OtterScript
set $milliseconds = $PSEval($minutes * 60 * 1000);
```

$PSEval runs the expression on the server currently in context, so use it inside of an [If/Else Block](/docs/executionengine/overview/statements-and-blocks/otter-execution-engine-statements-and-blocks-if-else) to perform different operations depending on the results of the expression on that server.

:::(Internal) (TODO)
## Technical Implementation
 - uses PowerShell runtime
 - Captures Write-Host as Write-Info
 - ## FAQ
 - 
:::
