We have a PowerShell script that contains "[Console]::TreatControlCAsInput = $true". The script is executed in a Azure DevOps Pipeline and fails with
Exception setting "TreatControlCAsInput": "The handle is invalid.
"
At line:1 char:3
+ [Console]::TreatControlCAsInput = $true
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
We've narrowed down the issue by reproducing this with a single line of code outside of an Azure Pipeline. Simply open Windows PowerShell ISE and run this:
[Console]::TreatControlCAsInput = $true
If we do this in a Windows PowerShell command window, the line of code succeeds.
Does anyone know what is different about the two environments that causes this difference in behavior? We are hoping that the answer would help us understand and resolve the larger issue with the script that runs in an Azure Pipeline.
The script runs fine if executed:
The script fails if executed in WIndows PowerShell ISE
Note:
This answer focuses on the behavior of the ISE.
Azure DevOps pipelines apparently exhibit similar behavior with respect to non-support of interactive console features, but - as Mathias R. Jessen points out - such features are pointless in an environment where only unattended execution ever takes place, i.e. where user interaction is fundamentally unsupported.
The upshot is:
Avoid the ISE if you need a full, interactive console experience, and consider migrating to Visual Studio Code (see bottom section).
Scripts run in Azure DevOps pipeline must avoid use of interactive features altogether.
The Windows PowerShell ISE does not allocate a console by default, which causes attempts to call the members of the static .NET [Console]
class to throw the exception you saw.
While it allocates a (hidden) console (used behind the scenes) on demand - namely the first time you run an external console application (e.g. cmd /c ver
) - after which [Console]
members then can be accessed, any interactive console functionality is fundamentally unsupported in the ISE.
Since [Console]::TreatControlCAsInput = $true
is only relevant to interactive functionality, notably [Console]::ReadKey()
:
There is no point in setting it in the ISE, and you can simply ignore the failure:
try { [Console]::TreatControlCAsInput = $true } catch {}
However, if your script relies on the ability to interpret Ctrl+C as a keypress rather than a request to abort execution, you may need to find an ISE-compatible solution.
Read-Host
and $host.UI.ReadLine()
do not allow configuring the behavior of Ctrl+C (they always abort), and that $host.UI.RawUI.ReadKey()
, which does, is not implemented in the ISE.Note:
The PowerShell ISE is no longer actively developed and there are reasons not to use it (bottom section), including the lack of a proper console, and, more fundamentally, the inability to run PowerShell (Core) 7+.
The actively developed, cross-platform editor that offers the best PowerShell development experience is Visual Studio Code with its PowerShell extension.