Running Executables from Powershell

A seemingly simple task, but what about passing parameters?

When you need to run a command from PowerShell there are several options. In this TechNet article you can find no less than 10 methods. I will not recite them all here, just click the link.

Things get interesting when you want to pass parameters. We’ll discuss Invoke-Expression and Invoke-Command.

Invoke-Expression (IEX)

Microsoft documentation. Invoke-Expression or IEX is an easy way to just run a string as a Powershell command. Such as ping 127.0.0.1. When you need to pass arguments, you need to change the string like this.

# Ping 192.168.1.1
$Expression = "ping 192.168.1.1"
Invoke-Expression $Expression

# Ping 192.168.1.2
$Expression = "ping 192.168.1.2"
Invoke-Expression $Expression

Invoke-Command ()

Microsoft documentation. Invoke-Command or ICM runs commands on local or remote computers and returns all output from the commands, including the errors. The cmdlet needs a ScriptBlock to run, this can be an inline ScriptBlock or from a file.

A trivial example

# All ScriptBlocks are built with curly braces
$ScriptBlock = {Get-Service}
Invoke-Command $ScriptBlock

# Or more descriptive
$ScriptBlock = [scriptblock]::Create({Get-Service})
Invoke-Command $ScriptBlock

# Of course, you could also just run
Invoke-Command {Get-Service}

Let’s add a parameter. Imagine that we want to restart a service with Restart-Service, then we need to pass a service name, for instance the print spooler aka Spooler.

# The idea is to pass parameters, so let's define one
$ServiceName = "Spooler"
Invoke-Command {param($SN) Restart-Service -Name $SN} -ArgumentList $ServiceName

Notice that we create a param() section in the scriptblock.

What if we have multiple parameters?

# The script
$ScriptBlock = {
    param(
        $ServiceName,
        $FileName = "TestFile",
        $Path = "C:\Temp"
    )

    Stop-Service -Name $ServiceName 
    New-Item -ItemType File -Name $FileName -Path $Path
}

# Just add the arguments after -ArgumentList
Invoke-Command $ScriptBlock -ArgumentList @("Spooler", "AnOutputFile.log")

For passing parameters I like to work with hashtables, so that I can use named parameters. So I want to call the scriptblock like this Invoke-Command $ScriptBlock -Argumentlist $Hashtable.

# The script
$ScriptBlock = {
    param(
        $ServiceName,
        $FileName = "TestFile",
        $Path = "C:\Temp"
    )

    Stop-Service -Name $ServiceName 
    New-Item -ItemType File -Name $FileName -Path $Path
}

# A hashtable containing the parameters
$Parameters = @{
    ServiceName = "Spooler"
    FileName = "TestFile$(Get-Random)"
}

# Here we actualy create a new scriptblok, passing the paramters to splat the hash.
Invoke-Command -ScriptBlock {param($P) &($ScriptBlock) @P} -ArgumentList $Parameters

A step further is when the scriptblock is stored somewhere and we want to run it with a hashtable. In this example the script is stored on C:\Temp\script.ps1.

# A hashtable containing the parameters
$Parameters = @{
    ServiceName = "Spooler"
    FileName = "TestFile$(Get-Random)"
}

# Get the script from C:\Temp\script.ps1 and splat the hashtable as arguments
$ScriptBlock = [scriptblock]::create(".{$(Get-Content -Path C:\Temp\script.ps1 -Raw)} $(&{$args} @Parameters)")
# No need to pass any parameters anymore, they are already in the scriptblock.
Invoke-Command -ScriptBlock $ScriptBlock

The above examples are not very exciting, but remember that you can combine Invoke-Command with Powershell Direct. This means that you can run commands on Virtual Machines regardless of network configuration or remote management settings.

I don’t want to pretend I’m a Powershell expert because I’m not, but these work for me.

Avatar
Sven de Windt
Systems Administrator

Systems engineer with an intrest in automation

Related

comments powered by Disqus