Invoke-Expression considered harmful

PowerShell Team

PowerShell Team

The PowerShell team frequently gets questions that start out “how do I get the quoting right for…” and the answer turns out to usually be – there is a simpler way – don’t use Invoke-Expression.

The problem arises when trying to run some command external to PowerShell.  Some common reasons people try Invoke-Expression:

  • running some command with a space in the path
  • some command takes an argument with characters that have special meaning in PowerShell, e.g. curly braces (‘{‘ and ‘}’)
  • some command argument needs to use a PowerShell variable, perhaps as part of a quoted argument

If you’re just running some command external to PowerShell (exe, cmd, etc.) and you’re using Invoke-Expression, you are just making things more difficult than you need to.

So what’s wrong with Invoke-Expression then?

  • It complicates getting quoting right
  • It makes maintaining your script harder
  • It’s slower than the alternatives
  • And maybe worst of all – it opens up a script to code injection attacks

If Invoke-Expression isn’t the right way – then what is?

If you’re running some command and the command path has spaces in it, then you need the command invocation operator ‘&’ (see help about_operators, look for “call operator”).

If your command runs, but your arguments are wrong, then there is a good chance you are getting the quotes wrong.  Invoke-Expression doesn’t help at all in this case, it just makes the problem more complicated. 

The bottom line: Invoke-Expression is a powerful and useful command for some scenarios such as creating new scripts at runtime, but in general, if you find yourself using Invoke-Expression, you should ask yourself, or maybe a respected colleague if there is a better way.

Jason Shirk
Windows PowerShell Team


Comments are closed.

  • Avatar
    Daniel Schroeder

    One thing I like about Invoke-Expression is that I can store the command I want it to run in a string variable, and then right before invoking it I can display the command, so in the output I can see exactly what expression is being ran. I find this very useful for troubleshooting things like calling other console executables with parameters.

    For example, this is a common pattern I use:
    [string] $command = “$someExeFilePath /arg1 $arg1 /arg2 $arg2”
    Write-Verbose “About to execute the command ‘$command’.”
    Invoke-Expression -Command $command

    If the exe gives an error, I have the exact command line that was used to call it, so I can go reproduce the problem locally and see what parameters it expects. It also makes it very obvious if I have a typo in my command, or if my code logic maybe didn’t construct the command the way I thought it would.

    It’s tougher to get that exact string that’s being ran when using &.

    I’m curious how it opens your scripts up to injection attacks? Do you mean if I’m using a parameter that was passed into the script, or that I prompted the user for, in my $command? That would make sense, although I don’t see how using & would avoid that or be any safer. Or are you referring to some other technique?

    • Avatar
      Robert HoltMicrosoft logo

      The following would be an injection attack:

      function Invoke-MyCommand

      Write-Output “Hello $Name”

      $Value = “`”; Remove-Item -Force -Recurse /”

      Invoke-Expression “Invoke-MyCommand -Name `”$Value`”” # Deletes root filesystem

      & “Invoke-MyCommand” -Name $Value # Prints ‘Hello “; Remove-Item -Force -Recurse /’

      In the Invoke-Expression case, some values of $Value subvert the string to bypass Invoke-MyCommand.
      When & is used, that’s not possible.

      For recording things like the command being run, Start-Transcript might be a good solution.