diff --git a/reference/docs-conceptual/PSScriptAnalyzer/Rules/AlignAssignmentStatement.md b/reference/docs-conceptual/PSScriptAnalyzer/Rules/AlignAssignmentStatement.md index c2709ac..f243d97 100644 --- a/reference/docs-conceptual/PSScriptAnalyzer/Rules/AlignAssignmentStatement.md +++ b/reference/docs-conceptual/PSScriptAnalyzer/Rules/AlignAssignmentStatement.md @@ -1,6 +1,6 @@ --- description: Align assignment statement -ms.date: 06/28/2023 +ms.date: 03/20/2026 ms.topic: reference title: AlignAssignmentStatement --- @@ -10,38 +10,54 @@ title: AlignAssignmentStatement ## Description -Consecutive assignment statements are more readable if they are aligned. By aligned, we imply that -the `equal` sign for all the assignment statements should be in the same column. +Consecutive assignment statements are more readable when they're aligned. Assignments are considered +aligned when their `equals` signs line up vertically. -The rule looks for key (property) value pairs in a hashtable (DSC configuration) to check if they -are aligned or not. Consider the following example in which the key value pairs are not aligned. +This rule looks at the key-value pairs in hashtables (including DSC configurations) as well as enum +definitions. + +Consider the following example with a hashtable and enum that isn't aligned. ```powershell $hashtable = @{ - property1 = 'value' + property = 'value' anotherProperty = 'another value' } + +enum Enum { + member = 1 + anotherMember = 2 +} ``` Alignment in this case would look like the following. ```powershell $hashtable = @{ - property1 = 'value' + property = 'value' anotherProperty = 'another value' } + +enum Enum { + member = 1 + anotherMember = 2 +} ``` -The rule ignores hashtables in which the assignment statements are on the same line. For example, -the rule ignores `$h = {a = 1; b = 2}`. +The rule ignores any assignments within hashtables and enums which are on the same line as others. +For example, the rule ignores `$h = @{a = 1; b = 2}`. ## Configuration ```powershell Rules = @{ PSAlignAssignmentStatement = @{ - Enable = $true - CheckHashtable = $true + Enable = $true + CheckHashtable = $true + AlignHashtableKvpWithInterveningComment = $true + CheckEnum = $true + AlignEnumMemberWithInterveningComment = $true + IncludeValuelessEnumMembers = $true } } ``` @@ -52,8 +68,118 @@ Rules = @{ Enable or disable the rule during ScriptAnalyzer invocation. -#### CheckHashtable: bool (Default value is `$false`) +#### CheckHashtable: bool (Default value is `$true`) Enforce alignment of assignment statements in a hashtable and in a DSC Configuration. There is only -one switch for hasthable and DSC configuration because the property value pairs in a DSC +one setting for hashtable and DSC configuration because the property value pairs in a DSC configuration are parsed as key-value pairs of a hashtable. + +#### AlignHashtableKvpWithInterveningComment: bool (Default value is `$true`) + +Include key-value pairs in the alignment that have an intervening comment - that is to say a comment +between the key name and the equals sign. + +Consider the following: + +```powershell +$hashtable = @{ + property = 'value' + anotherProperty <#A Comment#> = 'another value' + anotherDifferentProperty = 'yet another value' +} +``` + +With this setting disabled, the line with the comment is ignored, and it would be aligned like so: + +```powershell +$hashtable = @{ + property = 'value' + anotherProperty <#A Comment#> = 'another value' + anotherDifferentProperty = 'yet another value' +} +``` + +With it enabled, the comment line is included in alignment: + +```powershell +$hashtable = @{ + property = 'value' + anotherProperty <#A Comment#> = 'another value' + anotherDifferentProperty = 'yet another value' +} +``` + +#### CheckEnum: bool (Default value is `$true`) + +Enforce alignment of assignment statements of an Enum definition. + +#### AlignEnumMemberWithInterveningComment: bool (Default value is `$true`) + +Include enum members in the alignment that have an intervening comment - that is to say a comment +between the member name and the equals sign. + +Consider the following: + +```powershell +enum Enum { + member = 1 + anotherMember <#A Comment#> = 2 + anotherDifferentMember = 3 +} +``` + +With this setting disabled, the line with the comment is ignored, and it would be aligned like so: + +```powershell +enum Enum { + member = 1 + anotherMember <#A Comment#> = 2 + anotherDifferentMember = 3 +} +``` + +With it enabled, the comment line is included in alignment: + +```powershell +enum Enum { + member = 1 + anotherMember <#A Comment#> = 2 + anotherDifferentMember = 3 +} +``` + +#### IncludeValuelessEnumMembers: bool (Default value is `$true`) + +Include enum members in the alignment that don't have an explicitly assigned value. Enum's don't +need to be given a value when they're defined. + +Consider the following: + +```powershell +enum Enum { + member = 1 + anotherMember = 2 + anotherDifferentMember +} +``` + +With this setting disabled, the third line, which has no value, isn't considered when choosing where +to align assignments. It would be aligned like so: + +```powershell +enum Enum { + member = 1 + anotherMember = 2 + anotherDifferentMember +} +``` + +With it enabled, the valueless member is included in alignment as if it had a value: + +```powershell +enum Enum { + member = 1 + anotherMember = 2 + anotherDifferentMember +} +``` diff --git a/reference/docs-conceptual/PSScriptAnalyzer/Rules/AvoidLongLines.md b/reference/docs-conceptual/PSScriptAnalyzer/Rules/AvoidLongLines.md index 4a0d98d..89474c8 100644 --- a/reference/docs-conceptual/PSScriptAnalyzer/Rules/AvoidLongLines.md +++ b/reference/docs-conceptual/PSScriptAnalyzer/Rules/AvoidLongLines.md @@ -1,6 +1,6 @@ --- description: Avoid long lines -ms.date: 04/29/2025 +ms.date: 03/20/2026 ms.topic: reference title: AvoidLongLines --- @@ -10,8 +10,8 @@ title: AvoidLongLines ## Description -The length of lines, including leading spaces (indentation), should less than the configured number -of characters. The default length is 120 characters. +The length of lines, including leading spaces (indentation), should be less than the configured +number of characters. The default length is 120 characters. > [!NOTE] > This rule isn't enabled by default. The user needs to enable it through settings. diff --git a/reference/docs-conceptual/PSScriptAnalyzer/Rules/README.md b/reference/docs-conceptual/PSScriptAnalyzer/Rules/README.md index da1058b..fca031e 100644 --- a/reference/docs-conceptual/PSScriptAnalyzer/Rules/README.md +++ b/reference/docs-conceptual/PSScriptAnalyzer/Rules/README.md @@ -1,6 +1,6 @@ --- description: List of PSScriptAnalyzer rules -ms.date: 03/27/2024 +ms.date: 03/20/2026 ms.topic: reference title: List of PSScriptAnalyzer rules --- @@ -68,7 +68,10 @@ The PSScriptAnalyzer contains the following rule definitions. | [UseCompatibleSyntax](./UseCompatibleSyntax.md) | Warning | No | Yes | | [UseCompatibleTypes](./UseCompatibleTypes.md) | Warning | No | Yes | | [UseConsistentIndentation](./UseConsistentIndentation.md) | Warning | No | Yes | +| [UseConsistentParameterSetName](./UseConsistentParameterSetName.md) | Warning | No | | +| [UseConsistentParametersKind](./UseConsistentParametersKind.md) | Warning | No | Yes | | [UseConsistentWhitespace](./UseConsistentWhitespace.md) | Warning | No | Yes | +| [UseConstrainedLanguageMode](./UseConstrainedLanguageMode.md) | Warning | No | Yes | | [UseCorrectCasing](./UseCorrectCasing.md) | Information | No | Yes | | [UseDeclaredVarsMoreThanAssignments](./UseDeclaredVarsMoreThanAssignments.md) | Warning | Yes | | | [UseLiteralInitializerForHashtable](./UseLiteralInitializerForHashtable.md) | Warning | Yes | | @@ -76,6 +79,7 @@ The PSScriptAnalyzer contains the following rule definitions. | [UseProcessBlockForPipelineCommand](./UseProcessBlockForPipelineCommand.md) | Warning | Yes | | | [UsePSCredentialType](./UsePSCredentialType.md) | Warning | Yes | | | [UseShouldProcessForStateChangingFunctions](./UseShouldProcessForStateChangingFunctions.md) | Warning | Yes | | +| [UseSingleValueFromPipelineParameter](./UseSingleValueFromPipelineParameter.md) | Warning | No | | | [UseSingularNouns](./UseSingularNouns.md) | Warning | Yes | Yes | | [UseSupportsShouldProcess](./UseSupportsShouldProcess.md) | Warning | Yes | | | [UseToExportFieldsInManifest](./UseToExportFieldsInManifest.md) | Warning | Yes | | diff --git a/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseConsistentParameterSetName.md b/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseConsistentParameterSetName.md new file mode 100644 index 0000000..c9b4b12 --- /dev/null +++ b/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseConsistentParameterSetName.md @@ -0,0 +1,144 @@ +--- +description: Use consistent parameter set names and proper parameter set configuration. +ms.date: 03/20/2026 +ms.topic: reference +title: UseConsistentParameterSetName +--- + +# UseConsistentParameterSetName + +**Severity Level: Warning** + +## Description + +Parameter set names in PowerShell are case-sensitive, unlike most other PowerShell elements. This +rule ensures consistent casing and proper configuration of parameter sets to avoid runtime errors +and improve code clarity. + +The rule performs five different checks: + +1. **Missing DefaultParameterSetName** - Warns when parameter sets are used but no default is + specified +1. **Multiple parameter declarations** - Detects when a parameter is declared multiple times in the + same parameter set. This is ultimately a runtime exception - this check helps catch it sooner. +1. **Case mismatch between DefaultParameterSetName and ParameterSetName** - Ensures consistent + casing +1. **Case mismatch between different ParameterSetName values** - Ensures all references to the same + parameter set use identical casing +1. **Parameter set names containing newlines** - Warns against using newline characters in parameter + set names + +> [!NOTE] +> This rule isn't enabled by default. The user needs to enable it through settings. + +## How + +- Use a `DefaultParameterSetName` when defining multiple parameter sets +- Ensure consistent casing between `DefaultParameterSetName` and `ParameterSetName` values +- Use identical casing for all references to the same parameter set name +- Avoid declaring the same parameter multiple times in a single parameter set +- Do not use newline characters in parameter set names + +## Example + +### Wrong + +```powershell +# Missing DefaultParameterSetName +function Get-Data { + [CmdletBinding()] + param( + [Parameter(ParameterSetName='ByName')] + [string]$Name, + + [Parameter(ParameterSetName='ByID')] + [int]$ID + ) +} + +# Case mismatch between DefaultParameterSetName and ParameterSetName +function Get-Data { + [CmdletBinding(DefaultParameterSetName='ByName')] + param( + [Parameter(ParameterSetName='byname')] + [string]$Name, + + [Parameter(ParameterSetName='ByID')] + [int]$ID + ) +} + +# Inconsistent casing between ParameterSetName values +function Get-Data { + [CmdletBinding(DefaultParameterSetName='ByName')] + param( + [Parameter(ParameterSetName='ByName')] + [string]$Name, + + [Parameter(ParameterSetName='byname')] + [string]$DisplayName + ) +} + +# Multiple parameter declarations in same set +function Get-Data { + param( + [Parameter(ParameterSetName='ByName')] + [Parameter(ParameterSetName='ByName')] + [string]$Name + ) +} + +# Parameter set name with newline +function Get-Data { + param( + [Parameter(ParameterSetName="Set`nOne")] + [string]$Name + ) +} +``` + +### Correct + +```powershell +# Proper parameter set configuration +function Get-Data { + [CmdletBinding(DefaultParameterSetName='ByName')] + param( + [Parameter(ParameterSetName='ByName', Mandatory)] + [string]$Name, + + [Parameter(ParameterSetName='ByName')] + [Parameter(ParameterSetName='ByID')] + [string]$ComputerName, + + [Parameter(ParameterSetName='ByID', Mandatory)] + [int]$ID + ) +} +``` + +## Configuration + +```powershell +Rules = @{ + PSUseConsistentParameterSetName = @{ + Enable = $true + } +} +``` + +### Parameters + +- `Enable`: **bool** (Default value is `$false`) + + Enable or disable the rule during ScriptAnalyzer invocation. + +## Notes + +- Parameter set names are case-sensitive in PowerShell, making this different from most other + PowerShell elements +- The first occurrence of a parameter set name in your code is treated as the canonical casing +- Parameters without `[Parameter()]` attributes are automatically part of all parameter sets +- It's a PowerShell best practice to always specify a `DefaultParameterSetName` when using parameter + sets \ No newline at end of file diff --git a/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseConsistentParametersKind.md b/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseConsistentParametersKind.md new file mode 100644 index 0000000..993bcf4 --- /dev/null +++ b/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseConsistentParametersKind.md @@ -0,0 +1,68 @@ +description: Use the same pattern when defining parameters. +ms.date: 03/20/2026 +ms.topic: reference +title: UseConsistentParametersKind +--- +# UseConsistentParametersKind + +**Severity Level: Warning** + +## Description + +All functions should use the same pattern when defining parameters. Possible pattern types are: + +1. `Inline` + + ```powershell + function f([Parameter()]$FirstParam) { + return + } + ``` + +1. `ParamBlock` + + ```powershell + function f { + param([Parameter()]$FirstParam) + return + } + ``` + +In simple scenarios, both function definitions shown are considered to be equal. The purpose of this +rule is to enforce consistent code style across the codebase. + +## How to Fix + +Rewrite function so it defines parameters as specified in the rule + +## Example + +When the rule sets parameters definition kind to `Inline`: + +```powershell +# Correct +function f([Parameter()]$FirstParam) { + return +} + +# Incorrect +function g { + param([Parameter()]$FirstParam) + return +} +``` + +When the rule sets parameters definition kind to `ParamBlock`: + +```powershell +# Inorrect +function f([Parameter()]$FirstParam) { + return +} + +# Correct +function g { + param([Parameter()]$FirstParam) + return +} +``` diff --git a/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseConstrainedLanguageMode.md b/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseConstrainedLanguageMode.md new file mode 100644 index 0000000..19e1566 --- /dev/null +++ b/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseConstrainedLanguageMode.md @@ -0,0 +1,414 @@ +--- +description: Use patterns compatible with Constrained Language Mode +ms.date: 03/20/2026 +ms.topic: reference +title: UseConstrainedLanguageMode +--- +# UseConstrainedLanguageMode + +**Severity Level: Warning** + +## Description + +This rule identifies PowerShell patterns that are restricted or not permitted in Constrained +Language Mode (CLM). + +Constrained Language Mode is a PowerShell security feature that restricts: + +- .NET types that can be used +- COM objects that can be instantiated +- Commands that can be executed +- Language features that can be used + +CLM is commonly used in: + +- Application Control environments (Application Control for Business, AppLocker) +- Just Enough Administration (JEA) endpoints +- Secure environments requiring additional PowerShell restrictions + +Digitally signed scripts from trusted publishers execute in Full Language Mode (FLM) even in CLM +environments. The rule detects signature blocks (`# SIG # Begin signature block`) and adjusts checks +accordingly. Most restrictions don't apply to signed scripts, but certain checks (dot-sourcing, +parameter types, manifest best practices) are always enforced. + +> [!IMPORTANT] +> The rule performs a simple text check for signature blocks and does NOT validate signature +> authenticity or certificate trust. Actual signature validation is performed by PowerShell at +> runtime. + +## Constrained Language Mode Restrictions + +### Unsigned Scripts (Full CLM Checking) + +The following are flagged for unsigned scripts: + +1. **Add-Type** - Code compilation not permitted +1. **Disallowed COM Objects** - Only Scripting.Dictionary, Scripting.FileSystemObject, + VBScript.RegExp allowed +1. **Disallowed .NET Types** - Only ~70 allowed types (string, int, hashtable, pscredential, etc.) +1. **Type Constraints** - On parameters and variables +1. **Type Expressions** - Static type references like `[Type]::Method()` +1. **Type Casts** - Converting to disallowed types +1. **Member Invocations** - Methods/properties on disallowed types +1. **PowerShell Classes** - `class` keyword not permitted +1. **XAML/WPF** - Not permitted +1. **Invoke-Expression** - Restricted +1. **Dot-Sourcing** - May be restricted depending on the file being sourced +1. **Module Manifest Wildcards** - Wildcard exports not recommended +1. **Module Manifest .ps1 Files** - Script modules ending with .ps1 not allowed + +Always enforced, even for signed scripts + +### Signed Scripts (Selective Checking) + +For scripts with signature blocks, only these are checked: + +- Dot-sourcing +- Parameter type constraints +- Module manifest wildcards (.psd1 files) +- Module manifest script modules (.psd1 files) + +## Configuration + +### Basic Configuration + +```powershell +@{ + Rules = @{ + PSUseConstrainedLanguageMode = @{ + Enable = $true + } + } +} +``` + +### Parameters + +#### Enable: bool (Default value is `$false`) + +Enable or disable the rule during ScriptAnalyzer invocation. This rule is disabled by default +because not all scripts need CLM compatibility. + +#### IgnoreSignatures: bool (Default value is `$false`) + +Control signature detection behavior: + +- `$false` (default): Automatically detect signatures. Signed scripts get selective checking, + unsigned get full checking. +- `$true`: Bypass signature detection. ALL scripts get full CLM checking regardless of signature + status. + +```powershell +@{ + Rules = @{ + PSUseConstrainedLanguageMode = @{ + Enable = $true + IgnoreSignatures = $true # Enforce full CLM compliance for all scripts + } + } +} +``` + +Use `IgnoreSignatures = $true` when: + +- Auditing signed scripts for complete CLM compatibility +- Preparing scripts for untrusted environments +- Enforcing strict CLM compliance organization-wide +- Development/testing to see all potential issues + +## How to Fix + +### Replace Add-Type + +Use allowed cmdlets or pre-compile assemblies. + +### Replace Disallowed COM Objects + +Use only allowed COM objects (Scripting.Dictionary, Scripting.FileSystemObject, VBScript.RegExp) or +PowerShell cmdlets. + +### Replace Disallowed Types + +Use allowed type accelerators (`[string]`, `[int]`, `[hashtable]`, etc.) or allowed cmdlets instead +of disallowed .NET types. + +### Replace PowerShell Classes + +Use `New-Object PSObject` with `Add-Member` or hashtables instead of classes. + +> [!IMPORTANT] +> `[PSCustomObject]@{}` syntax is NOT allowed in CLM because it uses type casting. + +### Avoid XAML + +Don't use WPF/XAML in CLM-compatible scripts. + +### Replace Invoke-Expression + +Use direct execution (`&`) or safer alternatives. + +### Replace Dot-Sourcing + +Use modules with Import-Module instead of dot-sourcing when possible. + +### Fix Module Manifests + +- Replace wildcard exports (`*`) with explicit lists. +- Use `.psm1` or `.dll` instead of `.ps1` for RootModule/NestedModules. +- Don't use `ScriptsToProcess`. These scripts are loaded in the caller's scope and are blocked. + +## Examples + +### Example 1: Add-Type + +#### Wrong + +```powershell +Add-Type -TypeDefinition @" + public class Helper { + public static string DoWork() { return "Done"; } + } +"@ +``` + +#### Correct + +```powershell + # Code sign your scripts/modules using proper signing tools + # (for example, Set-AuthenticodeSignature or external signing processes) + # Use allowed cmdlets instead of Add-Type-defined types where possible + # Or pre-compile, sign, and load the assembly (for example, via Add-Type -Path) +``` + +### Example 2: COM Objects + +#### Wrong + +```powershell +$excel = New-Object -ComObject Excel.Application +``` + +#### Correct + +```powershell +# Use allowed COM object +$dict = New-Object -ComObject Scripting.Dictionary + +# Or use PowerShell cmdlets +Import-Excel -Path $file # From ImportExcel module +``` + +### Example 3: Disallowed Types + +#### Wrong + +```powershell +# Type constraint and member invocation flagged +function Download-File { + param([System.Net.WebClient]$Client) + $Client.DownloadString($url) +} + +# Type cast and method call flagged +[System.Net.WebClient]$client = New-Object System.Net.WebClient +$data = $client.DownloadData($url) +``` + +#### Correct + +```powershell +# Use allowed cmdlets +function Download-File { + param([string]$Url) + Invoke-WebRequest -Uri $Url +} + +# Use allowed types +function Process-Text { + param([string]$Text) + $upper = $Text.ToUpper() # String methods are allowed +} +``` + +### Example 4: PowerShell Classes + +#### Wrong + +```powershell +class MyClass { + [string]$Name + + [string]GetInfo() { + return $this.Name + } +} + +# Also wrong - uses type cast +$obj = [PSCustomObject]@{ + Name = "Test" +} +``` + +#### Correct + +```powershell +# Option 1: New-Object PSObject with Add-Member +$obj = New-Object PSObject -Property @{ + Name = "Test" +} + +$obj | Add-Member -MemberType ScriptMethod -Name GetInfo -Value { + return $this.Name +} + +Add-Member -InputObject $obj -NotePropertyMembers @{"Number" = 42} + +# Option 2: Hashtable +$obj = @{ + Name = "Test" + Number = 42 +} +``` + +### Example 5: Module Manifests + +#### Wrong + +```powershell +@{ + ModuleVersion = '1.0.0' + RootModule = 'MyModule.ps1' # .ps1 not recommended + FunctionsToExport = '*' # Wildcard not recommended + CmdletsToExport = '*' +} +``` + +#### Correct + +```powershell +@{ + ModuleVersion = '1.0.0' + RootModule = 'MyModule.psm1' # Use .psm1 or .dll + FunctionsToExport = @( # Explicit list + 'Get-MyFunction' + 'Set-MyFunction' + ) + CmdletsToExport = @() +} +``` + +### Example 6: Array Types + +#### Wrong + +```powershell +# Disallowed type in array +param([System.Net.WebClient[]]$Clients) +``` + +#### Correct + +```powershell +# Allowed types in arrays are fine +param([string[]]$Names) +param([int[]]$Numbers) +param([hashtable[]]$Configuration) +``` + +## Detailed Restrictions + +### 1. Add-Type + +`Add-Type` allows compiling arbitrary C# code and isn't permitted in CLM. + +**Enforced For**: Unsigned scripts only + +### 2. COM Objects + +Only three COM objects are allowed: + +- `Scripting.Dictionary` +- `Scripting.FileSystemObject` +- `VBScript.RegExp` + +All others (Excel.Application, WScript.Shell, etc.) are flagged. + +**Enforced For**: Unsigned scripts only + +### 3. .NET Types + +Only ~70 allowed types including: + +- Primitives: `string`, `int`, `bool`, `byte`, `char`, `datetime`, `decimal`, `double`, etc. +- Collections: `hashtable`, `array`, `arraylist` +- PowerShell: `pscredential`, `psobject`, `securestring` +- Utilities: `regex`, `guid`, `version`, `uri`, `xml` +- Arrays: `string[]`, `int[][]`, etc. (array of any allowed type) + +The rule checks type usage in: + +- Parameter type constraints (**always enforced, even for signed scripts**) +- Variable type constraints +- New-Object -TypeName +- Type expressions (`[Type]::Method()`) +- Type casts (`[Type]$variable`) +- Member invocations on typed variables + +**Enforced For**: Parameter constraints always; others unsigned only + +### 4. PowerShell Classes + +The `class` keyword is not permitted. Use `New-Object PSObject` with `Add-Member` or hashtables. + +**Note**: `[PSCustomObject]@{}` is also not allowed because it uses type casting. + +**Enforced For**: Unsigned scripts only + +### 5. XAML/WPF + +XAML and WPF are not permitted in CLM. + +**Enforced For**: Unsigned scripts only + +### 6. Invoke-Expression + +`Invoke-Expression` is restricted in CLM. + +**Enforced For**: Unsigned scripts only + +### 7. Dot-Sourcing + +Dot-sourcing (`. $PSScriptRoot\script.ps1`) may be restricted depending on source location. + +**Enforced For**: ALL scripts (unsigned and signed) + +### 8. Module Manifest Best Practices + +#### Wildcard Exports + +Don't use `*` in: `FunctionsToExport`, `CmdletsToExport`, `AliasesToExport`, `VariablesToExport` + +Use explicit lists for security and clarity. + +**Enforced For**: ALL .psd1 files (unsigned and signed) + +#### Script Module Files + +Don't use `.ps1` files in: `RootModule`, `ModuleToProcess`, `NestedModules` + +Use `.psm1` (script modules) or `.dll` (binary modules) for better performance and compatibility. + +**Enforced For**: ALL .psd1 files (unsigned and signed) + +## More Information + +- [About Language Modes][01] +- [PowerShell Constrained Language Mode][03] +- [PowerShell Module Function Export in Constrained Language][04] +- [PowerShell Constrained Language Mode and the Dot-Source Operator][02] + + +[01]: /powershell/module/microsoft.powershell.core/about/about_language_modes +[02]: https://devblogs.microsoft.com/powershell/powershell-constrained-language-mode-and-the-dot-source-operator/ +[03]: https://devblogs.microsoft.com/powershell/powershell-constrained-language-mode/ +[04]: https://devblogs.microsoft.com/powershell/powershell-module-function-export-in-constrained-language/ diff --git a/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseSingleValueFromPipelineParameter.md b/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseSingleValueFromPipelineParameter.md new file mode 100644 index 0000000..92c350e --- /dev/null +++ b/reference/docs-conceptual/PSScriptAnalyzer/Rules/UseSingleValueFromPipelineParameter.md @@ -0,0 +1,99 @@ +--- +description: Use at most a single ValueFromPipeline parameter per parameter set. +ms.date: 03/20/2026 +ms.topic: reference +title: UseSingleValueFromPipelineParameter +--- +# UseSingleValueFromPipelineParameter + +**Severity Level: Warning** + +## Description + +Parameter sets should have at most one parameter marked as `ValueFromPipeline = true`. + +This rule identifies functions where multiple parameters within the same parameter set have +`ValueFromPipeline` set to `true` (either explicitly or implicitly). + +## How + +Ensure that only one parameter per parameter set accepts pipeline input by value. If you need +multiple parameters to accept different types of pipeline input, use separate parameter sets. + +## Example + +### Wrong + +```powershell +function Process-Data { + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline)] + [string] $InputData, + + [Parameter(ValueFromPipeline)] + [string] $ProcessingMode + ) + + process { + Write-Output "$ProcessingMode`: $InputData" + } +} +``` + + +### Correct + +```powershell +function Process-Data { + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline)] + [string] $InputData, + + [Parameter(Mandatory)] + [string] $ProcessingMode + ) + process { + Write-Output "$ProcessingMode`: $InputData" + } +} +``` + +## Suppression + +To suppress this rule for a specific parameter set, use the `SuppressMessage` attribute with the +parameter set name: + +```powershell +function Process-Data { + [Diagnostics.CodeAnalysis.SuppressMessage('PSUseSingleValueFromPipelineParameter', 'MyParameterSet')] + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline, ParameterSetName='MyParameterSet')] + [string] $InputData, + + [Parameter(ValueFromPipeline, ParameterSetName='MyParameterSet')] + [string] $ProcessingMode + ) + process { + Write-Output "$ProcessingMode`: $InputData" + } +} +``` + +For the default parameter set, use `'default'` as the suppression target: + +```powershell +[Diagnostics.CodeAnalysis.SuppressMessage('PSUseSingleValueFromPipelineParameter', 'default')] +``` + +## Notes + +- This rule applies to both explicit `ValueFromPipeline = $true` and implicit `ValueFromPipeline` + (which is the same as using `= $true`) +- Parameters with `ValueFromPipeline=$false` are not flagged by this rule +- The rule correctly handles the default parameter set (`__AllParameterSets`) and named parameter + sets +- Different parameter sets can each have their own single `ValueFromPipeline` parameter without + triggering this rule diff --git a/reference/docs-conceptual/PSScriptAnalyzer/overview.md b/reference/docs-conceptual/PSScriptAnalyzer/overview.md index e29d96d..f187fcd 100644 --- a/reference/docs-conceptual/PSScriptAnalyzer/overview.md +++ b/reference/docs-conceptual/PSScriptAnalyzer/overview.md @@ -1,6 +1,6 @@ --- description: This article explains the purpose of the PSScriptAnalyzer module. -ms.date: 01/28/2026 +ms.date: 03/20/2026 title: PSScriptAnalyzer module --- # PSScriptAnalyzer module @@ -30,16 +30,16 @@ Supported PowerShell Versions and Platforms - Windows PowerShell 5.1 or greater - PowerShell 7.2.11 or greater on Windows/Linux/macOS -Install using PowerShellGet 2.x: +Install using PSResourceGet 1.x: ```powershell -Install-Module -Name PSScriptAnalyzer -Force +Install-PSResource -Name PSScriptAnalyzer -Reinstall ``` -Install using PSResourceGet 1.x: +Install using PowerShellGet 2.x: ```powershell -Install-PSResource -Name PSScriptAnalyzer -Reinstall +Install-Module -Name PSScriptAnalyzer -Force ``` The `-Force` or `-Reinstall` parameters are only necessary when you have an older version of diff --git a/reference/docs-conceptual/toc.yml b/reference/docs-conceptual/toc.yml index 72d3821..b4074ab 100644 --- a/reference/docs-conceptual/toc.yml +++ b/reference/docs-conceptual/toc.yml @@ -47,7 +47,7 @@ items: href: PlatyPS/step-3-test-markdown-help.md - name: Convert and publish the help files href: PlatyPS/step-4-convert-publish-help.md - - name: PSScriptAnalyzer v1.24 + - name: PSScriptAnalyzer v1.25 items: - name: Overview href: PSScriptAnalyzer/overview.md