Powershell: In what conditions is a parameter disallowed because it is a read-only variable?

Created on 13 Mar 2018  路  4Comments  路  Source: PowerShell/PowerShell

Sometimes read-only variables are allowed as parameters, other times they are not. Is there a rule or some understandable mechanism at play here? Is there a way to predict which names cause a VariableNotWritable error when used as parameters?

Steps to reproduce

& {param($PSEdition)} # fails
& { [bool]((Get-Variable PSEdition | % Options) -band 'Constant') } # true
& {param([Alias('PSEdition')]$__PSEdition)} # succeeds

Set-Variable IAmAConstant -Value 'constant value' -Option Constant
& { [bool]((Get-Variable IAmAConstant | % Options) -band 'Constant') } # true
& {param($IAmAConstant)} # succeeds

$m = New-Module {}
& $m { [bool]((Get-Variable PSEdition | % Options) -band 'Constant') } # true
& $m {param($PSEdition)} # succeeds

Expected behavior

I expected either all constant variables or none to be prohibited as parameters.

Actual behavior

In some cases constant variables are permitted as parameters, in other cases they cause errors like following:

Cannot overwrite variable PSEdition because it is read-only or constant.
At line:1 char:1
+ & {param($PSEdition)} # fails
+ ~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : WriteError: (PSEdition:String) [], SessionStateUnauthorizedAccessExce 
   ption
    + FullyQualifiedErrorId : VariableNotWritable

Environment data

> $PSVersionTable

Name                           Value                                            
----                           -----                                            
PSVersion                      6.0.0                                            
PSEdition                      Core                                             
GitCommitId                    v6.0.0                                           
OS                             Microsoft Windows 6.3.9600                       
Platform                       Win32NT                                          
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}                          
PSRemotingProtocolVersion      2.3                                              
SerializationVersion           1.1.0.1                                          
WSManStackVersion              3.0                                              
Issue-Bug WG-Engine

All 4 comments

Hi @alx9r, there are several things going on here.

In your first example with the parameter alias, you aren't actually touching the $PSEdition variable. The alias maps -PSEdition to $__PSEdition which is a completely different variable and so there is no error.

The behavior in the second example has to do with scopes and the AllScope option. A constant variable may be masked by a non-constant variable in a child scope unless that variable is marked AllScope. This is what's happening in your second example. Using '.' instead of '&' will cause it to fail because '.' evaluates the scriptblock in the current scope:

PS[1] (20) > & {param($IAmAConstant)} # succeeds because parameter is in child scope
PS[1] (21) > . {param($IAmAConstant)} # fails because parameter is in current scope
Cannot overwrite variable IAmAConstant because it is read-only or constant.
At line:1 char:1
+ . {param($IAmAConstant)} # fails because parameter is in current scop ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : WriteError: (IAmAConstant:String) [], SessionStateUnauthorizedAccessException
    + FullyQualifiedErrorId : VariableNotWritable

(See about_Scopes for more information about how scopes and scope options work.)

Finally the third example, involving module scopes, does seem to be a bug. Trying to modify a global, constant, allscope variable should result in an error even when evaluating a scriptblock in a module scope.

To summarize, marking a variable as AllScope prevents it from being hidden in a child scope. And there seems to be a bug in the way AllScope variables are being handled when evaluating a scriptblock in a module context. @lzybkr - any thoughts?

A constant variable may be masked by a non-constant variable in a child scope unless that variable is marked AllScope. This is what's happening in your second example.

I see. The following seems to be consistent with this explanation:

Set-Variable IAmAnAllScopeConstant -Value 'constant value' -Option Constant,AllScope
& { Get-Variable IAmAnAllScopeConstant | % Options } # Constant, AllScope
& {param($IAmAnAllScopeConstant)} # fails

Set-Variable IAmAConstant -Value 'constant value' -Option Constant
. {param($IAmAConstant)} # fails

Finally the third example, involving module scopes, does seem to be a bug. Trying to modify a global, constant, allscope variable should result in an error even when evaluating a scriptblock in a module scope.

I'm not sure this is quite on-point, but I've opened #6378 as it seems that a write to an AllScope variable from a module does not affect that variable's value in the "global" session state.

Finally the third example, involving module scopes, does seem to be a bug. Trying to modify a global, constant, allscope variable should result in an error even when evaluating a scriptblock in a module scope.

@BrucePay Unlike $Error and $PSDefaultParameterValues that are declared in every SessionState, $PSEdition is only declared in the default SessionState of the current Runspace. So the module scope doesn't have the variable PSEdition defined at all, and thus & $m {param($PSEdition)} works.

PS:106> $m = New-Module {}
PS:107> & $m {Get-Variable PSEdition -Scope local }
Get-Variable : Cannot find a variable with the name 'PSEdition'.
At line:1 char:7
+ & $m {Get-Variable PSEdition -Scope local }
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (PSEdition:String) [Get-Variable], ItemNotFoundException
+ FullyQualifiedErrorId : VariableNotFound,Microsoft.PowerShell.Commands.GetVariableCommand

Are you considering making PSEdition and other all scope const variables like $Host and $PID to be declared in every SessionState?

Was this page helpful?
0 / 5 - 0 ratings