PSAlternativeRemoting.psm1
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # + Library - Enum # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Enum ARClass { ARCimSession = 1 } Enum ARSessionType { CIM = 1 } # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # + Library - Class # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class ARCimSession { [System.String]$Computername [pscredential]$Credential [CimSession]$CimSession [System.Void]Create([System.String]$ComputerName, [System.Management.Automation.PSCredential]$Credential) { $this.ComputerName = $ComputerName $this.Credential = $Credential $WSMan = Test-WSMan -ComputerName $this.ComputerName -ErrorAction SilentlyContinue If (($WSMan -ne $null) -and ($WSMan.ProductVersion -match 'Stack: ([3-9]|[1-9][0-9]+)\.[0-9]+')) { $splats = @{ Credential = $Credential ComputerName = $ComputerName SessionOption = (New-CimSessionOption -Protocol Wsman) } } else { $splats = @{ Credential = $this.Credential ComputerName = $this.ComputerName SessionOption = (New-CimSessionOption -Protocol Dcom) } } $this.CimSession = New-CimSession @splats } [System.Boolean] Test() { if ($this.CimSession) { if (Get-CimSession -Id $this.CimSession.Id -ErrorAction SilentlyContinue) { return $true } else { return $false } } else { return $false } } [System.Void] Remove() { if ($this.CimSession) { Remove-CimSession -Id $this.CimSession.Id } } [System.Void] Execute([System.String]$encodedScriptBlock) { if ($this.Test() -eq $true) { $splats = @{ CimSession = $this.CimSession ClassName = "Win32_Process" MethodName = "Create" Arguments = @{ CommandLine = "powershell.exe (invoke-command ([scriptblock]::Create([system.text.encoding]::UTF8.GetString([System.convert]::FromBase64string('$($encodedScriptBlock)')))))" } } Invoke-CimMethod @splats } { throw 'Please create a CIM session before running a command' } } } # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # + Function - Private # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ function ConvertFrom-ARBase64ToObject { [CmdletBinding()] param ( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [Alias('string')] [System.String]$inputString ) $data = [System.convert]::FromBase64String($inputString) $memoryStream = [System.Io.MemoryStream]::new() $memoryStream.write($data, 0, $data.length) $memoryStream.seek(0, 0) | Out-Null $streamReader = [System.IO.StreamReader]::new([System.IO.Compression.GZipStream]::new($memoryStream, [System.IO.Compression.CompressionMode]::Decompress)) $decompressedData = ConvertFrom-ARCliXml ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($($streamReader.readtoend())))) return $decompressedData } function ConvertFrom-ARCliXml { param ( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [String[]]$InputObject ) begin { $OFS = "`n" [String]$xmlString = "" } process { $xmlString += $InputObject } end { $type = [PSObject].Assembly.GetType('System.Management.Automation.Deserializer') $ctor = $type.GetConstructor('instance,nonpublic', $null, @([xml.xmlreader]), $null) $sr = New-Object System.IO.StringReader $xmlString $xr = New-Object System.Xml.XmlTextReader $sr $deserializer = $ctor.Invoke($xr) $done = $type.GetMethod('Done', [System.Reflection.BindingFlags]'nonpublic,instance') while (!$type.InvokeMember("Done", "InvokeMethod,NonPublic,Instance", $null, $deserializer, @())) { try { $type.InvokeMember("Deserialize", "InvokeMethod,NonPublic,Instance", $null, $deserializer, @()) } catch { Write-Warning "Could not deserialize ${string}: $_" } } $xr.Close() $sr.Dispose() } } function ConvertTo-ARBase64StringFromObject { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [Alias('object', 'data', 'input')] [psobject]$inputObject ) $tempString = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes([management.automation.psserializer]::Serialize($inputObject))) $memoryStream = [System.IO.MemoryStream]::new() $compressionStream = [System.IO.Compression.GZipStream]::new($memoryStream, [System.io.compression.compressionmode]::Compress) $streamWriter = [System.IO.streamwriter]::new($compressionStream) $streamWriter.write($tempString) $streamWriter.close() $compressedData = [System.convert]::ToBase64String($memoryStream.ToArray()) return $compressedData } function ConvertTo-ARCliXml { param ( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [PSObject[]]$InputObject ) return [management.automation.psserializer]::Serialize($InputObject) } function Get-ARClass { [CmdletBinding()] [OutputType('System.Object')] Param( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ARClass] $Name, [Parameter( Position = 1, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [System.Boolean] $Cache = $true ) begin { $functionName = $MyInvocation.MyCommand.Name Write-Verbose "[${functionName}] Function started" } process { if ($Cache -eq $true) { $classObject = Get-Variable -Name $Name -Scope 'Script' -ValueOnly -ErrorAction SilentlyContinue if ($null -eq $classObject) { $classObject = New-ARClass -Name $Name Set-Variable -Name $Name -Value $classObject -Scope 'Script' } return $classObject } else { $classObject = New-ARClass -Name $Name return $classObject } } end { Write-Verbose "[${functionName}] Complete" } } function New-ARClass { [CmdletBinding()] [OutputType('System.Object')] Param( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ARClass] $Name ) begin { $functionName = $MyInvocation.MyCommand.Name Write-Verbose "[${functionName}] Function started" } process { $classObject = New-Object -TypeName $Name return $classObject } end { Write-Verbose "[${functionName}] Complete" } } function New-ARScriptBlockPreEncoded { [CmdletBinding()] [OutputType('System.String')] Param( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [System.Guid]$pipename, [Parameter( Position = 1, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [System.Management.Automation.ScriptBlock]$ScriptBlock ) begin { $functionName = $MyInvocation.MyCommand.Name Write-Verbose "[${functionName}] Function started" } process { $scriptBlockPreEncoded = [scriptblock]{ #region support functions function ConvertTo-ARCliXml { param ( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [PSObject[]]$InputObject ) return [management.automation.psserializer]::Serialize($InputObject) } function ConvertTo-ARBase64StringFromObject { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [Alias('object', 'data', 'input')] [psobject]$inputObject ) $tempString = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes([management.automation.psserializer]::Serialize($inputObject))) $memoryStream = [System.IO.MemoryStream]::new() $compressionStream = [System.IO.Compression.GZipStream]::new($memoryStream, [System.io.compression.compressionmode]::Compress) $streamWriter = [System.IO.streamwriter]::new($compressionStream) $streamWriter.write($tempString) $streamWriter.close() $compressedData = [System.convert]::ToBase64String($memoryStream.ToArray()) return $compressedData } #endregion #region open pipe server to send result $namedPipe = [System.IO.Pipes.NamedPipeServerStream]::new("<pipename>", "Out") $namedPipe.WaitForConnection() $streamWriter = [System.IO.StreamWriter]::new($namedPipe) $streamWriter.AutoFlush = $true $TempResultPreConversion = Invoke-Command -ScriptBlock { <scriptBlock> } -ErrorAction Stop $results = ConvertTo-ARBase64StringFromObject -inputObject $TempResultPreConversion $streamWriter.WriteLine("$($results)") $streamWriter.dispose() $namedPipe.dispose() #endregion } $scriptBlockPreEncoded = $scriptBlockPreEncoded -replace "<pipename>", $PipeName $scriptBlockPreEncoded = $scriptBlockPreEncoded -replace "<scriptBlock>", $ScriptBlock $byteCommand = [System.Text.encoding]::UTF8.GetBytes($scriptBlockPreEncoded) $encodedScriptBlock = [convert]::ToBase64string($byteCommand) return $encodedScriptBlock } end { Write-Verbose "[${functionName}] Complete" } } # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # + Function - Public # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ function Connect-ARSession { [CmdletBinding()] [OutputType('System.Void')] Param( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [System.String]$ComputerName, [Parameter( Position = 1, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential]$Credential ) begin { $functionName = $MyInvocation.MyCommand.Name Write-Verbose "[${functionName}] Function started" } process { $classObject = Get-ARClass -Name "ARCimSession" $classObject.Create($ComputerName, $Credential) } end { Write-Verbose "[${functionName}] Complete" } } function Disconnect-ARSession { [CmdletBinding()] [OutputType('System.Void')] Param() begin { $functionName = $MyInvocation.MyCommand.Name Write-Verbose "[${functionName}] Function started" } process { $classObject = Get-ARClass -Name "ARCimSession" $classObject.Remove() } end { Write-Verbose "[${functionName}] Complete" } } function Invoke-ARCommand { [CmdletBinding()] [OutputType('System.Void')] Param( [ValidateNotNullOrEmpty()] [Parameter( Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [System.Guid]$PipeName = [System.Guid]::NewGuid(), [Parameter( Position = 1, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [System.Management.Automation.ScriptBlock]$ScriptBlock, [Parameter( Position = 4, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [ValidateRange(1000, 900000)] [System.UInt32]$Timeout = 120000 ) begin { $functionName = $MyInvocation.MyCommand.Name Write-Verbose "[${functionName}] Function started" } process { $classObject = Get-ARClass -Name "ARCimSession" $encodedScriptBlock = New-ARScriptBlockPreEncoded -pipename $PipeName -ScriptBlock $ScriptBlock $classObject.Execute($encodedScriptBlock) $namedPipe = New-Object System.IO.Pipes.NamedPipeClientStream $classObject.Computername, "$($PipeName)", "In" $namedPipe.connect($timeout) $streamReader = New-Object System.IO.StreamReader $namedPipe while ($null -ne ($data = $streamReader.ReadLine())) { $tempData = $data } $streamReader.dispose() $namedPipe.dispose() ConvertFrom-ARBase64ToObject -inputString $tempData } end { Write-Verbose "[${functionName}] Complete" } } |