Bagikan melalui


Membuat aturan kustom

PSScriptAnalyzer menggunakan Kerangka Kerja Ekstensibilitas Terkelola (MEF) untuk mengimpor semua aturan yang ditentukan dalam rakitan. Itu juga dapat menggunakan aturan yang ditulis dalam skrip PowerShell.

Pengguna dapat menentukan aturan kustom menggunakan parameter CustomizedRulePath dari Invoke-ScriptAnalyzer cmdlet.

Artikel ini memberikan panduan dasar untuk membuat aturan khusus Anda sendiri.

Persyaratan dasar

Fungsi harus memiliki bantuan berbasis komentar

Sertakan .DESCRIPTION bidang. Bidang ini menjadi deskripsi untuk aturan yang disesuaikan.

<#
.SYNOPSIS
    Name of your rule.
.DESCRIPTION
    This would be the description of your rule. Please refer to Rule Documentation
    for consistent rule messages.
.EXAMPLE
.INPUTS
.OUTPUTS
.NOTES
#>

Jenis output harus DiagnosticRecord

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

Setiap fungsi harus memiliki array Token atau parameter Ast

Nama nama parameter Ast harus diakhiri dengan Ast.

Param
(
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [System.Management.Automation.Language.ScriptBlockAst]
    $testAst
)

Nama nama parameter Token harus diakhiri dengan Token.

Param
(
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [System.Management.Automation.Language.Token[]]
    $testToken
)

DiagnosticRecord harus memiliki properti yang diperlukan

DiagnosticRecord harus memiliki setidaknya empat properti:

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

Sejak versi 1.17.0, Anda dapat menyertakan properti SuggestedCorrections jenis IEnumerable<CorrectionExtent>. Pastikan untuk menentukan jenis yang benar. Contohnya:

[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'
$objParams = @{
  TypeName = 'Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent'
  ArgumentList = $startLineNumber, $endLineNumber, $startColumnNumber,
                 $endColumnNumber, $correction, $file, $optionalDescription
}
$correctionExtent = New-Object @objParams
$suggestedCorrections = New-Object System.Collections.ObjectModel.Collection[$($objParams.TypeName)]
$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
}

Pastikan Anda mengekspor fungsi

Anda harus mengekspor fungsi yang Anda buat sehingga PSScriptAnalyzer dapat menemukannya.

Export-ModuleMember -Function (FunctionName)

Contoh fungsi aturan

<#
    .SYNOPSIS
    Uses #Requires -RunAsAdministrator instead of your own methods.
    .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
    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
    Measure-RequiresRunAsAdministrator -ScriptBlockAst $ScriptBlockAst
    .INPUTS
    [System.Management.Automation.Language.ScriptBlockAst]
    .OUTPUTS
    [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
    .NOTES
    None
#>
function Measure-RequiresRunAsAdministrator
{
    [CmdletBinding()]
    [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
    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
            }

            # 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
        }
        catch
        {
            $PSCmdlet.ThrowTerminatingError($PSItem)
        }
    }
}

Contoh lainnya dapat ditemukan di folder CommunityAnalyzerRules di GitHub.