BetterPowerShellClient 1.0.2

dotnet add package BetterPowerShellClient --version 1.0.2
NuGet\Install-Package BetterPowerShellClient -Version 1.0.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BetterPowerShellClient" Version="1.0.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add BetterPowerShellClient --version 1.0.2
#r "nuget: BetterPowerShellClient, 1.0.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install BetterPowerShellClient as a Cake Addin
#addin nuget:?package=BetterPowerShellClient&version=1.0.2

// Install BetterPowerShellClient as a Cake Tool
#tool nuget:?package=BetterPowerShellClient&version=1.0.2

Better PowerShell Client

... is a simple easy to use .NET API for interacting with PowerShell, it handles subtleties for you, has overrides for getting back strong typed objects from PowerShell commands and scripts, and can execute code locally and remotely (which can be useful to manage remote machines, and unzip files down to them, etc.).

Note that for the following code samples the documentation will be using the synchronous methods, but all of them have async variants with the appropriate suffix.

Creating a Connection

To create a connected (to the local machine) client object, that writes output to the Console, use the following code:

using (var client = new PSClient(PSConnectionInfo.CreateLocalConnection()))
{
    client.ConfigureNonInteractiveConsoleHost();    

    // Client is connected and configured to write output to the console.

}

Note that ConfigureNonInteractiveSilentHost is also an option, as is manually configuring the UI Host.

Additionally we could instead call the default constructor with no parameters, and perform the connection manually using client.Open(cxnNfo); or await client.OpenAsync(cxnNfo); commands (i.e. if we wanted to defer the connection, or perform it asychronously).

It is important to create this object in a using block though, or to call client.Dispose in a finally block, as this will close the connection and free up any resources associated with it.

Simple Command Invocation

With a connected and configured client object, you can execute PowerShell scripts easily:

client.InvokeScript("Write-Host 'Hello, World' -ForegroundColor Yellow -NoNewLine");

You can also invoke commands, with strong typed parameters:

client.InvokeCommand
(
    // Command:
    "Write-Host",
    new
    {
        // Parameters:
        Object = "Hello, World",
        ForegroundColor = ConsoleColor.Cyan
    },
    // Flags
    "NoNewLine"
);

Getting Results Back

You can read back values from your commands, like so:

int result = client.InvokeScript<int>("5 * 24").Single();
Console.WriteLine(result); // 120

When we call one of these generic methods, we're signalling to PowerShell to hand us all of the objects that were on the stack that match the given type.

If we don't specify a generic method, we're telling PowerShell to consume the items that were left on the stack (which is what it does by default). When this happens, it will write those values out to the configured UI Host.

For example, the following code will consume the stack of FileSystemInfo objects handed back by the dir command and return them to your C# code as an ICollection<FileSystemInfo>:

var files = client.InvokeCommand<FileSystemInfo>("dir");

Whereas, the following code (which calls the exact same PowerShell command), does not specify a generic type, so it leaves the items on the stack, and they will be written to the configured UI host.

client.InvokeCommand("dir");

PowerShell File System Operations

There's a whole set of file system operations tucked away into the FileSystem object, which can be super useful if you're operating against a remote machine (but they will work with any connection, even to the local machine), in this example, we'll push down a text file from an array of bytes:

client.FileSystem.PutFile
(
    @"c:\path\to\file.txt", 
    Encoding.UTF8.GetBytes("Hello, World!")
);
Unziping Files

You can even unzip files to a connected PowerShell client.

using (var ms = new MemoryStream(/* ... get zip data somehow ... */))
using (var zipFile = new System.IO.Compression.ZipArchive(ms))
{
    client.FileSystem.UnzipTo
    (
        zipFile, 
        @"C:\path\to\unzip\"
    );
}

Please Note: the above code won't work as-is; you'll have to actually supply a zip file for that! 😉

Remote Connection Example

Performing remote connections is allowed, and the API is abstracted so that all commands will work the exact same as they do if you are operating locally, be warned though, that connecting to a remote machine requires configuration on both machines (see the troubleshooting below, for a way to test if your connection is valid).

using (var client = new PSClient())
{
    // Perform a remote connection:
    var cxnNfo = PSConnectionInfo.CreateRemoteConnection
    (
        "SomeComputer.westus.cloudapp.azure.com",
        "UserName",
        "Password".ToSecureString(),
        3389
    );
    cxnNfo.UseSecurePowerShell = true;      // use SSL . . .
    cxnNfo.RequireValidCertificate = false; // âš  but don't validate the cert! âš 
    
    client.Open(cxnNfo); // Or await client.OpenAsync(cxnNfo);
    client.ConfigureNonInteractiveConsoleHost();

    // Now, just do stuff with the connection, like normal:
    var files = client.InvokeCommand<FileSystemInfo>
    (
        "Get-ChildItem",
        new
        {
            Path = @"C:\"
        }
    );

    foreach (var file in files)
    {
        Console.WriteLine(file.FullName);
    }
Troubleshooting Remote Connections

If you're unable to make the Remote PowerShell stuff work, you probably have something configured wrong. I recommend testing that your configuration works via the PowerShell ISE, using the following script:

# ==================== Configure Me! ====================
$computerName = "SomeComputer.westus.cloudapp.azure.com";
$port = 3389;
$user = "UserName";
$rawPwd = "My Completely Insecure Password";
# =======================================================

$pwd = ($rawPwd | ConvertTo-SecureString -AsPlainText -Force);

$cred = New-Object -TypeName System.Management.Automation.PSCredential `
    -ArgumentList $user, $pwd;

Enter-PSSession -ComputerName $computerName -Port $port -UseSSL `
    -SessionOption(New-PSsessionOption -SkipCACheck -SkipCNCheck) -Credential $cred;

If you're able to make that work, then you should have no problem connecting via the API.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.2 590 8/23/2020
1.0.1 376 8/23/2020
1.0.0 560 1/9/2020