PowerShell Workflow中Azure VM脚本报unexpected Token错误,求修正
Let's break down the issues in your script and fix them step by step:
1. Immediate Syntax Error Fix
The error Unexpected Token in Expression or Statement on the line $tags2 ['ShutdownSchedule_AllowStop'] = "$False"; comes down to whitespace strictness in PowerShell Workflows. Unlike regular PowerShell, Workflows don't allow spaces between a variable and its indexing brackets.
Change this:
$tags2 ['ShutdownSchedule_AllowStop'] = "$False";
To this:
$tags2['ShutdownSchedule_AllowStop'] = $false; # Use boolean $false instead of string "$False" for better compatibility
Note: Using the boolean $false instead of a string "$False" is better practice, as Azure resources expect native boolean values for tag fields when applicable.
2. Additional Logic & Scope Issues
Your script has a few other hidden problems that will prevent it from working correctly even after fixing the syntax error:
a. Incorrect Pipeline Variable Usage
Inside your ForEach ($vm in $vms) loop, you're using $_.Tags—but $_ (the pipeline automatic variable) doesn't exist here. Since you're iterating with a named variable $vm, you need to reference $vm.Tags instead.
b. Misplaced Set-AzureRmResource Command
You're calling Set-AzureRmResource outside the inner VM loop, which means it will only apply tag changes to the last VM processed in the loop. Move this command inside the ForEach ($vm in $vms) block to update every VM's tags.
c. Parallel Loop Variable Scope
When using ForEach -Parallel in a Workflow, you need to ensure the variable containing your VM list is accessible in the parallel execution scope. First, collect all target VMs into a single array outside the parallel loop, then use the $Using: scope modifier to reference it from within the parallel block.
d. Handling VMs with No Existing Tags
If a VM has no existing tags, $vm.Tags will be $null, which will cause errors when trying to modify it. We'll add a check to create an empty hashtable in this case.
Fixed Complete Script
workflow Set-AzureRmTags-and-Start { [CmdletBinding()] Param( [Parameter(Mandatory=$True)] [string]$VmRG ) $Start = Get-Date Write-Output "Start Time: $Start" # Collect all matching resource groups $VmRGs = Get-AzureRmResourceGroup | Where-Object { $_.ResourceGroupName -like "*$VmRG*" } # Initialize array to hold all VMs for parallel startup later $allVMs = @() foreach ($rg in $VmRGs) { $VMs = Get-AzureRmVM -ResourceGroupName $rg.ResourceGroupName $allVMs += $VMs ForEach ($vm in $VMs) { # Handle VMs with no existing tags by creating an empty hashtable $tags2 = if ($vm.Tags) { $vm.Tags.Clone() } else { @{} } # Update the tag with a boolean value $tags2['ShutdownSchedule_AllowStop'] = $false; # Apply tag changes to the current VM Set-AzureRmResource -ResourceName $vm.Name ` -ResourceGroupName $vm.ResourceGroupName ` -ResourceType "Microsoft.Compute/virtualMachines" ` -Tag $tags2 ` -Force ` -Verbose } } $Middle = Get-Date Write-Output "Time Taken To Assign Tags: $((Get-Date) - $Start).Minutes Minutes" # Parallel VM startup using $Using: to access the allVMs array from workflow scope ForEach -Parallel ($vm in $Using:allVMs) { Start-AzureRmVM -Name $vm.Name ` -ResourceGroupName $vm.ResourceGroupName ` -Verbose ` -Confirm:$false } $End = Get-Date Write-Output "Time Taken To Start VMs: $((Get-Date) - $Middle).Minutes Minutes" Write-Output "Total Time Taken: $((Get-Date) - $Start).Minutes Minutes" }
Key Changes Explained
- Fixed the whitespace in hashtable indexing and switched to native boolean
$false. - Replaced
$_.Tagswith$vm.Tagsto correctly reference the current VM's tags. - Moved
Set-AzureRmResourceinside the VM loop to ensure every VM gets updated tags. - Collected all VMs into
$allVMsand used$Using:allVMsto make the array accessible in the parallel loop. - Added logic to handle VMs with no existing tags, preventing null reference errors.
内容的提问来源于stack exchange,提问作者Norrin Rad

