To offer more concise alternatives to your own effective solution - also assumes execution from PowerShell; the usual caveats about directly executing code downloaded from the web apply:[1]
powershell { iex (irm https://sample.com/cli.ps1) }
There are edge cases where the above malfunctions (see below); use the following robust form then, which also makes it easy to pass arguments to the script:
# More robust:
# Optionally pass arguments before the closing "}"
powershell { & ([scriptblock]::Create((irm https://sample.com/cli.ps1))) }
Assuming you know that the script contains no exit
statements (which would exit your whole session), you can even use in-process execution (as your solution via an aux. file does), as follows:
# Note: Execution via & { ... } prevents iex (Invoke-Expression) from
# effectively dot-sourcing the code, i.e. from running it
# directly in the calling scope, which can have unwanted side effects.
iex "& { $(irm https://sample.com/cli.ps1) }"
# More robust:
& ([scriptblock]::Create((irm https://sample.com/cli.ps1)))
In-process execution isn't just faster, it also enables a script to directly modify the current session's environment variables, and maintains full type fidelity with respect to the script's output objects, if any.
If the script does have exit
statements, downloading to a (temporary) file first - as in your solution - is necessary, though the proposed future enhancement discussed below would make that unnecessary.
Note:
irm
is a built-in alias of the Invoke-RestMethod
cmdlet - see the next section for details.
iex
is a built-in alias of the Invoke-Expression
cmdlet.
Note that while Invoke-Expression
is generally to be avoided, here it enables execution of source code without the need for an aux. script file (.ps1
).
[scriptblock]::Create($sourceCodeString)
is a way to parse source code into a script block (whose literal form is { ... }
), i.e. a reusable unit of code that can be invoked on demand later, using either &
, the call operator or .
, the dot-sourcing operator.
Its use bypasses the following Invoke-Expression
bugs, present up to at least PowerShell 7.3.3.:
Since no script file is involved in the above commands, the effective execution policy does not apply.
As an aside: If it did:
- you could override it for a given CLI call with
-ExecutionPolicy Bypass
- you could set it persistently, using
Set-ExecutionPolicy
, as in your own answer - see this answer for background information.
Potential future enhancement:
GitHub issue #5909 discusses enhancing the Invoke-Command
cmdlet to robustly support execution of scripts directly from the web, based on an Invoke-WebRequest
call as pipeline input.
If this were implemented, a robust, in-process invocation would look like this (using the built-in aliases iwr
and icm
alias of Invoke-WebRequest
and Invoke-Command
, respectively):
# WISHFUL THINKING as of PowerShell 7.3.3
# Append -- arg1 ... to pass arguments.
iwr https://sample.com/cli | icm
curl
vs. curl.exe
vs. Invoke-WebRequest
vs. Invoke-RestMethod
:
curl.exe
indeed comes with recent versions of Windows.
To call it from Windows PowerShell, be sure to call it as curl.exe
, i.e. with the .exe
extension.
- In Windows PowerShell,
curl
- without the .exe
part - is an alias of the Invoke-WebRequest
cmdlet, which is PowerShell's analog to curl.exe
, albeit with very different syntax.
Fortunately, this alias was removed from PowerShell (Core) 7+.
However, a built-in, PowerShell-idiomatic alias that can be used in both PowerShell editions to refer to Invoke-WebRequest
is iwr
.
The related Invoke-RestMethod
cmdlet - whose built-in alias is irm
- can be used in lieu of Invoke-WebRequest
in cases where you only care about the response's data, not also about a rich response-information object wrapping the data.
- Note: Plain-text data - such as a script's source code in your case - is returned as-is, but for XML- and JSON-based data
Invoke-RestMethod
conveniently parses the data into an XML DOM in the form of an [xml]
(System.Xml.XmlDocument
) instance and a custom-object graph ([pscustomobject]
), respectively.
[1] Only do this if you either fully control or implicitly trust the contents of the script, so as to prevent potentially malicious code from executing.