PowerShell

Jobs

Jobs have the advantage of being able to run in the background and return the cursor to the shell. They allow you to run more than one command at a time without using multiple shells.

One thing to note is that jobs only exist in the current shell

        PS C:\> (get-command -Noun job).name
        Debug-Job
        Get-Job
        Receive-Job
        Remove-Job
        Resume-Job
        Start-Job
        Stop-Job
        Suspend-Job
        

Starting a job is fairly easy just use Start-Job

        PS C:\> Start-Job -ScriptBlock {dir c:\ -Recurse}
    
        Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
        --     ----            -------------   -----         -----------     --------             -------
        1      Job1            BackgroundJob   Running       True            localhost            dir c:\ -Recurse
        

You can give your job a name which is a good idea if you are running multiple jobs

        PS C:\> Start-Job -ScriptBlock {dir c:\ -Recurse} -Name longdirectoryJob
    
        Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
        --     ----            -------------   -----         -----------     --------             -------
        3      longdirector... BackgroundJob   Running       True            localhost            dir c:\ -Recurse
        

The command Get-Job can be used to check if the jobs are still running

        PS C:\> get-job
    
        Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
        --     ----            -------------   -----         -----------     --------             -------
        1      Job1            BackgroundJob   Running       True            localhost            dir c:\ -Recurse
        3      longdirector... BackgroundJob   Running       True            localhost            dir c:\ -Recurse
        

The column HasMoreData tell you if there is any return value from the job

A job like this one that takes forever can be paused with Suspend-Job if the action allows it and resumed again with Resume-Job or it can be stopped with Stop-Job

        PS C:\> Stop-Job -Id 1    
        PS C:\> Get-Job
        Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
        --     ----            -------------   -----         -----------     --------             -------
        1      Job1            BackgroundJob   Stopped       True            localhost            dir c:\ -Recurse
        3      longdirector... BackgroundJob   Running       True            localhost            dir c:\ -Recurse
        Stop-Job -Name longdirectoryJob
        

If you do a Receive-Job -Name Job1 you get all the data back but HasMoreData is set as false and leave memory

However if use Receive-Job -Id 3 -Keep you get all the data back only this time HasMoreData is True and is still kept in memory

        PS C:\> Get-Job
        Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
        --     ----            -------------   -----         -----------     --------             -------
        1      Job1            BackgroundJob   Stopped       False           localhost            dir c:\ -Recurse
        3      longdirector... BackgroundJob   Stopped       True            localhost            dir c:\ -Recurse
        

To get rid of both of these jobs you can use: Get-Job | Remove-Job

Start-job is really for use on the local computer using local resources. To start jobs on remote computers use invoke-command

Invoke-Command -ScriptBlock {Get-EventLog -LogName Security -Newest 20 } -ComputerName localhost,w8p -AsJob -JobName eventlog

You can also use Get-WmiObject and commands that support as job

PS C:\> Get-WmiObject -Class win32_logicaldisk -ComputerName localhost,w10p -AsJob

Scheduled Jobs

PowerShell can do both scheduled jobs and scheduled tasks the main difference between the two is that Scheduled tasks appear in the task scheduler

        PS C:\> gcm -Module PSScheduledJob
    
        CommandType     Name                                               Version    Source
        -----------     ----                                               -------    ------
        Cmdlet          Add-JobTrigger                                     1.1.0.0    PSScheduledJob
        Cmdlet          Disable-JobTrigger                                 1.1.0.0    PSScheduledJob
        Cmdlet          Disable-ScheduledJob                               1.1.0.0    PSScheduledJob
        Cmdlet          Enable-JobTrigger                                  1.1.0.0    PSScheduledJob
        Cmdlet          Enable-ScheduledJob                                1.1.0.0    PSScheduledJob
        Cmdlet          Get-JobTrigger                                     1.1.0.0    PSScheduledJob
        Cmdlet          Get-ScheduledJob                                   1.1.0.0    PSScheduledJob
        Cmdlet          Get-ScheduledJobOption                             1.1.0.0    PSScheduledJob
        Cmdlet          New-JobTrigger                                     1.1.0.0    PSScheduledJob
        Cmdlet          New-ScheduledJobOption                             1.1.0.0    PSScheduledJob
        Cmdlet          Register-ScheduledJob                              1.1.0.0    PSScheduledJob
        Cmdlet          Remove-JobTrigger                                  1.1.0.0    PSScheduledJob
        Cmdlet          Set-JobTrigger                                     1.1.0.0    PSScheduledJob
        Cmdlet          Set-ScheduledJob                                   1.1.0.0    PSScheduledJob
        Cmdlet          Set-ScheduledJobOption                             1.1.0.0    PSScheduledJob
        Cmdlet          Unregister-ScheduledJob                            1.1.0.0    PSScheduledJob
        

