PowerShell Reusable Sessions: A Guide to Persistent Remote Connections

Published:2 January 2025 - 4 min. read

Managing remote systems can quickly become a headache, especially when running multiple commands across different machines. The constant back-and-forth of reconnecting to systems can waste time and slow down your workflow. Sound familiar? Don’t worry, there’s a better way!

In this guide, you’ll learn to use reusable PowerShell remoting sessions to make managing remote systems faster, easier, and more efficient.

Ready to level up your PowerShell remoting skills? Let’s dive in!

Creating and Using a Session

When working with remote systems, one of the most tedious tasks can be reconnecting to them each time you run a command. In such cases, reusable sessions come into play.

Creating a persistent session maintains a continuous connection. Doing so eliminates the need to connect and disconnect as you execute multiple commands repeatedly.

To start, you’ll need to create a reusable session.

The New-PSSession cmdlet establishes a persistent connection to the remote computer:

$session = New-PSSession -ComputerName SRV2

This session object can be passed to commands like Invoke-Command, allowing you to reuse the same session throughout your script.

For example, you can test the connection by running a basic command:

Invoke-Command -Session $session -ScriptBlock { hostname }

A reusable session also allows you to store and retrieve variables on the remote system:

Invoke-Command -Session $session -ScriptBlock { $foo = 'Please be here next time' }
Invoke-Command -Session $session -ScriptBlock { $foo }

This persistent storage capability is a significant advantage of reusable sessions, enabling you to maintain state across multiple commands.

Adapting the Function for Remote Use

Let’s adapt the inventory function to work with reusable sessions.

First, we add a Session parameter to accept a session object, where the Get-Member cmdlet helps identify the session object type:

$session | Get-Member

After identifying that the session object is of type System.Management.Automation.Runspaces.PSSession, the next step is to define the Get-WmiObjectValue function.

This function uses the session parameter to enable remote execution, allowing it to run a script block on the remote system and retrieve a specific WMI property.

function Get-WmiObjectValue {
    [CmdletBinding()]
    param(
        # Specify the name of the WMI property to query
        [Parameter(Mandatory)]
        [string]$PropertyName,

        # Specify the name of the WMI class to query
        [Parameter(Mandatory)]
        [string]$WmiClassName,

        # Specify the remote session object to use for invoking the command
        [Parameter(Mandatory)]
        [System.Management.Automation.Runspaces.PSSession]$Session
    )

    # Define the script block to execute on the remote machine
    $scriptBlock = {
        # Get the sum of the specified property from the WMI class
        $number = (Get-CimInstance -ClassName $using:WmiClassName | Measure-Object -Property $using:PropertyName -Sum).Sum

        # Convert the sum to gigabytes
        $numberGb = $number / 1GB

        # Round the result to 2 decimal places
        [math]::Round($numberGb, 2)
    }

    # Execute the script block on the remote machine using the provided session
    Invoke-Command -Session $Session -Scriptblock $scriptBlock
}

Let’s try out this function and see what happens.

Get-WmiObjectValue -PropertyName Capacity -WmiClassName Win32_PhysicalMemory -Session $session

You’ll run into this error at some point. But no worries. Read on, and you’ll learn how to address this error.

Addressing Authentication Issues

If you encounter “Access Denied” errors, it’s likely due to insufficient permissions. By default, reusable sessions inherit the credentials used to create them.

Check the currently authenticated user’s username on the system:

whoami

If the domain user, called user, doesn’t have permission to query WMI on the remote computer, elevate the Invoke-Command to use an admin account.

First, create a credential object with the Get-Credential cmdlet to specify alternate credentials when creating the session:

$adminCred = Get-Credential -UserName adam

Once authenticated, test the session:

Invoke-Command -Session $session -Scriptblock {hostname} -Credential $adminCred

This test ensures the session works as expected before running your inventory function.

But if the session didn’t work, it was already created with incorrect credentials. If so, you must remove the current session:

Remove-PSSession -Session $session

The Remove-PSSession cmdlet removes the session on both the local and remote computers.

With the old session removed, create another new session. But this time, create the session authenticated as the admin user:

$session = New-PSSession -ComputerName SRV2 -Credential $adminCred

Try the Invoke-Command again using the new session and see what happens.

Invoke-Command -Session $session -Scriptblock {hostname} -Credential $adminCred

Now that you’re confident the new session works, let’s try the function again.

Get-WmiObjectValue -PropertyName Capacity -WmiClassName Win32_PhysicalMemory -Session $session

Bringing it All Together

Hard work is over, and it’s time to combine everything in one script.

This final script gathers inventory information, displays the results, and cleans up the session:

function Get-WmiObjectValue {
    [CmdletBinding()]
    param(
        # Specify the name of the WMI property to query
        [Parameter(Mandatory)]
        [string]$PropertyName,

        # Specify the name of the WMI class to query
        [Parameter(Mandatory)]
        [string]$WmiClassName,

        # Specify the remote session object to use for invoking the command
        [Parameter(Mandatory)]
        [System.Management.Automation.Runspaces.PSSession]$Session
    )

    # Define the script block to execute on the remote machine
    $scriptBlock = {
        # Get the sum of the specified property from the WMI class
        $number = (Get-CimInstance -ClassName $using:WmiClassName | Measure-Object -Property $using:PropertyName -Sum).Sum

        # Convert the sum to gigabytes
        $numberGb = $number / 1GB

        # Round the result to 2 decimal places
        [math]::Round($numberGb, 2)
    }

    # Execute the script block on the remote machine using the provided session
    Invoke-Command -Session $Session -Scriptblock $scriptBlock
}

## Grab the alternate credential: Get-CimInstance will work on the remote computer
$adminCred = Get-Credential -UserName adam

## Create a session authenticating as the adam (admin) user
$session = New-PSSession -ComputerName SRV2 -Credential $adminCred

## Find the total memory and total volume storage space on the remote computer
$totalMemoryGb = Get-WmiObjectValue -PropertyName Capacity -WmiClassName Win32_PhysicalMemory -Session $session
$totalStorageGb = Get-WmiObjectValue -PropertyName FreeSpace -WmiClassName Win32_LogicalDisk -Session $session

Write-Host "The computer $($session.ComputerName) has $totalMemoryGb GB of memory and $totalStorageGb GB of free space across all volumes."

## Remove the shared session
Remove-PSSession -Session $session

Save and run the script (Get-InventoryInfo.ps1) to verify that it works as intended.

Conclusion

In this tutorial, you’ve learned to build a robust function that uses reusable sessions to manage remote computers effectively. You now know how to maintain a persistent connection across multiple commands and execute complex tasks on remote systems without disconnecting each time.

Moving forward, you can build on this knowledge by incorporating reusable sessions into larger automation workflows. One example would be expanding the inventory function to gather additional system metrics or automate maintenance tasks on multiple remote machines.

Combine reusable sessions with other PowerShell features. Streamline your IT management and automation efforts to save time and reduce manual intervention.

Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

Explore ATA Guidebooks

Looks like you're offline!