Scaling PowerShell with BrazenCloud

 

Challenges

One of the challenges of PowerShell is scaling it. Say you have a great script that works beautifully on your local machine or even running it remotely using PowerShell remoting. Getting that to work across multiple machines creates an intriguing challenge that might have you making tradeoffs between speed, efficiency, or usability. If you are leveraging PowerShell remoting, you'll be restricted by the capabilities of your workstation as well as the network and if you pick out an automation tool you will often be heavily restricted in scripting within their arbitrary boundaries. This is where Runway really shines. Using Runway, you can scale any PowerShell script by publishing it as a Runway Action and creating a Job to execute on your selected devices. Be they user endpoints, servers, containers, or anything else with a supported operating system.

Prerequisites

Before we get started, there are a couple of requirements:

And to clear up the Runway nomenclature:

  • Action : A package that tells the Runner what to execute and can also contain other files including, but not limited to scripts, configuration files, and executables.
  • Runner : A device with a supported operating system that has the Runway agent installed.

Video

Creating a PowerShell Action

Runway supports any command or script that will execute on an endpoint. So when we call something a 'PowerShell Action', we are referring to a Runway Action that happens to call PowerShell, regardless of PowerShell version as Runway supports both Windows PowerShell or PowerShell 6+ . And that includes delivering an Action that contains the portable PowerShell executable to systems that don't have PowerShell installed.

The Script

We'll start with a non-scaled PowerShell script. For example, lets say that we want to rename the local administrator (SID:*500) account on all of our endpoints. To do this, our script might look like:

Get-LocalUser | Where-Object {$_.Sid -like '*-500'} | Rename-LocalUser -NewName 'Badmin'

And then for a remote endpoint that has line of sight connectivity to our workstation, we can just add Invoke-Command:

Invoke-Command -Computer <computername> -ScriptBlock {
    Get-LocalUser | Where-Object {$_.Sid -like '*-500'} | Rename-LocalUser -NewName 'Badmin'
}

And if we were in an AD environment and wanted to run this command on all machines, we could do that serially with something like:

Get-ADComputer -Filter * -SearchBase '<ou>' | Foreach-Object {
    Invoke-Command -Computer $_.Name -ScriptBlock {
        Get-LocalUser | Where-Object {$_.Sid -like '*-500'} | Rename-LocalUser -NewName 'Badmin'
    }
}

Very straightforward. However, remember that this script requires your workstation to have network connectivity to each of the endpoints.

Converting the script to an Action

Since Runway runs the PowerShell script on each endpoint through a Runner (an agent), the script is going to be the simple one-liner that we started with:

Get-LocalUser | Where-Object {$_.Sid -like '*-500'} | Rename-LocalUser -NewName 'Badmin'

So with that script, lets create an Action! We'll start by giving it a name and call it: renameAdmin. Create a folder with that name, a file called manifest.txt, a file called repository.json, a subfolder called windows, and then place your script in the windows folder and call it script.ps1 (the name can be arbitrary).

The final result will look like:

renameAdmin
- manifest.txt
- repository.json
- windows
  - script.ps1

The manifest file tells the Runner what to do when it executes the Action. On a Windows device, we us the RUN_WIN command and we'll reference PowerShell to call the script:

COPY . .

RUN_WIN "powershell.exe -ExecutionPolicy Bypass -NoProfile -File .\windows\script.ps1"

The COPY . . line simply tells the Runner what to extract from the Action. In this case (and most cases), everything.

Action Metadata

The repository.json file is where we store metadata about an Action. This file is not required, but it makes finding your Action much easier. For this demo, we'll create the file with the following data:

{
    "Description": "Renames the local administrator account.",
    "Language": "PowerShell",
    "Tags": ["Users", "PowerShell", "Windows"]
}

With that, we are ready to get this Action published!

Publishing the Action

With the Runway utility, we'll first need to login :

runway login -u <emailaddress> -p <password>

Then we can publish the Action with the build command:

runway build -i C:\path\to\renameAdmin\manifest.txt -p endpoint:renameAdmin

Using the PowerShell Action

With the Action published, you can now go into Runway, select a Runner, and create a Job:

Creating a Job

In the Job creation screen, we'll select the Users tag in the Action pane on the right to filter down to our Action, and then we can add it to the Job by clicking on it:

Select Action

And with the Action in the Job, we can go ahead and run it by clicking on 'Create Job':

Create Job

Once the Action is complete, we can view the output from the Job by clicking on the log button in the Job screen:

Action Output

This will open up a new modal where you can see all the output from your script. In this case, no output means success!

Conclusion

Using Runway to deploy your PowerShell scripts is simple. In many cases, such as the one shown here, its only a matter of packaging the existing script in an Action without any additional changes. If you want to spice it up a bit more, you can certainly use Runway to add some parameters and instead of hardcoding the new user name, you could accept it as a parameter. There's a lot of flexibility with Runway.

Previous
Previous

Secure Remote PowerShell without PowerShell Remoting or a VPN