The three main conponents of scheduling a job are:

        New-JobTrigger
        New-ScheduledJobOption 
        Register-ScheduledJob 
        

First we will look at New-JobTrigger

        NAME
        New-JobTrigger
    
        SYNOPSIS
            Creates a job trigger for a scheduled job.
    
    
        SYNTAX
            New-JobTrigger [-Once] -At <DateTime> [-RandomDelay <TimeSpan>] [-RepeatIndefinitely] [-RepetitionDuration
            <TimeSpan>] [-RepetitionInterval <TimeSpan>] [<CommonParameters>]
    
            New-JobTrigger [-Daily] -At <DateTime> [-DaysInterval <Int32>] [-RandomDelay <TimeSpan>] [<CommonParameters>]
    
            New-JobTrigger [-Weekly] -At <DateTime> -DaysOfWeek {Sunday | Monday | Tuesday | Wednesday | Thursday | Friday |
            Saturday} [-RandomDelay <TimeSpan>] [-WeeksInterval <Int32>] [<CommonParameters>]
    
            New-JobTrigger [-AtLogOn] [-RandomDelay <TimeSpan>] [-User <String>] [<CommonParameters>]
    
            New-JobTrigger [-AtStartup] [-RandomDelay <TimeSpan>] [<CommonParameters>]
        

It all starts with the trigger and this one is set to run at logon

$trigger = New-JobTrigger -AtLogOn

The next thing needed is the scheduling option.

    
        NAME
            New-ScheduledJobOption
    
        SYNOPSIS
            Creates an object that contains advanced options for a scheduled job.
    
    
        SYNTAX
            New-ScheduledJobOption [-ContinueIfGoingOnBattery] [-DoNotAllowDemandStart] [-HideInTaskScheduler] [-IdleDuration
            <TimeSpan>] [-IdleTimeout <TimeSpan>] [-MultipleInstancePolicy {None | IgnoreNew | Parallel | Queue |
            StopExisting}] [-RequireNetwork] [-RestartOnIdleResume] [-RunElevated] [-StartIfIdle] [-StartIfOnBattery]
            [-StopIfGoingOffIdle] [-WakeToRun] [<CommonParameters>]
        

So this is how we set an option

        PS C:\> $option = New-ScheduledJobOption -RequireNetwork -WakeToRun
        PS C:\> $option
    
        StartIfOnBatteries     : False
        StopIfGoingOnBatteries : True
        WakeToRun              : True
        StartIfNotIdle         : True
        StopIfGoingOffIdle     : False
        RestartOnIdleResume    : False
        IdleDuration           : 00:10:00
        IdleTimeout            : 01:00:00
        ShowInTaskScheduler    : True
        RunElevated            : False
        RunWithoutNetwork      : False
        DoNotAllowDemandStart  : False
        MultipleInstancePolicy : IgnoreNew
        JobDefinition          :
        

We have an option and a trigger now it is time to register our scheduled job.

        NAME
        Register-ScheduledJob
    
        SYNOPSIS
            Creates a scheduled job.
    
    
        SYNTAX
            Register-ScheduledJob [-Name] <String> [-FilePath] <String> [-ArgumentList <Object[]>] [-Authentication {Default |
            Basic | Negotiate | NegotiateWithImplicitCredential | Credssp | Digest | Kerberos}] [-Confirm] [-Credential
            <PSCredential>] [-InitializationScript <ScriptBlock>] [-MaxResultCount <Int32>] [-RunAs32] [-RunEvery <TimeSpan>]
            [-RunNow] [-ScheduledJobOption <ScheduledJobOptions>] [-Trigger <ScheduledJobTrigger[]>] [-WhatIf]
            [<CommonParameters>]
    
            Register-ScheduledJob [-Name] <String> [-ScriptBlock] <ScriptBlock> [-ArgumentList <Object[]>] [-Authentication
            {Default | Basic | Negotiate | NegotiateWithImplicitCredential | Credssp | Digest | Kerberos}] [-Confirm]
            [-Credential <PSCredential>] [-InitializationScript <ScriptBlock>] [-MaxResultCount <Int32>] [-RunAs32] [-RunEvery
            <TimeSpan>] [-RunNow] [-ScheduledJobOption <ScheduledJobOptions>] [-Trigger <ScheduledJobTrigger[]>] [-WhatIf]
            [<CommonParameters>]
        

In this step we need to give it a name as well as a scriptblock.
So putting it all together the command would look like

        Register-ScheduledJob -Name "get processes at logon" -ScriptBlock {ps} -MaxResultCount 2 -Trigger $trigger -ScheduledJobOption $option 
        

If you were to log off and log back on again to trigger the job you could run get-job to view the status of this job and receive-job to get the output from the job.