core/modules/monkeyjob/public/Start-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 Start-MonkeyJob{
    <#
        .SYNOPSIS
 
        Starts a MonkeyJob using runspaces
 
        .DESCRIPTION
 
        .INPUTS
 
        .OUTPUTS
 
        .EXAMPLE
 
        .NOTES
            Author : Juan Garrido
            Twitter : @tr1ana
            File Name : Start-MonkeyJob
            Version : 1.0
 
        .LINK
            https://github.com/silverhack/monkey365
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Scope="Function")]
    [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(Mandatory=$false, HelpMessage="Job name")]
        [String]$JobName,

        [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="ApartmentState of the thread")]
        [ValidateSet("STA","MTA")]
        [String]$ApartmentState = "STA",

        [Parameter(Mandatory=$False, HelpMessage='ThrowOnRunspaceOpenError')]
        [Switch]$ThrowOnRunspaceOpenError
    )
    Begin{
        If (-not $PSBoundParameters.ContainsKey('ThrowOnRunspaceOpenError')) {
            $ThrowOnRunspaceOpenError = $False
        }
        $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']
        }
        #Create new runspace or reuse existing
        If (-not $PSBoundParameters.ContainsKey('Runspacepool')) {
            #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()
            }
        }
        else{
            if($Runspacepool.RunspacePoolStateInfo.State -eq [System.Management.Automation.Runspaces.RunspaceState]::BeforeOpen){
                #Open runspace
                Write-Verbose $script:messages.OpenRunspaceMessage
                $Runspacepool.Open()
            }
        }
        #Set Monkeyjobs variable
        $AllMonkeyJobs = [System.Collections.Generic.List[System.Management.Automation.PSObject]]::new()
    }
    Process{
        if($null -ne $Runspacepool -and $Runspacepool.RunspacePoolStateInfo.State -eq [System.Management.Automation.Runspaces.RunspaceState]::Opened){
            #Get scriptblock if any
            $param = @{
                RunspacePool = $Runspacepool;
                InputObject = $InputObject;
                Arguments = $Arguments;
            }
            if($PSCmdlet.ParameterSetName -eq 'ScriptBlock'){
                if($InputObject){
                    $sb = Set-ScriptBlock -ScriptBlock $ScriptBlock -AddInputObject
                }
                else{
                    $sb = Set-ScriptBlock -ScriptBlock $ScriptBlock
                }
                [void]$param.Add('ScriptBlock',$sb)
            }
            elseif($PSCmdlet.ParameterSetName -eq 'Command'){
                [void]$param.Add('Command',$Command)
            }
            #Get new PowerShell Object
            $Pipeline = New-PowerShellObject @param
            if($Pipeline){
                #Set Job name
                if($PSBoundParameters.ContainsKey('JobName') -and $PSBoundParameters['JobName']){
                    $MonkeyJobName = $PSBoundParameters['JobName'];
                }
                else{
                    $MonkeyJobName = ("MonkeyTask{0}" -f (Get-Random -Maximum 1000 -Minimum 1));
                }
                #Create a new Job
                $Job = [MonkeyJob]::new($Pipeline,$jobName);
                #Get new MonkeyJob object
                $newJob = New-MonkeyJobObject
                if($newJob -and $null -ne $Job){
                    #Populate job
                    $newJob.RunspacePoolId = $Pipeline.RunspacePool.InstanceId;
                    $newJob.Name = $MonkeyJobName;
                    $newJob.Job = $Job;
                    if($PSCmdlet.ParameterSetName -eq 'ScriptBlock'){
                        $newJob.Command = $scriptblock.ToString();
                    }
                    elseif($PSCmdlet.ParameterSetName -eq 'Command'){
                        $p = @{
                            Command = $Command;
                            InputObject = $InputObject;
                            Arguments = $Arguments;
                        }
                        $cmd = Format-Command @p
                        if($cmd){
                            $newJob.Command = $cmd.ToString();
                        }
                    }
                    #Add to list
                    [void]$AllMonkeyJobs.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{
            for($NumJob = 0 ; $NumJob -lt $AllMonkeyJobs.Count; $NumJob++){
                $MonkeyJob = $AllMonkeyJobs.Item($NumJob)
                $MonkeyJob.Task = $MonkeyJob.Job.StartTask();
                #Add to monkeyJob var
                if($null -eq (Get-Variable -Name MonkeyJobs -ErrorAction Ignore)){
                    $MonkeyJobs = [System.Collections.Generic.List[System.Management.Automation.PSObject]]::new()
                    [void]$MonkeyJobs.Add($MonkeyJob)
                }
                else{
                    [void]$MonkeyJobs.Add($MonkeyJob)
                }
            }
            #return jobs
            return $AllMonkeyJobs.ToArray()
        }
        catch{
            Write-Error ("MonkeyJob Error: {0}" -f $_)
        }
    }
}