Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Avoid global variables
ms.date: 05/28/2026
ms.date: 06/23/2026
ms.topic: reference
title: AvoidGlobalVars
---
Expand All @@ -10,11 +10,14 @@ title: AvoidGlobalVars

## Description

You should avoid modifying global variables in your scripts and functions because other scripts or
functions that run in the same session can depend on them. This can lead to unexpected behavior and
make it difficult to debug your code.

This rule detects usage of variables with the global scope modifier. PowerShell controls access to
variables, functions, aliases, and drives through a mechanism known as scoping. Variables and
functions that are present when PowerShell starts have been created in the global scope.

Globally scoped variables include:
functions that are present when PowerShell starts were created in the global scope. Globally scoped
variables include:

- Automatic variables
- Preference variables
Expand All @@ -34,21 +37,23 @@ Use local or script scope for variables instead of the global scope. To learn mo
### Noncompliant

```powershell
$Global:var1 = $null
function Test-NotGlobal ($var)
{
$a = $var + $var1
$var1 = 'foo'
function Test-NotGlobal ($var) {
$Global:var1 = $var
}
Test-NotGlobal 'bar'
$var1
```

### Compliant

```powershell
$var1 = $null
function Test-NotGlobal ($var1, $var2)
{
$a = $var1 + $var2
$var1 = 'foo'
function Test-NotGlobal ($var) {
$var1 = $var
}
Test-NotGlobal 'bar'
$var1
```

<!-- link references -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,15 @@ To learn more, see [Approved Verbs for PowerShell Commands][01].
### Noncompliant

```powershell
function Change-Item
{
function Change-Item {
...
}
```

### Compliant

```powershell
function Update-Item
{
function Update-Item {
...
}
```
Expand Down
155 changes: 54 additions & 101 deletions reference/docs-conceptual/PSScriptAnalyzer/create-custom-rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ Include the `.DESCRIPTION` field. This field becomes the description for the cus
#>
```

### Output type should be **DiagnosticRecord**
### Output type should be an array of **DiagnosticRecord** objects

```powershell
[OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
```

### Each function must have a Token array or an Ast parameter

The name of the **Ast** parameter name must end with **Ast**.
The name of the **Ast** parameter name must end with `Ast`.

```powershell
Param
Expand All @@ -53,7 +53,7 @@ Param
)
```

The name of the **Token** parameter name must end with **Token**.
The name of the **Token** parameter name must end with `Token`.

```powershell
Param
Expand All @@ -76,24 +76,24 @@ The **DiagnosticRecord** should have at least four properties:

```powershell
$result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{
"Message" = "This is a sample rule"
"Extent" = $ast.Extent
"RuleName" = $PSCmdlet.MyInvocation.InvocationName
"Severity" = "Warning"
Message = 'This is a sample rule'
Extent = $ast.Extent
RuleName = $PSCmdlet.MyInvocation.InvocationName
Severity = 'Warning'
}
```

Since version 1.17.0, you can include a **SuggestedCorrections** property of type
**IEnumerable\<CorrectionExtent\>**. Make sure to specify the correct type. For example:

```powershell
[int]$startLineNumber = $ast.Extent.StartLineNumber
[int]$endLineNumber = $ast.Extent.EndLineNumber
[int]$startColumnNumber = $ast.Extent.StartColumnNumber
[int]$endColumnNumber = $ast.Extent.EndColumnNumber
[string]$correction = 'Correct text that replaces Extent text'
[string]$file = $MyInvocation.MyCommand.Definition
[string]$optionalDescription = 'Useful but optional description text'
$startLineNumber = $ast.Extent.StartLineNumber
$endLineNumber = $ast.Extent.EndLineNumber
$startColumnNumber = $ast.Extent.StartColumnNumber
$endColumnNumber = $ast.Extent.EndColumnNumber
$correction = 'Correct text that replaces Extent text'
$file = $MyInvocation.MyCommand.Definition
$optionalDescription = 'Useful but optional description text'
$objParams = @{
TypeName = 'Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent'
ArgumentList = $startLineNumber, $endLineNumber, $startColumnNumber,
Expand All @@ -104,13 +104,12 @@ $suggestedCorrections = New-Object System.Collections.ObjectModel.Collection[$($
$suggestedCorrections.add($correctionExtent) | Out-Null

[Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord]@{
"Message" = "This is a rule with a suggested correction"
"Extent" = $ast.Extent
"RuleName" = $PSCmdlet.MyInvocation.InvocationName
"Severity" = "Warning"
"Severity" = "Warning"
"RuleSuppressionID" = "MyRuleSuppressionID"
"SuggestedCorrections" = $suggestedCorrections
Message = 'This is a rule with a suggested correction'
Extent = $ast.Extent
RuleName = $PSCmdlet.MyInvocation.InvocationName
Severity = 'Warning'
RuleSuppressionID = 'MyRuleSuppressionID'
SuggestedCorrections = $suggestedCorrections
}
```

