Installing flyctl via PowerShell fails when Set-StrictMode set to Latest

Simplified PowerShell script for repro:

Set-StrictMode -Version Latest
Invoke-WebRequest https://fly.io/install.ps1 -UseBasicParsing | Invoke-Expression

Set-StrictMode basically enables some additional code safety checks. I executed that script in an elevated PowerShell command prompt (version: 5.1) on Windows 11. I don’t think the PowerShell version or the OS matter here, though.

Resulting error message:

Invoke-Expression : The variable '$v' cannot be retrieved because it has not been set.

I downloaded the contents of your install.ps1 script and noticed that you are using $v even though it hasn’t been set yet. The call to “if ($v)” is causing the error.

Your current code:

$Version = if ($v) {
  $v
} elseif ($args.Length -eq 1) {
  $args.Get(0)
} else {
  "latest"
}

To make your $Version parameter code less wonky, I’d recommend deleting that code entirely, and adding this to the top of your script instead:

[CmdletBinding()]
[OutputType([void])]
param
(
    [Alias("v")]
    [Parameter(Mandatory = $false)]
    [string] $Version = "latest"
)

With that code, your script would now accept the version number in 3 ways:

  • install.ps1 3.0
  • install.ps1 -v 3.0 (For backwards compatibility. Judging from how you wrote your original code, I assume you meant for people to call your script that way, even though the script didn’t care about the name of that parameter at all.)
  • install.ps1 -Version 3.0 (so you don’t have to update the rest of the script)

If no version number is passed to the script, it will default to “latest”.

Possibly breaking behavior if you implement my suggestion:

  • install.ps1 will fail if more than one command line argument is passed to it. Currently, it doesn’t care how many parameters you pass to it.
  • install.ps1 will no longer check if the variable $v has been set in the current environment. In your current script, if $v is set for some reason, that value will overwrite whatever value has explicitly been passed to install.ps1 as a command line argument. Which is a scary thought!
  • Some callers may currently be doing something like “install.ps1 -GarbageParameterName 3.0”, and install.ps1 dutifully sets the version number to 3.0. If you implement my suggestion, the script will fail, saying “A parameter cannot be found that matches parameter name 'GarbageParameterName'.

To prevent other people from hitting this issue in the future, please add this line towards the top of your script as well:

Set-StrictMode -Version Latest

I assume your script may contain additional lines of code that will fail with Set-StrictMode enabled. I haven’t checked that yet.

For now, I have added this line of code just prior to executing the flyctl installation script:

Set-StrictMode -Off

It’s not ideal, but it works. I still shudder when I think that the script tries to look for the value of a variable that was set outside of the scope of that script.

I’d also prefer it if the script didn’t try to look for an environment variable called “FLYCTL_INSTALL”. Why not use named command line arguments for all of that stuff? And call it something more intuitive like “InstallationFolder”?