powershell

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2025 License: MIT Imports: 16 Imported by: 0

README

go-powershell

This is a fork of the original Gorilla implementation with many enhancements over the other forks found here!

This package was originally inspired by jPowerShell and allows one to run and remote-control a PowerShell session. Use this if you don't have a static script that you want to execute, bur rather run dynamic commands.

The session is kept hot in a single instance of powershell.exe such that you do not have the overhead of creating a new PowerShell process every time you want to run some commands.

Installation

go get github.com/fireflycons/go-powershell

Usage

To start a PowerShell shell, you need a backend. Backends take care of starting and controlling the actual powershell.exe process. In most cases, you will want to use the Local backend, which just uses os/exec to start the process.

Note that due to how the inter-process communication works with the underlying session, you cannot include any line breaks in your command string, otherwise it will stall the pipes. The command is sanity-checked for this before submission and execute methods will fail with [ErrInvalidCommandString]. If you want to send an entire script, this must be placed in the file system and a command sent to dot-source it.

package main

import (
    "context"
	"fmt"

	ps "github.com/fireflycons/go-powershell"
	"github.com/fireflycons/go-powershell/backend"
)

func main() {
	// choose a backend, which by default will use Windows Powershell (5.1)
	back := &backend.Local{}

	// start a local powershell process
	shell, err := ps.New(back)
	if err != nil {
		panic(err)
	}
	defer shell.Exit()

	// ... and interact with it
    // Can be a single command, or multiple chained with ;
    // Must not be multiline - see Excuting entire scripts below.
	stdout, stderr, err := shell.Execute("Get-WmiObject -Class Win32_Processor")
	if err != nil {
		panic(err)
	}

	fmt.Println(stdout)

    // ... or more safely
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()

	stdout, stderr, err := shell.ExecuteWithContext(ctx, "Write-Host 'Hello world'")
	if err != nil {
		panic(err)
	}

	fmt.Println(stdout)

}

Alternatively with pwsh (PowerShell version >= 6). This will be made to work for Linux and Mac in a future version.

package main

import (

	ps "github.com/fireflycons/go-powershell"
	"github.com/fireflycons/go-powershell/backend"
)

func main() {
	// choose a backend, requesting pwsh
	back := &backend.Local{Version: backend.Pwsh}

	// start a local powershell process
	shell, err := ps.New(back)
	if err != nil {
		panic(err)
	}
	defer shell.Exit()
}

Executing entire scripts

package main

import (

    "fmt"

	ps "github.com/fireflycons/go-powershell"
	"github.com/fireflycons/go-powershell/backend"
	"github.com/fireflycons/go-powershell/utils"
)

func main() {
	// choose a backend, requesting pwsh
	back := &backend.Local{}

	// start a local powershell process
	shell, err := ps.New(back)
	if err != nil {
		panic(err)
	}
	defer shell.Exit()

	script := `
Write-Host "hello"
Write-Host "goodbye"
`

    sout, _, err := shell.ExecuteScript(script)

    if err != nil {
        panic(err)
    }

    fmt.Println(sout)

    scriptFile = "this-file-must-exist.ps1"

    sout, _, err = shell.ExecuteScript(scriptFile)

    if err != nil {
        panic(err)
    }

}

Remote Sessions

You can use an existing PS shell to use PSSession cmdlets to connect to remote computers. Instead of manually handling that, you can use the Session middleware, which takes care of authentication. Note that you can still use the "raw" shell to execute commands on the computer where the PowerShell host process is running.

package main

import (
	"fmt"

	ps "github.com/fireflycons/go-powershell"
	"github.com/fireflycons/go-powershell/backend"
	"github.com/fireflycons/go-powershell/middleware"
)

func main() {
	// choose a backend
	back := &backend.Local{}

	// start a local powershell process
	shell, err := ps.New(back)
	if err != nil {
		panic(err)
	}

	// prepare remote session configuration
	config := middleware.NewSessionConfig()
	config.ComputerName = "remote-pc-1"

	// create a new shell by wrapping the existing one in the session middleware
	session, err := middleware.NewSession(shell, config)
	if err != nil {
		panic(err)
	}
	defer session.Exit() // will also close the underlying ps shell!

	// everything run via the session is run on the remote machine
	stdout, stderr, err = session.Execute("Get-WmiObject -Class Win32_Processor")
	if err != nil {
		panic(err)
	}

	fmt.Println(stdout)
}

Note that all commands that you execute are wrapped in special echo statements to delimit the stdout/stderr streams. After .Execute()ing a command, you can therefore not access $LastExitCode anymore and expect meaningful results.

Enhancements

