External commands arguments

I am learning how to pass arguments to external commands in Powershell. I decided to settle on schtasks.exe as a scenario to play with.

What I have decided to do as an exercise to learn is to create functions to getting scheduledtasks and creating scheduledtasks.

During my creation of the function, I hit a bit of a snag. I want to parameterize the arguments and pass that to schtasks.exe.
So I tried this from the schtasks.exe based of an example from schtasks.exe
SCHTASKS /Create /S ABC /U user /P password /RU runasuser /RP runaspassword /SC WEEKLY /TN report /TR notepad.exe
and parameterize it like so:

$comp = "server1"
$Server = "/S $comp"
$schedule = " /SC WEEKLY"
$taskname = "/TN report"
$taskrun = "/TR notepad.exe"
SCHTASKS "/Create $server $schedule $taskname $taskrun"

It totally bombs with:

SCHTASKS : ERROR: Invalid argument/option - '/s win2k12-dc1'.
At line:6 char:1
+ SCHTASKS /Create $server $schedule $taskname $taskrun
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (ERROR: Invalid ...s win2k12-dc1'.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
 
Type "SCHTASKS /CREATE /?" for usage.

I saw an example on the internet:

$arg1 = "filename1"
$arg2 = "-someswitch"
$arg3 = "C:\documents and settings\user\desktop\some other file.txt"
$arg4 = "-yetanotherswitch"

$allArgs = @($arg1, $arg2, $arg3, $arg4)

& "C:\Program Files\someapp\somecmd.exe" $allArgs

My question is how does it know what arguments apply to what in schtasks? Or is there a better way to pass arguments to the external command in powershell? Thanks in advance for any help.

You need to remove the quotes in

SCHTASKS "/Create $server $schedule $taskname $taskrun"

Hi Sam,

I tried it as

SCHTASKS /Create $server $schedule $taskname $taskrun

It still doesnt work…

To control the switches, you need to really leverage a Powershell function or minimally parameters. You have a great deal of control of how to build the command such as ValidateSet. To call a exe, you’ll need to use .\SCHTASKS.EXE… or & SCHTASKS.EXE…, there is a lot of information out there on calling cmd utilities with Powershell. However, I would recommend using Start-Process because you can pass arguments as an array. Here is an example:

Output:

VERBOSE: SCHTASKS.EXE /CREATE /S server1 /SC WEEKLY /TN report /TR notepad.exe

Code:

function Create-ScheduledTask {
    [CmdletBinding()]
    param (
        [Parameter(Position=0, Mandatory=$true)]
        [Alias('ServerName')]
        [string]$ComputerName,
        [Parameter(Position=1, Mandatory=$true)]
        [ValidateSet('MINUTE','HOURLY','DAILY','WEEKLY','MONTHLY','ONCE','ONSTART','ONLOGON','ONIDLE','ONEVENT')]
        [Alias('SC')]
        [string]$Schedule,
        [Parameter(Position=2, Mandatory=$true)]
        [Alias('TN')]
        [string]$TaskName,
        [Parameter(Position=3, Mandatory=$true)]
        [Alias('TR')]
        [string]$TaskRun
    )
    begin {}
    process {
        $argList = @('/CREATE')

        if ($PSBoundParameters.ContainsKey('ComputerName')) {
            $argList += '/S {0}' -f $ComputerName
        }

        if ($PSBoundParameters.ContainsKey('Schedule')) {
            $argList += '/SC {0}' -f $Schedule
        }

        if ($PSBoundParameters.ContainsKey('TaskName')) {
            $argList += '/TN {0}' -f $TaskName
        }

        if ($PSBoundParameters.ContainsKey('TaskRun')) {
            $argList += '/TR {0}' -f $TaskRun
        }

        $tskParams = @{
            FilePath = 'SCHTASKS.EXE'
            ArgumentList = $argList
            Wait = $true
            NoNewWindow = $true
        }
        
        #Start-Process @tskParams
        
        Write-Verbose -Message ('SCHTASKS.EXE {0}' -f ($argList -join ' '))
    }
    end {}
}

$params = @{
    ComputerName = "server1"
    Schedule     = "WEEKLY"
    Taskname     = "report"
    Taskrun      = "notepad.exe"
}

Create-ScheduledTask @params -Verbose

Thanks Rob,

Thats the explaination I was looking for . Thats perfect! Many thanks

Rob, If its ok to ask what does the {0} mean?

The -f indicates a string format. The curly brackets are placeholders and the number is an index number of the item in the array you are passing:

$personName = 'Jane'
$dogName = 'Spot'

"See {0} run. See {1} run." -f $personName, $dogName

Output:

See Jane run. See Spot run.

It’s strictly preference, but you can also do formatting for dates, numbers, timepans and many other things. For instance, numbers: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings

$total = 2.75

"Your total is {0:c}" -f $total

This will automatically format it as currency. Using other string concatenation methods, while they work, string formats have never failed me.

Write-Verbose -Message ('SCHTASKS.EXE {0}' -f ($argList -join ' '))

This old thing? Okay, so as you’ve seen, PowerShell will natively insert the value of a variable in a double-quoted string. It won’t do this for single-quoted strings, however:

$Var = "hello"
PS C:\>"this says $Var"
this says hello
PS C:\>'this says $Var'
this says $var

So, Powershell has a string operator, -f. If you’re at all familiar with .NET, or any of the old C-family printf functions, it behaves similarly. You insert markers, like {0}, {1}, etc., into the string, and after the -f operator, you give it a comma-separated list of the different things to insert:

PS C:\>"{0}..{1}..{0}" -f 99,1
99..1..99

It can do a lot of interesting things, too, like specify a date format for a datetime object, force a number to display to a certain number of decimal places, force a number to display leading zeroes, etc., etc. It’s quite versatile, allowing you to convert values to various string equivalents with ease.

Some examples:

PS C:\>"Date: {0:yyyyMMdd}" -f (Get-Date)
Date: 20180501
PS C:\>"{0:0000}, {1:0000}, {2:0000}" -f 1, 100, 10000
0001, 0100, 10000
PS C:\>"{0:N2}, {0:0000}, {0:X}" -f 100
100.00, 0100, 64 
Though it's not clear, that very last one? Hexadecimal display. Basically anything you can usually do in .NET with format strings, you can do in PS natively with the -f operator. It works with both single and double quoted strings, so it's extremely versatile.