Scheduling a powershell script to run every 20 minutes

吃可爱长大的小学妹 提交于 2021-02-20 05:12:51

问题


I am writing a script to check mail and I want it to check the unread mail every 20 or 30 minutes how can I schedule this task to run every 30 minutes using powershell.


回答1:


Here's a sample Powershell script which creates a scheduled task which can run with admin privileges

passing argument -RunLevel Highest makes it run as administrator, you may need to provide valid username/password which has admin right on the machine

$frequencyInMin = 20

$taskAction = New-ScheduledTaskAction -Execute 'Powershell.exe' `
-Argument '-NoProfile -WindowStyle Hidden -command "& {get-eventlog -logname Application -After ((get-date).AddDays(-1)) | Export-Csv -Path c:\temp\logs.csv -Force -NoTypeInformation}"'

$trigger = New-ScheduledTaskTrigger `
    -Once `
    -At (Get-Date) `
    -RepetitionInterval (New-TimeSpan -Minutes $frequencyInMin) `
    -RepetitionDuration (New-TimeSpan -Days (365 * $frequencyInMin))

Register-ScheduledTask -Action $taskAction -Trigger $trigger -TaskName "MyTasks\AppLog" -Description "Daily App Logs" -RunLevel Highest



回答2:


You didn't specified a version of PowerShell or the operation system under which you want to check for your emails.

For Windows:

The Task Scheduler in Windows has so many problems that I don't know where to begin.

Unfortunately, you can schedule a task which re-occurs every 20-30 min easily. You have to schedule a daily task at 00:00, 00:30, 01:00 etc. in cycle, so you would end up with 48 or 72 scheduled tasks in your scheduler. 🤔

If we are talking about Windows on notebooks, laptops even desktop PCs with UPS backup, you can't rely on the task scheduler. The tasks are not executed according the schedule because battery management interfere.

Another problem is that every 20-30 min a window of PowerShell will popup on your screen for a brief moment. You can suppress this by register the task as system service => under system service account. Other approach is to run PowerShell in CMD and hand over the script which must be executed. This is not an option if you don't have an admin rights.

Another, very tricky problem is that use of one of the must include switch (technically, you don't have to use this switch but the task gets executed faster) for scheduled PowerShell tasks: the -NonInteractive switch will actively stop you from using some of the interactive cmdlets like Pause, Read-Host or Get-Credential in your script which you want to execute. You have to design your scheduled task according the instruction which you want to execute in PowerShell session.

Not ideal option is the schedule the trigger as -Once and setup this trigger with repetition:

$frequencyOfCheckInMin = 20
$howManyYearYouWantToCheckForEmails = 5
$params = @{
    Once = $true
    At = (Get-Date)
    RepetitionInterval = (New-TimeSpan -Minutes $frequencyOfCheckInMin)
    RepetitionDuration = (New-TimeSpan -Days ($howManyYearYouWantToCheckForEmails * 365 * $frequencyOfCheckInMin)) 
}
$trigger = New-ScheduledTaskTrigger @params

Disadvantages are:

  1. You have to know how long you want to schedule it for.
  2. Take leap years into account.

A better approach would be to register one task with PowerShell script that gets executed in 30 min for the task registration time manually. Your PowerShell script would contain a logic for an email check and update of the scheduled task trigger to a new, 30 min later from the time of the execution of the scheduled task trigger. Code follows.

# A definition of a general task which takes a script as argument and execute task 
$pathToScript = 'D:\Fortis Backup\Test.ps1'
$ScheduledTaskActionParams = @{
    Execute  = "PowerShell.exe"
    Argument = "-NoLogo -NoProfile -NonInteractive -File `"$pathToScript`""
}
$registerTaskParameters = @{
    Principal = New-ScheduledTaskPrincipal -UserId "$ENV:USERDOMAIN\$ENV:USERNAME"
    Trigger   = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
    TaskName  = 'Check email'
    Action    = New-ScheduledTaskAction @ScheduledTaskActionParams
}
Register-ScheduledTask @registerTaskParameters -Force

Your script

Get-EmailStatus
$newTrigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
Set-ScheduledTask -TaskName 'Check email' -Trigger $newTrigger

Advantages:

  1. You can implement a logic for upper boundary or even skipping some days.
  2. You can simply turn on/off this task with other PowerShell scripts or just cmdlets.

Disadvantages:

  1. You have to handle Daylight saving time changes.

For details about the trigger, have a look over here.

For Linux (PowerShell Core):

It is a super simple.

  1. Install PowerShell Core.
  2. Make your script with check email logic.
  3. Schedule execution in CRON, e.g. */20 * * * * /root/powershell/7/pwsh /root/CheckEmail.ps1

More robust solution for Windows Task Scheduler:

To tackle problems with non-interactive cmdlets, I've designed these functions which I'm using in my private Swiss Army Knife Module for various PowerShell task.

function Confirm-PowerShellScriptForNonInteractiveMode
{
    [CmdletBinding(PositionalBinding)]
    Param (
        # A path to PS1 file to test
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ValueFromPipeline,
            Position = 0)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( { Test-Path $_ })]
        [ValidateScript( { $_ -like '*.ps1' })]
        [System.IO.FileInfo]
        $Path
    )
    Begin
    {
        Write-Information 'Starting to validate script(s)...'
    }
    Process
    {
        $nonInteractiveCmdlets = @('Pause', 'Read-Host', 'Get-Credential')
        $isValid = [System.Boolean]$true
        $content = Get-Content -Path $Path
        foreach ($cmdlet in $nonInteractiveCmdlets)
        {
            Write-Verbose "Script status: $isValid, testing $cmdlet"
            $isValid = $isValid -AND !($content -match "$cmdlet")
        }
        return $isValid
    }
    End
    {
        Write-Information 'All scripts has been validated.'
    }
}

function Register-PowerShellScheduledTask
{
    [CmdletBinding(PositionalBinding)]
    Param (
        # A path to PS1 file to register
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ValueFromPipeline,
            Position = 0)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( { Test-Path $_ })]
        [ValidateScript( { $_ -like '*.ps1' })]
        [ValidateScript( { Confirm-PowerShellScriptForNonInteractiveMode $_ })]
        [System.IO.FileInfo]
        $Path,
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            Position = 1)]
        [ValidateNotNullOrEmpty()]
        [Microsoft.Management.Infrastructure.CimInstance]
        $TaskTrigger = (New-ScheduledTaskTrigger -AtLogOn)
    )
    Begin
    {
        Write-Information 'Starting to register task(s)...'
    }
    Process
    {
        $ScheduledTaskActionParams = @{
            Execute  = "PowerShell.exe"
            Argument = "-NoLogo -NoProfile -NonInteractive -File `"$Path`""
        }
        $registerTaskParameters = @{
            Principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME"
            Trigger   = $TaskTrigger
            TaskName  = "Run $(Split-Path -Path $Path -Leaf) by PowerShell"
            Action    = New-ScheduledTaskAction @ScheduledTaskActionParams
        }
        Register-ScheduledTask @registerTaskParameters -Force

        Get-ScheduledTask | Write-Verbose
    }
    End
    {
        Write-Information 'Registered task:'
        Get-ScheduledTask -TaskName 'Run * by PowerShell' | Write-Information
    }
}

Use of these functions:

$trigger = New-ScheduledTaskTrigger -At ((Get-Date).AddSeconds(10))
Register-CustomScheduledTask -Path "C:\temp\test.ps1" -Trigger $trigger


来源:https://stackoverflow.com/questions/61673254/scheduling-a-powershell-script-to-run-every-20-minutes

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!