The following enhancements have been made to the original code:

  • Support pwsh, the newer .NET Core version of PowerShell. You can choose whether to start this or regular Windows PowerShell.
  • Wrap all submitted commands in a try block to properly capture errors and ensure the output boundary markers are properly written.
  • Make the session thread safe.
  • Optimize the streamReader function to perform fewer allocations.
  • Avoid panics if Shell.Exit is called on a closed shell. Return an error instead.
  • Add ability to pre-load modules when the shell is started.
  • Added an additional shell method ExecuteWithContext that takes a context argument. If a shell command isn't well formed then the stdout and stderr pipes do not return anything and the Execute method will block indefinitely in this case. The underlying session will be restarted before context.DeadlineExceeded is returned to the caller. A restarted shell may be unstable!
  • Added an additional shell method Version which returns the underlying PowerShell host's version.
  • Added additional shell methods ExecuteScript and ExecuteScriptWithContext to run multiline scripts or external script files.
  • Sentinel errors returned by shell methods that can be tested with Errors.Is
  • Much enlarged test suite.

Future changes:

  • Support Linux and Mac (pwsh only)

License

MIT, see LICENSE file.

Documentation

Overview

Package powershell provides an interface to run PowerShell code in a session that remains "hot", such that you do not have to create a new instance of the powershell process with each invocation, with many enhancements over other packages of the same name! Supports Windows PowerShell and pwsh, contexts and multithreaded use.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidCommandString is returned if the command passed contains any CR or LF characters
	// since this will stall the pipe. Note that leading and trailing space will be trimmed automatically.
	// Multiple commands should be chained with semicolon, not line breaks.
	ErrInvalidCommandString = errors.New("invalid command")

	// ErrShellClosed is returned if an attempt is made to perform an operation on a closed shell
	ErrShellClosed = errors.New("shell is closed")

	// ErrPipeWrite is returned if there was a problem sending the command
	// to the session's sdtin pipe.
	ErrPipeWrite = errors.New("error sending command")

	// ErrCommandFailed will be returned if any output from the command sent was
	// read from stderr. This will include unhandled exceptions (uncaught throws)
	// and any direct writes to stderr like Console::Error.WriteLine().
	ErrCommandFailed = errors.New("data written to stderr")

	// ErrScript can be returned by the ExecuteScript functions if the
	// type of script passed as an argument cannot be determined
	ErrScript = errors.New("cannot determine script type")
)

Functions

This section is empty.

Types

type Shell

type Shell interface {
	// Execute runs PowerShell commands in the session instance, capturing stdout and stderr streams
	Execute(cmd string) (string, string, error)

	// ExecuteWithContext runs PowerShell commands in the session instance, capturing stdout and stderr streams.
	//
	// The context allows cancellation of the command. Streams to/from the PowerShell session may
	// block indefinitely if the command is not well formed as the input may still be waiting.
	//
	// Note that if the error is "context deadline exceeded", the underlying session will be
	// unstable and it will be restarted. A restarted shell _may_ be unstable!
	ExecuteWithContext(ctx context.Context, cmd string) (string, string, error)

	// ExecuteScript runs a multiline script or external script file in the session instance, capturing stdout and stderr streams
	//
	// If the argument is a path to an existing script file, then that will be executed,
	// otherwise the value is assumed to be a multiline string.
	ExecuteScript(scriptOrPath string) (string, string, error)

	// ExecuteScriptWithContext runs a multiline script or external script file in the session instance, capturing stdout and stderr streams.
	//
	//
	// If the argument is a path to an existing script file, then that will be executed,
	// otherwise the value is assumed to be a multiline string.
	//
	// The context allows cancellation of the command. Streams to/from the PowerShell session may
	// block indefinitely if the command is not well formed as the input may still be waiting.
	//
	// Note that if the error is "context deadline exceeded", the underlying session will be
	// unstable and it will be restarted. A restarted shell _may_ be unstable!
	ExecuteScriptWithContext(ctx context.Context, scriptOrPath string) (string, string, error)

	// Version returns the PowerShell version as reported by the $Host built-in variable.
	// If there was an error reading this, version.Major will be -1.
	Version() *semver.Version

	// Exit terminates the underlying PowerShell process.
	// Error will be non-nil if shell already closed.
	Exit() error
}

Shell is the interface to a PowerShell session

func New

func New(backend backend.Starter, opts ...ShellOptionFunc) (Shell, error)

New creates a new PowerShell session

type ShellOptionFunc added in v0.2.0

type ShellOptionFunc func(*ShellOptions)

ShellOptionFunc describes optional argmuments to add to the New call.

func WithModules added in v0.2.0

func WithModules(modules ...string) ShellOptionFunc

WithModules specifies a list of PowerShell modules to import into the shell when it starts

type ShellOptions added in v0.2.0

type ShellOptions struct {
	// contains filtered or unexported fields
}

ShellOptions represents options passed to the shell when it is started.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL