During some testing today I came across an unexpected issue and I do not understand why it is happening. Below is the code I am using to duplicate the issue. It is only a very small portion of the larger project.
Testing is being cone on Windows 10 Build 1709, if that helps
Both the PS1 File and the BAT File are named the same.
Ways to cause the errors
Right-Click - Run with PowerShell will cause the errorPowerShell ISE in Non-Admin Mode, then opening/running the script will cause the errorWays to avoid the errors
PowerShell ISE in Admin Mode, then opening/running the script will not cause the errorScript: in front of the variables on the last 2 lines of code will not cause the error no matter how the script is executedAdmin, it will launch PowerShell.exe outside of VSCode and work without issue-
Why do I have Script: in front of the variables in the functions? It was the only way I could get variables set in the functions to be used outside the functions. The other 25 or so variables not listed in this post do not have an issue, however, they are not modified like these two are after they are set.
The Questions
Admin Mode, it will work?Something isn't making sense and I cannot pinpoint it.
Here are the errors
Cannot overwrite variable NetFX3 because the variable has been optimized. Try using the New-Variable or Set-Variable cmdlet (without any aliases), or dot-source the command that you are using to set the variable. At C:\Users\a502690530\Desktop\Testing2.ps1:14 char:5 + [string]$Script:NetFX3 = $BAT_Files_Path + "NetFX3.zip" + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : WriteError: (NetFX3:String) [], SessionStateUnauthorizedAccessException + FullyQualifiedErrorId : VariableNotWritableRare
Cannot overwrite variable Power_Plan because the variable has been optimized. Try using the New-Variable or Set-Variable cmdlet (without any aliases), or dot-source the command that you are using to set the variable. At C:\Users\a502690530\Desktop\Testing2.ps1:15 char:5 + [string]$Script:Power_Plan = $BAT_Files_Path + "Power_Plan.zip" + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : WriteError: (Power_Plan:String) [], SessionStateUnauthorizedAccessException + FullyQualifiedErrorId : VariableNotWritableRare
Here is the code
# Checks if running as an administrator. If not, it will relaunch as an administrator
If (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$Arguments = "& '" + $MyInvocation.MyCommand.Definition + "'"
Start-Process Powershell -Verb RunAs -ArgumentList $Arguments
Break
}
[string]$ErrorActionPreference = "Continue"
[string]$BAT_Files = $Root_Path + "BAT_Files\"
Function Set-FilePaths ([string]$BAT_Files_Path) {
# BAT Files Paths (ZIPs only!!!)
[string]$Script:NetFX3 = $BAT_Files_Path + "NetFX3.zip"
[string]$Script:Power_Plan = $BAT_Files_Path + "Power_Plan.zip"
Set-Lists
}
function Set-Lists {
# List of BAT Files (ZIPs)
[System.Collections.ArrayList]$Script:List_Of_BAT_Files = @(
$NetFX3
$Power_Plan
)
}
Set-FilePaths `
-BAT_Files_Path $BAT_Files
PAUSE
$NetFX3 = ((Split-Path $NetFX3 -Parent) + "\NetFX3\")
$Power_Plan = ((Split-Path $Power_Plan -Parent) + "\Power_Plan\")
BAT File to launch
REG ADD "HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" /T REG_SZ /V ExecutionPolicy /D Unrestricted /F
Start PowerShell.exe -Command "& '%~dpn0.ps1'"
I have no specific answer, but a pointer:
Your issue sounds like a PowerShell bug related to the DLR (Dynamic Language Runtime), a technology PowerShell uses behind the scenes (since v3); there's at least one open bug report on GitHub that sounds related.
Aside from the workaround you already know of - using scope modifier script consistently - I suggest avoiding variable access across scope boundaries as a general best practice, which should also avoid the problem.
PowerShell is very flexible in what it can return (output) from a function, so it's better to set variables in the caller's scope based on a function's output.
Specifically, I suggest refactoring your code as follows:
Function Get-FilePaths ([string]$BAT_Files_Path) {
# Output the paths as an *array*.
($BAT_Files_Path + "NetFX3.zip"), ($BAT_Files_Path + "Power_Plan.zip")
}
# Call the function in the script scope and capture its output in variables.
$List_Of_BAT_Files = Get-FilePaths
# Use a destructuring assignment to store the elements of the array
# in individual variables
$NetFX3, $Power_Plan = $List_Of_BAT_Files
If there are a lot of individual variables to set, you can make the function output a hash table instead, and use the hash table's named entries instead of individual variables (requires PSv3+, due to use of [ordered] to create a hash table with ordered keys):
Function Get-FilePaths ([string]$BAT_Files_Path) {
# Output the paths as a *hash table*, using its
# entries for named access instead of individual variables.
$outHash = [ordered] @{
NetFX3 = $BAT_Files_Path + "NetFX3.zip"
Power_Plan = $BAT_Files_Path + "Power_Plan.zip"
}
# Add a 'List' entry that contains all values added above as an array.
# Note the need to use @(...) to force creation of a new array from the
# hash table's value collection.
$outHash.List = @($outHash.Values)
# Output the hash table.
$outHash
}
# Call the function in the script scope and capture its output in
# a single variable that receives the hash table.
$hash = Get-FilePaths
# Now you can access the invididual values by name - e.g., $hash.NetFX3 -
# or use $hash.List to get all values.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With