functions/public/Invoke-MultiSessionCommand.ps1


<#
.SYNOPSIS
    Executes a script block on multiple remote computers using PSSession.
 
.DESCRIPTION
    This function creates PSSession connections to multiple computers and executes a script block
    in parallel across all sessions. It handles session creation with error checking, command
    execution with throttling, and automatic session cleanup.
     
    Returns an object containing command output and connection error information for failed connections.
 
.PARAMETER ComputerName
    An array of computer names or IP addresses to connect to for command execution.
 
.PARAMETER Credential
    PSCredential object containing the username and password for remote authentication.
 
.PARAMETER ScriptBlock
    The script block to execute on each remote computer.
 
.PARAMETER CommandThrottleLimit
    The maximum number of commands to execute in parallel across sessions.
    Default value is 10.
 
.PARAMETER SessionOption
    Optional PSSessionOption object to customize session connection behavior.
    If not specified, default session options are used.
 
.PARAMETER SessionThrottleLimit
    The maximum number of PSSession connections to establish in parallel during creation.
    Default value is 32.
 
.PARAMETER CleanUpSessions
    Switch to automatically remove all PSSession connections after command execution.
    Default is $true. Set to $false to keep sessions open for reuse.
 
.EXAMPLE
    PS> $cred = Get-Credential
    PS> $computers = @("Server1", "Server2", "Server3")
    PS> $result = Invoke-MultiSessionCommand -ComputerName $computers `
    PS> -Credential $cred -ScriptBlock { Get-Process -Name svchost } -CommandThrottleLimit 5
    PS> $result.CommandOutput
     
    Retrieves svchost processes from three servers in parallel.
 
.EXAMPLE
    PS> $computers = @("Server1", "Server2")
    PS> $result = Invoke-MultiSessionCommand -ComputerName $computers `
    PS> -Credential $cred -ScriptBlock { whoami } -CleanUpSessions $false
    PS> $result.SessionErrorInfo
    PS> $result.CommandErrorInfo
     
    Executes whoami command and keeps sessions open for reuse. Check SessionErrorInfo for connection failures and CommandErrorInfo for command execution errors.
 
.OUTPUTS
    PSCustomObject with the following properties:
    - CommandOutput: Output from the executed script block on all remote computers
    - SessionErrorInfo: Array of connection failures with error details
    - CommandErrorInfo: Any errors encountered during command execution
 
.NOTES
    - Failed connections are tracked in the SessionErrorInfo property of the output
    - Command execution errors are tracked in the CommandErrorInfo property of the output
    - Sessions are cleaned up by default unless CleanUpSessions is set to $false
    - Use SessionOption to customize connection behavior (e.g., SkipCertificateCheck)
    - CommandThrottleLimit controls parallelism during command execution, not session creation
 
.LINK
    Invoke-PSSessionProxy
    New-PSSession
    Invoke-Command
    Remove-PSSession
#>

function Invoke-MultiSessionCommand {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        [string[]]
        $ComputerName,

        [Parameter(Mandatory = $true)]
        [pscredential]
        $Credential,

        [Parameter(Mandatory = $true)]
        [ScriptBlock]$ScriptBlock,

        [int]
        $CommandThrottleLimit = 10,

        [System.Management.Automation.Remoting.PSSessionOption]
        $SessionOption = $null,

        [int]
        $SessionThrottleLimit = 32,

        [bool]
        $CleanUpSessions = $true
    )

    Begin {
        $sessionParameters = @{
            ComputerName = $ComputerName 
            Credential = $Credential
            SessionThrottleLimit = $SessionThrottleLimit
            ErrorVariable = 'sessionError'
            ErrorAction = 'SilentlyContinue'
        }

        if ($SessionOption) { $sessionParameters.Add('SessionOption', $SessionOption)}

        $sessionResults = Invoke-PSSessionProxy @sessionParameters

        $sessions = $sessionResults.Sessions
        $sessionCount = $sessions.Count
        $sessionErrorInfo = $sessionResults.SessionErrorInfo
        
    }

    Process {

        if ($sessionCount -eq 0) {
            Write-Warning "No sessions were created successfully. Skipping command invocation."
            return
        }

        Write-Verbose "Invoking command on $sessionCount sessions with throttle limit of $CommandThrottleLimit..."
        
        $commandParameters = @{
            Session = $sessions
            ScriptBlock = $ScriptBlock
            ThrottleLimit = $CommandThrottleLimit
            ErrorVariable = 'commandError'
        }
        
        $commandOutput = Invoke-Command @commandParameters 
    
    }

    End {

        $output = [PSCustomObject]@{
            CommandOutput = $commandOutput
            SessionErrorInfo = $sessionErrorInfo
            CommandErrorInfo = $commandError
        }

        Write-Output $output
        
        if ($CleanUpSessions -and $sessionCount -gt 0) {
            # Clean up all sessions
            Write-Verbose "Cleaning up sessions..."            
            Remove-PSSession -Session $sessions -ErrorAction $ErrorActionPreference
        }
        
    }
}