Expand All @@ -131,8 +130,8 @@ Export-ModuleMember -Function (FunctionName)
.DESCRIPTION
The #Requires statement prevents a script from running unless the Windows PowerShell
version, modules, snap-ins, and module and snap-in version prerequisites are met.
From Windows PowerShell 4.0, the #Requires statement let script developers require that
sessions be run with elevated user rights (run as Administrator). Script developers does
Since Windows PowerShell 4.0, the #Requires statement lets script developers require that
sessions be run with elevated user rights (run as Administrator). Script developers do
not need to write their own methods any more. To fix a violation of this rule, please
consider using #Requires -RunAsAdministrator instead of your own methods.
.EXAMPLE
Expand All @@ -144,95 +143,49 @@ Export-ModuleMember -Function (FunctionName)
.NOTES
None
#>
function Measure-RequiresRunAsAdministrator
{
function Measure-RequiresRunAsAdministrator {
[CmdletBinding()]
[OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
Param
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.Language.ScriptBlockAst]
$ScriptBlockAst
)

Process
{
$results = @()
try
{
#region Define predicates to find ASTs.
# Finds specific method, IsInRole.
[ScriptBlock]$predicate1 = {
param ([System.Management.Automation.Language.Ast]$Ast)
[bool]$returnValue = $false
if ($Ast -is [System.Management.Automation.Language.MemberExpressionAst])
{
[System.Management.Automation.Language.MemberExpressionAst]$meAst = $Ast
if ($meAst.Member -is [System.Management.Automation.Language.StringConstantExpressionAst])
{
[System.Management.Automation.Language.StringConstantExpressionAst]$sceAst = $meAst.Member
if ($sceAst.Value -eq 'isinrole')
{
$returnValue = $true
}
}
}
return $returnValue
}
begin {
$MeasureRequiresAdmin = @(
'The #Requires statement prevents a script from running unless the PowerShell version,'
'modules, snap-ins, and module and snap-in version prerequisites are met. Since'
'Windows PowerShell 4.0, the #Requires statement lets script developers require that'
'sessions be run with elevated user rights (run as Administrator). Script developers'
'don''t need to write their own methods to test for elevated rights. To fix a violation'
'of this rule, use #Requires -RunAsAdministrator instead of your own methods.'
) -join ' '

# Finds specific method, IsInRole.
[ScriptBlock]$predicate = {
param($Ast)
return $Ast.Member.Value -eq 'IsInRole'
}
}

# Finds specific value, [system.security.principal.windowsbuiltinrole]::administrator.
[ScriptBlock]$predicate2 = {
param ([System.Management.Automation.Language.Ast]$Ast)
[bool]$returnValue = $false
if ($Ast -is [System.Management.Automation.Language.AssignmentStatementAst])
{
[System.Management.Automation.Language.AssignmentStatementAst]$asAst = $Ast
if ($asAst.Right.ToString() -eq '[system.security.principal.windowsbuiltinrole]::administrator')
{
$returnValue = $true
}
}
return $returnValue
}
#endregion
#region Finds ASTs that match the predicates.

[System.Management.Automation.Language.Ast[]]$methodAst = $ScriptBlockAst.FindAll($predicate1, $true)
[System.Management.Automation.Language.Ast[]]$assignmentAst = $ScriptBlockAst.FindAll($predicate2, $true)
if ($null -ne $ScriptBlockAst.ScriptRequirements)
{
if ((!$ScriptBlockAst.ScriptRequirements.IsElevationRequired) -and
($methodAst.Count -ne 0) -and ($assignmentAst.Count -ne 0))
{
$result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{
'Message' = $Messages.MeasureRequiresRunAsAdministrator
'Extent' = $assignmentAst.Extent
'RuleName' = $PSCmdlet.MyInvocation.InvocationName
'Severity' = 'Information'
}
$results += $result
}
}
else
{
if (($methodAst.Count -ne 0) -and ($assignmentAst.Count -ne 0))
{
$result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{
'Message' = $Messages.MeasureRequiresRunAsAdministrator
'Extent' = $assignmentAst.Extent
'RuleName' = $PSCmdlet.MyInvocation.InvocationName
'Severity' = 'Information'
}
$results += $result
}
}
return $results
#endregion
process {
# Exit early if the script block has a #Requires -RunAsAdministrator statement.
if ($ScriptBlockAst.ScriptRequirements.IsElevationRequired) {
return
}
catch
{
$PSCmdlet.ThrowTerminatingError($PSItem)

# Test for calls to IsInRole() method
[System.Management.Automation.Language.Ast]$methodAst = $ScriptBlockAst.Find($predicate, $true)
if ($methodAst) {
[Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{
Message = $MeasureRequiresAdmin
Extent = $methodAst.Extent
RuleName = $PSCmdlet.MyInvocation.InvocationName
Severity = 'Information'
}
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions reference/docs-conceptual/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ This documentation set contains cmdlet reference for the following modules.
- [PlatyPS][05]
- [PSScriptAnalyzer][06]

Deprecated modules
Archived modules

- [Microsoft.PowerShell.SecretManagement][03]
- [Microsoft.PowerShell.SecretStore][04]
- [Microsoft.PowerShell.Crescendo][02] - **Deprecated May 2026**
- [AIShell][01] - **Deprecated January 2026**
- [Microsoft.PowerShell.SecretManagement][03] - Retired June 2026
- [Microsoft.PowerShell.SecretStore][04] - Retired June 2026
- [Microsoft.PowerShell.Crescendo][02] - Retired May 2026
- [AIShell][01] - Retired January 2026

<!-- link references -->
[01]: ./AIShell/overview.md
Expand Down
Loading
Loading