diff --git a/reference/docs-conceptual/PSScriptAnalyzer/Rules/AvoidGlobalVars.md b/reference/docs-conceptual/PSScriptAnalyzer/Rules/AvoidGlobalVars.md index add12c6..ba8413e 100644 --- a/reference/docs-conceptual/PSScriptAnalyzer/Rules/AvoidGlobalVars.md +++ b/reference/docs-conceptual/PSScriptAnalyzer/Rules/AvoidGlobalVars.md @@ -1,6 +1,6 @@ --- description: Avoid global variables -ms.date: 05/28/2026 +ms.date: 06/23/2026 ms.topic: reference title: AvoidGlobalVars --- @@ -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 @@ -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 ``` diff --git a/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseApprovedVerbs.md b/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseApprovedVerbs.md index 436db23..0223627 100644 --- a/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseApprovedVerbs.md +++ b/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseApprovedVerbs.md @@ -25,8 +25,7 @@ To learn more, see [Approved Verbs for PowerShell Commands][01]. ### Noncompliant ```powershell -function Change-Item -{ +function Change-Item { ... } ``` @@ -34,8 +33,7 @@ function Change-Item ### Compliant ```powershell -function Update-Item -{ +function Update-Item { ... } ``` diff --git a/reference/docs-conceptual/PSScriptAnalyzer/create-custom-rule.md b/reference/docs-conceptual/PSScriptAnalyzer/create-custom-rule.md index 748d200..c4a3fea 100644 --- a/reference/docs-conceptual/PSScriptAnalyzer/create-custom-rule.md +++ b/reference/docs-conceptual/PSScriptAnalyzer/create-custom-rule.md @@ -33,7 +33,7 @@ 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[]])] @@ -41,7 +41,7 @@ Include the `.DESCRIPTION` field. This field becomes the description for the cus ### 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 @@ -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 @@ -76,10 +76,10 @@ 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' } ``` @@ -87,13 +87,13 @@ Since version 1.17.0, you can include a **SuggestedCorrections** property of typ **IEnumerable\**. 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, @@ -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 } ``` @@ -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 @@ -144,11 +143,10 @@ 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()] @@ -156,83 +154,38 @@ function Measure-RequiresRunAsAdministrator $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' + } } } } diff --git a/reference/docs-conceptual/overview.md b/reference/docs-conceptual/overview.md index 2ee5308..7c4f14d 100644 --- a/reference/docs-conceptual/overview.md +++ b/reference/docs-conceptual/overview.md @@ -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 [01]: ./AIShell/overview.md diff --git a/reference/docs-conceptual/toc.yml b/reference/docs-conceptual/toc.yml index 7f05ada..0b7d953 100644 --- a/reference/docs-conceptual/toc.yml +++ b/reference/docs-conceptual/toc.yml @@ -181,53 +181,55 @@ items: href: PSScriptAnalyzer/Rules/UseUsingScopeModifierInNewRunspaces.md - name: UseUTF8EncodingForHelpFile href: PSScriptAnalyzer/Rules/UseUTF8EncodingForHelpFile.md - - name: SecretManagement and SecretStore + - name: Archived modules items: - - name: Overview - href: SecretManagement/overview.md - - name: Understanding the security features of SecretManagement - href: SecretManagement/security-concepts.md - - name: Get started - items: - - name: Get started with the SecretStore module - href: SecretManagement/get-started/using-secretstore.md - - name: Understanding the SecretManagement module - href: SecretManagement/get-started/understanding-secretmanagement.md - - name: How to - items: - - name: Managing a SecretStore vault - href: SecretManagement/how-to/manage-secretstore.md - - name: Use the SecretStore in automation - href: SecretManagement/how-to/using-secrets-in-automation.md - - name: Use Azure Key Vault in automation - href: SecretManagement/how-to/using-azure-keyvault.md - - name: AIShell - href: AIShell/toc.yml - - name: Crescendo - items: - - name: Overview - href: Crescendo/overview.md - - name: Get started - items: - - name: Install Crescendo - href: Crescendo/get-started/install-crescendo.md - - name: Choose a command-line tool - href: Crescendo/get-started/choose-command-line-tool.md - - name: Decide which features to amplify - href: Crescendo/get-started/research-tool.md - - name: Create a Crescendo cmdlet - href: Crescendo/get-started/create-new-cmdlet.md - - name: Generate and test a Crescendo module - href: Crescendo/get-started/generate-module.md - - name: What's new in Crescendo + - name: SecretManagement and SecretStore items: - - name: What's new in Crescendo 1.1 - href: Crescendo/whats-new/whats-new-in-crescendo-11.md - - name: Advanced topics + - name: Overview + href: SecretManagement/overview.md + - name: Understanding the security features of SecretManagement + href: SecretManagement/security-concepts.md + - name: Get started + items: + - name: Get started with the SecretStore module + href: SecretManagement/get-started/using-secretstore.md + - name: Understanding the SecretManagement module + href: SecretManagement/get-started/understanding-secretmanagement.md + - name: How to + items: + - name: Managing a SecretStore vault + href: SecretManagement/how-to/manage-secretstore.md + - name: Use the SecretStore in automation + href: SecretManagement/how-to/using-secrets-in-automation.md + - name: Use Azure Key Vault in automation + href: SecretManagement/how-to/using-azure-keyvault.md + - name: AIShell + href: AIShell/toc.yml + - name: Crescendo items: - - name: Create a configuration using the cmdlets - href: Crescendo/advanced/using-crescendo-cmdlets.md - - name: Handling errors in Crescendo - href: Crescendo/advanced/handling-errors.md - - name: Transforming arguments in Crescendo - href: Crescendo/advanced/transforming-arguments.md + - name: Overview + href: Crescendo/overview.md + - name: Get started + items: + - name: Install Crescendo + href: Crescendo/get-started/install-crescendo.md + - name: Choose a command-line tool + href: Crescendo/get-started/choose-command-line-tool.md + - name: Decide which features to amplify + href: Crescendo/get-started/research-tool.md + - name: Create a Crescendo cmdlet + href: Crescendo/get-started/create-new-cmdlet.md + - name: Generate and test a Crescendo module + href: Crescendo/get-started/generate-module.md + - name: What's new in Crescendo + items: + - name: What's new in Crescendo 1.1 + href: Crescendo/whats-new/whats-new-in-crescendo-11.md + - name: Advanced topics + items: + - name: Create a configuration using the cmdlets + href: Crescendo/advanced/using-crescendo-cmdlets.md + - name: Handling errors in Crescendo + href: Crescendo/advanced/handling-errors.md + - name: Transforming arguments in Crescendo + href: Crescendo/advanced/transforming-arguments.md