Public/Invoke-CIMPowerShell.ps1
function Invoke-CIMPowerShell { [CmdletBinding(DefaultParameterSetName = 'ComputerName')] param ( [Parameter(Mandatory = $false)] $PipeName = ([guid]::NewGuid()).Guid.ToString(), [Parameter(Mandatory = $true)] [scriptblock]$ScriptBlock, [Parameter(Mandatory = $false)] [string[]]$FunctionsToLoad, [Parameter(Mandatory = $false)] [ValidateRange(1000, 900000)] [int32]$Timeout = 120000, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'CimSession')] [Microsoft.Management.Infrastructure.CimSession[]]$CimSession, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ComputerName')] [Alias('Connection', 'PSComputerName', 'PSConnectionName', 'IPAddress', 'ServerName', 'HostName', 'DNSHostName')] [string[]]$ComputerName = $env:ComputerName ) begin { $invokeCIMPowerShellSplat = @{ ClassName = 'Win32_Process' MethodName = 'Create' } $SupportFunctions = Convert-FunctionToString -FunctionToConvert 'ConvertTo-CliXml', 'ConvertTo-Base64StringFromObject' $HelperFunctions = switch ($PSBoundParameters.ContainsKey('FunctionsToLoad')) { $true { Convert-FunctionToString -FunctionToConvert $FunctionsToLoad } } $ScriptBlockString = [string]::Format(@' {0} $namedPipe = New-Object System.IO.Pipes.NamedPipeServerStream "{1}", "Out" $namedPipe.WaitForConnection() $streamWriter = New-Object System.IO.StreamWriter $namedPipe $streamWriter.AutoFlush = $true $TempResultPreConversion = & {{ {2} {3} }} $results = ConvertTo-Base64StringFromObject -inputObject $TempResultPreConversion $streamWriter.WriteLine("$($results)") $streamWriter.dispose() $namedPipe.dispose() '@ , $SupportFunctions, $PipeName, $HelperFunctions, $ScriptBlock) $scriptBlockPreEncoded = [scriptblock]::Create($ScriptBlockString) $byteCommand = [System.Text.encoding]::UTF8.GetBytes($scriptBlockPreEncoded) $encodedScriptBlock = [convert]::ToBase64string($byteCommand) } process { foreach ($Connection in (Get-Variable -Name $PSCmdlet.ParameterSetName -ValueOnly -Scope Local)) { $Computer = switch ($PSCmdlet.ParameterSetName) { 'ComputerName' { Write-Output -InputObject $Connection switch ($Connection -eq $env:ComputerName) { $false { if ($ExistingCimSession = Get-CimSession -ComputerName $Connection -ErrorAction Ignore) { Write-Verbose "Active CimSession found for $Connection - Passing CimSession to CIM cmdlets" $invokeCIMPowerShellSplat.Remove('ComputerName') $invokeCIMPowerShellSplat['CimSession'] = $ExistingCimSession } else { Write-Verbose "No active CimSession found for $Connection - falling back to -ComputerName parameter for CIM cmdlets" $invokeCIMPowerShellSplat.Remove('CimSession') $invokeCIMPowerShellSplat['ComputerName'] = $Connection } } $true { $invokeCIMPowerShellSplat.Remove('CimSession') $invokeCIMPowerShellSplat.Remove('ComputerName') Write-Verbose 'Local computer is being queried - skipping computername, and cimsession parameter' } } } 'CimSession' { Write-Verbose "Active CimSession found for $Connection - Passing CimSession to CIM cmdlets" Write-Output -InputObject $Connection.ComputerName $invokeCIMPowerShellSplat.Remove('ComputerName') $invokeCIMPowerShellSplat['CimSession'] = $Connection } } $invokeCIMPowerShellSplat['Arguments'] = @{ CommandLine = [string]::Format("powershell.exe (invoke-command ([scriptblock]::Create([system.text.encoding]::UTF8.GetString([System.convert]::FromBase64string('{0}')))))", $encodedScriptBlock) } $null = Invoke-CimMethod @invokeCIMPowerShellSplat $namedPipe = New-Object System.IO.Pipes.NamedPipeClientStream $Computer, "$($PipeName)", "In" $namedPipe.Connect($timeout) $streamReader = New-Object System.IO.StreamReader $namedPipe while ($null -ne ($data = $streamReader.ReadLine())) { $tempData = $data } $streamReader.dispose() $namedPipe.dispose() if (-not [string]::IsNullOrWhiteSpace($tempData)) { ConvertFrom-Base64ToObject -inputString $tempData } } } } |