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.