Write-RemoteDataCollector.ps1
function Write-RemoteDataCollector { <# .Synopsis Writes a function that collects information from several machines using fan-out remoting .Description Writes a function that collects information from several machines using fan-out remoting .Example Write-RemoteDataCollector -Name 'Get-ComputerSystem' -ScriptBlock { Get-WmiObject Win32_ComputerSystem } .Link about_Remote_Faq #> param( # The name of the function to create [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true)] [string] $Name, # A ScriptBlock of information to collect from the remote machines [Parameter(Mandatory=$true,Position=1)] [ScriptBlock] $ScriptBlock, # A description of the task [Parameter(ValueFromPipelineByPropertyName=$true)] [string] $Description ) begin { # Get the command metadata for Invoke-Command $invokeCommandMetaData = [Management.Automation.CommandMetadata](Get-Command Invoke-Command) # Remove the parameters that do not exist in the parameter sets we are interested in, # and a number of other parameters related to these items $toRemove = @($invokeCommandMetaData.Parameters.Keys | Where-Object { $keys = $invokeCommandMetaData.Parameters[$_].ParameterSets | Select-Object -ExpandProperty Keys $keys -notcontains 'Session' -and $keys -notcontains 'ComputerName' -and $keys -notcontains '__AllParameterSets' }) + 'ScriptBlock', 'AsJob', 'HideComputerName', 'InputObject', 'ArgumentList', 'JobName', 'ThrottleLimit' foreach ($tr in $toRemove) { $null = $invokeCommandMetaData.Parameters.Remove($tr) } # Make all of the remaining parameters ValueFromPipelineByPropertyName $invokeCommandMetaData.Parameters.Values | ForEach-Object { $_.ParameterSets.Values } | ForEach-Object { $_.ValueFromPipelineByPropertyName = $true } # Remove the parameter sets that no longer exist from the parameters that still have them $invokeCommandMetaData.Parameters.Values | ForEach-Object { $null = $_.ParameterSets.Remove("FilePathComputerName") $null = $_.ParameterSets.Remove("FilePathRunspace") $null = $_.ParameterSets.Remove("FilePathUri") $null = $_.ParameterSets.Remove("Uri") } # Make the ComputerName parameter ValueFromPipeline and Position 1 (-not position 0) $invokeCommandMetaData.Parameters["ComputerName"].ParameterSets.Values | ForEach-Object { $_.ValueFromPipeline = $true $_.Position = 1 } # Make the Session parameter position 1 $invokeCommandMetaData.Parameters["Session"].ParameterSets.Values | ForEach-Object { $_.Position = 1 } # Create a parameter block $parameterBlock = [Management.Automation.ProxyCommand]::GetParamBlock($invokeCommandMetaData) # Create a process block $processBlock = { $params = @{ ScriptBlock = $scriptBlock } + $psBoundParameters $null = $in.AddLast($params) } # Create an end block $endBlock ={ if ($psCmdlet.ParameterSetName -eq "Local") { if ($activity) { Write-Progress "Starting $Activity" " " } & $scriptBlock if ($activity) { Write-Progress "$Activity Completed" " " } } else { $jobs = @() foreach ($i in $in) { $jobs += Invoke-Command @i -AsJob } $runningJobs = $jobs | Where-Object { $_.State -eq "Running" } while ($runningJobs) { $runningJobs = @($jobs | Where-Object { $_.State -eq "Running" }) $jobs | Wait-Job -Timeout 1 | Out-Null $percent = 100 - ($runningJobs.Count * 100 / $jobs.Count) if ($activity) { Write-Progress "Waiting for $Activity to Complete" "$($Jobs.COunt - $runningJobs.Count) out of $($Jobs.Count) Completed" -PercentComplete $percent } else { Write-Progress "Waiting for Remote Execution to Complete" "$($Jobs.COunt - $runningJobs.Count) out of $($Jobs.Count) Completed" -PercentComplete $percent } } $jobs | Receive-Job } } } process { if (-not $description) { $description = $Name } # Only the Begin block changes, and only slightly $beginBlock = @" `$in = New-Object Collections.Generic.LinkedList[Hashtable] `$activity = "$description" `$scriptBlock = { $ScriptBlock } "@ @" function ${Name} { <# .Synopsis $Description .Description $Description .Example ${Name} #> [CmdletBinding(DefaultParameterSetName="Local")] param( $parameterBlock ) begin { $beginBlock } process { $processBlock } end { $endBlock } } "@ } } |