rule.psm1
function Measure-OperatorLowerCase { <# .SYNOPSIS Operators (-join, -split, etc...) should be lowercase. .DESCRIPTION Operators should not be capitalized. .EXAMPLE # BAD $Foo -Join $Bar #GOOD $Foo -join $Bar .INPUTS [System.Management.Automation.Language.ScriptBlockAst] .OUTPUTS [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]] .NOTES Reference: Personal preference Note: Whether you prefer lowercase operators or otherwise, consistency is what matters most. #> [CmdletBinding()] [OutputType([Object[]])] Param( [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Language.ScriptBlockAst] $ScriptBlockAst ) Process { [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]$Results = @() try { $Predicate = { Param( [System.Management.Automation.Language.Ast] $Ast ) ($Ast -is [System.Management.Automation.Language.BinaryExpressionAst]) -and ($Ast.ErrorPosition.Text -cmatch '[A-Z]') } $Violations = $ScriptBlockAst.FindAll($Predicate, $False) foreach ($Violation in $Violations) { $Results += [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{ Message = 'Operators should be lowercase.' RuleName = 'OperatorLowerCase' Severity = 'Warning' Extent = $Violation.Extent } } return $Results } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } } } function Measure-ParamPascalCase { <# .SYNOPSIS Param block keyword should be PascalCase. .DESCRIPTION The "p" of "param" should be capitalized. .EXAMPLE # BAD param() #GOOD Param() .INPUTS [System.Management.Automation.Language.ScriptBlockAst] .OUTPUTS [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]] .NOTES Reference: Personal preference Note: Whether you prefer PascalCase or otherwise, consistency is what matters most. #> [CmdletBinding()] [OutputType([Object[]])] Param( [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Language.ScriptBlockAst] $ScriptBlockAst ) Process { [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]$Results = @() try { $Predicate = { Param( [System.Management.Automation.Language.Ast] $Ast ) ($Ast -is [System.Management.Automation.Language.ParamBlockAst]) -and -not ($Ast.Extent.Text -cmatch 'Param\s*\(') } $Violations = $ScriptBlockAst.FindAll($Predicate, $False) foreach ($Violation in $Violations) { $Results += [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{ Message = 'Param block keyword should be PascalCase.' RuleName = 'ParamPascalCase' Severity = 'Warning' Extent = $Violation.Extent } } return $Results } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } } } function Measure-VariablePascalCase { <# .SYNOPSIS Variables ($Foo, $Bar, etc...) should be PascalCase. .DESCRIPTION The first letter of a variable names should be capitalized. .EXAMPLE # BAD $foo = 'foo' $bar = 'bar' #GOOD $Foo = 'foo' $Bar = 'bar' .INPUTS [System.Management.Automation.Language.ScriptBlockAst] .OUTPUTS [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]] .NOTES Reference: Personal preference Note: Whether you prefer PascalCase variable names or otherwise, consistency is what matters most. #> [CmdletBinding()] [OutputType([Object[]])] Param( [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Language.ScriptBlockAst] $ScriptBlockAst ) Process { [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]$Results = @() try { $Predicate = { Param( [System.Management.Automation.Language.Ast] $Ast ) $IsVariableExpression = $Ast -is [System.Management.Automation.Language.VariableExpressionAst] $Name = $Ast.Extent.Text -replace '[{}]','' $IsVariableExpression -and -not $Name.StartsWith('$_') -and ($Name -ne '$this') -and ($Name[1] -cnotmatch '[A-Z]') } $Violations = $ScriptBlockAst.FindAll($Predicate, $False) foreach ($Violation in $Violations) { $Results += [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{ Message = "The variable name, `"$($Violation.Extent.Text)`", should be PascalCase." RuleName = 'VariablePascalCase' Severity = 'Warning' Extent = $Violation.Extent } } return $Results } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } } } function Measure-UseRequiresDirective { <# .SYNOPSIS "Requires" should be used instead of "Import-Module" .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 3.0, the #Requires statement let script developers specify Windows PowerShell modules that the script requires. .EXAMPLE #BAD Import-Module -Name SomeModule #GOOD (at top of file) #Requires -Modules SomeModule .INPUTS [System.Management.Automation.Language.ScriptBlockAst] .OUTPUTS [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]] .NOTES See https://github.com/PowerShell/PSScriptAnalyzer/blob/development/Tests/Engine/CommunityAnalyzerRules/CommunityAnalyzerRules.psm1 #> [CmdletBinding()] [OutputType([Object[]])] Param( [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Language.ScriptBlockAst] $ScriptBlockAst ) Process { $Results = @() $RuleName = 'RequireDirective' $Message = '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. To fix a violation of this rule, please consider to use #Requires -RunAsAdministrator instead of using Import-Module' try { $Predicate = { Param( [System.Management.Automation.Language.Ast] $Ast ) $ReturnValue = $False if ($Ast -is [System.Management.Automation.Language.CommandAst]) { [System.Management.Automation.Language.CommandAst]$CommandAst = $Ast; if ($Null -ne $CommandAst.GetCommandName()) { if ($CommandAst.GetCommandName() -eq 'import-module') { $ReturnValue = $True } } } return $ReturnValue } [System.Management.Automation.Language.Ast[]]$Violations = $ScriptBlockAst.FindAll($Predicate, $True) if ($Null -ne $ScriptBlockAst.ScriptRequirements) { if (($ScriptBlockAst.ScriptRequirements.RequiredModules.Count -eq 0) -and ($Null -ne $Violations)) { foreach ($Violation in $Violations) { $Results += [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{ Message = $Message RuleName = $RuleName Severity = 'Information' Extent = $Violation.Extent } } } } else { if ($Null -ne $Violations) { foreach ($Violation in $Violations) { $Results += [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{ RuleName = $RuleName Message = $Message Severity = 'Information' Extent = $Violation.Extent } } } } return $Results } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } } } |