core/modules/monkeyjob/public/Invoke-MonkeyJob.ps1
# Monkey365 - the PowerShell Cloud Security Tool for Azure and Microsoft 365 (copyright 2022) by Juan Garrido # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. Function Invoke-MonkeyJob{ <# .SYNOPSIS .DESCRIPTION .INPUTS .OUTPUTS .EXAMPLE .NOTES Author : Juan Garrido Twitter : @tr1ana File Name : Invoke-MonkeyJob Version : 1.0 .LINK https://github.com/silverhack/monkey365 #> [cmdletbinding(DefaultParameterSetName='ScriptBlock')] Param ( [Parameter(Mandatory=$True,position=0,ParameterSetName='ScriptBlock')] [System.Management.Automation.ScriptBlock]$ScriptBlock, [Parameter(Mandatory=$True, ParameterSetName = 'Command')] [String]$Command, [Parameter(Mandatory=$false, HelpMessage="arguments")] [Object]$Arguments, [Parameter(Mandatory=$false,ValueFromPipeline=$true)] $InputObject, [Parameter(HelpMessage="Variables to import into runspace")] [Object]$ImportVariables, [Parameter(HelpMessage="runspace")] [System.Management.Automation.Runspaces.RunspacePool]$Runspacepool, [Parameter(HelpMessage="modules to import into sessionState")] [Object]$ImportModules, [Parameter(HelpMessage="commands to import into sessionState")] [Object]$ImportCommands, [Parameter(HelpMessage="commands as AST to import into sessionState")] [Object]$ImportCommandAst, [Parameter(HelpMessage="Startup scripts (*ps1 files) to execute")] [System.Object[]]$StartUpScripts, [Parameter(HelpMessage="Minimum number of runspaces")] [ValidateRange(1,65535)] [int32]$MinThrottle = 1, [Parameter(HelpMessage="Maximum number of runspaces")] [ValidateRange(1,65535)] [int32]$Throttle = 2, [Parameter(HelpMessage="BatchSize")] [int32]$BatchSize = 80, [Parameter(HelpMessage="Pause between batchs in milliseconds")] [int32]$BatchSleep = 0, [Parameter(HelpMessage="Timeout before a thread stops trying to gather the information")] [ValidateRange(1,65535)] [int32]$Timeout = 10, [Parameter(HelpMessage="Increase Sleep Timer in seconds between child objects")] [ValidateRange(1,65535)] [int32]$SleepTimer = 5, [Parameter(HelpMessage="Increase Sleep Timer in seconds between child objects")] [ValidateRange(1,65535)] [int32]$MaxQueue = 10, [Parameter(HelpMessage="ApartmentState of the thread")] [ValidateSet("STA","MTA")] [String]$ApartmentState = "STA", [Parameter(HelpMessage="Reuse runspacePool")] [Switch]$ReuseRunspacePool, [Parameter(Mandatory=$False, HelpMessage='ThrowOnRunspaceOpenError')] [Switch]$ThrowOnRunspaceOpenError ) Begin{ $Verbose = $False; $Debug = $False; $InformationAction = 'SilentlyContinue' If($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters.Verbose){ $Verbose = $True } If($PSBoundParameters.ContainsKey('Debug') -and $PSBoundParameters.Debug){ $DebugPreference = 'Continue' $Debug = $True } If($PSBoundParameters.ContainsKey('InformationAction')){ $InformationAction = $PSBoundParameters['InformationAction'] } If($PSBoundParameters.ContainsKey('ReuseRunspacePool') -and $PSBoundParameters['ReuseRunspacePool'].IsPresent){ $reuseRSP = $True } Else{ $reuseRSP = $false } If (-not $PSBoundParameters.ContainsKey('ThrowOnRunspaceOpenError')) { $ThrowOnRunspaceOpenError = $False } IF( -not $PSBoundParameters.ContainsKey('MaxQueue') ) { $MaxQueue = 3 * $MaxQueue } Else { $MaxQueue = $MaxQueue } if($PSBoundParameters.ContainsKey('Runspacepool') -and $PSBoundParameters['Runspacepool']){ if($Runspacepool.RunspacePoolStateInfo.State -eq [System.Management.Automation.Runspaces.RunspaceState]::BeforeOpen){ #Open runspace Write-Verbose $script:messages.OpenRunspaceMessage $Runspacepool.Open() } } Else{ #Create a new runspacePool $localparams = @{ ImportVariables = $ImportVariables; ImportModules = $ImportModules; ImportCommands = $ImportCommands; ImportCommandsAst = $ImportCommandAst; ApartmentState = $ApartmentState; MinThrottle = $MinThrottle; Throttle = $Throttle; StartUpScripts = $StartUpScripts; ThrowOnRunspaceOpenError = $ThrowOnRunspaceOpenError; Verbose = $Verbose; Debug = $Debug; InformationAction = $InformationAction; } #Get runspace pool $Runspacepool = New-RunspacePool @localparams If($null -ne $Runspacepool -and $Runspacepool -is [System.Management.Automation.Runspaces.RunspacePool]){ #Open runspace Write-Verbose $script:messages.OpenRunspaceMessage $Runspacepool.Open() #Add RunspacePool to array If($null -ne (Get-Variable -Name MonkeyRSP -ErrorAction Ignore)){ [void]$MonkeyRSP.Add($Runspacepool); } } } #Set Monkeyjobs variable $MyMonkeyJobs = [System.Collections.Generic.List[System.Management.Automation.PSObject]]::new() #Set timers, vars $Timer = [system.diagnostics.stopwatch]::StartNew() $SubTimer = [system.diagnostics.stopwatch]::StartNew() [int]$script:jobsCollected = 0 If($PSBoundParameters.ContainsKey('Timeout') -and $PSBoundParameters['TimeOut']){ #TimeOut is in Milliseconds [int]$Timeout = $PSBoundParameters['TimeOut'] * 1000 } Else{ #TimeOut is in Milliseconds [int]$Timeout = 10 * 1000 } } Process{ If($null -ne $Runspacepool -and $Runspacepool.RunspacePoolStateInfo.State -eq [System.Management.Automation.Runspaces.RunspaceState]::Opened){ #Check If MaxQueue If($MyMonkeyJobs.Count -ge $MaxQueue){ Write-Verbose ($script:messages.TimeSpentInvokeBatchMessage -f ($MyMonkeyJobs.Count / $BatchSize), $SubTimer.Elapsed.ToString()) $SubTimer.Reset();$SubTimer.Start() $p = @{ Jobs = $MyMonkeyJobs; BatchSize = $BatchSize; Timeout = $Timeout; Jobscollected = ([ref]$Script:jobsCollected); } Watch-MonkeyJob @p $SubTimer.Stop() Write-Verbose ($script:messages.TimeSpentCollectBatchMessage -f ($MyMonkeyJobs.Count / $BatchSize), $SubTimer.Elapsed.ToString()) $SubTimer.Reset() If($BatchSleep){ Write-Verbose ($script:messages.SleepMessage -f $BatchSleep) Start-Sleep -Milliseconds $BatchSleep } $SubTimer.Start() $MaxQueue += $BatchSize } #Get Start-MonkeyJob Param $MetaData = New-Object -TypeName "System.Management.Automation.CommandMetaData" (Get-Command -Name "Start-MonkeyJob") $newPsboundParams = @{} If($null -ne $MetaData){ $param = $MetaData.Parameters.Keys ForEach($p in $param.GetEnumerator()){ If($PSBoundParameters.ContainsKey($p)){ $newPsboundParams.Add($p,$PSBoundParameters[$p]) } } } #Check if RunspacePool If(-NOT $newPsboundParams.ContainsKey('RunspacePool')){ [void]$newPsboundParams.Add('RunspacePool',$Runspacepool); } $newJob = Start-MonkeyJob @newPsboundParams IF($newJob){ #Add to list [void]$MyMonkeyJobs.Add($newJob); } } Else{ If($Runspacepool.RunspacePoolStateInfo.State -ne [System.Management.Automation.Runspaces.RunspaceState]::Opened){ Write-Error ($script:messages.RunspaceError) return } Else{ Write-Error ($script:messages.UnknownError) return } } } End{ Try{ #All jobs are invoked at this time, just collect all of them Write-Verbose "Invoked all Jobs, Collecting the last jobs that are running" #Collect all jobs While ((@($MyMonkeyJobs | Where-Object {$_.Job.State -eq [System.Management.Automation.JobState]::Running})).count -gt 0){ #We want to collect all the Jobs, so just double the BatchSize $BS = (@($MyMonkeyJobs | Where-Object {$_.Job.State -eq [System.Management.Automation.JobState]::Running})).count * 2 $p = @{ Jobs = $MyMonkeyJobs; BatchSize = $BS; Timeout = $Timeout; Jobscollected = ([ref]$Script:jobsCollected); } Watch-MonkeyJob @p } } Catch{ Write-Error ("MonkeyJob Error: {0}" -f $_) } Finally{ #Get Data $completedJobs = $MyMonkeyJobs | Where-Object {$_.Job.State -eq [System.Management.Automation.JobState]::Completed} #Receive jobs $completedJobs | Receive-MonkeyJob #Clean objects if($MyMonkeyJobs.Count -gt 0){ Write-Verbose ($script:messages.TerminateJobMessage -f $MyMonkeyJobs.Count) If($reuseRSP){ #$MyMonkeyJobs | Remove-MonkeyJob -KeepRunspacePool } Else{ #$MyMonkeyJobs | Remove-MonkeyJob } } #Stop timer If($Timer.Isrunning){ Write-Verbose "Exiting script" Write-Verbose ("Jobs Collected: {0}" -f $script:jobsCollected) Write-Verbose ("Time took to Invoke and Complete the Jobs : {0}" -f $Timer.Elapsed.ToString()) $Timer.Stop() } } } } |