From 5645046fd9b7b0e8a648eded28a6abf85edabfd3 Mon Sep 17 00:00:00 2001 From: Sean Wheeler Date: Tue, 23 Jun 2026 08:23:06 -0500 Subject: [PATCH 1/3] Update landing page and toc (#424) * Update landing page and toc * Change wording to match TOC * formatting --- reference/docs-conceptual/overview.md | 10 +-- reference/docs-conceptual/toc.yml | 96 ++++++++++++++------------- 2 files changed, 54 insertions(+), 52 deletions(-) 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 From cbf290cfeb7bac344a0dae521f108c7955cf44e8 Mon Sep 17 00:00:00 2001 From: Sean Wheeler Date: Tue, 23 Jun 2026 11:21:16 -0500 Subject: [PATCH 2/3] Update example based on UUF comments (#425) * Update example based on UUF comments * Simplify example --- .../PSScriptAnalyzer/create-custom-rule.md | 153 ++++++------------ 1 file changed, 53 insertions(+), 100 deletions(-) diff --git a/reference/docs-conceptual/PSScriptAnalyzer/create-custom-rule.md b/reference/docs-conceptual/PSScriptAnalyzer/create-custom-rule.md index 748d200..1e2a21c 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 } ``` @@ -132,7 +131,7 @@ Export-ModuleMember -Function (FunctionName) 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 + 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' + } } } } From 980f86dc7a8df24dabd106eab80d20c2ede8ec11 Mon Sep 17 00:00:00 2001 From: Sean Wheeler Date: Tue, 23 Jun 2026 15:25:31 -0500 Subject: [PATCH 3/3] Add reason for AvoidGlobalVars (#426) * Update example based on UUF comments * Simplify example * Add reason for AvoidGlobalVars * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../PSScriptAnalyzer/Rules/AvoidGlobalVars.md | 29 +++++++++++-------- .../Rules/UseApprovedVerbs.md | 6 ++-- .../PSScriptAnalyzer/create-custom-rule.md | 2 +- 3 files changed, 20 insertions(+), 17 deletions(-) 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 1e2a21c..c4a3fea 100644 --- a/reference/docs-conceptual/PSScriptAnalyzer/create-custom-rule.md +++ b/reference/docs-conceptual/PSScriptAnalyzer/create-custom-rule.md @@ -130,7 +130,7 @@ 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 + 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.