Last active
December 19, 2023 13:27
-
-
Save MVKozlov/607a7ae0faa8a6390b6b32595776c79c to your computer and use it in GitHub Desktop.
Run selected script for computer list and save results in a file. Can be used multiple times until all collected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Run selected script for computer list and save results in a file. Can be used multiple times until all collected | |
.DESCRIPTION | |
Run the specified script for the list of computers and save the launch results to the Clixml file | |
The result file contains the script being launched (Script) and the results of previous executions (Data). | |
Restarting works only for computers with no success | |
If the file has already been created, only the path to the file is needed for repeated runs | |
PoshRSJob module required for parallel script execution | |
.PARAMETER Path | |
Path to the file with the results | |
.PARAMETER Script | |
The script to run. The computer name is passed to the script | |
.PARAMETER ComputerList | |
List of processed computers. Adds to an already existing list | |
.PARAMETER ArgumentHash | |
Hash of @{ComputerName=ArgumentList} additional arguments for every computer. Adds to an already existing hash | |
.PARAMETER Force | |
Overwrite the computer in the list if it already exists there (and therefore overrun the script) | |
.PARAMETER Remove | |
Remove the listed computers from the results | |
.PARAMETER RunFilter | |
Restart the script only for computers whose name falls under the filter | |
.PARAMETER MaxConcurrentJob | |
The number of simultaneously executed copies of the script | |
.PARAMETER PingTimeout | |
Ping timeout | |
.EXAMPLE | |
# Collect (first): | |
$c = Get-ADComputer -SearchBase 'ou=SOME_OU,dc=domain, dc=local' -Filter * | Where-Object { $_.Enabled } | |
Invoke-DataCollector.ps1 -Path d:\diskdrivesdata.xml -ComputerList $c.Name -Script ` | |
{ param($comp) $cim = New-CimSession $comp; if ($cim) { Get-Disk -CimSession $cim } else { throw "Can't create cim session " } } | |
# Repeat periodically until all results are successful (until all data is collected) | |
Invoke-DataCollector.ps1 -Path d:\diskdrivesdata.xml | |
# Export and use it: | |
$data = Import-Clixml D:\diskdrivesdata.xml | |
$mydata = $data.Data.Values | Where-Object { $_.Status -eq 'Success' } | Select-Object -Expand $_.Data | |
$mydata | Export-Csv -Delimiter ';' -Encoding UTF8 -NoTypeInformation -Path d:\diskdrivesdata.csv | |
.NOTES | |
Requires Posh-RSJob module | |
Author: Max Kozlov | |
.LINK | |
https://gist.github.com/MVKozlov/607a7ae0faa8a6390b6b32595776c79c | |
#> | |
#require Posh-RSJob | |
[CmdletBinding(DefaultParameterSetName='collect', SupportsShouldProcess=$true, ConfirmImpact='High')] | |
param( | |
[Parameter(Position=0, Mandatory = $true)] | |
[string]$Path, | |
[Parameter(ParameterSetName = 'collect')] | |
[scriptblock]$Script, | |
[Parameter(ParameterSetName = 'collect')] | |
[Parameter(ParameterSetName = 'remove', Mandatory = $true)] | |
[string[]]$ComputerList, | |
[Parameter(ParameterSetName = 'collect')] | |
[hashtable]$ArgumentHash = @{}, | |
[Parameter(ParameterSetName = 'collect')] | |
[string]$RunFilter, | |
[Parameter(ParameterSetName = 'collect')] | |
[switch]$Force, | |
[Parameter(ParameterSetName = 'collect')] | |
[int]$MaxConcurrentJob = 10, | |
[Parameter(ParameterSetName = 'collect')] | |
[int]$PingTimeout = 500, | |
[Parameter(ParameterSetName = 'remove')] | |
[switch]$Remove | |
) | |
function Ping-ComputerAsync { | |
param( | |
[Alias('Name', 'FullDomainName')] | |
[string[]][Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=0)]$ComputerName, | |
[int]$Timeout = 200, | |
[switch]$Quiet | |
) | |
BEGIN { | |
$wt1 = 0; $wt2 = 0 | |
[System.Threading.ThreadPool]::GetMinThreads([ref]$wt1, [ref]$wt2) | |
[void][System.Threading.ThreadPool]::SetMinThreads(256, $wt2) | |
$Tasks = {}.Invoke() | |
} | |
PROCESS { | |
foreach ($comp in $ComputerName) { | |
# AD Workaround | |
$comp = $comp -replace '\$$' | |
Write-Verbose ('Testing {0}' -f $comp) | |
$Tasks.Add([PSCustomObject] @{ | |
ComputerName = $comp | |
Task = (New-Object System.Net.NetworkInformation.Ping).SendPingAsync($comp, $Timeout) | |
}) | |
} | |
} | |
END { | |
try { | |
[Threading.Tasks.Task]::WaitAll($Tasks.Task) | |
} | |
catch {} | |
[void][System.Threading.ThreadPool]::SetMinThreads($wt1, $wt2) | |
$Tasks | Foreach-Object { | |
$o = New-Object -TypeName PSObject -Property @{ ComputerName = $_.ComputerName } | |
#$o | Add-Member -MemberType AliasProperty -Name Name -Value ComputerName | |
$o | Add-Member -Name "Status" -MemberType NoteProperty -Value "NotFound" -PassThru | | |
Add-Member -Name "Address" -MemberType NoteProperty -Value "" -PassThru | | |
Add-Member -Name "Name" -MemberType AliasProperty -Value ComputerName -PassThru | | |
Add-Member -Name "PSComputerName" -MemberType AliasProperty -Value ComputerName -PassThru | | |
Add-Member -Name "RoundtripTime" -MemberType NoteProperty -Value -1 | |
if ($_.Task.IsFaulted) { | |
#$o.Status = $_.Task.Exception.InnerException.InnerException.Message | |
} | |
else { | |
$o.Status = $_.Task.Result.Status; $o.Address=$_.Task.Result.Address; $o.RoundtripTime = $_.Task.Result.RoundtripTime | |
} | |
if ($Quiet) { if ($o.Status -eq 'Success') { $o.ComputerName } } | |
else { $o } | |
} | |
} | |
} | |
try { | |
$data = Import-Clixml $Path | |
Write-Host "Data readed from $Path" -ForegroundColor Cyan | |
$Script = [scriptblock]::Create($data.Script) | |
} | |
catch { | |
Write-Host $_ -ForegroundColor Red | |
Write-Host "Creating new data" -ForegroundColor Cyan | |
$data = [PSCustomObject]@{ | |
Script = $Script | |
Data = @{} | |
} | |
} | |
if ($PSCmdlet.ParameterSetName -eq 'remove' -and $ComputerList) { | |
foreach ($n in $ComputerList) { | |
if ($PSCmdlet.ShouldProcess($n, "Remove from Data file" )) { | |
Write-Host "Remove computer $n" | |
$data.Data.Remove($n) | |
} | |
} | |
$data | Export-Clixml -Path $Path | |
Write-Host "Data saved to $Path" -ForegroundColor Cyan | |
return | |
} | |
if ($null -eq $Script) { | |
throw "Script to run is mandatory on first launch" | |
} | |
if ($ComputerList) { | |
foreach ($ComputerName in $ComputerList) { | |
if (!$data.Data.ContainsKey($ComputerName) -or $Force) { | |
$data.Data[$ComputerName] = [PSCustomObject]@{ | |
ComputerName = $ComputerName | |
Status = 'Unknown' | |
Arguments = @() | |
Data = $null | |
} | |
} | |
} | |
} | |
if ($ArgumentHash) { | |
foreach ($arg in $ArgumentHash.GetEnumerator()) { | |
if ($data.Data.ContainsKey($arg.Key) -and (-not $data.Data[$arg.key].Arguments -or $Force)) { | |
$data.Data[$arg.key].Arguments = [array]$arg.Value | |
} | |
} | |
} | |
if ($RunFilter) { | |
Write-Host "RunFilter: $RunFilter" -ForegroundColor Cyan | |
} | |
$batch = (Split-Path Path) -replace '\.*$' | |
[array]$comps = $data.Data.GetEnumerator() | Where-Object { $_.Value.Status -ne 'Success' } | | |
Where-Object { -not $RunFilter -or $_.Key -match $RunFilter } | | |
ForEach-Object { $_.Key } | |
$avail = $null | |
if ($comps) { | |
Write-Host "Check $($comps.Count) computer(s) for availability" -ForegroundColor Cyan | |
$comps = $comps | Ping-ComputerAsync -Timeout $PingTimeout | |
$na = $comps | Where-Object { $_.Status -ne 'Success'} | |
if ($na) { | |
$data.Data[$na.ComputerName] | | |
ForEach-Object { | |
Write-Host "Skip $($_.ComputerName) because it not available" -ForegroundColor Gray | |
$_.Status = 'Not Available' | |
} | |
} | |
$avail = $comps | Where-Object { $_.Status -eq 'Success'} | |
} | |
if ($avail) { | |
$jobs = $data.Data[$avail.ComputerName] | | |
ForEach-Object { | |
Write-Host "Start script job for $($_.ComputerName)" -ForegroundColor DarkGreen | |
$argumentList = ,$_.ComputerName + $_.Arguments | |
Start-RSJob -ScriptBlock $Script -ArgumentList $argumentList -Throttle $MaxConcurrentJob -Batch $batch -Name $_.ComputerName | |
} | |
Write-Host "Waiting for Jobs" | |
$jobs | Wait-RSJob | ForEach-Object { | |
if ($_.HasErrors) { | |
Write-Host "Save Error to $($_.Name) - $($_.Error)" -ForegroundColor Red | |
$data.Data[$_.Name].Data = $_.Error | |
$data.Data[$_.Name].Status = 'Error' | |
} | |
else { | |
Write-Host "Save Success to $($_.Name)" -ForegroundColor Green | |
$data.Data[$_.Name].Data = $_ | Receive-RSJob | |
$data.Data[$_.Name].Status = 'Success' | |
} | |
$_ | Remove-RSJob | |
} | |
} | |
$data | Export-Clixml -Path $Path | |
Write-Host "Data saved to $Path" -ForegroundColor Cyan | |
$data.Data.GetEnumerator() | Group-Object { $_.Value.Status } -NoElement | Out-Host |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment