From f24c629655f3e1224606f90ace004b8fc88c1011 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Mon, 30 May 2022 15:43:07 +0800 Subject: [PATCH 01/43] add scriptAnalyzer --- .azure-pipelines/util/analyze-steps.yml | 3 + build.proj | 10 +- .../FilesChangedTask.cs | 9 +- tools/PrepareAutorestModule.ps1 | 2 +- tools/PrepareForSecurityCheck.ps1 | 2 +- .../AnalyzeRules/CommandName.psm1 | 124 +++ .../AnalyzeRules/ParameterNameAndValue.psm1 | 505 ++++++++++++ .../DemoMarkdowns/Add-AzEnvironment.md | 773 ++++++++++++++++++ .../DemoMarkdowns/New-AzADDomainService.md | 509 ++++++++++++ .../DemoMarkdowns/New-AzAksNodePool.md | 453 ++++++++++ .../Measure-MarkdownOrScript.ps1 | 95 +++ tools/ScriptAnalyzer/utils.ps1 | 484 +++++++++++ 12 files changed, 2963 insertions(+), 6 deletions(-) create mode 100644 tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 create mode 100644 tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 create mode 100644 tools/ScriptAnalyzer/DemoMarkdowns/Add-AzEnvironment.md create mode 100644 tools/ScriptAnalyzer/DemoMarkdowns/New-AzADDomainService.md create mode 100644 tools/ScriptAnalyzer/DemoMarkdowns/New-AzAksNodePool.md create mode 100644 tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 create mode 100644 tools/ScriptAnalyzer/utils.ps1 diff --git a/.azure-pipelines/util/analyze-steps.yml b/.azure-pipelines/util/analyze-steps.yml index d4fa720120cb..7f872808f1f8 100644 --- a/.azure-pipelines/util/analyze-steps.yml +++ b/.azure-pipelines/util/analyze-steps.yml @@ -32,6 +32,9 @@ steps: - pwsh: 'Install-Module platyPS -Force -Confirm:$false -Scope CurrentUser' displayName: 'Install platyPS' +- pwsh: 'Install-Module PSScriptAnalyzer' + displayName: 'Install PSScriptAnalyzer' + - task: DotNetCoreCLI@2 displayName: 'Generate Help' inputs: diff --git a/build.proj b/build.proj index c83d798e4f4a..b89d5237a996 100644 --- a/build.proj +++ b/build.proj @@ -118,7 +118,7 @@ - + @@ -263,7 +263,13 @@ - + + + + + + + diff --git a/tools/BuildPackagesTask/Microsoft.Azure.Build.Tasks/FilesChangedTask.cs b/tools/BuildPackagesTask/Microsoft.Azure.Build.Tasks/FilesChangedTask.cs index c487919e763e..1b2de68410ce 100644 --- a/tools/BuildPackagesTask/Microsoft.Azure.Build.Tasks/FilesChangedTask.cs +++ b/tools/BuildPackagesTask/Microsoft.Azure.Build.Tasks/FilesChangedTask.cs @@ -49,6 +49,11 @@ public class FilesChangedTask : Task /// public string TargetModule { get; set; } + /// + /// Gets or set the OutputFile, store FilesChanged.txt in 'artifacts' folder + /// + public string OutputFile { get; set; } + /// /// Gets or sets the files changed produced by the task. /// @@ -148,10 +153,10 @@ public override bool Execute() return true; } - // This method will record the changed files into FilesChanged.txt under root folder for other task to consum. + // This method will record the changed files into FilesChanged.txt under '.\artifacts' folder for other task to consum. private void SerializeChangedFilesToFile(string[] FilesChanged) { - File.WriteAllLines("FilesChanged.txt", FilesChanged); + File.WriteAllLines(OutputFile, FilesChanged); } } } diff --git a/tools/PrepareAutorestModule.ps1 b/tools/PrepareAutorestModule.ps1 index 6d39253d5d3d..ec0ab66e1991 100644 --- a/tools/PrepareAutorestModule.ps1 +++ b/tools/PrepareAutorestModule.ps1 @@ -17,7 +17,7 @@ param( ) -$ChangedFiles = Get-Content -Path "$PSScriptRoot\..\FilesChanged.txt" +$ChangedFiles = Get-Content -Path "$PSScriptRoot\..\artifacts\FilesChanged.txt" $ALL_MODULE = "ALL_MODULE" diff --git a/tools/PrepareForSecurityCheck.ps1 b/tools/PrepareForSecurityCheck.ps1 index f4c5111a5211..a0b48705c549 100644 --- a/tools/PrepareForSecurityCheck.ps1 +++ b/tools/PrepareForSecurityCheck.ps1 @@ -17,7 +17,7 @@ param( ) -$ChangedFiles = Get-Content -Path "$PSScriptRoot\..\FilesChanged.txt" +$ChangedFiles = Get-Content -Path "$PSScriptRoot\..\artifacts\FilesChanged.txt" $SecurityTmpFolder = "$PSScriptRoot\..\SecurityTmp" New-Item -ItemType Directory -Force -Path $SecurityTmpFolder diff --git a/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 new file mode 100644 index 000000000000..d4f48a795607 --- /dev/null +++ b/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 @@ -0,0 +1,124 @@ +<# + .SYNOPSIS + Custom rule for command name. + .NOTES + File: CommandName.psm1 +#> + + +# Import-Module AzPreview +# Import-Module AzureRM +Import-Module -Name ./artifacts/Debug/Az.Accounts -Scope Global +Import-Module -Name ./artifacts/Debug/Az.ADDomainServices -Scope Global +Import-Module -Name ./artifacts/Debug/Az.Aks -Scope Global +Import-Module -Name ./artifacts/Debug/Az.ApiManagement -Scope Global + + +enum RuleNames { + Invalid_Cmdlet + Is_Alias + Capitalization_Conventions_Violated +} + +<# + .SYNOPSIS + Returns invaild, alias or unrecognized cmdlets. +#> +function Measure-CommandName { + [CmdletBinding()] + [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])] + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.Language.ScriptBlockAst] + $ScriptBlockAst + ) + + process { + $Results = @() + $global:CommandParameterPair = @() + $global:Ast = $null + + try { + [ScriptBlock]$Predicate = { + param([System.Management.Automation.Language.Ast]$Ast) + $global:Ast = $Ast + + #find all command in .ps1 + if ($Ast -is [System.Management.Automation.Language.CommandAst]) { + [System.Management.Automation.Language.CommandAst]$CommandAst = $Ast + if ($CommandAst.InvocationOperator -eq "Unknown") { + # $CommandName = $CommandAst.GetCommandName() + $CommandName = $CommandAst.CommandElements[0].Extent.Text + $GetCommand = Get-Command $CommandName -ErrorAction SilentlyContinue + if ($GetCommand -eq $null) { + # CommandName is not valid. + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = "" + } + return $true + } + else { + if ($GetCommand.CommandType -eq "Alias") { + # CommandName is an alias. + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = "" + } + return $true + } + if ($CommandName -cnotmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { + # CommandName doesn't follow the Capitalization Conventions. + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = "" + } + return $true + } + } + } + } + + return $false + } + + # find all false scriptblock + [System.Management.Automation.Language.Ast[]]$Asts = $ScriptBlockAst.FindAll($Predicate, $false) + for ($i = 0; $i -lt $Asts.Count; $i++) { + if ($global:CommandParameterPair[$i].ParameterName -eq "") { + $Message = "`"$($CommandParameterPair[$i].CommandName)`" is not a valid command name." + $RuleName = [RuleNames]::Invalid_Cmdlet + } + if ($global:CommandParameterPair[$i].ParameterName -eq "") { + $Message = "`"$($CommandParameterPair[$i].CommandName)`" is an alias of `"$((Get-Alias $CommandParameterPair[$i].CommandName)[0].ResolvedCommandName)`"." + $RuleName = [RuleNames]::Is_Alias + } + if ($global:CommandParameterPair[$i].ParameterName -eq "") { + $Message = "`"$($CommandParameterPair[$i].CommandName)`" doesn't follow the Capitalization Conventions." + $RuleName = [RuleNames]::Capitalization_Conventions_Violated + } + $Result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ + Message = $Message; + Extent = $Asts[$i].Extent; + RuleName = $RuleName; + Severity = "Error" + } + $Results += $Result + } + return $Results + } + catch { + $Result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ + Message = $_.Exception.Message; + Extent = $global:Ast.Extent; + RuleName = $PSCmdlet.MyInvocation.InvocationName; + Severity = "Error" + } + $Results += $Result + return $Results + } + } +} + +Export-ModuleMember -Function Measure-* diff --git a/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 new file mode 100644 index 000000000000..2a1df02e7f67 --- /dev/null +++ b/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -0,0 +1,505 @@ +<# + .SYNOPSIS + Custom rule for parameter name and value. + .NOTES + File: ParameterNameAndValue.psm1 +#> + +# Import-Module AzPreview +# Import-Module AzureRM + +enum RuleNames { + Unknown_Parameter_Set + Invalid_Parameter_Name + Duplicate_Parameter_Name + Unassigned_Parameter + Unassigned_Variable + Unbinded_Parameter_Name + Mismatched_Parameter_Value_Type +} + + +<# + .SYNOPSIS + Gets the actual value from ast. +#> +function Get-ActualVariableValue { + param([System.Management.Automation.Language.Ast]$CommandElementAst) + + while ($true) { + if ($CommandElementAst.Expression -ne $null) { + $CommandElementAst = $CommandElementAst.Expression + } + elseif ($CommandElementAst.Target -ne $null) { + $CommandElementAst = $CommandElementAst.Target + } + elseif ($CommandElementAst.Pipeline -ne $null) { + $CommandElementAst = $CommandElementAst.Pipeline + } + elseif ($CommandElementAst.PipelineElements -ne $null) { + $CommandElementAst = $CommandElementAst.PipelineElements[-1] + } + else { + break + } + } + return $CommandElementAst +} + +<# + .SYNOPSIS + Detects parameter and expression error. +#> +function Measure-ParameterNameAndValue { + [CmdletBinding()] + [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])] + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.Language.ScriptBlockAst] + $ScriptBlockAst + ) + + process { + $Results = @() + $global:CommandParameterPair = @() + $global:Ast = $null + $global:AssignmentLeftAndRight = @{} + $global:ParameterSet = $null + $global:AppearedParameters = @() + $global:AppearedExpressions = @() + $global:ParameterExpressionPair = @() + $global:SkipNextCommandElementAst = $false + + try { + [ScriptBlock]$Predicate = { + param([System.Management.Automation.Language.Ast]$Ast) + $global:Ast = $Ast + + if ($Ast -is [System.Management.Automation.Language.AssignmentStatementAst]) { + [System.Management.Automation.Language.AssignmentStatementAst]$AssignmentStatementAst = $Ast + if ($global:AssignmentLeftAndRight.ContainsKey($AssignmentStatementAst.Left.Extent.Text)) { + $global:AssignmentLeftAndRight.($AssignmentStatementAst.Left.Extent.Text) = $AssignmentStatementAst.Right + } + else { + $global:AssignmentLeftAndRight += @{ + $AssignmentStatementAst.Left.Extent.Text = $AssignmentStatementAst.Right + } + } + } + + if ($Ast -is [System.Management.Automation.Language.CommandElementAst] -and $Ast.Parent -is [System.Management.Automation.Language.CommandAst]) { + [System.Management.Automation.Language.CommandElementAst]$CommandElementAst = $Ast + if ($global:SkipNextCommandElementAst) { + $global:SkipNextCommandElementAst = $false + return $false + } + + $CommandAst = $CommandElementAst.Parent + # $CommandName = $CommandAst.GetCommandName() + $CommandName = $CommandAst.CommandElements[0].Extent.Text + $GetCommand = Get-Command $CommandName -ErrorAction SilentlyContinue + if ($GetCommand -eq $null) { + return $false + } + + # Get command from alias + if ($GetCommand.CommandType -eq "Alias") { + $CommandNameNotAlias = $GetCommand.ResolvedCommandName + $GetCommand = Get-Command $CommandNameNotAlias + } + + if ($CommandElementAst -is [System.Management.Automation.Language.ExpressionAst] -and + $CommandAst.CommandElements.Extent.Text.IndexOf($CommandElementAst.Extent.Text) -eq 0) { + # This CommandElement is the first CommandElement of the command. + $global:AppearedParameters = @() #empty + $global:AppearedExpressions = @() #empty + + # $AllParameters is the set of the command required to have + # sort ParameterSets, move ParameterSets that have position parameters to the front. + $ParameterSets = @() + + # ($GetCommand.ParameterSets | where {$_.Name -eq $GetCommand.DefaultParameterSet}) + + ($GetCommand.ParameterSets | Sort-Object {($_.Parameters.Position | where {$_ -ge 0}).Count} -Descending) + foreach ($ParameterSet in $ParameterSets) { + # ParameterSets.Count is 0 when CommandName is an alias. + $AllParameterNamesInASet_Flag = $true + $Parameters = $ParameterSet.Parameters.Name + $ParameterSet.Parameters.Aliases + $AllParameters = $GetCommand.Parameters.Values.Name + $GetCommand.Parameters.Values.Aliases + foreach ($CommandElement in $CommandAst.CommandElements) { + if ($CommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { + $ParameterName = ([System.Management.Automation.Language.CommandParameterAst]$CommandElement).ParameterName + # Unknown_Parameter_Set + if ($ParameterName -in $AllParameters -and $ParameterName -notin $Parameters) { + # Exclude ParameterNames that are not in AllParameters. They will be reported later. + $AllParameterNamesInASet_Flag = $false + break + } + } + } + if ($AllParameterNamesInASet_Flag) { + break + } + } + if ($AllParameterNamesInASet_Flag -eq $false) { + # Not all parameters in a same ParameterSet. + $global:ParameterSet = $null + $global:CommandParameterPair += @{ + CommandName = $CommandAst.Extent.Text + ParameterName = "" + ExpressionToParameter = "" + } + return $true + } + else { + # Create ParameterExpressionPair + $global:ParameterSet = $ParameterSet + $global:ParameterExpressionPair = @() + for ($i = 1; $i -lt $CommandAst.CommandElements.Count;) { + $CommandElement = $CommandAst.CommandElements[$i] + $NextCommandElement = $CommandAst.CommandElements[$i + 1] + # $PositionParameters = $global:ParameterSet.Parameters | where {$_.Position -ge 0} + # $PositionParameters = $PositionParameters.Name + $PositionParameters.Aliases + + if ($CommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { + $CommandParameterElement = [System.Management.Automation.Language.CommandParameterAst]$CommandElement + $ParameterName = $CommandParameterElement.ParameterName + $ParameterNameNotAlias = $GetCommand.Parameters.Values.Name | where { + $ParameterName -in $GetCommand.Parameters.$_.Name -or + $ParameterName -in $GetCommand.Parameters.$_.Aliases + } + # Invalid_Parameter_Name + if ($ParameterNameNotAlias -eq $null) { + # ParameterName is not in AllParameters. + # will report later. + $global:ParameterExpressionPair += @{ + ParameterName = $ParameterName + ExpressionToParameter = "" + } + if ($NextCommandElement -eq $null -or $NextCommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { + $i += 1 + } + else { + $i += 2 + } + continue + } + if ($GetCommand.Parameters.$ParameterNameNotAlias.SwitchParameter -eq $true) { + # SwitchParameter + $global:ParameterExpressionPair += @{ + ParameterName = $ParameterNameNotAlias + ExpressionToParameter = "" + } + $i += 1 + } + else { + # Unassigned_Parameter + # not a SwitchParameter + if ($NextCommandElement -eq $null -or $NextCommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { + # NonSwitchParameter + Parameter + # Parameter must be assigned with a value. + # will report later. + $global:ParameterExpressionPair += @{ + ParameterName = $ParameterNameNotAlias + ExpressionToParameter = "" + } + $i += 1 + } + else { + # NonSwitchParameter + Expression + $global:ParameterExpressionPair += @{ + ParameterName = $ParameterNameNotAlias + ExpressionToParameter = $NextCommandElement + } + $i += 2 + } + } + } + elseif ($CommandElement -is [System.Management.Automation.Language.ExpressionAst]) { + # Expression + $CommandExpressionElement = [System.Management.Automation.Language.ExpressionAst]$CommandElement + $PositionMaximum = ($global:ParameterSet.Parameters.Position | Measure-Object -Maximum).Maximum + for ($Position = 0; $Position -le $PositionMaximum; $Position++) { + $ImplicitParameterName = @() + ($global:ParameterSet.Parameters | where {$_.Position -eq $Position}).Name + if ($ImplicitParameterName.Count -ne 0 -and $ImplicitParameterName -notin $global:ParameterExpressionPair.ParameterName) { + $global:ParameterExpressionPair += @{ + ParameterName = $ImplicitParameterName[0] + ExpressionToParameter = $CommandExpressionElement + } + $i += 1 + break + } + } + # Unbinded_Parameter_Name + if ($Position -gt $PositionMaximum) { + # This expression doesn't belong to any parameters. + $global:ParameterExpressionPair += @{ + ParameterName = "" + ExpressionToParameter = $CommandExpressionElement + } + $i += 1 + } + } + } + } + } + + if ($global:ParameterSet -eq $null) { + # skip commands that can't determine their ParameterSets. + return $false + } + + if ($CommandElementAst -is [System.Management.Automation.Language.CommandParameterAst]) { + # This CommandElement is CommandParameter. + $index = $CommandAst.CommandElements.Extent.Text.IndexOf($CommandElementAst.Extent.Text) + $NextCommandElement = $CommandAst.CommandElements[$index + 1] + $ParameterName = ([System.Management.Automation.Language.CommandParameterAst]$CommandElementAst).ParameterName + $ParameterNameNotAlias = $GetCommand.Parameters.Values.Name | where { + $ParameterName -in $GetCommand.Parameters.$_.Name -or + $ParameterName -in $GetCommand.Parameters.$_.Aliases + } + if ($ParameterNameNotAlias -eq $null) { + # ParameterName is not in AllParameters. + # Invalid_Parameter_Name + if ($NextCommandElement -is [System.Management.Automation.Language.ExpressionAst]) { + $global:SkipNextCommandElementAst = $true + } + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = $ParameterName + ExpressionToParameter = "" + } + return $true + } + else { + # ParameterName is correct. + if ($ParameterNameNotAlias -in $global:AppearedParameters) { + # This parameter appeared more than once. + # Duplicate_Parameter_Name + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = $ParameterNameNotAlias + ExpressionToParameter = "<2>" + } + return $true + } + else { + $global:AppearedParameters += $ParameterNameNotAlias + } + + if ($GetCommand.Parameters.$ParameterNameNotAlias.SwitchParameter -eq $false) { + # Parameter is not a SwitchParameter. + if ($NextCommandElement -eq $null -or $NextCommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { + # Parameter is not assigned with a value. + # Unassigned_Parameter + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = $ParameterName + ExpressionToParameter = $null + } + return $true + } + else { + # Parameter is assigned with a value. + $global:SkipNextCommandElementAst = $true + $NextCommandElement_Copy = Get-ActualVariableValue $NextCommandElement + + while ($NextCommandElement_Copy -is [System.Management.Automation.Language.VariableExpressionAst]) { + # get the actual value + $NextCommandElement_Copy = Get-ActualVariableValue $global:AssignmentLeftAndRight.($NextCommandElement_Copy.Extent.Text) + if ($NextCommandElement_Copy -eq $null) { + # Variable is not assigned with a value. + # Unassigned_Variable + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = "-$ParameterName" + ExpressionToParameter = $NextCommandElement.Extent.Text + " is a null-valued parameter value." + } + return $true + } + } + if ($NextCommandElement_Copy -is [System.Management.Automation.Language.CommandAst]) { + # value is an command + $GetNextElementCommand = Get-Command $NextCommandElement_Copy.CommandElements[0].Extent.Text -ErrorAction SilentlyContinue + if ($GetNextElementCommand -eq $null) { + # CommandName is not valid. + # will be reported in next CommandAst + return $false + } + $ReturnType = $GetNextElementCommand.OutputType[0].Type + if ($ReturnType -eq $null) + { + $ReturnType = [Object] + } + $ExpectedType = $GetCommand.Parameters.$ParameterNameNotAlias.ParameterType + # Mismatched_Parameter_Value_Type + if ($ReturnType -ne $ExpectedType -and $ReturnType -isnot $ExpectedType -and + !$ReturnType.GetInterfaces().Contains($ExpectedType) -and !$ReturnType.GetInterfaces().Contains($ExpectedType.GetElementType())) { + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = "-$ParameterName" + ExpressionToParameter = $NextCommandElement.Extent.Text + } + return $true + } + } + else { + # value is a constant expression + $ExpectedType = $GetCommand.Parameters.$ParameterNameNotAlias.ParameterType + $ConvertedObject = $NextCommandElement_Copy.Extent.Text -as $ExpectedType + # Mismatched_Parameter_Value_Type + if ($NextCommandElement_Copy.StaticType -ne $ExpectedType -and $ConvertedObject -eq $null) { + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = "-$ParameterName" + ExpressionToParameter = $NextCommandElement.Extent.Text + } + return $true + } + } + } + } + } + } + + if ($CommandElementAst -is [System.Management.Automation.Language.ExpressionAst] -and + $CommandAst.CommandElements.Extent.Text.IndexOf($CommandElementAst.Extent.Text) -ne 0) { + # This CommandElement is an expression with implicit parameter and is not the first CommandElement. + # When there are same parameter values: + $index = ($global:AppearedExpressions | where {$_.Extent.Text -eq $CommandElementAst.Extent.Text}).Count + $PairWithThisExpression = $global:ParameterExpressionPair | where {$_.ExpressionToParameter.Extent.Text -eq $CommandElementAst.Extent.Text} + if ((@() + $PairWithThisExpression).Count -eq 1) { + # Convert to Array + $ImplicitParameterName = $PairWithThisExpression.ParameterName + } + else { + $ImplicitParameterName = $PairWithThisExpression[$index].ParameterName + } + $global:AppearedExpressions += $CommandElementAst + + if ($ImplicitParameterName -eq "") { + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = $ImplicitParameterName + ExpressionToParameter = $CommandElementAst.Extent.Text + } + return $true + } + + $CommandElementAst_Copy = Get-ActualVariableValue $CommandElementAst + while ($CommandElementAst_Copy -is [System.Management.Automation.Language.VariableExpressionAst]) { + # get the actual value + $CommandElementAst_Copy = Get-ActualVariableValue $global:AssignmentLeftAndRight.($CommandElementAst_Copy.Extent.Text) + if ($CommandElementAst_Copy -eq $null) { + # Variable is not assigned with a value. + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = "[-$ImplicitParameterName]" + ExpressionToParameter = $CommandElementAst.Extent.Text + " is a null-valued parameter value." + } + return $true + } + } + if ($CommandElementAst_Copy -is [System.Management.Automation.Language.CommandAst]) { + # value is an command + $GetElementCommand = Get-Command $CommandElementAst_Copy.CommandElements[0].Extent.Text -ErrorAction SilentlyContinue + if ($GetElementCommand -eq $null) { + # CommandName is not valid. + # will be reported in next CommandAst + return $false + } + $ReturnType = $GetElementCommand.OutputType[0].Type + if ($ReturnType -eq $null) + { + $ReturnType = [Object] + } + $ExpectedType = $GetCommand.Parameters.$ImplicitParameterName.ParameterType + if ($ReturnType -ne $ExpectedType -and $ReturnType -isnot $ExpectedType -and + !$ReturnType.GetInterfaces().Contains($ExpectedType) -and !$ReturnType.GetInterfaces().Contains($ExpectedType.GetElementType())) { + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = "[-$ImplicitParameterName]" + ExpressionToParameter = $CommandElementAst.Extent.Text + } + return $true + } + } + else { + # value is a constant expression + $ExpectedType = $GetCommand.Parameters.$ImplicitParameterName.ParameterType + $ConvertedObject = $CommandElementAst_Copy.Extent.Text -as $ExpectedType + if ($CommandElementAst_Copy.StaticType -ne $ExpectedType -and $ConvertedObject -eq $null) { + $global:CommandParameterPair += @{ + CommandName = $CommandName + ParameterName = "[-$ImplicitParameterName]" + ExpressionToParameter = $CommandElementAst.Extent.Text + } + return $true + } + } + } + } + + return $false + } + + [System.Management.Automation.Language.Ast[]]$Asts = $ScriptBlockAst.FindAll($Predicate, $false) + for ($i = 0; $i -lt $Asts.Count; $i++) { + if ($global:CommandParameterPair[$i].ParameterName -eq "" -and $global:CommandParameterPair[$i].ExpressionToParameter -eq "") { + $Message = "`"$($CommandParameterPair[$i].CommandName)`" has a parameter not in the same ParameterSet as others." + $RuleName = [RuleNames]::Unknown_Parameter_Set + $Severity = "Error" + } + elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq "") { + $Message = "`"$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName)`" is not a valid parameter name." + $RuleName = [RuleNames]::Invalid_Parameter_Name + $Severity = "Error" + } + elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq "<2>") { + $Message = "`"$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName)`" appeared more than once." + $RuleName = [RuleNames]::Duplicate_Parameter_Name + $Severity = "Error" + } + elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq $null) { + $Message = "`"$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName)`" must be assigned with a value." + $RuleName = [RuleNames]::Unassigned_Parameter + $Severity = "Error" + } + elseif ($global:CommandParameterPair[$i].ExpressionToParameter.EndsWith(" is a null-valued parameter value.")) { + $Message = "`"$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter)`"" + $RuleName = [RuleNames]::Unassigned_Variable + $Severity = "Warning" + } + elseif ($global:CommandParameterPair[$i].ParameterName -eq "") { + $Message = "`"$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ExpressionToParameter)`" is not explicitly assigned to a parameter." + $RuleName = [RuleNames]::Unbinded_Parameter_Name + $Severity = "Warning" + } + else { + $Message = "`"$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter)`" is not an expected parameter value type." + $RuleName = [RuleNames]::Mismatched_Parameter_Value_Type + $Severity = "Warning" + } + $Result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ + Message = $Message; + Extent = $Asts[$i].Extent; + RuleName = $RuleName; + Severity = $Severity + } + $Results += $Result + } + return $Results + } + catch { + $Result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ + Message = $_.Exception.Message; + Extent = $global:Ast.Extent; + RuleName = $PSCmdlet.MyInvocation.InvocationName; + Severity = "Error" + } + $Results += $Result + return $Results + } + } +} + +Export-ModuleMember -Function Measure-* diff --git a/tools/ScriptAnalyzer/DemoMarkdowns/Add-AzEnvironment.md b/tools/ScriptAnalyzer/DemoMarkdowns/Add-AzEnvironment.md new file mode 100644 index 000000000000..28a77ae285fe --- /dev/null +++ b/tools/ScriptAnalyzer/DemoMarkdowns/Add-AzEnvironment.md @@ -0,0 +1,773 @@ +--- +external help file: Microsoft.Azure.PowerShell.Cmdlets.Accounts.dll-Help.xml +Module Name: Az.Accounts +online version: https://docs.microsoft.com/powershell/module/az.accounts/add-azenvironment +schema: 2.0.0 +--- + +# Add-AzEnvironment + +## SYNOPSIS +Adds endpoints and metadata for an instance of Azure Resource Manager. + +## SYNTAX + +### Name (Default) +``` +Add-AzEnvironment [-Name] [[-PublishSettingsFileUrl] ] [[-ServiceEndpoint] ] + [[-ManagementPortalUrl] ] [[-StorageEndpoint] ] [[-ActiveDirectoryEndpoint] ] + [[-ResourceManagerEndpoint] ] [[-GalleryEndpoint] ] + [[-ActiveDirectoryServiceEndpointResourceId] ] [[-GraphEndpoint] ] + [[-AzureKeyVaultDnsSuffix] ] [[-AzureKeyVaultServiceEndpointResourceId] ] + [[-TrafficManagerDnsSuffix] ] [[-SqlDatabaseDnsSuffix] ] + [[-AzureDataLakeStoreFileSystemEndpointSuffix] ] + [[-AzureDataLakeAnalyticsCatalogAndJobEndpointSuffix] ] [-EnableAdfsAuthentication] + [[-AdTenant] ] [[-GraphAudience] ] [[-DataLakeAudience] ] + [[-BatchEndpointResourceId] ] [[-AzureOperationalInsightsEndpointResourceId] ] + [[-AzureOperationalInsightsEndpoint] ] [-AzureAnalysisServicesEndpointSuffix ] + [-AzureAnalysisServicesEndpointResourceId ] [-AzureAttestationServiceEndpointSuffix ] + [-AzureAttestationServiceEndpointResourceId ] [-AzureSynapseAnalyticsEndpointSuffix ] + [-ContainerRegistryEndpointSuffix ] [-AzureSynapseAnalyticsEndpointResourceId ] + [-MicrosoftGraphEndpointResourceId ] [-MicrosoftGraphUrl ] [-Scope ] + [-DefaultProfile ] [-WhatIf] [-Confirm] [] +``` + +### ARMEndpoint +``` +Add-AzEnvironment [-Name] [[-StorageEndpoint] ] [-ARMEndpoint] + [[-AzureKeyVaultDnsSuffix] ] [[-AzureKeyVaultServiceEndpointResourceId] ] + [[-DataLakeAudience] ] [[-BatchEndpointResourceId] ] + [[-AzureOperationalInsightsEndpointResourceId] ] [[-AzureOperationalInsightsEndpoint] ] + [-AzureAnalysisServicesEndpointSuffix ] [-AzureAnalysisServicesEndpointResourceId ] + [-AzureAttestationServiceEndpointSuffix ] [-AzureAttestationServiceEndpointResourceId ] + [-AzureSynapseAnalyticsEndpointSuffix ] [-ContainerRegistryEndpointSuffix ] + [-AzureSynapseAnalyticsEndpointResourceId ] [-Scope ] + [-DefaultProfile ] [-WhatIf] [-Confirm] [] +``` + +### Discovery +``` +Add-AzEnvironment [-AutoDiscover] [-Uri ] [-Scope ] + [-DefaultProfile ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +The Add-AzEnvironment cmdlet adds endpoints and metadata to enable Azure Resource Manager cmdlets to connect with a new instance of Azure Resource Manager. +The built-in environments AzureCloud and AzureChinaCloud target existing public instances of Azure Resource Manager. + +## EXAMPLES + +### Example 1: Creating and modifying a new environment +```powershell +Add-AzEnvironment -Name TestEnvironment ` + -ActiveDirectoryEndpoint TestADEndpoint ` + -ActiveDirectoryServiceEndpointResourceId TestADApplicationId ` + -ResourceManagerEndpoint TestRMEndpoint ` + -GalleryEndpoint TestGalleryEndpoint ` + -GraphEndpoint TestGraphEndpoint + +Name Resource Manager Url ActiveDirectory Authority +---- -------------------- ------------------------- +TestEnvironment TestRMEndpoint TestADEndpoint/ + +Set-AzEnvironment -Name TestEnvironment ` + -ActiveDirectoryEndpoint NewTestADEndpoint ` + -GraphEndpoint NewTestGraphEndpoint | Format-List + +Name : TestEnvironment +EnableAdfsAuthentication : False +OnPremise : False +ActiveDirectoryServiceEndpointResourceId : TestADApplicationId +AdTenant : +GalleryUrl : TestGalleryEndpoint +ManagementPortalUrl : +ServiceManagementUrl : +PublishSettingsFileUrl : +ResourceManagerUrl : TestRMEndpoint +SqlDatabaseDnsSuffix : +StorageEndpointSuffix : +ActiveDirectoryAuthority : NewTestADEndpoint +GraphUrl : NewTestGraphEndpoint +GraphEndpointResourceId : +TrafficManagerDnsSuffix : +AzureKeyVaultDnsSuffix : +DataLakeEndpointResourceId : +AzureDataLakeStoreFileSystemEndpointSuffix : +AzureDataLakeAnalyticsCatalogAndJobEndpointSuffix : +AzureKeyVaultServiceEndpointResourceId : +AzureOperationalInsightsEndpointResourceId : +AzureOperationalInsightsEndpoint : +AzureAnalysisServicesEndpointSuffix : +AzureAttestationServiceEndpointSuffix : +AzureAttestationServiceEndpointResourceId : +AzureSynapseAnalyticsEndpointSuffix : +AzureSynapseAnalyticsEndpointResourceId : +VersionProfiles : {} +ExtendedProperties : {} +BatchEndpointResourceId : +``` + +In this example we are creating a new Azure environment with sample endpoints using Add-AzEnvironment, and then we are changing the value of the ActiveDirectoryEndpoint and GraphEndpoint attributes of the created environment using the cmdlet Set-AzEnvironment. + +### Example 2: Discovering a new environment via Uri +```powershell +<# +Uri https://configuredmetadata.net returns an array of environment metadata. The following example contains a payload for the AzureCloud default environment. + +[ + { + "portal": "https://portal.azure.com", + "authentication": { + "loginEndpoint": "https://login.microsoftonline.com/", + "audiences": [ + "https://management.core.windows.net/" + ], + "tenant": "common", + "identityProvider": "AAD" + }, + "media": "https://rest.media.azure.net", + "graphAudience": "https://graph.windows.net/", + "graph": "https://graph.windows.net/", + "name": "AzureCloud", + "suffixes": { + "azureDataLakeStoreFileSystem": "azuredatalakestore.net", + "acrLoginServer": "azurecr.io", + "sqlServerHostname": ".database.windows.net", + "azureDataLakeAnalyticsCatalogAndJob": "azuredatalakeanalytics.net", + "keyVaultDns": "vault.azure.net", + "storage": "core.windows.net", + "azureFrontDoorEndpointSuffix": "azurefd.net" + }, + "batch": "https://batch.core.windows.net/", + "resourceManager": "https://management.azure.com/", + "vmImageAliasDoc": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json", + "activeDirectoryDataLake": "https://datalake.azure.net/", + "sqlManagement": "https://management.core.windows.net:8443/", + "gallery": "https://gallery.azure.com/" + }, +…… +] +#> + +Add-AzEnvironment -AutoDiscover -Uri https://configuredmetadata.net +``` + +```Output +Name Resource Manager Url ActiveDirectory Authority +---- -------------------- ------------------------- +TestEnvironment TestRMEndpoint TestADEndpoint/ +``` + +In this example, we are discovering a new Azure environment from the `https://configuredmetadata.net` Uri. + +## PARAMETERS + +### -ActiveDirectoryEndpoint +Specifies the base authority for Azure Active Directory authentication. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: AdEndpointUrl, ActiveDirectory, ActiveDirectoryAuthority + +Required: False +Position: 5 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -ActiveDirectoryServiceEndpointResourceId +Specifies the audience for tokens that authenticate requests to Azure Resource Manager or Service Management (RDFE) endpoints. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: + +Required: False +Position: 8 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AdTenant +Specifies the default Active Directory tenant. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: + +Required: False +Position: 17 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -ARMEndpoint +The Azure Resource Manager endpoint + +```yaml +Type: System.String +Parameter Sets: ARMEndpoint +Aliases: ArmUrl + +Required: True +Position: 1 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AutoDiscover +Discovers environments via default or configured endpoint. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: Discovery +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AzureAnalysisServicesEndpointResourceId +The resource identifier of the Azure Analysis Services resource. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AzureAnalysisServicesEndpointSuffix +The endpoint to use when communicating with the Azure Log Analytics API. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AzureAttestationServiceEndpointResourceId +The The resource identifier of the Azure Attestation service that is the recipient of the requested token. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AzureAttestationServiceEndpointSuffix +Dns suffix of Azure Attestation service. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AzureDataLakeAnalyticsCatalogAndJobEndpointSuffix +Dns Suffix of Azure Data Lake Analytics job and catalog services + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: + +Required: False +Position: 15 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AzureDataLakeStoreFileSystemEndpointSuffix +Dns Suffix of Azure Data Lake Store FileSystem. Example: azuredatalake.net + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: + +Required: False +Position: 14 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AzureKeyVaultDnsSuffix +Dns suffix of Azure Key Vault service. Example is vault-int.azure-int.net + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: 10 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AzureKeyVaultServiceEndpointResourceId +Resource identifier of Azure Key Vault data service that is the recipient of the requested token. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: 11 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AzureOperationalInsightsEndpoint +The endpoint to use when communicating with the Azure Log Analytics API. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: 22 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AzureOperationalInsightsEndpointResourceId +The audience for tokens authenticating with the Azure Log Analytics API. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: 21 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AzureSynapseAnalyticsEndpointResourceId +The The resource identifier of the Azure Synapse Analytics that is the recipient of the requested token. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -AzureSynapseAnalyticsEndpointSuffix +Dns suffix of Azure Synapse Analytics. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -BatchEndpointResourceId +The resource identifier of the Azure Batch service that is the recipient of the requested token + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: BatchResourceId, BatchAudience + +Required: False +Position: 20 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -ContainerRegistryEndpointSuffix +Suffix of Azure Container Registry. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -DataLakeAudience +The audience for tokens authenticating with the AD Data Lake services Endpoint. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: DataLakeEndpointResourceId, DataLakeResourceId + +Required: False +Position: 19 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -DefaultProfile +The credentials, tenant and subscription used for communication with azure + +```yaml +Type: Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core.IAzureContextContainer +Parameter Sets: (All) +Aliases: AzContext, AzureRmContext, AzureCredential + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EnableAdfsAuthentication +Indicates that Active Directory Federation Services (ADFS) on-premise authentication is allowed. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: Name +Aliases: OnPremise + +Required: False +Position: 16 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -GalleryEndpoint +Specifies the endpoint for the Azure Resource Manager gallery of deployment templates. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: Gallery, GalleryUrl + +Required: False +Position: 7 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -GraphAudience +The audience for tokens authenticating with the AD Graph Endpoint. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: GraphEndpointResourceId, GraphResourceId + +Required: False +Position: 18 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -GraphEndpoint +Specifies the URL for Graph (Active Directory metadata) requests. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: Graph, GraphUrl + +Required: False +Position: 9 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -ManagementPortalUrl +Specifies the URL for the Management Portal. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -MicrosoftGraphEndpointResourceId +The resource identifier of Microsoft Graph + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -MicrosoftGraphUrl +Microsoft Graph Url + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Name +Specifies the name of the environment to add. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -PublishSettingsFileUrl +Specifies the URL from which .publishsettings files can be downloaded. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: + +Required: False +Position: 1 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -ResourceManagerEndpoint +Specifies the URL for Azure Resource Manager requests. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: ResourceManager, ResourceManagerUrl + +Required: False +Position: 6 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Scope +Determines the scope of context changes, for example, whether changes apply only to the current process, or to all sessions started by this user. + +```yaml +Type: Microsoft.Azure.Commands.Profile.Common.ContextModificationScope +Parameter Sets: (All) +Aliases: +Accepted values: Process, CurrentUser + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ServiceEndpoint +Specifies the endpoint for Service Management (RDFE) requests. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: ServiceManagement, ServiceManagementUrl + +Required: False +Position: 2 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -SqlDatabaseDnsSuffix +Specifies the domain-name suffix for Azure SQL Database servers. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: + +Required: False +Position: 13 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -StorageEndpoint +Specifies the endpoint for storage (blob, table, queue, and file) access. + +```yaml +Type: System.String +Parameter Sets: Name, ARMEndpoint +Aliases: StorageEndpointSuffix + +Required: False +Position: 4 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -TrafficManagerDnsSuffix +Specifies the domain-name suffix for Azure Traffic Manager services. + +```yaml +Type: System.String +Parameter Sets: Name +Aliases: + +Required: False +Position: 12 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Uri +Specifies URI of the internet resource to fetch environments. + +```yaml +Type: System.Uri +Parameter Sets: Discovery +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### System.String + +### System.Management.Automation.SwitchParameter + +## OUTPUTS + +### Microsoft.Azure.Commands.Profile.Models.PSAzureEnvironment + +## NOTES + +## RELATED LINKS + +[Get-AzEnvironment](./Get-AzEnvironment.md) + +[Remove-AzEnvironment](./Remove-AzEnvironment.md) + +[Set-AzEnvironment](./Set-AzEnvironment.md) + diff --git a/tools/ScriptAnalyzer/DemoMarkdowns/New-AzADDomainService.md b/tools/ScriptAnalyzer/DemoMarkdowns/New-AzADDomainService.md new file mode 100644 index 000000000000..1cd73655d3bf --- /dev/null +++ b/tools/ScriptAnalyzer/DemoMarkdowns/New-AzADDomainService.md @@ -0,0 +1,509 @@ +--- +external help file: +Module Name: Az.ADDomainServices +online version: https://docs.microsoft.com/powershell/module/az.addomainservices/new-azaddomainservice +schema: 2.0.0 +content_git_url: https://github.com/Azure/azure-powershell/blob/main/src/ADDomainServices/help/New-AzADDomainService.md +original_content_git_url: https://github.com/Azure/azure-powershell/blob/main/src/ADDomainServices/help/New-AzADDomainService.md +--- + +# New-AzADDomainService + +## SYNOPSIS +The Create Domain Service operation creates a new domain service with the specified parameters. +If the specific service already exists, then any patchable properties will be updated and any immutable properties will remain unchanged. + +## SYNTAX + +``` +New-AzADDomainService -Name -ResourceGroupName -DomainName + -ReplicaSet [-SubscriptionId ] [-DomainConfigurationType ] + [-DomainSecuritySettingNtlmV1 ] [-DomainSecuritySettingSyncKerberosPassword ] + [-DomainSecuritySettingSyncNtlmPassword ] [-DomainSecuritySettingSyncOnPremPassword ] + [-DomainSecuritySettingTlsV1 ] [-FilteredSync ] [-ForestTrust ] + [-LdapSettingExternalAccess ] [-LdapSettingLdaps ] [-LdapSettingPfxCertificate ] + [-LdapSettingPfxCertificatePassword ] [-NotificationSettingAdditionalRecipient ] + [-NotificationSettingNotifyDcAdmin ] [-NotificationSettingNotifyGlobalAdmin ] + [-ResourceForest ] [-Sku ] [-Tag ] [-DefaultProfile ] [-AsJob] [-NoWait] + [-Confirm] [-WhatIf] [] +``` + +## DESCRIPTION +The Create Domain Service operation creates a new domain service with the specified parameters. +If the specific service already exists, then any patchable properties will be updated and any immutable properties will remain unchanged. + +## EXAMPLES + +### Example 1: Create new ADDomainService +```powershell +$replicaSet = New-AzADDomainServiceReplicaSetObject -Location westus -SubnetId /subscriptions/********-****-****-****-**********/resourceGroups/yishitest/providers/Microsoft.Network/virtualNetworks/aadds-vnet/subnets/default +New-AzADDomainService -name youriADdomain -ResourceGroupName youriAddomain -DomainName youriAddomain.com -ReplicaSet $replicaSet -debug +``` + +```output +Name Domain Name Location Sku +---- ----------- -------- --- +youriADdomain youriAddomain.com westus Enterprise +``` + +Create new ADDomainService + +## PARAMETERS + +### -AsJob +Run the command as a job + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DefaultProfile +The credentials, account, tenant, and subscription used for communication with Azure. + +```yaml +Type: System.Management.Automation.PSObject +Parameter Sets: (All) +Aliases: AzureRMContext, AzureCredential + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DomainConfigurationType +Domain Configuration Type + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DomainName +The name of the Azure domain that the user would like to deploy Domain Services to. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DomainSecuritySettingNtlmV1 +A flag to determine whether or not NtlmV1 is enabled or disabled. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DomainSecuritySettingSyncKerberosPassword +A flag to determine whether or not SyncKerberosPasswords is enabled or disabled. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DomainSecuritySettingSyncNtlmPassword +A flag to determine whether or not SyncNtlmPasswords is enabled or disabled. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DomainSecuritySettingSyncOnPremPassword +A flag to determine whether or not SyncOnPremPasswords is enabled or disabled. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DomainSecuritySettingTlsV1 +A flag to determine whether or not TlsV1 is enabled or disabled. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -FilteredSync +Enabled or Disabled flag to turn on Group-based filtered sync + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ForestTrust +List of settings for Resource Forest +To construct, see NOTES section for FORESTTRUST properties and create a hash table. + +```yaml +Type: Microsoft.Azure.PowerShell.Cmdlets.ADDomainServices.Models.Api202001.IForestTrust[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LdapSettingExternalAccess +A flag to determine whether or not Secure LDAP access over the internet is enabled or disabled. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LdapSettingLdaps +A flag to determine whether or not Secure LDAP is enabled or disabled. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LdapSettingPfxCertificate +The certificate required to configure Secure LDAP. +The parameter passed here should be a base64encoded representation of the certificate pfx file. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LdapSettingPfxCertificatePassword +The password to decrypt the provided Secure LDAP certificate pfx file. + +```yaml +Type: System.Security.SecureString +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Name +The name of the domain service. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: DomainServiceName + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -NotificationSettingAdditionalRecipient +The list of additional recipients + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -NotificationSettingNotifyDcAdmin +Should domain controller admins be notified + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -NotificationSettingNotifyGlobalAdmin +Should global admins be notified + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -NoWait +Run the command asynchronously + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ReplicaSet +List of ReplicaSets +To construct, see NOTES section for REPLICASET properties and create a hash table. + +```yaml +Type: Microsoft.Azure.PowerShell.Cmdlets.ADDomainServices.Models.Api202001.IReplicaSet[] +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ResourceForest +Resource Forest + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ResourceGroupName +The name of the resource group within the user's subscription. +The name is case insensitive. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Sku +Sku Type + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SubscriptionId +Gets subscription credentials which uniquely identify the Microsoft Azure subscription. +The subscription ID forms part of the URI for every service call. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: (Get-AzContext).Subscription.Id +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Tag +Resource tags + +```yaml +Type: System.Collections.Hashtable +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +### Microsoft.Azure.PowerShell.Cmdlets.ADDomainServices.Models.Api202001.IDomainService + +## NOTES + +ALIASES + +COMPLEX PARAMETER PROPERTIES + +To create the parameters described below, construct a hash table containing the appropriate properties. For information on hash tables, run Get-Help about_Hash_Tables. + + +FORESTTRUST : List of settings for Resource Forest + - `[FriendlyName ]`: Friendly Name + - `[RemoteDnsIP ]`: Remote Dns ips + - `[TrustDirection ]`: Trust Direction + - `[TrustPassword ]`: Trust Password + - `[TrustedDomainFqdn ]`: Trusted Domain FQDN + +REPLICASET : List of ReplicaSets + - `[Location ]`: Virtual network location + - `[SubnetId ]`: The name of the virtual network that Domain Services will be deployed on. The id of the subnet that Domain Services will be deployed on. /virtualNetwork/vnetName/subnets/subnetName. + +## RELATED LINKS + diff --git a/tools/ScriptAnalyzer/DemoMarkdowns/New-AzAksNodePool.md b/tools/ScriptAnalyzer/DemoMarkdowns/New-AzAksNodePool.md new file mode 100644 index 000000000000..eb7374b548ce --- /dev/null +++ b/tools/ScriptAnalyzer/DemoMarkdowns/New-AzAksNodePool.md @@ -0,0 +1,453 @@ +--- +external help file: Microsoft.Azure.PowerShell.Cmdlets.Aks.dll-Help.xml +Module Name: Az.Aks +online version: https://docs.microsoft.com/powershell/module/az.aks/new-azaksnodepool +schema: 2.0.0 +content_git_url: https://github.com/Azure/azure-powershell/blob/main/src/Aks/Aks/help/New-AzAksNodePool.md +original_content_git_url: https://github.com/Azure/azure-powershell/blob/main/src/Aks/Aks/help/New-AzAksNodePool.md +--- + +# New-AzAksNodePool + +## SYNOPSIS +Create a new node pool in specified cluster. + +## SYNTAX + +### defaultParameterSet +``` +New-AzAksNodePool -ResourceGroupName -ClusterName -Name [-Count ] + [-OsDiskSize ] [-VmSize ] [-VnetSubnetID ] [-MaxPodCount ] [-OsType ] + [-EnableNodePublicIp] [-NodePublicIPPrefixID ] [-ScaleSetPriority ] + [-ScaleSetEvictionPolicy ] [-VmSetType ] [-AvailabilityZone ] [-Force] + [-KubernetesVersion ] [-MinCount ] [-MaxCount ] [-EnableAutoScaling] + [-DefaultProfile ] [-WhatIf] [-Confirm] [-SubscriptionId ] + [] +``` + +### ParentObjectParameterSet +``` +New-AzAksNodePool -Name -ClusterObject [-Count ] [-OsDiskSize ] + [-VmSize ] [-VnetSubnetID ] [-MaxPodCount ] [-OsType ] [-EnableNodePublicIp] + [-NodePublicIPPrefixID ] [-ScaleSetPriority ] [-ScaleSetEvictionPolicy ] + [-VmSetType ] [-AvailabilityZone ] [-Force] [-KubernetesVersion ] + [-MinCount ] [-MaxCount ] [-EnableAutoScaling] [-DefaultProfile ] + [-WhatIf] [-Confirm] [-SubscriptionId ] [] +``` + +## DESCRIPTION +Create a new node pool in specified cluster. + +## EXAMPLES + +### Create node pool with default parameters +```powershell +New-AzAksNodePool -ResourceGroupName myResouceGroup -ClusterName myCluster -Name mydefault +``` + +### Create Windows Server container on an AKS +```powershell +$cred = ConvertTo-SecureString -AsPlainText "Password!!123" -Force +New-AzAks -ResourceGroupName myResourceGroup -Name myCluster -WindowsProfileAdminUserName azureuser -WindowsProfileAdminUserPassword $cred -NetworkPlugin azure -NodeVmSetType VirtualMachineScaleSets +New-AzAksNodePool -ResourceGroupName myResourceGroup -ClusterName myCluster -Name win1 -OsType Windows -VmSetType VirtualMachineScaleSets +``` + +## PARAMETERS + +### -AvailabilityZone +Availability zones for nodes. Must use VirtualMachineScaleSets AgentPoolType. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ClusterName +The name of the managed cluster resource. + +```yaml +Type: System.String +Parameter Sets: defaultParameterSet +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ClusterObject +Specify cluster object in which to create node pool. + +```yaml +Type: Microsoft.Azure.Commands.Aks.Models.PSKubernetesCluster +Parameter Sets: ParentObjectParameterSet +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Count +The default number of nodes for the node pools. + +```yaml +Type: System.Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DefaultProfile +The credentials, account, tenant, and subscription used for communication with Azure. + +```yaml +Type: Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core.IAzureContextContainer +Parameter Sets: (All) +Aliases: AzContext, AzureRmContext, AzureCredential + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EnableAutoScaling +Whether to enable auto-scaler + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EnableNodePublicIp +Whether to enable public IP for nodes. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +Create node pool even if it already exists + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -KubernetesVersion +The version of Kubernetes to use for creating the cluster. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -MaxCount +Maximum number of nodes for auto-scaling + +```yaml +Type: System.Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -MaxPodCount +Maximum number of pods that can run on node. + +```yaml +Type: System.Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -MinCount +Minimum number of nodes for auto-scaling. + +```yaml +Type: System.Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Name +The name of the node pool. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -NodePublicIPPrefixID +The resource Id of public IP prefix for node pool. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -OsDiskSize +The default number of nodes for the node pools. + +```yaml +Type: System.Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -OsType +OsType to be used to specify os type. +Choose from Linux and Windows. +Default to Linux. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ResourceGroupName +The name of the resource group. + +```yaml +Type: System.String +Parameter Sets: defaultParameterSet +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ScaleSetEvictionPolicy +ScaleSetEvictionPolicy to be used to specify eviction policy for low priority virtual machine scale set. +Default to Delete. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ScaleSetPriority +ScaleSetPriority to be used to specify virtual machine scale set priority. +Default to regular. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SubscriptionId +The ID of the subscription. +By default, cmdlets are executed in the subscription that is set in the current context. If the user specifies another subscription, the current cmdlet is executed in the subscription specified by the user. +Overriding subscriptions only take effect during the lifecycle of the current cmdlet. It does not change the subscription in the context, and does not affect subsequent cmdlets. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -VmSetType +Represents types of an node pool. +Possible values include: 'VirtualMachineScaleSets', 'AvailabilitySet' + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -VmSize +The size of the Virtual Machine. Default value is Standard_D2_v2. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -VnetSubnetID +VNet SubnetID specifies the VNet's subnet identifier. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### Microsoft.Azure.Commands.Aks.Models.PSKubernetesCluster + +## OUTPUTS + +### Microsoft.Azure.Commands.Aks.Models.PSNodePool + +## NOTES + +## RELATED LINKS diff --git a/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 new file mode 100644 index 000000000000..3a759680bb30 --- /dev/null +++ b/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 @@ -0,0 +1,95 @@ +<# + .SYNOPSIS + The script to find examples in ".md" and analyze the examples by custom rules. + .NOTES + File Name: Measure-MarkdownOrScript.ps1 +#> + +#Requires -Modules PSScriptAnalyzer + +[CmdletBinding(DefaultParameterSetName = "Markdown")] +param ( + [Parameter(Mandatory, HelpMessage = "Markdown searching paths. Empty for current path. Supports wildcard.", ParameterSetName = "Markdown")] + [AllowEmptyString()] + [string[]]$MarkdownPaths, + [Parameter(Mandatory, HelpMessage = "PowerShell scripts searching paths. Empty for current path. Supports wildcard.", ParameterSetName = "Script")] + [AllowEmptyString()] + [string[]]$ScriptPaths, + [Parameter(HelpMessage = "PSScriptAnalyzer custom rules paths. Empty for current path. Supports wildcard.")] + [string[]]$RulePaths, + [switch]$Recurse, + [switch]$IncludeDefaultRules, + [string]$OutputFolder = "./artifacts/StaticAnalysisResults/ExampleAnalysis", + [Parameter(ParameterSetName = "Markdown")] + [switch]$AnalyzeScriptsInFile, + [Parameter(ParameterSetName = "Markdown")] + [switch]$OutputScriptsInFile, + [switch]$OutputResultsByModule, + [switch]$CleanScripts +) + +. $PSScriptRoot\utils.ps1 + +if ($PSCmdlet.ParameterSetName -eq "Markdown") { + $ScriptsByExampleFolder = "ScriptsByExample" + $scaleTable = @() + $missingTable = @() + $deletePromptAndSeparateOutputTable = @() +} +$analysisResultsTable = @() + +# Clean caches, remove files in "output" folder +if ($OutputScriptsInFile.IsPresent) { + Remove-Item $OutputFolder\$ScriptsByExampleFolder -Recurse -ErrorAction SilentlyContinue +} +Remove-Item $OutputFolder\*.csv -Recurse -ErrorAction SilentlyContinue +# find examples in ".md", output ".ps1" +if ($PSCmdlet.ParameterSetName -eq "Markdown") { + $MarkdownPath = Get-Content $MarkdownPaths + (Get-ChildItem $MarkdownPath) | foreach{ + if ($_ -cmatch ".*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { + Write-Output "Searching in file $($_.FullName) ..." + $cmdlet = $_.BaseName + $result = Measure-SectionMissingAndOutputScript $module $cmdlet $_.FullName ` + -OutputScriptsInFile:$OutputScriptsInFile.IsPresent ` + -OutputFolder $OutputFolder\$ScriptsByExampleFolder + $scaleTable += $result.Scale + $missingTable += $result.Missing + $deletePromptAndSeparateOutputTable += $result.DeletePromptAndSeparateOutput + } + } + if ($AnalyzeScriptsInFile.IsPresent) { + $ScriptPaths = "$OutputFolder\$ScriptsByExampleFolder" + } + # Summarize searching results + $null = New-Item -ItemType Directory -Path $OutputFolder -ErrorAction SilentlyContinue + $scaleTable | Export-Csv "$OutputFolder\Scale.csv" -NoTypeInformation + $missingTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\Missing.csv" -NoTypeInformation + $deletePromptAndSeparateOutputTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\DeletingSeparating.csv" -NoTypeInformation +} + + +# Analyze scripts +if ($PSCmdlet.ParameterSetName -eq "Script" -or $AnalyzeScriptsInFile.IsPresent) { + $analysisResultsTable = @() + @() + (Get-Item $ScriptPaths) + (Get-ChildItem $ScriptPaths -Recurse:$Recurse.IsPresent -Attributes Directory) | foreach { + $module = (Get-Item $_).Name + $analysisResults = @() + # read and analyze ".ps1" in \ScriptsByExample + Get-ChildItem $_ -Attributes !Directory -Filter "*.ps1" -ErrorAction Stop | foreach { + Write-Output "Analyzing file $($_.FullName) ..." + $analysisResults += Get-ScriptAnalyzerResult $module $_.FullName $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent -ErrorAction Continue + } + if ($OutputResultsByModule.IsPresent -and (Get-ChildItem $_ -Attributes !Directory -Filter "*.ps1").Count -ne 0) { + $analysisResults | Export-Csv "$OutputFolder\$module.csv" -NoTypeInformation + } + $analysisResultsTable += $analysisResults + } + # Summarize analysis results, output in Result.csv + $analysisResultsTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\Results-$(Get-Date -UFormat %s).csv" -NoTypeInformation +} + +# Clean caches +if ($CleanScripts.IsPresent) { + Remove-Item $ScriptPaths -Exclude *.csv -Recurse -ErrorAction Continue +} diff --git a/tools/ScriptAnalyzer/utils.ps1 b/tools/ScriptAnalyzer/utils.ps1 new file mode 100644 index 000000000000..799c3f0037ad --- /dev/null +++ b/tools/ScriptAnalyzer/utils.ps1 @@ -0,0 +1,484 @@ +<# + .SYNOPSIS + Tools for Measure-MarkdownOrScript.ps1. + .NOTES + File Name: utils.ps1 + Class: Scale + Missing + DeletePromptAndSeparateOutput + Functions: Get-ExamplesDetailsFromMd + Measure-ScriptFile + Add-ContentToHeadOfRule + Get-ScriptAnalyzerResult + Measure-SectionMissingAndOutputScript +#> + +$SYNOPSIS_HEADING = "## SYNOPSIS" +$SYNTAX_HEADING = "## SYNTAX" +$DESCRIPTION_HEADING = "## DESCRIPTION" +$EXAMPLES_HEADING = "## EXAMPLES" +$PARAMETERS_HEADING = "## PARAMETERS" +$SINGLE_EXAMPLE_HEADING_REGEX = "\n###\s*" +$SINGLE_EXAMPLE_TITLE_HEADING_REGEX = "$SINGLE_EXAMPLE_HEADING_REGEX.+" +$CODE_BLOCK_REGEX = "``````(powershell)?\s*\n(.*\n)+?\s*``````" +$OUTPUT_BLOCK_REGEX = "``````output\s*\n(.*\n)+?\s*``````" + +class Scale { + [string]$Module + [string]$Cmdlet + [int]$Examples +} + +class Missing { + [string]$Module + [string]$Cmdlet + [int]$MissingSynopsis + [int]$MissingDescription + [int]$MissingExampleTitle + [int]$MissingExampleCode + [int]$MissingExampleOutput + [int]$MissingExampleDescription +} + +class DeletePromptAndSeparateOutput { + [string]$Module + [string]$Cmdlet + [int]$NeedDeleting + [int]$NeedSplitting +} + +<# + .SYNOPSIS + Get examples details from ".md". + .DESCRIPTION + Splits title, code, output, description according to regular expression. +#> +function Get-ExamplesDetailsFromMd { + param ( + [string]$MarkdownPath + ) + + $fileContent = Get-Content $MarkdownPath -Raw + $indexOfExamples = $fileContent.IndexOf($EXAMPLES_HEADING) + $indexOfParameters = $fileContent.IndexOf($PARAMETERS_HEADING) + + $exampleNumber = 0 + $examplesProperties = @() + $examplesContent = $fileContent.Substring($indexOfExamples, $indexOfParameters - $indexOfExamples) + $examplesTitles = ($examplesContent | Select-String -Pattern $SINGLE_EXAMPLE_TITLE_HEADING_REGEX -AllMatches).Matches + # Skip the 1st because it is $EXAMPLES_HEADING. Extract content without title. + $examplesContentWithoutTitle = $examplesContent -split $SINGLE_EXAMPLE_TITLE_HEADING_REGEX | Select-Object -Skip 1 + + + foreach ($exampleContent in $examplesContentWithoutTitle) { + $exampleTitle = ($examplesTitles[$exampleNumber].Value -split $SINGLE_EXAMPLE_HEADING_REGEX)[1].Trim() + $exampleNumber++ + $exampleCodes = @() + $exampleOutputs = @() + $exampleDescriptions = @() + + $exampleCodeBlocks = ($exampleContent | Select-String -Pattern $CODE_BLOCK_REGEX -AllMatches).Matches + $exampleOutputBlocks = ($exampleContent | Select-String -Pattern $OUTPUT_BLOCK_REGEX -AllMatches).Matches + if ($exampleCodeBlocks.Count -eq 0) { + $description = $exampleContent.Trim() + if ($description -ne "") { + $exampleDescriptions += $description + } + } + else { + # From the start to the start of the first codeblock is example description. + $description = $exampleContent.SubString(0, $exampleCodeBlocks[0].Index).Trim() + if ($description -ne "") { + $exampleDescriptions += $description + } + + # if there is no ```output``` split codelines and outputlines + if ($exampleOutputBlocks.Count -eq 0) { + foreach ($exampleCodeBlock in $exampleCodeBlocks) { + #$exampleCodeLines = ($exampleCodeBlock.Value | Select-String -Pattern "((\n(([A-Za-z \t\\:>])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*)*[ \t]*[A-Za-z]\w+-[A-Za-z]\w+\b(?!(-| +\w)))|(\n(([A-Za-z \t\\:>])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*)*[ \t]*((@?\(.+\) *[|.-] *\w)|(\[.+\]\$)|(@{.+})|('[^\n\r']*' *[|.-] *\w)|(`"[^\n\r`"]*`" *[|.-] *\w)|\$)))([\w-~``'`"$= \t:;<>@()\[\]{},.+*/|\\&!?%#]*[``|] *(\n|\r\n))*[\w-~``'`"$= \t:;<>@()\[\]{},.+*/|\\&!?%#]*(?=\n|\r\n|#)" -CaseSensitive -AllMatches).Matches + #$exampleCodeLines = ($exampleCodeBlock.Value | Select-String -Pattern "\n(([A-Za-z \t])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*)*[ \t]*((([A-Za-z]\w+-[A-Za-z]\w+\b(?!(-| +\w)))|((@?\(.+\) *[|.-] *\w)|(\[.+\]\$)|(@{.+})|('[^\n\r']*' *[|.-] *\w)|(`"[^\n\r`"]*`" *[|.-] *\w)|\$))([\w-~``'`"$= \t:;<>@()\[\]{},.+*/|\\&!?%#]*[``|][ \t]*(\n|\r\n)?)*([\w-~``'`"$= \t:;<>@()\[\]{},.+*/|\\&!?%#]*(?=\n|\r\n|#)))" -CaseSensitive -AllMatches).Matches + $codeRegex = "\n(([A-Za-z \t])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*)*[ \t]*((([A-Za-z]\w+-[A-Za-z]\w+\b(.ps1)?(?!(-| +\w)))|(" + + "(@?\((?>\((?)|[^\(\)]+|\)(?<-pair>))*(?(pair)(?!))\) *[|.-] *\w)|" + + "(\[(?>\[(?)|[^\[\]]+|\](?<-pair>))*(?(pair)(?!))\]\$)|" + + "(@{(?>{(?)|[^{}]+|}(?<-pair>))*(?(pair)(?!))})|" + + "('(?>'(?)|[^']+|'(?<-pair>))*(?(pair)(?!))' *[|.-] *\w)|" + + "((?(?)|[\s\S]|(?))*(?(pair)(?!))(?@()\[\]{},.+*/|\\&!?%#]*[``|][ \t]*(\n|\r\n)?)*([\w-~``'`"$= \t:;<>@()\[\]{},.+*/|\\&!?%#]*(?=\n|\r\n|#)))" + $exampleCodeLines = ($exampleCodeBlock.Value | Select-String -Pattern $codeRegex -CaseSensitive -AllMatches).Matches + if ($exampleCodeLines.Count -eq 0) { + $exampleCodes = @() + $exampleOutputs = @() + } + else { + for ($i = 0; $i -lt $exampleCodeLines.Count; $i++) { + # If a codeline contains " :", it's not a codeline but an output line of "Format-List". + if ($exampleCodeLines[$i].Value -notmatch " : *\w") { + # If a codeline ends with "`", "\r", or "\n", it should end at the last "`". + $lastCharacter = $exampleCodeLines[$i].Value.Substring($exampleCodeLines[$i].Value.Length - 1, 1) + if ($lastCharacter -eq "``" -or $lastCharacter -eq "`r" -or $lastCharacter -eq "`n") { + $exampleCodes += $exampleCodeLines[$i].Value.Substring(0, $exampleCodeLines[$i].Value.LastIndexOf("``")).Trim() + } + else { + $exampleCodes += $exampleCodeLines[$i].Value.Trim() + } + + # Content before the first codeline, between codelines, and after the last codeline is output. + if ($i -eq 0) { + $startIndex = $exampleCodeBlock.Value.IndexOf("`n") + $output = $exampleCodeBlock.Value.Substring($startIndex, $exampleCodeLines[$i].Index - $startIndex).Trim() + if ($output -ne "") { + $exampleOutputs += $output + } + } + $startIndex = $exampleCodeLines[$i].Index + $exampleCodeLines[$i].Length + if ($i -lt $exampleCodeLines.Count - 1) { + $nextStartIndex = $exampleCodeLines[$i + 1].Index + } + else { + $nextStartIndex = $exampleCodeBlock.Value.LastIndexOf("`n") + } + # If an output line starts with "-", it's an incomplete codeline, but it should still be added to output. + $output = $exampleCodeBlock.Value.Substring($startIndex, $nextStartIndex - $startIndex).Trim() + if ($output -match "^-+\w") { + $exampleOutputs += $output + } + elseif ($output -ne "") { + $exampleOutputs += $output + } + } + } + } + } + } + # if there is ```output``` + else { + # extract code from the first "\n" to the last "\n" + foreach ($exampleCodeBlock in $exampleCodeBlocks) { + $code = $exampleCodeBlock.Value.Substring($exampleCodeBlock.Value.IndexOf("`n"), $exampleCodeBlock.Value.LastIndexOf("`n") - $exampleCodeBlock.Value.IndexOf("`n")).Trim() + if ($code -ne "") { + $exampleCodes += $code + } + } + # extract output from the first "\n" to the last "\n" + foreach ($exampleOutputBlock in $exampleOutputBlocks) { + $output = $exampleOutputBlock.Value.Substring($exampleOutputBlock.Value.IndexOf("`n"), $exampleOutputBlock.Value.LastIndexOf("`n") - $exampleOutputBlock.Value.IndexOf("`n")).Trim() + if ($output -ne "") { + $exampleOutputs += $output + } + } + } + + # From the end of the last codeblock to the end is example description. + $description = $exampleContent.SubString($exampleCodeBlocks[-1].Index + $exampleCodeBlocks[-1].Length).Trim() + if ($description -ne "") { + $exampleDescriptions += $description + } + } + + $examplesProperties += [PSCustomObject]@{ + Title = $exampleTitle + Codes = $exampleCodes + CodeBlocks = $exampleCodeBlocks + Outputs = $exampleOutputs + OutputBlocks = $exampleOutputBlocks + Description = ([string]$exampleDescriptions).Trim() + } + } + + return $examplesProperties +} + +<# + .SYNOPSIS + Tests whether the script is integral, outputs examples in ".md" to ".ps1" + and records the Scale, Missing, DeletePromptAndSeparateOutput class. +#> +function Measure-SectionMissingAndOutputScript { + param ( + [string]$Module, + [string]$Cmdlet, + [string]$MarkdownPath, + [switch]$OutputScriptsInFile, + [string]$OutputFolder + ) + + $fileContent = Get-Content $MarkdownPath -Raw + + $indexOfSynopsis = $fileContent.IndexOf($SYNOPSIS_HEADING) + $indexOfSyntax = $fileContent.IndexOf($SYNTAX_HEADING) + $indexOfDescription = $fileContent.IndexOf($DESCRIPTION_HEADING) + $indexOfExamples = $fileContent.IndexOf($EXAMPLES_HEADING) + + $exampleNumber = 0 + $missingSynopsis = 0 + $missingDescription = 0 + $missingExampleTitle = 0 + $missingExampleCode = 0 + $missingExampleOutput = 0 + $missingExampleDescription = 0 + $needDeleting = 0 + $needSplitting = 0 + + # If Synopsis section exists + if ($indexOfSynopsis -ne -1) { + $synopsisContent = $fileContent.Substring($indexOfSynopsis + $SYNOPSIS_HEADING.Length, $indexOfSyntax - ($indexOfSynopsis + $SYNOPSIS_HEADING.Length)) + if ($synopsisContent.Trim() -eq "") { + $missingSynopsis = 1 + } + else { + $missingSynopsis = ($synopsisContent | Select-String -Pattern "{{[A-Za-z ]*}}").Count + } + } + else { + $missingSynopsis = 1 + } + + # If Description section exists + if ($indexOfDescription -ne -1) { + $descriptionContent = $fileContent.Substring($indexOfDescription + $DESCRIPTION_HEADING.Length, $indexOfExamples - ($indexOfDescription + $DESCRIPTION_HEADING.Length)) + if ($descriptionContent.Trim() -eq "") { + $missingDescription = 1 + } + else { + $missingDescription = ($descriptionContent | Select-String -Pattern "{{[A-Za-z ]*}}").Count + } + } + else { + $missingDescription = 1 + } + + $examplesDetails = Get-ExamplesDetailsFromMd $MarkdownPath + # If no examples + if ($examplesDetails.Count -eq 0) { + $missingExampleTitle++ + $missingExampleCode++ + $missingExampleOutput++ + $missingExampleDescription++ + } + else { + foreach ($exampleDetails in $examplesDetails) { + $exampleNumber++ + + switch ($exampleDetails) { + {$exampleDetails.Title -eq ""} { + $missingExampleTitle++ + } + {$exampleDetails.Codes.Count -eq 0} { + $missingExampleCode++ + } + {$exampleDetails.OutputBlocks.Count -ne 0 -and $exampleDetails.Outputs.Count -eq 0} { + $missingExampleOutput++ + } + {$exampleDetails.OutputBlocks.Count -eq 0 -and $exampleDetails.Outputs.Count -ne 0} { + $needSplitting++ + } + {$exampleDetails.Description -eq ""} { + $missingExampleDescription++ + } + } + $needDeleting = ($examplesDetails.CodeBlocks | Select-String -Pattern "\n([A-Za-z \t\\:>])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*[ \t]*" -CaseSensitive).Count + + ($examplesDetails.CodeBlocks | Select-String -Pattern "(?<=[A-Za-z]\w+-[A-Za-z]\w+)\.ps1" -CaseSensitive).Count + + # Delete prompts + $exampleCodes = $exampleDetails.Codes + for ($i = $exampleCodes.Count - 1; $i -ge 0; $i--) { + $newCode = $exampleDetails.Codes[$i] -replace "([A-Za-z \t\\:>])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*[ \t]*", "" + $newCode = $newCode -replace "(?<=[A-Za-z]\w+-[A-Za-z]\w+)\.ps1", "" + $exampleCodes[$i] = $newCode + } + + $cmdletExamplesScriptPath = "$OutputFolder\$module" + # Output codes by example + if ($OutputScriptsInFile.IsPresent) { + $null = New-Item -ItemType Directory -Path $cmdletExamplesScriptPath -ErrorAction SilentlyContinue + [IO.File]::WriteAllText((New-Item -Type File .\$cmdletExamplesScriptPath\$cmdlet-$exampleNumber.ps1).FullName, $exampleCodes -join "`n", (New-Object Text.UTF8Encoding($false))) + } + } + } + + # ScaleTable + $examples = $examplesDetails.Count + $scale = [Scale]@{ + Module = $module + Cmdlet = $cmdlet + Examples = $examples + } + + # MissingTable + $missingExampleTitle += ($examplesDetails.Title | Select-String -Pattern "{{[A-Za-z ]*}}").Count + $missingExampleCode += ($examplesDetails.Codes | Select-String -Pattern "{{[A-Za-z ]*}}").Count + $missingExampleOutput += ($examplesDetails.Outputs | Select-String -Pattern "{{[A-Za-z ]*}}").Count + $missingExampleDescription += ($examplesDetails.Description | Select-String -Pattern "{{[A-Za-z ]*}}").Count + + if ($missingSynopsis -ne 0 -or $missingDescription -ne 0 -or $missingExampleTitle -ne 0 -or $missingExampleCode -ne 0 -or $missingExampleOutput -ne 0 -or $missingExampleDescription -ne 0) { + $missing = [Missing]@{ + Module = $module + Cmdlet = $cmdlet + MissingSynopsis = $missingSynopsis + MissingDescription = $missingDescription + MissingExampleTitle = $missingExampleTitle + MissingExampleCode = $missingExampleCode + MissingExampleOutput = $missingExampleOutput + MissingExampleDescription = $missingExampleDescription + } + } + + # DeletePromptAndSeparateOutputTable + if ($needDeleting -ne 0 -or $needSplitting -ne 0) { + $deletePromptAndSeparateOutput = [DeletePromptAndSeparateOutput]@{ + Module = $module + Cmdlet = $cmdlet + NeedDeleting = $needDeleting + NeedSplitting = $needSplitting + } + } + + return @{ + Scale = $scale + Missing = $missing + DeletePromptAndSeparateOutput = $deletePromptAndSeparateOutput + } +} + + +<# + .SYNOPSIS + Tests whether the script invokes other scripts and whether it is vaild. +#> +function Measure-ScriptFile { + param ( + [Parameter(Mandatory)] + [string]$ScriptPath + ) + + $assignmentLeftAndRight = @{} + $importSucceededScriptPaths = @() + $importFailedScriptPaths = @() + $assignmentLeftAndRight += @{ + '$PSScriptRoot' = [IO.Path]::GetDirectoryName($ScriptPath) + } + + # parseFile method returns ScriptBlockAst + [System.Management.Automation.Language.Parser]::ParseFile($ScriptPath, [ref]$null, [ref]$null).FindAll({$args[0] -is [System.Management.Automation.Language.Ast]}, $true) | foreach { + if ($_ -is [System.Management.Automation.Language.CommandAst]) { + [System.Management.Automation.Language.CommandAst]$commandAst = $_ + # if invocate the command by ".", then get into the scriptPath + if ($commandAst.InvocationOperator -eq "Dot") { + $importedScriptPath = $commandAst.CommandElements[0].Extent.Text + while ($importedScriptPath -ne $importedScriptPath_Before) { + $importedScriptPath_Before = $importedScriptPath + $assignmentLeftAndRight.Keys.ForEach({ + $importedScriptPath = $importedScriptPath.Replace($_, $assignmentLeftAndRight.$_) + }) + } + $importedScriptPath = Invoke-Expression "Write-Output $importedScriptPath" + if (Test-Path $importedScriptPath -PathType Leaf) { + $importSucceededScriptPaths += $importedScriptPath + } + else { + $importFailedScriptPaths += $commandAst.CommandElements[0].Extent.Text + } + } + } + if ($_ -is [System.Management.Automation.Language.AssignmentStatementAst]) { + # Variable assignment, if variable exists, replace its value, or add the pair to $assignmentLeftAndRight + [System.Management.Automation.Language.AssignmentStatementAst]$assignmentStatementAst = $_ + if ($assignmentLeftAndRight.ContainsKey($assignmentStatementAst.Left.Extent.Text)) { + $assignmentLeftAndRight.($assignmentStatementAst.Left.Extent.Text) = $assignmentStatementAst.Right.Extent.Text + } + else { + $assignmentLeftAndRight += @{ + $assignmentStatementAst.Left.Extent.Text = $assignmentStatementAst.Right.Extent.Text + } + } + } + } + return @{ + SucceededResults = $importSucceededScriptPaths + FailedResults = $importFailedScriptPaths + } +} + +<# + .SYNOPSIS + Adds the script invoked other scripts and the rules into one script. +#> +function Add-ContentToHeadOfRule { + param ( + [string[]]$SrcFilePaths, + [string]$DstFolderPath, + [string]$ImportContent + ) + + # add content of the script invoked by "." and rules.psm1 to tempFolder + $null = New-Item -ItemType Directory -Path $DstFolderPath -ErrorAction SilentlyContinue + # filter *.psm1 files + Get-ChildItem $SrcFilePaths -Filter *.psm1 | foreach { + ($ImportContent + (Get-Content $_ -Raw)) | Out-File "$DstFolderPath\$($_.Name)" -Encoding utf8 + } +} + +<# + .SYNOPSIS + Invoke PSScriptAnalyzer with custom rules, return the error set. +#> +function Get-ScriptAnalyzerResult { + param ( + [string]$Module, + [string]$ScriptPath, + [Parameter(Mandatory, HelpMessage = "PSScriptAnalyzer custom rules path. Supports wildcard.")] + [string[]]$RulePath, + [switch]$IncludeDefaultRules + ) + + # Validate script file exists. + if (!(Test-Path $ScriptPath -PathType Leaf)) { + throw "Cannot find cached script file '$ScriptPath'." + } + + # get script file name + $scriptName = [IO.Path]::GetFileName($ScriptPath) + $scriptBaseName = [IO.Path]::GetFileNameWithoutExtension($ScriptPath) + if ($scriptBaseName.Split("-").Count -eq 3 -and $scriptBaseName.Split("-")[2] -as [int]) { + $cmdlet = $scriptBaseName.Split("-")[0..1] -join "-" + $example = $scriptBaseName.Split("-")[2] + } + else { + $cmdlet = $scriptName + $example = "" + } + $importFailedResults = @() + $importContent = "" + + $importResults = Measure-ScriptFile $ScriptPath + foreach ($path in $importResults.FailedResults) { + $importFailedResults += [PSCustomObject]@{ + Module = $module + Cmdlet = $cmdlet + Example = $example + RuleName = "Imported_Script_Not_Exist" + Message = "Imported Script $path doesn't exist." + Extent = ". $path" + } + } + $importResults.SucceededResults | foreach { + $importContent += ". $_`n" + } + $tempFolderPath = "TempPSSARules" + Add-ContentToHeadOfRule $RulePaths $tempFolderPath $importContent + # Invoke PSScriptAnalyzer : input scriptblock, output error set in $result with property: RuleName, Message, Extent + if ($RulePath -eq $null) { + $results = Invoke-ScriptAnalyzer -Path $ScriptPath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent + } + else { + $results = Invoke-ScriptAnalyzer -Path $ScriptPath -CustomRulePath $RulePath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent + } + Remove-Item $tempFolderPath -Recurse + + return $importFailedResults + $results | Select-Object -Property @{Name = "Module"; Expression = {$Module}}, + @{Name = "Cmdlet";Expression={$Cmdlet}}, + @{Name ="Example";Expression={$Example}}, + RuleName, Message, Extent +} + + From 4de10ef885b1fd03d465b943ffb69014e98fb7f8 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Mon, 30 May 2022 15:57:28 +0800 Subject: [PATCH 02/43] test for change cs --- .../ApiManagement/Commands/RemoveAzureApiManagement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs b/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs index 28351cdb3c1d..85d7a8b7c88d 100644 --- a/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs +++ b/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs @@ -29,7 +29,7 @@ public class RemoveAzureApiManagement : AzureApiManagementCmdletBase HelpMessage = "Name of resource group under which API Management service exists.")] [ResourceGroupCompleter()] [ValidateNotNullOrEmpty] - public string ResourceGroupName { get; set; } + public string ResourceGroupNameTest { get; set; } [Parameter( ValueFromPipelineByPropertyName = true, @@ -63,7 +63,7 @@ public override void ExecuteCmdlet() } ExecuteCmdLetWrap( - () => Client.DeleteApiManagement(ResourceGroupName, Name), + () => Client.DeleteApiManagement(ResourceGroupNameTest, Name), PassThru.IsPresent); } } From 41e4d3d76e9eb13d69e55b3ffb07106bf6cbea32 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Mon, 30 May 2022 16:35:25 +0800 Subject: [PATCH 03/43] test for change both .cs and .md --- .../ApiManagement/Commands/RemoveAzureApiManagement.cs | 6 +++--- .../ApiManagement/help/Remove-AzApiManagement.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs b/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs index 85d7a8b7c88d..9d442c75c2c0 100644 --- a/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs +++ b/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs @@ -29,7 +29,7 @@ public class RemoveAzureApiManagement : AzureApiManagementCmdletBase HelpMessage = "Name of resource group under which API Management service exists.")] [ResourceGroupCompleter()] [ValidateNotNullOrEmpty] - public string ResourceGroupNameTest { get; set; } + public string ResourceGroupName { get; set; } [Parameter( ValueFromPipelineByPropertyName = true, @@ -63,8 +63,8 @@ public override void ExecuteCmdlet() } ExecuteCmdLetWrap( - () => Client.DeleteApiManagement(ResourceGroupNameTest, Name), + () => Client.DeleteApiManagement(ResourceGroupName, Name), PassThru.IsPresent); } } -} +} diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md index 75b252b23b02..4a6d96f30e03 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md @@ -25,7 +25,7 @@ The **Remove-AzApiManagement** cmdlet removes an Azure API Management service. ### Example 1: Remove an API Management service ```powershell -Remove-AzApiManagement -ResourceGroupName "ContosoGroup02" -Name "ContosoApi" +Remove-AzApiManagement -ResourceGroupName "ContosoGroup02" -Name "ContosoApiTest" ``` This command removes the API Management service named ContosoApi. From 7bc2b2621b9c6c423ecea85b5298c28750f26f45 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Mon, 30 May 2022 17:10:58 +0800 Subject: [PATCH 04/43] fix a bug: error when no changed file --- tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 | 9 +-------- .../AnalyzeRules/ParameterNameAndValue.psm1 | 3 --- tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 | 3 ++- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 index d4f48a795607..aede85bd9e82 100644 --- a/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 @@ -5,14 +5,7 @@ File: CommandName.psm1 #> - -# Import-Module AzPreview -# Import-Module AzureRM -Import-Module -Name ./artifacts/Debug/Az.Accounts -Scope Global -Import-Module -Name ./artifacts/Debug/Az.ADDomainServices -Scope Global -Import-Module -Name ./artifacts/Debug/Az.Aks -Scope Global -Import-Module -Name ./artifacts/Debug/Az.ApiManagement -Scope Global - +Import-Module (Get-ChildItem -Path ./Debug).FullName -Scope Global enum RuleNames { Invalid_Cmdlet diff --git a/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index 2a1df02e7f67..63efcffeff8c 100644 --- a/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -5,9 +5,6 @@ File: ParameterNameAndValue.psm1 #> -# Import-Module AzPreview -# Import-Module AzureRM - enum RuleNames { Unknown_Parameter_Set Invalid_Parameter_Name diff --git a/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 index 3a759680bb30..be80ffc7f894 100644 --- a/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 @@ -43,8 +43,10 @@ if ($OutputScriptsInFile.IsPresent) { Remove-Item $OutputFolder\$ScriptsByExampleFolder -Recurse -ErrorAction SilentlyContinue } Remove-Item $OutputFolder\*.csv -Recurse -ErrorAction SilentlyContinue + # find examples in ".md", output ".ps1" if ($PSCmdlet.ParameterSetName -eq "Markdown") { + $null = New-Item -ItemType Directory -Path $OutputFolder\$ScriptsByExampleFolder -ErrorAction SilentlyContinue $MarkdownPath = Get-Content $MarkdownPaths (Get-ChildItem $MarkdownPath) | foreach{ if ($_ -cmatch ".*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { @@ -62,7 +64,6 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { $ScriptPaths = "$OutputFolder\$ScriptsByExampleFolder" } # Summarize searching results - $null = New-Item -ItemType Directory -Path $OutputFolder -ErrorAction SilentlyContinue $scaleTable | Export-Csv "$OutputFolder\Scale.csv" -NoTypeInformation $missingTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\Missing.csv" -NoTypeInformation $deletePromptAndSeparateOutputTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\DeletingSeparating.csv" -NoTypeInformation From 143ef5340175beeba7e6ca6098a5cabe874da92e Mon Sep 17 00:00:00 2001 From: MoChilia Date: Tue, 31 May 2022 09:55:35 +0800 Subject: [PATCH 05/43] fix output of result --- tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 | 7 +------ .../ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 | 2 -- tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 | 5 +++-- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 index d4f48a795607..a9ecae593fb3 100644 --- a/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 @@ -6,12 +6,7 @@ #> -# Import-Module AzPreview -# Import-Module AzureRM -Import-Module -Name ./artifacts/Debug/Az.Accounts -Scope Global -Import-Module -Name ./artifacts/Debug/Az.ADDomainServices -Scope Global -Import-Module -Name ./artifacts/Debug/Az.Aks -Scope Global -Import-Module -Name ./artifacts/Debug/Az.ApiManagement -Scope Global +Import-Module (Get-ChildItem -Path ./Debug).FullName -Scope Global enum RuleNames { diff --git a/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index 2a1df02e7f67..1da8e59bc301 100644 --- a/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -5,8 +5,6 @@ File: ParameterNameAndValue.psm1 #> -# Import-Module AzPreview -# Import-Module AzureRM enum RuleNames { Unknown_Parameter_Set diff --git a/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 index 3a759680bb30..e3a9ec68e03f 100644 --- a/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 @@ -45,10 +45,12 @@ if ($OutputScriptsInFile.IsPresent) { Remove-Item $OutputFolder\*.csv -Recurse -ErrorAction SilentlyContinue # find examples in ".md", output ".ps1" if ($PSCmdlet.ParameterSetName -eq "Markdown") { + $null = New-Item -ItemType Directory -Path $OutputFolder\$ScriptsByExampleFolder -ErrorAction SilentlyContinue $MarkdownPath = Get-Content $MarkdownPaths (Get-ChildItem $MarkdownPath) | foreach{ - if ($_ -cmatch ".*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { + if ($_ -cmatch ".*/help.*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { Write-Output "Searching in file $($_.FullName) ..." + $module = ($_ -split "/")[-3] $cmdlet = $_.BaseName $result = Measure-SectionMissingAndOutputScript $module $cmdlet $_.FullName ` -OutputScriptsInFile:$OutputScriptsInFile.IsPresent ` @@ -62,7 +64,6 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { $ScriptPaths = "$OutputFolder\$ScriptsByExampleFolder" } # Summarize searching results - $null = New-Item -ItemType Directory -Path $OutputFolder -ErrorAction SilentlyContinue $scaleTable | Export-Csv "$OutputFolder\Scale.csv" -NoTypeInformation $missingTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\Missing.csv" -NoTypeInformation $deletePromptAndSeparateOutputTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\DeletingSeparating.csv" -NoTypeInformation From 90dc8c757354a13b2a7177026f5278aef7dace29 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Tue, 31 May 2022 10:37:41 +0800 Subject: [PATCH 06/43] test for wrong md --- .../ApiManagement/Commands/RemoveAzureApiManagement.cs | 2 +- .../ApiManagement/Commands/RestoreAzureApiManagement.cs | 2 +- src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md | 2 +- .../ApiManagement/help/Remove-AzApiManagementApi.md | 2 +- tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 | 3 ++- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs b/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs index 9d442c75c2c0..28351cdb3c1d 100644 --- a/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs +++ b/src/ApiManagement/ApiManagement/Commands/RemoveAzureApiManagement.cs @@ -67,4 +67,4 @@ public override void ExecuteCmdlet() PassThru.IsPresent); } } -} +} diff --git a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs index 117431f70096..8466e1578dc5 100644 --- a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs +++ b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs @@ -64,7 +64,7 @@ public class RestoreAzureApiManagement : AzureApiManagementCmdletBase public string SourceBlobName { get; set; } [Parameter( - Mandatory = false, + Mandatory = true, HelpMessage = "The type of access to be used for the storage account. The default value is AccessKey.")] [PSArgumentCompleter(SdkModels.AccessType.AccessKey, SdkModels.AccessType.SystemAssignedManagedIdentity, SdkModels.AccessType.UserAssignedManagedIdentity)] public string AccessType { get; set; } diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md index 4a6d96f30e03..dab920dcfe9b 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md @@ -25,7 +25,7 @@ The **Remove-AzApiManagement** cmdlet removes an Azure API Management service. ### Example 1: Remove an API Management service ```powershell -Remove-AzApiManagement -ResourceGroupName "ContosoGroup02" -Name "ContosoApiTest" +Remove-AzApiManagement -ResourceGroupName "ContosoGroup02" -Name "ContosoApi" -Test ``` This command removes the API Management service named ContosoApi. diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md index 5920b3c12014..a175ef2bdaa6 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md @@ -26,7 +26,7 @@ The **Remove-AzApiManagementApi** cmdlet removes an existing API. ### Example 1: Remove an API ```powershell $apimContext = New-AzApiManagementContext -ResourceGroupName "Api-Default-WestUS" -ServiceName "contoso" -Remove-AzApiManagementApi -Context $apimContext -ApiId "0123456789" +Remove-AzApiManagementApiTest -Context $apimContext -ApiId "0123456789" ``` This command removes the API with the specified ID. diff --git a/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 index be80ffc7f894..a5ccf93c1fa6 100644 --- a/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 @@ -49,8 +49,9 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { $null = New-Item -ItemType Directory -Path $OutputFolder\$ScriptsByExampleFolder -ErrorAction SilentlyContinue $MarkdownPath = Get-Content $MarkdownPaths (Get-ChildItem $MarkdownPath) | foreach{ - if ($_ -cmatch ".*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { + if ($_ -cmatch ".*/help.*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { Write-Output "Searching in file $($_.FullName) ..." + $module = ($_ -split "/")[-3] $cmdlet = $_.BaseName $result = Measure-SectionMissingAndOutputScript $module $cmdlet $_.FullName ` -OutputScriptsInFile:$OutputScriptsInFile.IsPresent ` From 60420cfe669bf08afcdb6c339b0156e00ed6340b Mon Sep 17 00:00:00 2001 From: MoChilia Date: Tue, 31 May 2022 15:05:25 +0800 Subject: [PATCH 07/43] fix import-module --- tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 | 2 +- tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 index aede85bd9e82..7086f16c484c 100644 --- a/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 @@ -5,7 +5,7 @@ File: CommandName.psm1 #> -Import-Module (Get-ChildItem -Path ./Debug).FullName -Scope Global +Import-Module (Get-ChildItem -Path ./artifacts/Debug).FullName -Scope Global enum RuleNames { Invalid_Cmdlet diff --git a/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 index a5ccf93c1fa6..c2c6336ccd36 100644 --- a/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 @@ -44,11 +44,12 @@ if ($OutputScriptsInFile.IsPresent) { } Remove-Item $OutputFolder\*.csv -Recurse -ErrorAction SilentlyContinue -# find examples in ".md", output ".ps1" +# find examples in "help\*.md", output ".ps1" if ($PSCmdlet.ParameterSetName -eq "Markdown") { $null = New-Item -ItemType Directory -Path $OutputFolder\$ScriptsByExampleFolder -ErrorAction SilentlyContinue $MarkdownPath = Get-Content $MarkdownPaths - (Get-ChildItem $MarkdownPath) | foreach{ + (Get-Item $MarkdownPath) | foreach{ + # Filter the .md of overview in /help if ($_ -cmatch ".*/help.*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { Write-Output "Searching in file $($_.FullName) ..." $module = ($_ -split "/")[-3] From 05b8bacbc283cf5eb112f85ba0db2210ad03e123 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Tue, 31 May 2022 15:18:03 +0800 Subject: [PATCH 08/43] sync up with shiying/ci-example --- .../ApiManagement/Commands/RestoreAzureApiManagement.cs | 2 +- src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md | 2 +- .../ApiManagement/help/Remove-AzApiManagementApi.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs index 8466e1578dc5..b73569bb6d58 100644 --- a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs +++ b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs @@ -126,4 +126,4 @@ public override void ExecuteCmdlet() } } } -} +} diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md index dab920dcfe9b..a0ae535f5ebf 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md @@ -25,7 +25,7 @@ The **Remove-AzApiManagement** cmdlet removes an Azure API Management service. ### Example 1: Remove an API Management service ```powershell -Remove-AzApiManagement -ResourceGroupName "ContosoGroup02" -Name "ContosoApi" -Test +Remove-AzApiManagement -ResourceGroupName "ContosoGroup01" -Name "ContosoApi" -Test ``` This command removes the API Management service named ContosoApi. diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md index a175ef2bdaa6..ba8cda2541e1 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md @@ -26,7 +26,7 @@ The **Remove-AzApiManagementApi** cmdlet removes an existing API. ### Example 1: Remove an API ```powershell $apimContext = New-AzApiManagementContext -ResourceGroupName "Api-Default-WestUS" -ServiceName "contoso" -Remove-AzApiManagementApiTest -Context $apimContext -ApiId "0123456789" +Remove-AzApiManagementApiTest -Context $apimContext -ApiId "012345678" ``` This command removes the API with the specified ID. From 44ea8097e3694e6908182cb9609cfc50dc808cb9 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Tue, 31 May 2022 23:04:35 +0800 Subject: [PATCH 09/43] output by issueChecker --- build.proj | 6 +- documentation/tooling/static-analysis.md | 2 + .../Commands/RestoreAzureApiManagement.cs | 4 +- .../help/Remove-AzApiManagement.md | 2 +- .../help/Remove-AzApiManagementApi.md | 2 +- .../DemoMarkdowns/Add-AzEnvironment.md | 773 ------------------ .../DemoMarkdowns/New-AzADDomainService.md | 509 ------------ .../DemoMarkdowns/New-AzAksNodePool.md | 453 ---------- .../AnalyzeRules/CommandName.psm1 | 4 +- .../AnalyzeRules/ParameterNameAndValue.psm1 | 0 .../ExampleAnalyzer/ExampleIssue.cs | 68 ++ .../Measure-MarkdownOrScript.ps1 | 0 .../ExampleAnalyzer}/utils.ps1 | 0 .../IssueChecker/IssueChecker.cs | 6 + tools/StaticAnalysis/ReportRecordFactory.cs | 5 + 15 files changed, 91 insertions(+), 1743 deletions(-) delete mode 100644 tools/ScriptAnalyzer/DemoMarkdowns/Add-AzEnvironment.md delete mode 100644 tools/ScriptAnalyzer/DemoMarkdowns/New-AzADDomainService.md delete mode 100644 tools/ScriptAnalyzer/DemoMarkdowns/New-AzAksNodePool.md rename tools/{ScriptAnalyzer => StaticAnalysis/ExampleAnalyzer}/AnalyzeRules/CommandName.psm1 (95%) rename tools/{ScriptAnalyzer => StaticAnalysis/ExampleAnalyzer}/AnalyzeRules/ParameterNameAndValue.psm1 (100%) create mode 100644 tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs rename tools/{ScriptAnalyzer => StaticAnalysis/ExampleAnalyzer}/Measure-MarkdownOrScript.ps1 (100%) rename tools/{ScriptAnalyzer => StaticAnalysis/ExampleAnalyzer}/utils.ps1 (100%) diff --git a/build.proj b/build.proj index b89d5237a996..f9bdcc88b3f1 100644 --- a/build.proj +++ b/build.proj @@ -263,10 +263,10 @@ - - + + - + diff --git a/documentation/tooling/static-analysis.md b/documentation/tooling/static-analysis.md index 8a0b508c065d..d461d6f67927 100644 --- a/documentation/tooling/static-analysis.md +++ b/documentation/tooling/static-analysis.md @@ -85,6 +85,8 @@ The dependency analyzer can be found in the [`DependencyAnalyzer`](https://githu - The implementation of the `IReportRecord` interface; defines what a missing assembly exception looks like when it's reported in the `MissingAssembly.csv` file that is found in the build artifacts of a CI run, as well as how to compare a new record to a record found in the existing `MissingAssembly.csv` file used for exception suppressions - `SharedAssemblyConflict` - The implementation of the `IReportRecord` interface; defines what a shared conflict exception looks like when it's reported in the `SharedAssemblyConflict.csv` file that is found in the build artifacts of a CI run, as well as how to compare a new record to a record found in the existing `SharedAssemblyConflict.csv` file used for exception suppressions +- `ExampleIssue` + - The implementation of the `IReportRecord` interface; defines what a shared conflict exception looks like when it's reported in the `ExampleIssues.csv` file that is found in the build artifacts of a CI run, as well as how to compare a new record to a record found in the existing `ExampleIssues.csv` file used for exception suppressions #### Help Analyzer diff --git a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs index b73569bb6d58..117431f70096 100644 --- a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs +++ b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs @@ -64,7 +64,7 @@ public class RestoreAzureApiManagement : AzureApiManagementCmdletBase public string SourceBlobName { get; set; } [Parameter( - Mandatory = true, + Mandatory = false, HelpMessage = "The type of access to be used for the storage account. The default value is AccessKey.")] [PSArgumentCompleter(SdkModels.AccessType.AccessKey, SdkModels.AccessType.SystemAssignedManagedIdentity, SdkModels.AccessType.UserAssignedManagedIdentity)] public string AccessType { get; set; } @@ -126,4 +126,4 @@ public override void ExecuteCmdlet() } } } -} +} diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md index a0ae535f5ebf..75b252b23b02 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md @@ -25,7 +25,7 @@ The **Remove-AzApiManagement** cmdlet removes an Azure API Management service. ### Example 1: Remove an API Management service ```powershell -Remove-AzApiManagement -ResourceGroupName "ContosoGroup01" -Name "ContosoApi" -Test +Remove-AzApiManagement -ResourceGroupName "ContosoGroup02" -Name "ContosoApi" ``` This command removes the API Management service named ContosoApi. diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md index ba8cda2541e1..5920b3c12014 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md @@ -26,7 +26,7 @@ The **Remove-AzApiManagementApi** cmdlet removes an existing API. ### Example 1: Remove an API ```powershell $apimContext = New-AzApiManagementContext -ResourceGroupName "Api-Default-WestUS" -ServiceName "contoso" -Remove-AzApiManagementApiTest -Context $apimContext -ApiId "012345678" +Remove-AzApiManagementApi -Context $apimContext -ApiId "0123456789" ``` This command removes the API with the specified ID. diff --git a/tools/ScriptAnalyzer/DemoMarkdowns/Add-AzEnvironment.md b/tools/ScriptAnalyzer/DemoMarkdowns/Add-AzEnvironment.md deleted file mode 100644 index 28a77ae285fe..000000000000 --- a/tools/ScriptAnalyzer/DemoMarkdowns/Add-AzEnvironment.md +++ /dev/null @@ -1,773 +0,0 @@ ---- -external help file: Microsoft.Azure.PowerShell.Cmdlets.Accounts.dll-Help.xml -Module Name: Az.Accounts -online version: https://docs.microsoft.com/powershell/module/az.accounts/add-azenvironment -schema: 2.0.0 ---- - -# Add-AzEnvironment - -## SYNOPSIS -Adds endpoints and metadata for an instance of Azure Resource Manager. - -## SYNTAX - -### Name (Default) -``` -Add-AzEnvironment [-Name] [[-PublishSettingsFileUrl] ] [[-ServiceEndpoint] ] - [[-ManagementPortalUrl] ] [[-StorageEndpoint] ] [[-ActiveDirectoryEndpoint] ] - [[-ResourceManagerEndpoint] ] [[-GalleryEndpoint] ] - [[-ActiveDirectoryServiceEndpointResourceId] ] [[-GraphEndpoint] ] - [[-AzureKeyVaultDnsSuffix] ] [[-AzureKeyVaultServiceEndpointResourceId] ] - [[-TrafficManagerDnsSuffix] ] [[-SqlDatabaseDnsSuffix] ] - [[-AzureDataLakeStoreFileSystemEndpointSuffix] ] - [[-AzureDataLakeAnalyticsCatalogAndJobEndpointSuffix] ] [-EnableAdfsAuthentication] - [[-AdTenant] ] [[-GraphAudience] ] [[-DataLakeAudience] ] - [[-BatchEndpointResourceId] ] [[-AzureOperationalInsightsEndpointResourceId] ] - [[-AzureOperationalInsightsEndpoint] ] [-AzureAnalysisServicesEndpointSuffix ] - [-AzureAnalysisServicesEndpointResourceId ] [-AzureAttestationServiceEndpointSuffix ] - [-AzureAttestationServiceEndpointResourceId ] [-AzureSynapseAnalyticsEndpointSuffix ] - [-ContainerRegistryEndpointSuffix ] [-AzureSynapseAnalyticsEndpointResourceId ] - [-MicrosoftGraphEndpointResourceId ] [-MicrosoftGraphUrl ] [-Scope ] - [-DefaultProfile ] [-WhatIf] [-Confirm] [] -``` - -### ARMEndpoint -``` -Add-AzEnvironment [-Name] [[-StorageEndpoint] ] [-ARMEndpoint] - [[-AzureKeyVaultDnsSuffix] ] [[-AzureKeyVaultServiceEndpointResourceId] ] - [[-DataLakeAudience] ] [[-BatchEndpointResourceId] ] - [[-AzureOperationalInsightsEndpointResourceId] ] [[-AzureOperationalInsightsEndpoint] ] - [-AzureAnalysisServicesEndpointSuffix ] [-AzureAnalysisServicesEndpointResourceId ] - [-AzureAttestationServiceEndpointSuffix ] [-AzureAttestationServiceEndpointResourceId ] - [-AzureSynapseAnalyticsEndpointSuffix ] [-ContainerRegistryEndpointSuffix ] - [-AzureSynapseAnalyticsEndpointResourceId ] [-Scope ] - [-DefaultProfile ] [-WhatIf] [-Confirm] [] -``` - -### Discovery -``` -Add-AzEnvironment [-AutoDiscover] [-Uri ] [-Scope ] - [-DefaultProfile ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -The Add-AzEnvironment cmdlet adds endpoints and metadata to enable Azure Resource Manager cmdlets to connect with a new instance of Azure Resource Manager. -The built-in environments AzureCloud and AzureChinaCloud target existing public instances of Azure Resource Manager. - -## EXAMPLES - -### Example 1: Creating and modifying a new environment -```powershell -Add-AzEnvironment -Name TestEnvironment ` - -ActiveDirectoryEndpoint TestADEndpoint ` - -ActiveDirectoryServiceEndpointResourceId TestADApplicationId ` - -ResourceManagerEndpoint TestRMEndpoint ` - -GalleryEndpoint TestGalleryEndpoint ` - -GraphEndpoint TestGraphEndpoint - -Name Resource Manager Url ActiveDirectory Authority ----- -------------------- ------------------------- -TestEnvironment TestRMEndpoint TestADEndpoint/ - -Set-AzEnvironment -Name TestEnvironment ` - -ActiveDirectoryEndpoint NewTestADEndpoint ` - -GraphEndpoint NewTestGraphEndpoint | Format-List - -Name : TestEnvironment -EnableAdfsAuthentication : False -OnPremise : False -ActiveDirectoryServiceEndpointResourceId : TestADApplicationId -AdTenant : -GalleryUrl : TestGalleryEndpoint -ManagementPortalUrl : -ServiceManagementUrl : -PublishSettingsFileUrl : -ResourceManagerUrl : TestRMEndpoint -SqlDatabaseDnsSuffix : -StorageEndpointSuffix : -ActiveDirectoryAuthority : NewTestADEndpoint -GraphUrl : NewTestGraphEndpoint -GraphEndpointResourceId : -TrafficManagerDnsSuffix : -AzureKeyVaultDnsSuffix : -DataLakeEndpointResourceId : -AzureDataLakeStoreFileSystemEndpointSuffix : -AzureDataLakeAnalyticsCatalogAndJobEndpointSuffix : -AzureKeyVaultServiceEndpointResourceId : -AzureOperationalInsightsEndpointResourceId : -AzureOperationalInsightsEndpoint : -AzureAnalysisServicesEndpointSuffix : -AzureAttestationServiceEndpointSuffix : -AzureAttestationServiceEndpointResourceId : -AzureSynapseAnalyticsEndpointSuffix : -AzureSynapseAnalyticsEndpointResourceId : -VersionProfiles : {} -ExtendedProperties : {} -BatchEndpointResourceId : -``` - -In this example we are creating a new Azure environment with sample endpoints using Add-AzEnvironment, and then we are changing the value of the ActiveDirectoryEndpoint and GraphEndpoint attributes of the created environment using the cmdlet Set-AzEnvironment. - -### Example 2: Discovering a new environment via Uri -```powershell -<# -Uri https://configuredmetadata.net returns an array of environment metadata. The following example contains a payload for the AzureCloud default environment. - -[ - { - "portal": "https://portal.azure.com", - "authentication": { - "loginEndpoint": "https://login.microsoftonline.com/", - "audiences": [ - "https://management.core.windows.net/" - ], - "tenant": "common", - "identityProvider": "AAD" - }, - "media": "https://rest.media.azure.net", - "graphAudience": "https://graph.windows.net/", - "graph": "https://graph.windows.net/", - "name": "AzureCloud", - "suffixes": { - "azureDataLakeStoreFileSystem": "azuredatalakestore.net", - "acrLoginServer": "azurecr.io", - "sqlServerHostname": ".database.windows.net", - "azureDataLakeAnalyticsCatalogAndJob": "azuredatalakeanalytics.net", - "keyVaultDns": "vault.azure.net", - "storage": "core.windows.net", - "azureFrontDoorEndpointSuffix": "azurefd.net" - }, - "batch": "https://batch.core.windows.net/", - "resourceManager": "https://management.azure.com/", - "vmImageAliasDoc": "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json", - "activeDirectoryDataLake": "https://datalake.azure.net/", - "sqlManagement": "https://management.core.windows.net:8443/", - "gallery": "https://gallery.azure.com/" - }, -…… -] -#> - -Add-AzEnvironment -AutoDiscover -Uri https://configuredmetadata.net -``` - -```Output -Name Resource Manager Url ActiveDirectory Authority ----- -------------------- ------------------------- -TestEnvironment TestRMEndpoint TestADEndpoint/ -``` - -In this example, we are discovering a new Azure environment from the `https://configuredmetadata.net` Uri. - -## PARAMETERS - -### -ActiveDirectoryEndpoint -Specifies the base authority for Azure Active Directory authentication. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: AdEndpointUrl, ActiveDirectory, ActiveDirectoryAuthority - -Required: False -Position: 5 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -ActiveDirectoryServiceEndpointResourceId -Specifies the audience for tokens that authenticate requests to Azure Resource Manager or Service Management (RDFE) endpoints. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: - -Required: False -Position: 8 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AdTenant -Specifies the default Active Directory tenant. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: - -Required: False -Position: 17 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -ARMEndpoint -The Azure Resource Manager endpoint - -```yaml -Type: System.String -Parameter Sets: ARMEndpoint -Aliases: ArmUrl - -Required: True -Position: 1 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AutoDiscover -Discovers environments via default or configured endpoint. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: Discovery -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -AzureAnalysisServicesEndpointResourceId -The resource identifier of the Azure Analysis Services resource. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -AzureAnalysisServicesEndpointSuffix -The endpoint to use when communicating with the Azure Log Analytics API. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -AzureAttestationServiceEndpointResourceId -The The resource identifier of the Azure Attestation service that is the recipient of the requested token. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AzureAttestationServiceEndpointSuffix -Dns suffix of Azure Attestation service. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AzureDataLakeAnalyticsCatalogAndJobEndpointSuffix -Dns Suffix of Azure Data Lake Analytics job and catalog services - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: - -Required: False -Position: 15 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AzureDataLakeStoreFileSystemEndpointSuffix -Dns Suffix of Azure Data Lake Store FileSystem. Example: azuredatalake.net - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: - -Required: False -Position: 14 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AzureKeyVaultDnsSuffix -Dns suffix of Azure Key Vault service. Example is vault-int.azure-int.net - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: 10 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AzureKeyVaultServiceEndpointResourceId -Resource identifier of Azure Key Vault data service that is the recipient of the requested token. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: 11 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AzureOperationalInsightsEndpoint -The endpoint to use when communicating with the Azure Log Analytics API. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: 22 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AzureOperationalInsightsEndpointResourceId -The audience for tokens authenticating with the Azure Log Analytics API. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: 21 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AzureSynapseAnalyticsEndpointResourceId -The The resource identifier of the Azure Synapse Analytics that is the recipient of the requested token. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -AzureSynapseAnalyticsEndpointSuffix -Dns suffix of Azure Synapse Analytics. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -BatchEndpointResourceId -The resource identifier of the Azure Batch service that is the recipient of the requested token - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: BatchResourceId, BatchAudience - -Required: False -Position: 20 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -ContainerRegistryEndpointSuffix -Suffix of Azure Container Registry. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -DataLakeAudience -The audience for tokens authenticating with the AD Data Lake services Endpoint. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: DataLakeEndpointResourceId, DataLakeResourceId - -Required: False -Position: 19 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -DefaultProfile -The credentials, tenant and subscription used for communication with azure - -```yaml -Type: Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core.IAzureContextContainer -Parameter Sets: (All) -Aliases: AzContext, AzureRmContext, AzureCredential - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EnableAdfsAuthentication -Indicates that Active Directory Federation Services (ADFS) on-premise authentication is allowed. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: Name -Aliases: OnPremise - -Required: False -Position: 16 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -GalleryEndpoint -Specifies the endpoint for the Azure Resource Manager gallery of deployment templates. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: Gallery, GalleryUrl - -Required: False -Position: 7 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -GraphAudience -The audience for tokens authenticating with the AD Graph Endpoint. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: GraphEndpointResourceId, GraphResourceId - -Required: False -Position: 18 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -GraphEndpoint -Specifies the URL for Graph (Active Directory metadata) requests. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: Graph, GraphUrl - -Required: False -Position: 9 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -ManagementPortalUrl -Specifies the URL for the Management Portal. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: - -Required: False -Position: 3 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -MicrosoftGraphEndpointResourceId -The resource identifier of Microsoft Graph - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -MicrosoftGraphUrl -Microsoft Graph Url - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -Name -Specifies the name of the environment to add. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -PublishSettingsFileUrl -Specifies the URL from which .publishsettings files can be downloaded. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -ResourceManagerEndpoint -Specifies the URL for Azure Resource Manager requests. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: ResourceManager, ResourceManagerUrl - -Required: False -Position: 6 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -Scope -Determines the scope of context changes, for example, whether changes apply only to the current process, or to all sessions started by this user. - -```yaml -Type: Microsoft.Azure.Commands.Profile.Common.ContextModificationScope -Parameter Sets: (All) -Aliases: -Accepted values: Process, CurrentUser - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ServiceEndpoint -Specifies the endpoint for Service Management (RDFE) requests. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: ServiceManagement, ServiceManagementUrl - -Required: False -Position: 2 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -SqlDatabaseDnsSuffix -Specifies the domain-name suffix for Azure SQL Database servers. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: - -Required: False -Position: 13 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -StorageEndpoint -Specifies the endpoint for storage (blob, table, queue, and file) access. - -```yaml -Type: System.String -Parameter Sets: Name, ARMEndpoint -Aliases: StorageEndpointSuffix - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -TrafficManagerDnsSuffix -Specifies the domain-name suffix for Azure Traffic Manager services. - -```yaml -Type: System.String -Parameter Sets: Name -Aliases: - -Required: False -Position: 12 -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -Uri -Specifies URI of the internet resource to fetch environments. - -```yaml -Type: System.Uri -Parameter Sets: Discovery -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### System.String - -### System.Management.Automation.SwitchParameter - -## OUTPUTS - -### Microsoft.Azure.Commands.Profile.Models.PSAzureEnvironment - -## NOTES - -## RELATED LINKS - -[Get-AzEnvironment](./Get-AzEnvironment.md) - -[Remove-AzEnvironment](./Remove-AzEnvironment.md) - -[Set-AzEnvironment](./Set-AzEnvironment.md) - diff --git a/tools/ScriptAnalyzer/DemoMarkdowns/New-AzADDomainService.md b/tools/ScriptAnalyzer/DemoMarkdowns/New-AzADDomainService.md deleted file mode 100644 index 1cd73655d3bf..000000000000 --- a/tools/ScriptAnalyzer/DemoMarkdowns/New-AzADDomainService.md +++ /dev/null @@ -1,509 +0,0 @@ ---- -external help file: -Module Name: Az.ADDomainServices -online version: https://docs.microsoft.com/powershell/module/az.addomainservices/new-azaddomainservice -schema: 2.0.0 -content_git_url: https://github.com/Azure/azure-powershell/blob/main/src/ADDomainServices/help/New-AzADDomainService.md -original_content_git_url: https://github.com/Azure/azure-powershell/blob/main/src/ADDomainServices/help/New-AzADDomainService.md ---- - -# New-AzADDomainService - -## SYNOPSIS -The Create Domain Service operation creates a new domain service with the specified parameters. -If the specific service already exists, then any patchable properties will be updated and any immutable properties will remain unchanged. - -## SYNTAX - -``` -New-AzADDomainService -Name -ResourceGroupName -DomainName - -ReplicaSet [-SubscriptionId ] [-DomainConfigurationType ] - [-DomainSecuritySettingNtlmV1 ] [-DomainSecuritySettingSyncKerberosPassword ] - [-DomainSecuritySettingSyncNtlmPassword ] [-DomainSecuritySettingSyncOnPremPassword ] - [-DomainSecuritySettingTlsV1 ] [-FilteredSync ] [-ForestTrust ] - [-LdapSettingExternalAccess ] [-LdapSettingLdaps ] [-LdapSettingPfxCertificate ] - [-LdapSettingPfxCertificatePassword ] [-NotificationSettingAdditionalRecipient ] - [-NotificationSettingNotifyDcAdmin ] [-NotificationSettingNotifyGlobalAdmin ] - [-ResourceForest ] [-Sku ] [-Tag ] [-DefaultProfile ] [-AsJob] [-NoWait] - [-Confirm] [-WhatIf] [] -``` - -## DESCRIPTION -The Create Domain Service operation creates a new domain service with the specified parameters. -If the specific service already exists, then any patchable properties will be updated and any immutable properties will remain unchanged. - -## EXAMPLES - -### Example 1: Create new ADDomainService -```powershell -$replicaSet = New-AzADDomainServiceReplicaSetObject -Location westus -SubnetId /subscriptions/********-****-****-****-**********/resourceGroups/yishitest/providers/Microsoft.Network/virtualNetworks/aadds-vnet/subnets/default -New-AzADDomainService -name youriADdomain -ResourceGroupName youriAddomain -DomainName youriAddomain.com -ReplicaSet $replicaSet -debug -``` - -```output -Name Domain Name Location Sku ----- ----------- -------- --- -youriADdomain youriAddomain.com westus Enterprise -``` - -Create new ADDomainService - -## PARAMETERS - -### -AsJob -Run the command as a job - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DefaultProfile -The credentials, account, tenant, and subscription used for communication with Azure. - -```yaml -Type: System.Management.Automation.PSObject -Parameter Sets: (All) -Aliases: AzureRMContext, AzureCredential - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DomainConfigurationType -Domain Configuration Type - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DomainName -The name of the Azure domain that the user would like to deploy Domain Services to. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DomainSecuritySettingNtlmV1 -A flag to determine whether or not NtlmV1 is enabled or disabled. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DomainSecuritySettingSyncKerberosPassword -A flag to determine whether or not SyncKerberosPasswords is enabled or disabled. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DomainSecuritySettingSyncNtlmPassword -A flag to determine whether or not SyncNtlmPasswords is enabled or disabled. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DomainSecuritySettingSyncOnPremPassword -A flag to determine whether or not SyncOnPremPasswords is enabled or disabled. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DomainSecuritySettingTlsV1 -A flag to determine whether or not TlsV1 is enabled or disabled. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -FilteredSync -Enabled or Disabled flag to turn on Group-based filtered sync - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ForestTrust -List of settings for Resource Forest -To construct, see NOTES section for FORESTTRUST properties and create a hash table. - -```yaml -Type: Microsoft.Azure.PowerShell.Cmdlets.ADDomainServices.Models.Api202001.IForestTrust[] -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LdapSettingExternalAccess -A flag to determine whether or not Secure LDAP access over the internet is enabled or disabled. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LdapSettingLdaps -A flag to determine whether or not Secure LDAP is enabled or disabled. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LdapSettingPfxCertificate -The certificate required to configure Secure LDAP. -The parameter passed here should be a base64encoded representation of the certificate pfx file. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LdapSettingPfxCertificatePassword -The password to decrypt the provided Secure LDAP certificate pfx file. - -```yaml -Type: System.Security.SecureString -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Name -The name of the domain service. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: DomainServiceName - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -NotificationSettingAdditionalRecipient -The list of additional recipients - -```yaml -Type: System.String[] -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -NotificationSettingNotifyDcAdmin -Should domain controller admins be notified - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -NotificationSettingNotifyGlobalAdmin -Should global admins be notified - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -NoWait -Run the command asynchronously - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ReplicaSet -List of ReplicaSets -To construct, see NOTES section for REPLICASET properties and create a hash table. - -```yaml -Type: Microsoft.Azure.PowerShell.Cmdlets.ADDomainServices.Models.Api202001.IReplicaSet[] -Parameter Sets: (All) -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ResourceForest -Resource Forest - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ResourceGroupName -The name of the resource group within the user's subscription. -The name is case insensitive. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Sku -Sku Type - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -SubscriptionId -Gets subscription credentials which uniquely identify the Microsoft Azure subscription. -The subscription ID forms part of the URI for every service call. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: (Get-AzContext).Subscription.Id -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Tag -Resource tags - -```yaml -Type: System.Collections.Hashtable -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -### Microsoft.Azure.PowerShell.Cmdlets.ADDomainServices.Models.Api202001.IDomainService - -## NOTES - -ALIASES - -COMPLEX PARAMETER PROPERTIES - -To create the parameters described below, construct a hash table containing the appropriate properties. For information on hash tables, run Get-Help about_Hash_Tables. - - -FORESTTRUST : List of settings for Resource Forest - - `[FriendlyName ]`: Friendly Name - - `[RemoteDnsIP ]`: Remote Dns ips - - `[TrustDirection ]`: Trust Direction - - `[TrustPassword ]`: Trust Password - - `[TrustedDomainFqdn ]`: Trusted Domain FQDN - -REPLICASET : List of ReplicaSets - - `[Location ]`: Virtual network location - - `[SubnetId ]`: The name of the virtual network that Domain Services will be deployed on. The id of the subnet that Domain Services will be deployed on. /virtualNetwork/vnetName/subnets/subnetName. - -## RELATED LINKS - diff --git a/tools/ScriptAnalyzer/DemoMarkdowns/New-AzAksNodePool.md b/tools/ScriptAnalyzer/DemoMarkdowns/New-AzAksNodePool.md deleted file mode 100644 index eb7374b548ce..000000000000 --- a/tools/ScriptAnalyzer/DemoMarkdowns/New-AzAksNodePool.md +++ /dev/null @@ -1,453 +0,0 @@ ---- -external help file: Microsoft.Azure.PowerShell.Cmdlets.Aks.dll-Help.xml -Module Name: Az.Aks -online version: https://docs.microsoft.com/powershell/module/az.aks/new-azaksnodepool -schema: 2.0.0 -content_git_url: https://github.com/Azure/azure-powershell/blob/main/src/Aks/Aks/help/New-AzAksNodePool.md -original_content_git_url: https://github.com/Azure/azure-powershell/blob/main/src/Aks/Aks/help/New-AzAksNodePool.md ---- - -# New-AzAksNodePool - -## SYNOPSIS -Create a new node pool in specified cluster. - -## SYNTAX - -### defaultParameterSet -``` -New-AzAksNodePool -ResourceGroupName -ClusterName -Name [-Count ] - [-OsDiskSize ] [-VmSize ] [-VnetSubnetID ] [-MaxPodCount ] [-OsType ] - [-EnableNodePublicIp] [-NodePublicIPPrefixID ] [-ScaleSetPriority ] - [-ScaleSetEvictionPolicy ] [-VmSetType ] [-AvailabilityZone ] [-Force] - [-KubernetesVersion ] [-MinCount ] [-MaxCount ] [-EnableAutoScaling] - [-DefaultProfile ] [-WhatIf] [-Confirm] [-SubscriptionId ] - [] -``` - -### ParentObjectParameterSet -``` -New-AzAksNodePool -Name -ClusterObject [-Count ] [-OsDiskSize ] - [-VmSize ] [-VnetSubnetID ] [-MaxPodCount ] [-OsType ] [-EnableNodePublicIp] - [-NodePublicIPPrefixID ] [-ScaleSetPriority ] [-ScaleSetEvictionPolicy ] - [-VmSetType ] [-AvailabilityZone ] [-Force] [-KubernetesVersion ] - [-MinCount ] [-MaxCount ] [-EnableAutoScaling] [-DefaultProfile ] - [-WhatIf] [-Confirm] [-SubscriptionId ] [] -``` - -## DESCRIPTION -Create a new node pool in specified cluster. - -## EXAMPLES - -### Create node pool with default parameters -```powershell -New-AzAksNodePool -ResourceGroupName myResouceGroup -ClusterName myCluster -Name mydefault -``` - -### Create Windows Server container on an AKS -```powershell -$cred = ConvertTo-SecureString -AsPlainText "Password!!123" -Force -New-AzAks -ResourceGroupName myResourceGroup -Name myCluster -WindowsProfileAdminUserName azureuser -WindowsProfileAdminUserPassword $cred -NetworkPlugin azure -NodeVmSetType VirtualMachineScaleSets -New-AzAksNodePool -ResourceGroupName myResourceGroup -ClusterName myCluster -Name win1 -OsType Windows -VmSetType VirtualMachineScaleSets -``` - -## PARAMETERS - -### -AvailabilityZone -Availability zones for nodes. Must use VirtualMachineScaleSets AgentPoolType. - -```yaml -Type: System.String[] -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ClusterName -The name of the managed cluster resource. - -```yaml -Type: System.String -Parameter Sets: defaultParameterSet -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ClusterObject -Specify cluster object in which to create node pool. - -```yaml -Type: Microsoft.Azure.Commands.Aks.Models.PSKubernetesCluster -Parameter Sets: ParentObjectParameterSet -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: True (ByValue) -Accept wildcard characters: False -``` - -### -Count -The default number of nodes for the node pools. - -```yaml -Type: System.Int32 -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DefaultProfile -The credentials, account, tenant, and subscription used for communication with Azure. - -```yaml -Type: Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core.IAzureContextContainer -Parameter Sets: (All) -Aliases: AzContext, AzureRmContext, AzureCredential - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EnableAutoScaling -Whether to enable auto-scaler - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EnableNodePublicIp -Whether to enable public IP for nodes. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Force -Create node pool even if it already exists - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KubernetesVersion -The version of Kubernetes to use for creating the cluster. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -MaxCount -Maximum number of nodes for auto-scaling - -```yaml -Type: System.Int32 -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -MaxPodCount -Maximum number of pods that can run on node. - -```yaml -Type: System.Int32 -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -MinCount -Minimum number of nodes for auto-scaling. - -```yaml -Type: System.Int32 -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Name -The name of the node pool. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -NodePublicIPPrefixID -The resource Id of public IP prefix for node pool. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -OsDiskSize -The default number of nodes for the node pools. - -```yaml -Type: System.Int32 -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -OsType -OsType to be used to specify os type. -Choose from Linux and Windows. -Default to Linux. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ResourceGroupName -The name of the resource group. - -```yaml -Type: System.String -Parameter Sets: defaultParameterSet -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ScaleSetEvictionPolicy -ScaleSetEvictionPolicy to be used to specify eviction policy for low priority virtual machine scale set. -Default to Delete. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ScaleSetPriority -ScaleSetPriority to be used to specify virtual machine scale set priority. -Default to regular. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -SubscriptionId -The ID of the subscription. -By default, cmdlets are executed in the subscription that is set in the current context. If the user specifies another subscription, the current cmdlet is executed in the subscription specified by the user. -Overriding subscriptions only take effect during the lifecycle of the current cmdlet. It does not change the subscription in the context, and does not affect subsequent cmdlets. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: True (ByPropertyName) -Accept wildcard characters: False -``` - -### -VmSetType -Represents types of an node pool. -Possible values include: 'VirtualMachineScaleSets', 'AvailabilitySet' - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -VmSize -The size of the Virtual Machine. Default value is Standard_D2_v2. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -VnetSubnetID -VNet SubnetID specifies the VNet's subnet identifier. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### Microsoft.Azure.Commands.Aks.Models.PSKubernetesCluster - -## OUTPUTS - -### Microsoft.Azure.Commands.Aks.Models.PSNodePool - -## NOTES - -## RELATED LINKS diff --git a/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 similarity index 95% rename from tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 rename to tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 index 7086f16c484c..df9035e8ff18 100644 --- a/tools/ScriptAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 @@ -5,7 +5,9 @@ File: CommandName.psm1 #> -Import-Module (Get-ChildItem -Path ./artifacts/Debug).FullName -Scope Global +# Import-Module (Get-ChildItem -Path ./artifacts/Debug).FullName -Scope Global +Import-Module -Name ./artifacts/Debug/Az.Accounts -Scope Global +Import-Module -Name ./artifacts/Debug/Az.ApiManagement -Scope Global enum RuleNames { Invalid_Cmdlet diff --git a/tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 similarity index 100% rename from tools/ScriptAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 rename to tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 diff --git a/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs b/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs new file mode 100644 index 000000000000..4938546af115 --- /dev/null +++ b/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs @@ -0,0 +1,68 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Text.RegularExpressions; +using Tools.Common.Issues; + +namespace StaticAnalysis.ExampleAnalyzer +{ + public class ExampleIssue : IReportRecord + { + public int ProblemId { get; set; } + public string Description { get; set; } + public string Remediation { get; set; } + public int Severity { get; set; } + public string PrintHeaders() + { + return "\"Severity\",\"ProblemId\",\"Description\",\"Remediation\""; + } + + public string FormatRecord() + { + return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\"", + Severity, ProblemId, Description, Remediation); + } + + public bool Match(IReportRecord other) + { + var result = false; + var record = other as ExampleIssue; + if (record != null) + { + result =(record.ProblemId == ProblemId) && + (record.Severity == Severity) && + (record.Description == Description); + } + + return result; + } + + public IReportRecord Parse(string line) + { + var matcher = "\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\""; + var match = Regex.Match(line, matcher); + if (!match.Success || match.Groups.Count < 5) + { + throw new InvalidOperationException(string.Format("Could not parse '{0}' as ExampleIssue record", line)); + } + + Severity = int.Parse(match.Groups[1].Value); + ProblemId = int.Parse(match.Groups[2].Value); + Description = match.Groups[3].Value; + Remediation = match.Groups[4].Value; + return this; + } + } +} \ No newline at end of file diff --git a/tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 similarity index 100% rename from tools/ScriptAnalyzer/Measure-MarkdownOrScript.ps1 rename to tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 diff --git a/tools/ScriptAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 similarity index 100% rename from tools/ScriptAnalyzer/utils.ps1 rename to tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 diff --git a/tools/StaticAnalysis/IssueChecker/IssueChecker.cs b/tools/StaticAnalysis/IssueChecker/IssueChecker.cs index c8a853947948..94943dc2f7b9 100644 --- a/tools/StaticAnalysis/IssueChecker/IssueChecker.cs +++ b/tools/StaticAnalysis/IssueChecker/IssueChecker.cs @@ -25,6 +25,7 @@ using StaticAnalysis.BreakingChangeAnalyzer; using StaticAnalysis.DependencyAnalyzer; using StaticAnalysis.SignatureVerifier; +using StaticAnalysis.ExampleAnalyzer; namespace StaticAnalysis.IssueChecker { @@ -39,6 +40,7 @@ public class IssueChecker : IStaticAnalyzer ("MissingAssemblies.csv", typeof(MissingAssembly).FullName), ("ExtraAssemblies.csv", typeof(ExtraAssembly).FullName), ("SignatureIssues.csv", typeof(SignatureIssue).FullName), + ("ExampleIssues.csv", typeof(ExampleIssue).FullName), }; public AnalysisLogger Logger { get; set; } @@ -56,6 +58,7 @@ public void Analyze(IEnumerable scopes) public void Analyze(IEnumerable scopes, IEnumerable modulesToAnalyze) { + Console.WriteLine("bp 4"); foreach (string scope in scopes) { Console.WriteLine(scope); @@ -66,9 +69,11 @@ public void Analyze(IEnumerable scopes, IEnumerable modulesToAna } string reportsDirectory = scopes.First(); + Console.WriteLine("bp 3"); bool hasCriticalIssue = false; foreach ((string, string) item in exceptionLogInfoList) { + Console.WriteLine("bp 1"); string exceptionFileName = item.Item1; string recordTypeName = item.Item2; @@ -109,6 +114,7 @@ private bool IsSingleExceptionFileHasCriticalIssue(string exceptionFilePath, str { if (record.Severity < 2) { + Console.WriteLine("bp 2"); hasError = true; errorText.AppendLine(record.FormatRecord()); } diff --git a/tools/StaticAnalysis/ReportRecordFactory.cs b/tools/StaticAnalysis/ReportRecordFactory.cs index fa05a8db38cb..53618559bdcd 100644 --- a/tools/StaticAnalysis/ReportRecordFactory.cs +++ b/tools/StaticAnalysis/ReportRecordFactory.cs @@ -17,6 +17,7 @@ using StaticAnalysis.DependencyAnalyzer; using StaticAnalysis.HelpAnalyzer; using StaticAnalysis.SignatureVerifier; +using StaticAnalysis.ExampleAnalyzer; using System; using System.Collections.Generic; @@ -58,6 +59,10 @@ public static IReportRecord Create(string type) { return new SignatureIssue(); } + if (type.Equals(typeof(ExampleIssue).FullName)) + { + return new ExampleIssue(); + } return null; } From a61081d9805e9aaa3a0a155899854500aa5ee8ca Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 1 Jun 2022 11:35:04 +0800 Subject: [PATCH 10/43] fix the bug for exceptionFilePath --- tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs | 2 +- tools/StaticAnalysis/IssueChecker/IssueChecker.cs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs b/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs index 4938546af115..d41af51466e2 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs +++ b/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs @@ -51,7 +51,7 @@ public bool Match(IReportRecord other) public IReportRecord Parse(string line) { - var matcher = "\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\""; + var matcher = "\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\""; var match = Regex.Match(line, matcher); if (!match.Success || match.Groups.Count < 5) { diff --git a/tools/StaticAnalysis/IssueChecker/IssueChecker.cs b/tools/StaticAnalysis/IssueChecker/IssueChecker.cs index 94943dc2f7b9..46334dd4c675 100644 --- a/tools/StaticAnalysis/IssueChecker/IssueChecker.cs +++ b/tools/StaticAnalysis/IssueChecker/IssueChecker.cs @@ -58,7 +58,6 @@ public void Analyze(IEnumerable scopes) public void Analyze(IEnumerable scopes, IEnumerable modulesToAnalyze) { - Console.WriteLine("bp 4"); foreach (string scope in scopes) { Console.WriteLine(scope); @@ -69,17 +68,17 @@ public void Analyze(IEnumerable scopes, IEnumerable modulesToAna } string reportsDirectory = scopes.First(); - Console.WriteLine("bp 3"); bool hasCriticalIssue = false; foreach ((string, string) item in exceptionLogInfoList) { - Console.WriteLine("bp 1"); string exceptionFileName = item.Item1; string recordTypeName = item.Item2; string exceptionFilePath = Path.Combine(reportsDirectory, exceptionFileName); + Console.WriteLine(exceptionFilePath); if (!File.Exists(exceptionFilePath)) { + Console.WriteLine("bp1"); continue; } if (IsSingleExceptionFileHasCriticalIssue(exceptionFilePath, recordTypeName)) @@ -114,7 +113,6 @@ private bool IsSingleExceptionFileHasCriticalIssue(string exceptionFilePath, str { if (record.Severity < 2) { - Console.WriteLine("bp 2"); hasError = true; errorText.AppendLine(record.FormatRecord()); } From 2eb38a3b1f3ce21bd08ab9fd78e65c5e0e2529a7 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 1 Jun 2022 17:18:53 +0800 Subject: [PATCH 11/43] Report errors according to the .csv --- .../Debugging-StaticAnalysis-Errors.md | 3 + .../AnalyzeRules/CommandName.psm1 | 6 +- .../AnalyzeRules/ParameterNameAndValue.psm1 | 14 +-- .../ExampleAnalyzer/ExampleIssue.cs | 47 +++++--- .../Measure-MarkdownOrScript.ps1 | 11 +- .../StaticAnalysis/ExampleAnalyzer/utils.ps1 | 103 +----------------- 6 files changed, 56 insertions(+), 128 deletions(-) diff --git a/documentation/Debugging-StaticAnalysis-Errors.md b/documentation/Debugging-StaticAnalysis-Errors.md index 3f35f5dfd69c..c105b023bd0b 100644 --- a/documentation/Debugging-StaticAnalysis-Errors.md +++ b/documentation/Debugging-StaticAnalysis-Errors.md @@ -52,3 +52,6 @@ Signature issues occur when your cmdlets do not follow PowerShell standards. Pl ### Help Issues Most help issues that cause StaticAnalysis to fail occur when help has not been added for a particular cmdlet. If you have not generated help for your new cmdlets, please follow the instructions [here](https://github.com/Azure/azure-powershell/blob/main/documentation/development-docs/help-generation.md). If this is not the issue, follow the steps listed under "Remediation" for each violation listed in HelpIssues.csv. + +### Example Issues +Todo \ No newline at end of file diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 index df9035e8ff18..26c31e3978a5 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 @@ -82,15 +82,15 @@ function Measure-CommandName { [System.Management.Automation.Language.Ast[]]$Asts = $ScriptBlockAst.FindAll($Predicate, $false) for ($i = 0; $i -lt $Asts.Count; $i++) { if ($global:CommandParameterPair[$i].ParameterName -eq "") { - $Message = "`"$($CommandParameterPair[$i].CommandName)`" is not a valid command name." + $Message = "$($CommandParameterPair[$i].CommandName) is not a valid command name." $RuleName = [RuleNames]::Invalid_Cmdlet } if ($global:CommandParameterPair[$i].ParameterName -eq "") { - $Message = "`"$($CommandParameterPair[$i].CommandName)`" is an alias of `"$((Get-Alias $CommandParameterPair[$i].CommandName)[0].ResolvedCommandName)`"." + $Message = "$($CommandParameterPair[$i].CommandName) is an alias of `"$((Get-Alias $CommandParameterPair[$i].CommandName)[0].ResolvedCommandName)`"." $RuleName = [RuleNames]::Is_Alias } if ($global:CommandParameterPair[$i].ParameterName -eq "") { - $Message = "`"$($CommandParameterPair[$i].CommandName)`" doesn't follow the Capitalization Conventions." + $Message = "$($CommandParameterPair[$i].CommandName) doesn't follow the Capitalization Conventions." $RuleName = [RuleNames]::Capitalization_Conventions_Violated } $Result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index 63efcffeff8c..f032addc5951 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -442,37 +442,37 @@ function Measure-ParameterNameAndValue { [System.Management.Automation.Language.Ast[]]$Asts = $ScriptBlockAst.FindAll($Predicate, $false) for ($i = 0; $i -lt $Asts.Count; $i++) { if ($global:CommandParameterPair[$i].ParameterName -eq "" -and $global:CommandParameterPair[$i].ExpressionToParameter -eq "") { - $Message = "`"$($CommandParameterPair[$i].CommandName)`" has a parameter not in the same ParameterSet as others." + $Message = "$($CommandParameterPair[$i].CommandName) has a parameter not in the same ParameterSet as others." $RuleName = [RuleNames]::Unknown_Parameter_Set $Severity = "Error" } elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq "") { - $Message = "`"$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName)`" is not a valid parameter name." + $Message = "$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) is not a valid parameter name." $RuleName = [RuleNames]::Invalid_Parameter_Name $Severity = "Error" } elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq "<2>") { - $Message = "`"$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName)`" appeared more than once." + $Message = "$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) appeared more than once." $RuleName = [RuleNames]::Duplicate_Parameter_Name $Severity = "Error" } elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq $null) { - $Message = "`"$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName)`" must be assigned with a value." + $Message = "$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) must be assigned with a value." $RuleName = [RuleNames]::Unassigned_Parameter $Severity = "Error" } elseif ($global:CommandParameterPair[$i].ExpressionToParameter.EndsWith(" is a null-valued parameter value.")) { - $Message = "`"$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter)`"" + $Message = "$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter)" $RuleName = [RuleNames]::Unassigned_Variable $Severity = "Warning" } elseif ($global:CommandParameterPair[$i].ParameterName -eq "") { - $Message = "`"$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ExpressionToParameter)`" is not explicitly assigned to a parameter." + $Message = "$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ExpressionToParameter) is not explicitly assigned to a parameter." $RuleName = [RuleNames]::Unbinded_Parameter_Name $Severity = "Warning" } else { - $Message = "`"$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter)`" is not an expected parameter value type." + $Message = "$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter) is not an expected parameter value type." $RuleName = [RuleNames]::Mismatched_Parameter_Value_Type $Severity = "Warning" } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs b/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs index d41af51466e2..62cb6d86a08e 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs +++ b/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs @@ -15,24 +15,29 @@ using System; using System.Text.RegularExpressions; using Tools.Common.Issues; +using System.Collections.Generic; namespace StaticAnalysis.ExampleAnalyzer { public class ExampleIssue : IReportRecord { + public string Module { get; set; } + public string Cmdlet { get; set; } + public int Example { get; set; } + public string RuleName { get; set; } public int ProblemId { get; set; } + public int Severity { get; set; } public string Description { get; set; } public string Remediation { get; set; } - public int Severity { get; set; } public string PrintHeaders() { - return "\"Severity\",\"ProblemId\",\"Description\",\"Remediation\""; + return "\"Module\",\"Cmdlet\",\"Example\",\"RuleName\",\"ProblemId\",\"Severity\",\"Description\",\"Remediation\""; } public string FormatRecord() { - return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\"", - Severity, ProblemId, Description, Remediation); + return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\"", + Module, Cmdlet, Example, RuleName, ProblemId, Severity, Description, Remediation); } public bool Match(IReportRecord other) @@ -41,8 +46,7 @@ public bool Match(IReportRecord other) var record = other as ExampleIssue; if (record != null) { - result =(record.ProblemId == ProblemId) && - (record.Severity == Severity) && + result =(record.Severity == Severity) && (record.Description == Description); } @@ -51,17 +55,34 @@ public bool Match(IReportRecord other) public IReportRecord Parse(string line) { - var matcher = "\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\""; + var matcher = "\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\""; var match = Regex.Match(line, matcher); - if (!match.Success || match.Groups.Count < 5) + if (!match.Success || match.Groups.Count < 8) { throw new InvalidOperationException(string.Format("Could not parse '{0}' as ExampleIssue record", line)); } - - Severity = int.Parse(match.Groups[1].Value); - ProblemId = int.Parse(match.Groups[2].Value); - Description = match.Groups[3].Value; - Remediation = match.Groups[4].Value; + Module = match.Groups[1].Value; + Cmdlet = match.Groups[2].Value; + Example = int.Parse(match.Groups[3].Value); + RuleName = match.Groups[4].Value; + var problemMap = new Dictionary + { + {"Invalid_Cmdlet", 3001}, + {"Is_Alias", 3002}, + {"Capitalization_Conventions_Violated", 3003}, + {"Unknown_Parameter_Set",3101}, + {"Invalid_Parameter_Name",3102}, + {"Duplicate_Parameter_Name",3103}, + {"Unassigned_Parameter",3104} + }; + ProblemId = problemMap[RuleName]; + var severityMap = new Dictionary + { + {"Error", 1} + }; + Severity = severityMap[match.Groups[5].Value]; + Description = match.Groups[6].Value; + Remediation = match.Groups[7].Value; return this; } } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 index c2c6336ccd36..b2807d4bdb3f 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 @@ -48,11 +48,12 @@ Remove-Item $OutputFolder\*.csv -Recurse -ErrorAction SilentlyContinue if ($PSCmdlet.ParameterSetName -eq "Markdown") { $null = New-Item -ItemType Directory -Path $OutputFolder\$ScriptsByExampleFolder -ErrorAction SilentlyContinue $MarkdownPath = Get-Content $MarkdownPaths - (Get-Item $MarkdownPath) | foreach{ - # Filter the .md of overview in /help - if ($_ -cmatch ".*/help.*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { + (Get-ChildItem $MarkdownPath) | foreach{ + # Filter the .md of overview in \help\ + if ($_.FullName -cmatch ".*help.*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { Write-Output "Searching in file $($_.FullName) ..." - $module = ($_ -split "/")[-3] + $module = ($_.FullName -split "\")[-3]#/|\ + Write-Output $module $cmdlet = $_.BaseName $result = Measure-SectionMissingAndOutputScript $module $cmdlet $_.FullName ` -OutputScriptsInFile:$OutputScriptsInFile.IsPresent ` @@ -89,7 +90,7 @@ if ($PSCmdlet.ParameterSetName -eq "Script" -or $AnalyzeScriptsInFile.IsPresent) $analysisResultsTable += $analysisResults } # Summarize analysis results, output in Result.csv - $analysisResultsTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\Results-$(Get-Date -UFormat %s).csv" -NoTypeInformation + $analysisResultsTable | where {$_ -ne $null} | Export-Csv ".\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation } # Clean caches diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index 799c3f0037ad..67f5238e5038 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -340,84 +340,6 @@ function Measure-SectionMissingAndOutputScript { } } - -<# - .SYNOPSIS - Tests whether the script invokes other scripts and whether it is vaild. -#> -function Measure-ScriptFile { - param ( - [Parameter(Mandatory)] - [string]$ScriptPath - ) - - $assignmentLeftAndRight = @{} - $importSucceededScriptPaths = @() - $importFailedScriptPaths = @() - $assignmentLeftAndRight += @{ - '$PSScriptRoot' = [IO.Path]::GetDirectoryName($ScriptPath) - } - - # parseFile method returns ScriptBlockAst - [System.Management.Automation.Language.Parser]::ParseFile($ScriptPath, [ref]$null, [ref]$null).FindAll({$args[0] -is [System.Management.Automation.Language.Ast]}, $true) | foreach { - if ($_ -is [System.Management.Automation.Language.CommandAst]) { - [System.Management.Automation.Language.CommandAst]$commandAst = $_ - # if invocate the command by ".", then get into the scriptPath - if ($commandAst.InvocationOperator -eq "Dot") { - $importedScriptPath = $commandAst.CommandElements[0].Extent.Text - while ($importedScriptPath -ne $importedScriptPath_Before) { - $importedScriptPath_Before = $importedScriptPath - $assignmentLeftAndRight.Keys.ForEach({ - $importedScriptPath = $importedScriptPath.Replace($_, $assignmentLeftAndRight.$_) - }) - } - $importedScriptPath = Invoke-Expression "Write-Output $importedScriptPath" - if (Test-Path $importedScriptPath -PathType Leaf) { - $importSucceededScriptPaths += $importedScriptPath - } - else { - $importFailedScriptPaths += $commandAst.CommandElements[0].Extent.Text - } - } - } - if ($_ -is [System.Management.Automation.Language.AssignmentStatementAst]) { - # Variable assignment, if variable exists, replace its value, or add the pair to $assignmentLeftAndRight - [System.Management.Automation.Language.AssignmentStatementAst]$assignmentStatementAst = $_ - if ($assignmentLeftAndRight.ContainsKey($assignmentStatementAst.Left.Extent.Text)) { - $assignmentLeftAndRight.($assignmentStatementAst.Left.Extent.Text) = $assignmentStatementAst.Right.Extent.Text - } - else { - $assignmentLeftAndRight += @{ - $assignmentStatementAst.Left.Extent.Text = $assignmentStatementAst.Right.Extent.Text - } - } - } - } - return @{ - SucceededResults = $importSucceededScriptPaths - FailedResults = $importFailedScriptPaths - } -} - -<# - .SYNOPSIS - Adds the script invoked other scripts and the rules into one script. -#> -function Add-ContentToHeadOfRule { - param ( - [string[]]$SrcFilePaths, - [string]$DstFolderPath, - [string]$ImportContent - ) - - # add content of the script invoked by "." and rules.psm1 to tempFolder - $null = New-Item -ItemType Directory -Path $DstFolderPath -ErrorAction SilentlyContinue - # filter *.psm1 files - Get-ChildItem $SrcFilePaths -Filter *.psm1 | foreach { - ($ImportContent + (Get-Content $_ -Raw)) | Out-File "$DstFolderPath\$($_.Name)" -Encoding utf8 - } -} - <# .SYNOPSIS Invoke PSScriptAnalyzer with custom rules, return the error set. @@ -447,25 +369,7 @@ function Get-ScriptAnalyzerResult { $cmdlet = $scriptName $example = "" } - $importFailedResults = @() - $importContent = "" - - $importResults = Measure-ScriptFile $ScriptPath - foreach ($path in $importResults.FailedResults) { - $importFailedResults += [PSCustomObject]@{ - Module = $module - Cmdlet = $cmdlet - Example = $example - RuleName = "Imported_Script_Not_Exist" - Message = "Imported Script $path doesn't exist." - Extent = ". $path" - } - } - $importResults.SucceededResults | foreach { - $importContent += ". $_`n" - } - $tempFolderPath = "TempPSSARules" - Add-ContentToHeadOfRule $RulePaths $tempFolderPath $importContent + # Invoke PSScriptAnalyzer : input scriptblock, output error set in $result with property: RuleName, Message, Extent if ($RulePath -eq $null) { $results = Invoke-ScriptAnalyzer -Path $ScriptPath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent @@ -473,12 +377,11 @@ function Get-ScriptAnalyzerResult { else { $results = Invoke-ScriptAnalyzer -Path $ScriptPath -CustomRulePath $RulePath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent } - Remove-Item $tempFolderPath -Recurse - return $importFailedResults + $results | Select-Object -Property @{Name = "Module"; Expression = {$Module}}, + return $results | Select-Object -Property @{Name = "Module"; Expression = {$Module}}, @{Name = "Cmdlet";Expression={$Cmdlet}}, @{Name ="Example";Expression={$Example}}, - RuleName, Message, Extent + RuleName, Severity, @{Name = "Description";Expression={$results.Message}}, Extent } From 955c5be80a5c3194a2ae2b9dc8e18200b8c6b7ee Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 1 Jun 2022 17:29:14 +0800 Subject: [PATCH 12/43] Test output of scriptAnalyzer --- src/Accounts/Accounts/help/Add-AzEnvironment.md | 2 +- .../ApiManagement/Commands/RestoreAzureApiManagement.cs | 2 +- src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md | 2 +- .../ApiManagement/help/Remove-AzApiManagementApi.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Accounts/Accounts/help/Add-AzEnvironment.md b/src/Accounts/Accounts/help/Add-AzEnvironment.md index 28a77ae285fe..2c4815564dc1 100644 --- a/src/Accounts/Accounts/help/Add-AzEnvironment.md +++ b/src/Accounts/Accounts/help/Add-AzEnvironment.md @@ -3,7 +3,7 @@ external help file: Microsoft.Azure.PowerShell.Cmdlets.Accounts.dll-Help.xml Module Name: Az.Accounts online version: https://docs.microsoft.com/powershell/module/az.accounts/add-azenvironment schema: 2.0.0 ---- +--- # Add-AzEnvironment diff --git a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs index 117431f70096..a6f4c2f0cfcc 100644 --- a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs +++ b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs @@ -126,4 +126,4 @@ public override void ExecuteCmdlet() } } } -} +} diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md index 75b252b23b02..dab920dcfe9b 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md @@ -25,7 +25,7 @@ The **Remove-AzApiManagement** cmdlet removes an Azure API Management service. ### Example 1: Remove an API Management service ```powershell -Remove-AzApiManagement -ResourceGroupName "ContosoGroup02" -Name "ContosoApi" +Remove-AzApiManagement -ResourceGroupName "ContosoGroup02" -Name "ContosoApi" -Test ``` This command removes the API Management service named ContosoApi. diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md index 5920b3c12014..a175ef2bdaa6 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md @@ -26,7 +26,7 @@ The **Remove-AzApiManagementApi** cmdlet removes an existing API. ### Example 1: Remove an API ```powershell $apimContext = New-AzApiManagementContext -ResourceGroupName "Api-Default-WestUS" -ServiceName "contoso" -Remove-AzApiManagementApi -Context $apimContext -ApiId "0123456789" +Remove-AzApiManagementApiTest -Context $apimContext -ApiId "0123456789" ``` This command removes the API with the specified ID. From 919c760c05367f0130ce8ac7de3a111ad40e8271 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 1 Jun 2022 17:41:51 +0800 Subject: [PATCH 13/43] test --- src/Accounts/Accounts/help/Add-AzEnvironment.md | 2 +- .../ApiManagement/Commands/RestoreAzureApiManagement.cs | 2 +- src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md | 2 +- .../ApiManagement/help/Remove-AzApiManagementApi.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Accounts/Accounts/help/Add-AzEnvironment.md b/src/Accounts/Accounts/help/Add-AzEnvironment.md index 2c4815564dc1..9106e8dc6d2a 100644 --- a/src/Accounts/Accounts/help/Add-AzEnvironment.md +++ b/src/Accounts/Accounts/help/Add-AzEnvironment.md @@ -3,7 +3,7 @@ external help file: Microsoft.Azure.PowerShell.Cmdlets.Accounts.dll-Help.xml Module Name: Az.Accounts online version: https://docs.microsoft.com/powershell/module/az.accounts/add-azenvironment schema: 2.0.0 ---- +--- # Add-AzEnvironment diff --git a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs index a6f4c2f0cfcc..7812fb18d9ff 100644 --- a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs +++ b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs @@ -126,4 +126,4 @@ public override void ExecuteCmdlet() } } } -} +} diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md index dab920dcfe9b..da6466d09045 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md @@ -4,7 +4,7 @@ Module Name: Az.ApiManagement ms.assetid: CD582654-1B0C-4960-9E18-454F857B56E7 online version: https://docs.microsoft.com/powershell/module/az.apimanagement/remove-azapimanagement schema: 2.0.0 ---- +--- # Remove-AzApiManagement diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md index a175ef2bdaa6..c2cd984c9c6a 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md @@ -4,7 +4,7 @@ Module Name: Az.ApiManagement ms.assetid: F23D9274-63B9-4654-897B-6E84757774D2 online version: https://docs.microsoft.com/powershell/module/az.apimanagement/remove-azapimanagementapi schema: 2.0.0 ---- +--- # Remove-AzApiManagementApi From f3578f43e069b04510f78de38bea6142907a25ee Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 1 Jun 2022 17:48:44 +0800 Subject: [PATCH 14/43] restore --- src/Accounts/Accounts/help/Add-AzEnvironment.md | 2 +- .../ApiManagement/Commands/RestoreAzureApiManagement.cs | 2 +- .../ApiManagement/help/Remove-AzApiManagement.md | 4 ++-- .../ApiManagement/help/Remove-AzApiManagementApi.md | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Accounts/Accounts/help/Add-AzEnvironment.md b/src/Accounts/Accounts/help/Add-AzEnvironment.md index 9106e8dc6d2a..28a77ae285fe 100644 --- a/src/Accounts/Accounts/help/Add-AzEnvironment.md +++ b/src/Accounts/Accounts/help/Add-AzEnvironment.md @@ -3,7 +3,7 @@ external help file: Microsoft.Azure.PowerShell.Cmdlets.Accounts.dll-Help.xml Module Name: Az.Accounts online version: https://docs.microsoft.com/powershell/module/az.accounts/add-azenvironment schema: 2.0.0 ---- +--- # Add-AzEnvironment diff --git a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs index 7812fb18d9ff..117431f70096 100644 --- a/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs +++ b/src/ApiManagement/ApiManagement/Commands/RestoreAzureApiManagement.cs @@ -126,4 +126,4 @@ public override void ExecuteCmdlet() } } } -} +} diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md index da6466d09045..75b252b23b02 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagement.md @@ -4,7 +4,7 @@ Module Name: Az.ApiManagement ms.assetid: CD582654-1B0C-4960-9E18-454F857B56E7 online version: https://docs.microsoft.com/powershell/module/az.apimanagement/remove-azapimanagement schema: 2.0.0 ---- +--- # Remove-AzApiManagement @@ -25,7 +25,7 @@ The **Remove-AzApiManagement** cmdlet removes an Azure API Management service. ### Example 1: Remove an API Management service ```powershell -Remove-AzApiManagement -ResourceGroupName "ContosoGroup02" -Name "ContosoApi" -Test +Remove-AzApiManagement -ResourceGroupName "ContosoGroup02" -Name "ContosoApi" ``` This command removes the API Management service named ContosoApi. diff --git a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md index c2cd984c9c6a..5920b3c12014 100644 --- a/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md +++ b/src/ApiManagement/ApiManagement/help/Remove-AzApiManagementApi.md @@ -4,7 +4,7 @@ Module Name: Az.ApiManagement ms.assetid: F23D9274-63B9-4654-897B-6E84757774D2 online version: https://docs.microsoft.com/powershell/module/az.apimanagement/remove-azapimanagementapi schema: 2.0.0 ---- +--- # Remove-AzApiManagementApi @@ -26,7 +26,7 @@ The **Remove-AzApiManagementApi** cmdlet removes an existing API. ### Example 1: Remove an API ```powershell $apimContext = New-AzApiManagementContext -ResourceGroupName "Api-Default-WestUS" -ServiceName "contoso" -Remove-AzApiManagementApiTest -Context $apimContext -ApiId "0123456789" +Remove-AzApiManagementApi -Context $apimContext -ApiId "0123456789" ``` This command removes the API with the specified ID. From 22d693a7ed109815124a7e8a2dcc3c72d7dfdaed Mon Sep 17 00:00:00 2001 From: MoChilia Date: Thu, 2 Jun 2022 11:02:39 +0800 Subject: [PATCH 15/43] fix bug for module name --- .../ExampleAnalyzer/Measure-MarkdownOrScript.ps1 | 5 ++--- tools/StaticAnalysis/IssueChecker/IssueChecker.cs | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 index b2807d4bdb3f..ec92d4becaba 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 @@ -50,10 +50,9 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { $MarkdownPath = Get-Content $MarkdownPaths (Get-ChildItem $MarkdownPath) | foreach{ # Filter the .md of overview in \help\ - if ($_.FullName -cmatch ".*help.*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { + if ((Get-Item -Path $_.FullName).Directory.Name -eq "help" -and $_.FullName -cmatch ".*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { Write-Output "Searching in file $($_.FullName) ..." - $module = ($_.FullName -split "\")[-3]#/|\ - Write-Output $module + $module = (Get-Item -Path $_.FullName).Directory.Parent.Parent.Name $cmdlet = $_.BaseName $result = Measure-SectionMissingAndOutputScript $module $cmdlet $_.FullName ` -OutputScriptsInFile:$OutputScriptsInFile.IsPresent ` diff --git a/tools/StaticAnalysis/IssueChecker/IssueChecker.cs b/tools/StaticAnalysis/IssueChecker/IssueChecker.cs index 46334dd4c675..70cb8f0a0f2f 100644 --- a/tools/StaticAnalysis/IssueChecker/IssueChecker.cs +++ b/tools/StaticAnalysis/IssueChecker/IssueChecker.cs @@ -75,10 +75,8 @@ public void Analyze(IEnumerable scopes, IEnumerable modulesToAna string recordTypeName = item.Item2; string exceptionFilePath = Path.Combine(reportsDirectory, exceptionFileName); - Console.WriteLine(exceptionFilePath); if (!File.Exists(exceptionFilePath)) { - Console.WriteLine("bp1"); continue; } if (IsSingleExceptionFileHasCriticalIssue(exceptionFilePath, recordTypeName)) From 3ab5bbcd08e8c2bec5ef99933c594b99ff71d92f Mon Sep 17 00:00:00 2001 From: MoChilia Date: Thu, 2 Jun 2022 11:42:42 +0800 Subject: [PATCH 16/43] fix bug for module name --- .../StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 index ec92d4becaba..9e033407eb4b 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 @@ -52,7 +52,7 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { # Filter the .md of overview in \help\ if ((Get-Item -Path $_.FullName).Directory.Name -eq "help" -and $_.FullName -cmatch ".*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { Write-Output "Searching in file $($_.FullName) ..." - $module = (Get-Item -Path $_.FullName).Directory.Parent.Parent.Name + $module = (Get-Item -Path $_.FullName).Directory.Parent.Name $cmdlet = $_.BaseName $result = Measure-SectionMissingAndOutputScript $module $cmdlet $_.FullName ` -OutputScriptsInFile:$OutputScriptsInFile.IsPresent ` From e7b9b57b07d1fa0b424b2a96dc1c3be84414c83f Mon Sep 17 00:00:00 2001 From: MoChilia Date: Thu, 9 Jun 2022 13:49:04 +0800 Subject: [PATCH 17/43] build dependent-module for md; complete csv; output in one ps1 --- .ci-config.json | 2 +- .../AnalyzeRules/CommandName.psm1 | 32 ++- .../AnalyzeRules/ParameterNameAndValue.psm1 | 53 +++-- .../ExampleAnalyzer/ExampleIssue.cs | 38 ++-- .../Measure-MarkdownOrScript.ps1 | 33 ++- .../StaticAnalysis/ExampleAnalyzer/utils.ps1 | 191 +++++++++++++++--- 6 files changed, 256 insertions(+), 93 deletions(-) diff --git a/.ci-config.json b/.ci-config.json index c3ea25f1fd8d..49b5fe98c401 100644 --- a/.ci-config.json +++ b/.ci-config.json @@ -87,7 +87,7 @@ "src/{ModuleName}/**/*.md$" ], "phases": [ - "build:module", + "build:dependent-module", "help:module" ] }, diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 index 26c31e3978a5..4fddfb3964fb 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 @@ -5,10 +5,6 @@ File: CommandName.psm1 #> -# Import-Module (Get-ChildItem -Path ./artifacts/Debug).FullName -Scope Global -Import-Module -Name ./artifacts/Debug/Az.Accounts -Scope Global -Import-Module -Name ./artifacts/Debug/Az.ApiManagement -Scope Global - enum RuleNames { Invalid_Cmdlet Is_Alias @@ -28,7 +24,9 @@ function Measure-CommandName { [System.Management.Automation.Language.ScriptBlockAst] $ScriptBlockAst ) - + begin{ + Get-Item ./artifacts/Debug/Az.*/Az.*.psd1 | Import-Module -Global + } process { $Results = @() $global:CommandParameterPair = @() @@ -42,6 +40,12 @@ function Measure-CommandName { #find all command in .ps1 if ($Ast -is [System.Management.Automation.Language.CommandAst]) { [System.Management.Automation.Language.CommandAst]$CommandAst = $Ast + if ($CommandAst.Parent.Parent -is [System.Management.Automation.Language.AssignmentStatementAst]){ + $ModuleCmdletExNum = $CommandAst.Parent.Parent.Parent.Parent.Parent.Name + } + else{ + $ModuleCmdletExNum = $CommandAst.Parent.Parent.Parent.Parent.Name + } if ($CommandAst.InvocationOperator -eq "Unknown") { # $CommandName = $CommandAst.GetCommandName() $CommandName = $CommandAst.CommandElements[0].Extent.Text @@ -51,6 +55,7 @@ function Measure-CommandName { $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = "" + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -60,6 +65,7 @@ function Measure-CommandName { $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = "" + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -68,6 +74,7 @@ function Measure-CommandName { $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = "" + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -84,20 +91,33 @@ function Measure-CommandName { if ($global:CommandParameterPair[$i].ParameterName -eq "") { $Message = "$($CommandParameterPair[$i].CommandName) is not a valid command name." $RuleName = [RuleNames]::Invalid_Cmdlet + $RuleSuppressionID = "3000" + $Remediation = "Check the spell of $($CommandParameterPair[$i].CommandName)." } if ($global:CommandParameterPair[$i].ParameterName -eq "") { $Message = "$($CommandParameterPair[$i].CommandName) is an alias of `"$((Get-Alias $CommandParameterPair[$i].CommandName)[0].ResolvedCommandName)`"." $RuleName = [RuleNames]::Is_Alias + $RuleSuppressionID = "3100" + $Remediation = "Use formal name `"$((Get-Alias $CommandParameterPair[$i].CommandName)[0].ResolvedCommandName)`" of the alias `"$($CommandParameterPair[$i].CommandName)`"." } if ($global:CommandParameterPair[$i].ParameterName -eq "") { $Message = "$($CommandParameterPair[$i].CommandName) doesn't follow the Capitalization Conventions." $RuleName = [RuleNames]::Capitalization_Conventions_Violated + $RuleSuppressionID = "3101" + $name = $($CommandParameterPair[$i].CommandName) + $textInfo = (Get-Culture).TextInfo + $CorrectName = $textInfo.ToTitleCase(($name -split "-")[0]) + $CorrectName += "-Az" + $CorrectName += $textInfo.ToTitleCase(($name -split "Az")[1]) + $Remediation = "Check the Capitalization Conventions. Suggest format: $CorrectName" } + $ModuleCmdletExNum = $($CommandParameterPair[$i].ModuleCmdletExNum) $Result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ - Message = $Message; + Message = "$ModuleCmdletExNum-@$Message@$Remediation"; Extent = $Asts[$i].Extent; RuleName = $RuleName; Severity = "Error" + RuleSuppressionID = $RuleSuppressionID } $Results += $Result } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index f032addc5951..191d8a09b569 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -15,7 +15,6 @@ enum RuleNames { Mismatched_Parameter_Value_Type } - <# .SYNOPSIS Gets the actual value from ast. @@ -56,7 +55,9 @@ function Measure-ParameterNameAndValue { [System.Management.Automation.Language.ScriptBlockAst] $ScriptBlockAst ) - + begin{ + Get-Item ./artifacts/Debug/Az.*/Az.*.psd1 | Import-Module -Global + } process { $Results = @() $global:CommandParameterPair = @() @@ -87,6 +88,7 @@ function Measure-ParameterNameAndValue { if ($Ast -is [System.Management.Automation.Language.CommandElementAst] -and $Ast.Parent -is [System.Management.Automation.Language.CommandAst]) { [System.Management.Automation.Language.CommandElementAst]$CommandElementAst = $Ast + $ModuleCmdletExNum = $CommandElementAst.Parent.Parent.Parent.Parent.Parent.Name if ($global:SkipNextCommandElementAst) { $global:SkipNextCommandElementAst = $false return $false @@ -144,6 +146,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandAst.Extent.Text ParameterName = "" ExpressionToParameter = "" + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -154,8 +157,6 @@ function Measure-ParameterNameAndValue { for ($i = 1; $i -lt $CommandAst.CommandElements.Count;) { $CommandElement = $CommandAst.CommandElements[$i] $NextCommandElement = $CommandAst.CommandElements[$i + 1] - # $PositionParameters = $global:ParameterSet.Parameters | where {$_.Position -ge 0} - # $PositionParameters = $PositionParameters.Name + $PositionParameters.Aliases if ($CommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { $CommandParameterElement = [System.Management.Automation.Language.CommandParameterAst]$CommandElement @@ -264,6 +265,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandName ParameterName = $ParameterName ExpressionToParameter = "" + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -276,6 +278,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandName ParameterName = $ParameterNameNotAlias ExpressionToParameter = "<2>" + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -292,6 +295,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandName ParameterName = $ParameterName ExpressionToParameter = $null + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -310,6 +314,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandName ParameterName = "-$ParameterName" ExpressionToParameter = $NextCommandElement.Extent.Text + " is a null-valued parameter value." + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -335,6 +340,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandName ParameterName = "-$ParameterName" ExpressionToParameter = $NextCommandElement.Extent.Text + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -349,6 +355,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandName ParameterName = "-$ParameterName" ExpressionToParameter = $NextCommandElement.Extent.Text + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -378,6 +385,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandName ParameterName = $ImplicitParameterName ExpressionToParameter = $CommandElementAst.Extent.Text + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -392,6 +400,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandName ParameterName = "[-$ImplicitParameterName]" ExpressionToParameter = $CommandElementAst.Extent.Text + " is a null-valued parameter value." + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -416,6 +425,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandName ParameterName = "[-$ImplicitParameterName]" ExpressionToParameter = $CommandElementAst.Extent.Text + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -429,6 +439,7 @@ function Measure-ParameterNameAndValue { CommandName = $CommandName ParameterName = "[-$ImplicitParameterName]" ExpressionToParameter = $CommandElementAst.Extent.Text + ModuleCmdletExNum = $ModuleCmdletExNum } return $true } @@ -442,45 +453,61 @@ function Measure-ParameterNameAndValue { [System.Management.Automation.Language.Ast[]]$Asts = $ScriptBlockAst.FindAll($Predicate, $false) for ($i = 0; $i -lt $Asts.Count; $i++) { if ($global:CommandParameterPair[$i].ParameterName -eq "" -and $global:CommandParameterPair[$i].ExpressionToParameter -eq "") { - $Message = "$($CommandParameterPair[$i].CommandName) has a parameter not in the same ParameterSet as others." + $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) has a parameter not in the same ParameterSet as others." $RuleName = [RuleNames]::Unknown_Parameter_Set $Severity = "Error" + $RuleSuppressionID = "3010" + $Remediation = "Make sure the parameters are from the same parameter set." } elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq "") { - $Message = "$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) is not a valid parameter name." + $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) is not a valid parameter name." $RuleName = [RuleNames]::Invalid_Parameter_Name $Severity = "Error" + $RuleSuppressionID = "3011" + $Remediation = "Check validity of the parameter $($CommandParameterPair[$i].ParameterName)." } elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq "<2>") { - $Message = "$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) appeared more than once." + $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) appeared more than once." $RuleName = [RuleNames]::Duplicate_Parameter_Name $Severity = "Error" + $RuleSuppressionID = "3012" + $Remediation = "Remove redundant parameter $($CommandParameterPair[$i].ParameterName)." } elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq $null) { - $Message = "$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) must be assigned with a value." + $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) must be assigned with a value." $RuleName = [RuleNames]::Unassigned_Parameter $Severity = "Error" + $RuleSuppressionID = "3013" + $Remediation = "Assign value for the parameter $($CommandParameterPair[$i].ParameterName)." } elseif ($global:CommandParameterPair[$i].ExpressionToParameter.EndsWith(" is a null-valued parameter value.")) { - $Message = "$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter)" + $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter)" $RuleName = [RuleNames]::Unassigned_Variable $Severity = "Warning" + $RuleSuppressionID = "3110" + $variable = $CommandParameterPair[$i].ExpressionToParameter -replace " is a null-valued parameter value." + $Remediation = "Assign value for $variable." } elseif ($global:CommandParameterPair[$i].ParameterName -eq "") { - $Message = "$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ExpressionToParameter) is not explicitly assigned to a parameter." + $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ExpressionToParameter) is not explicitly assigned to a parameter." $RuleName = [RuleNames]::Unbinded_Parameter_Name - $Severity = "Warning" + $Severity = "Error" + $RuleSuppressionID = "3014" + $Remediation = "Assign $($CommandParameterPair[$i].ExpressionToParameter) explicitly to the parameter." } else { - $Message = "$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter) is not an expected parameter value type." + $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter) is not an expected parameter value type." $RuleName = [RuleNames]::Mismatched_Parameter_Value_Type $Severity = "Warning" + $RuleSuppressionID = "3111" + $Remediation = "Use correct parameter value type." } $Result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ - Message = $Message; + Message = "$Message@$Remediation"; Extent = $Asts[$i].Extent; RuleName = $RuleName; Severity = $Severity + RuleSuppressionID = $RuleSuppressionID } $Results += $Result } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs b/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs index 62cb6d86a08e..1e254cde7ee3 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs +++ b/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs @@ -25,19 +25,20 @@ public class ExampleIssue : IReportRecord public string Cmdlet { get; set; } public int Example { get; set; } public string RuleName { get; set; } + public string Extent { get; set; } public int ProblemId { get; set; } public int Severity { get; set; } public string Description { get; set; } public string Remediation { get; set; } public string PrintHeaders() { - return "\"Module\",\"Cmdlet\",\"Example\",\"RuleName\",\"ProblemId\",\"Severity\",\"Description\",\"Remediation\""; + return "\"Module\",\"Cmdlet\",\"Example\",\"RuleName\",\"ProblemId\",\"Severity\",\"Description\",\"Extent\",\"Remediation\""; } public string FormatRecord() { - return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\"", - Module, Cmdlet, Example, RuleName, ProblemId, Severity, Description, Remediation); + return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\"", + Module, Cmdlet, Example, RuleName, ProblemId, Severity, Description, Extent, Remediation); } public bool Match(IReportRecord other) @@ -46,18 +47,16 @@ public bool Match(IReportRecord other) var record = other as ExampleIssue; if (record != null) { - result =(record.Severity == Severity) && - (record.Description == Description); + result = (record.ProblemId == ProblemId); } - return result; } public IReportRecord Parse(string line) { - var matcher = "\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\""; + var matcher = "\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\""; var match = Regex.Match(line, matcher); - if (!match.Success || match.Groups.Count < 8) + if (!match.Success || match.Groups.Count < 10) { throw new InvalidOperationException(string.Format("Could not parse '{0}' as ExampleIssue record", line)); } @@ -65,24 +64,11 @@ public IReportRecord Parse(string line) Cmdlet = match.Groups[2].Value; Example = int.Parse(match.Groups[3].Value); RuleName = match.Groups[4].Value; - var problemMap = new Dictionary - { - {"Invalid_Cmdlet", 3001}, - {"Is_Alias", 3002}, - {"Capitalization_Conventions_Violated", 3003}, - {"Unknown_Parameter_Set",3101}, - {"Invalid_Parameter_Name",3102}, - {"Duplicate_Parameter_Name",3103}, - {"Unassigned_Parameter",3104} - }; - ProblemId = problemMap[RuleName]; - var severityMap = new Dictionary - { - {"Error", 1} - }; - Severity = severityMap[match.Groups[5].Value]; - Description = match.Groups[6].Value; - Remediation = match.Groups[7].Value; + ProblemId = int.Parse(match.Groups[5].Value); + Severity = int.Parse(match.Groups[6].Value); + Description = match.Groups[7].Value; + Extent = match.Groups[8].Value; + Remediation = match.Groups[9].Value; return this; } } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 index 9e033407eb4b..5ba0fdf14374 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 @@ -40,13 +40,17 @@ $analysisResultsTable = @() # Clean caches, remove files in "output" folder if ($OutputScriptsInFile.IsPresent) { - Remove-Item $OutputFolder\$ScriptsByExampleFolder -Recurse -ErrorAction SilentlyContinue + Remove-Item $OutputFolder\TempScript.ps1 -ErrorAction SilentlyContinue + Remove-Item $OutputFolder\*.csv -Recurse -ErrorAction SilentlyContinue + Remove-Item .\artifacts\StaticAnalysisResults\ExampleIssues.csv -ErrorAction SilentlyContinue + Remove-Item $OutputFolder -ErrorAction SilentlyContinue } -Remove-Item $OutputFolder\*.csv -Recurse -ErrorAction SilentlyContinue + # find examples in "help\*.md", output ".ps1" if ($PSCmdlet.ParameterSetName -eq "Markdown") { - $null = New-Item -ItemType Directory -Path $OutputFolder\$ScriptsByExampleFolder -ErrorAction SilentlyContinue + $null = New-Item -ItemType Directory -Path $OutputFolder -ErrorAction SilentlyContinue + $null = New-Item -ItemType File $OutputFolder\TempScript.ps1 $MarkdownPath = Get-Content $MarkdownPaths (Get-ChildItem $MarkdownPath) | foreach{ # Filter the .md of overview in \help\ @@ -56,14 +60,15 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { $cmdlet = $_.BaseName $result = Measure-SectionMissingAndOutputScript $module $cmdlet $_.FullName ` -OutputScriptsInFile:$OutputScriptsInFile.IsPresent ` - -OutputFolder $OutputFolder\$ScriptsByExampleFolder + -OutputFolder $OutputFolder $scaleTable += $result.Scale $missingTable += $result.Missing $deletePromptAndSeparateOutputTable += $result.DeletePromptAndSeparateOutput + $analysisResultsTable += $result.Errors } } if ($AnalyzeScriptsInFile.IsPresent) { - $ScriptPaths = "$OutputFolder\$ScriptsByExampleFolder" + $ScriptPaths = "$OutputFolder\TempScript.ps1" } # Summarize searching results $scaleTable | Export-Csv "$OutputFolder\Scale.csv" -NoTypeInformation @@ -74,20 +79,10 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { # Analyze scripts if ($PSCmdlet.ParameterSetName -eq "Script" -or $AnalyzeScriptsInFile.IsPresent) { - $analysisResultsTable = @() - @() + (Get-Item $ScriptPaths) + (Get-ChildItem $ScriptPaths -Recurse:$Recurse.IsPresent -Attributes Directory) | foreach { - $module = (Get-Item $_).Name - $analysisResults = @() - # read and analyze ".ps1" in \ScriptsByExample - Get-ChildItem $_ -Attributes !Directory -Filter "*.ps1" -ErrorAction Stop | foreach { - Write-Output "Analyzing file $($_.FullName) ..." - $analysisResults += Get-ScriptAnalyzerResult $module $_.FullName $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent -ErrorAction Continue - } - if ($OutputResultsByModule.IsPresent -and (Get-ChildItem $_ -Attributes !Directory -Filter "*.ps1").Count -ne 0) { - $analysisResults | Export-Csv "$OutputFolder\$module.csv" -NoTypeInformation - } - $analysisResultsTable += $analysisResults - } + # read and analyze ".ps1" in \ScriptsByExample + Write-Output "Analyzing file ..." + $analysisResultsTable += Get-ScriptAnalyzerResult (Get-Item -Path $ScriptPaths) $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent -ErrorAction Continue + # Summarize analysis results, output in Result.csv $analysisResultsTable | where {$_ -ne $null} | Export-Csv ".\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index 67f5238e5038..545d03944d0c 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -47,6 +47,18 @@ class DeletePromptAndSeparateOutput { [int]$NeedSplitting } +class AnalysisOutput{ + [string]$Module + [string]$Cmdlet + [int]$Example + [string]$RuleName + [int]$ProblemID + [int]$Severity + [string]$Description + [string]$Extent + [String]$Remediation +} + <# .SYNOPSIS Get examples details from ".md". @@ -95,8 +107,6 @@ function Get-ExamplesDetailsFromMd { # if there is no ```output``` split codelines and outputlines if ($exampleOutputBlocks.Count -eq 0) { foreach ($exampleCodeBlock in $exampleCodeBlocks) { - #$exampleCodeLines = ($exampleCodeBlock.Value | Select-String -Pattern "((\n(([A-Za-z \t\\:>])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*)*[ \t]*[A-Za-z]\w+-[A-Za-z]\w+\b(?!(-| +\w)))|(\n(([A-Za-z \t\\:>])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*)*[ \t]*((@?\(.+\) *[|.-] *\w)|(\[.+\]\$)|(@{.+})|('[^\n\r']*' *[|.-] *\w)|(`"[^\n\r`"]*`" *[|.-] *\w)|\$)))([\w-~``'`"$= \t:;<>@()\[\]{},.+*/|\\&!?%#]*[``|] *(\n|\r\n))*[\w-~``'`"$= \t:;<>@()\[\]{},.+*/|\\&!?%#]*(?=\n|\r\n|#)" -CaseSensitive -AllMatches).Matches - #$exampleCodeLines = ($exampleCodeBlock.Value | Select-String -Pattern "\n(([A-Za-z \t])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*)*[ \t]*((([A-Za-z]\w+-[A-Za-z]\w+\b(?!(-| +\w)))|((@?\(.+\) *[|.-] *\w)|(\[.+\]\$)|(@{.+})|('[^\n\r']*' *[|.-] *\w)|(`"[^\n\r`"]*`" *[|.-] *\w)|\$))([\w-~``'`"$= \t:;<>@()\[\]{},.+*/|\\&!?%#]*[``|][ \t]*(\n|\r\n)?)*([\w-~``'`"$= \t:;<>@()\[\]{},.+*/|\\&!?%#]*(?=\n|\r\n|#)))" -CaseSensitive -AllMatches).Matches $codeRegex = "\n(([A-Za-z \t])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*)*[ \t]*((([A-Za-z]\w+-[A-Za-z]\w+\b(.ps1)?(?!(-| +\w)))|(" + "(@?\((?>\((?)|[^\(\)]+|\)(?<-pair>))*(?(pair)(?!))\) *[|.-] *\w)|" + "(\[(?>\[(?)|[^\[\]]+|\](?<-pair>))*(?(pair)(?!))\]\$)|" + @@ -176,6 +186,7 @@ function Get-ExamplesDetailsFromMd { } $examplesProperties += [PSCustomObject]@{ + Num = $exampleNumber Title = $exampleTitle Codes = $exampleCodes CodeBlocks = $exampleCodeBlocks @@ -201,6 +212,7 @@ function Measure-SectionMissingAndOutputScript { [switch]$OutputScriptsInFile, [string]$OutputFolder ) + $results = @() $fileContent = Get-Content $MarkdownPath -Raw @@ -232,6 +244,20 @@ function Measure-SectionMissingAndOutputScript { else { $missingSynopsis = 1 } + if($missingSynopsis -ne 0){ + $result = [AnalysisOutput]@{ + Module = $Module + Cmdlet = $Cmdlet + Example = "" + Description = "Synopsis is missing." + RuleName = "MissingSynopsis" + Severity = 1 + Extent = "$Module\help\$Cmdlet.md" + ProblemID = 3040 + Remediation = "Add Synopsis. Remove any placeholders." + } + $results += $result + } # If Description section exists if ($indexOfDescription -ne -1) { @@ -246,6 +272,20 @@ function Measure-SectionMissingAndOutputScript { else { $missingDescription = 1 } + if($missingDescription -ne 0){ + $result = [AnalysisOutput]@{ + Module = $Module + Cmdlet = $Cmdlet + Example = "" + Description = "Description is missing." + RuleName = "MissingDescription" + Severity = 1 + Extent = "$Module\help\$Cmdlet.md" + ProblemID = 3041 + Remediation = "Add Description. Remove any placeholders." + } + $results += $result + } $examplesDetails = Get-ExamplesDetailsFromMd $MarkdownPath # If no examples @@ -254,6 +294,18 @@ function Measure-SectionMissingAndOutputScript { $missingExampleCode++ $missingExampleOutput++ $missingExampleDescription++ + $result = [AnalysisOutput]@{ + Module = $Module + Cmdlet = $Cmdlet + Example = "" + Description = "Example is missing." + RuleName = "MissingExample" + Severity = 1 + Extent = "$Module\help\$Cmdlet.md" + ProblemID = 3042 + Remediation = "Add Example. Remove any placeholders." + } + $results += $result } else { foreach ($exampleDetails in $examplesDetails) { @@ -262,22 +314,97 @@ function Measure-SectionMissingAndOutputScript { switch ($exampleDetails) { {$exampleDetails.Title -eq ""} { $missingExampleTitle++ + $result = [AnalysisOutput]@{ + Module = $Module + Cmdlet = $Cmdlet + Example = $exampleDetails.Num + Description = "Title of the example is missing." + RuleName = "MissingExampleTitle" + Severity = 1 + Extent = "$Module\help\$Cmdlet.md" + ProblemID = 3043 + Remediation = "Add title for the example. Remove any placeholders." + } + $results += $result } {$exampleDetails.Codes.Count -eq 0} { $missingExampleCode++ + $result = [AnalysisOutput]@{ + Module = $Module + Cmdlet = $Cmdlet + Example = $exampleDetails.Num + Description = "Code of the example is missing." + RuleName = "MissingExampleCode" + Severity = 1 + Extent = "$Module\help\$Cmdlet.md" + ProblemID = 3044 + Remediation = "Add code for the example. Remove any placeholders." + } + $results += $result } {$exampleDetails.OutputBlocks.Count -ne 0 -and $exampleDetails.Outputs.Count -eq 0} { $missingExampleOutput++ + $result = [AnalysisOutput]@{ + Module = $Module + Cmdlet = $Cmdlet + Example = $exampleDetails.Num + Description = "Output of the example is missing." + RuleName = "MissingExampleOutput" + Severity = 1 + Extent = "$Module\help\$Cmdlet.md" + ProblemID = 3045 + Remediation = "Add output for the example. Remove any placeholders." + } + $results += $result } {$exampleDetails.OutputBlocks.Count -eq 0 -and $exampleDetails.Outputs.Count -ne 0} { $needSplitting++ + $result = [AnalysisOutput]@{ + Module = $Module + Cmdlet = $Cmdlet + Example = $exampleDetails.Num + Description = "The output need to be split from example." + RuleName = "NeedSplitting" + Severity = 1 + Extent = "$Module\help\$Cmdlet.md" + ProblemID = 3051 + Remediation = "Split output from example." + } + $results += $result } {$exampleDetails.Description -eq ""} { $missingExampleDescription++ + $result = [AnalysisOutput]@{ + Module = $Module + Cmdlet = $Cmdlet + Example = $exampleDetails.Num + Description = "Description of the example is missing." + RuleName = "MissingExampleDescription" + Severity = 1 + Extent = "$Module\help\$Cmdlet.md" + ProblemID = 3046 + Remediation = "Add description for the example. Remove any placeholders." + } + $results += $result } } $needDeleting = ($examplesDetails.CodeBlocks | Select-String -Pattern "\n([A-Za-z \t\\:>])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*[ \t]*" -CaseSensitive).Count + ($examplesDetails.CodeBlocks | Select-String -Pattern "(?<=[A-Za-z]\w+-[A-Za-z]\w+)\.ps1" -CaseSensitive).Count + + if($needDeleting -ne 0){ + $result = [AnalysisOutput]@{ + Module = $Module + Cmdlet = $Cmdlet + Example = $exampleDetails.Num + Description = "The prompt of example need to be deleted." + RuleName = "NeedDeleting" + Severity = 1 + Extent = "$Module\help\$Cmdlet.md" + ProblemID = 3051 + Remediation = "Delete the prompt of example." + } + $results += $result + } # Delete prompts $exampleCodes = $exampleDetails.Codes @@ -286,12 +413,16 @@ function Measure-SectionMissingAndOutputScript { $newCode = $newCode -replace "(?<=[A-Za-z]\w+-[A-Za-z]\w+)\.ps1", "" $exampleCodes[$i] = $newCode } - - $cmdletExamplesScriptPath = "$OutputFolder\$module" + # Output codes by example if ($OutputScriptsInFile.IsPresent) { - $null = New-Item -ItemType Directory -Path $cmdletExamplesScriptPath -ErrorAction SilentlyContinue - [IO.File]::WriteAllText((New-Item -Type File .\$cmdletExamplesScriptPath\$cmdlet-$exampleNumber.ps1).FullName, $exampleCodes -join "`n", (New-Object Text.UTF8Encoding($false))) + $cmdletExamplesScriptPath = "$OutputFolder\TempScript.ps1" + $functionHead = "function $Module-$Cmdlet-$exampleNumber{" + Add-Content -Path (Get-Item $cmdletExamplesScriptPath).FullName -Value $functionHead + $exampleCodes = $exampleCodes -join "`n" + Add-Content -Path (Get-Item $cmdletExamplesScriptPath).FullName -Value $exampleCodes + $functionTail = "}`n" + Add-Content -Path (Get-Item $cmdletExamplesScriptPath).FullName -Value $functionTail } } } @@ -337,6 +468,7 @@ function Measure-SectionMissingAndOutputScript { Scale = $scale Missing = $missing DeletePromptAndSeparateOutput = $deletePromptAndSeparateOutput + Errors = $results } } @@ -346,7 +478,6 @@ function Measure-SectionMissingAndOutputScript { #> function Get-ScriptAnalyzerResult { param ( - [string]$Module, [string]$ScriptPath, [Parameter(Mandatory, HelpMessage = "PSScriptAnalyzer custom rules path. Supports wildcard.")] [string[]]$RulePath, @@ -357,31 +488,35 @@ function Get-ScriptAnalyzerResult { if (!(Test-Path $ScriptPath -PathType Leaf)) { throw "Cannot find cached script file '$ScriptPath'." } - - # get script file name - $scriptName = [IO.Path]::GetFileName($ScriptPath) - $scriptBaseName = [IO.Path]::GetFileNameWithoutExtension($ScriptPath) - if ($scriptBaseName.Split("-").Count -eq 3 -and $scriptBaseName.Split("-")[2] -as [int]) { - $cmdlet = $scriptBaseName.Split("-")[0..1] -join "-" - $example = $scriptBaseName.Split("-")[2] - } - else { - $cmdlet = $scriptName - $example = "" - } # Invoke PSScriptAnalyzer : input scriptblock, output error set in $result with property: RuleName, Message, Extent if ($RulePath -eq $null) { - $results = Invoke-ScriptAnalyzer -Path $ScriptPath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent + $analysisResults = Invoke-ScriptAnalyzer -Path $ScriptPath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent } else { - $results = Invoke-ScriptAnalyzer -Path $ScriptPath -CustomRulePath $RulePath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent + $analysisResults = Invoke-ScriptAnalyzer -Path $ScriptPath -CustomRulePath $RulePath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent + } + $results = @() + foreach($analysisResult in $analysisResults){ + if($analysisResult.Severity -eq "Error"){ + $Severity = 1 + } + elseif($analysisResult.Severity -eq "Warning"){ + $Severity = 2 + } + $result = [AnalysisOutput]@{ + Module = ($analysisResult.Message -split "-")[0] + Cmdlet = ($analysisResult.Message -split "-")[1] + "-" + ($analysisResult.Message -split "-")[2] + Example = ($analysisResult.Message -split "-")[3] + RuleName = $analysisResult.RuleName + Description = ($analysisResult.Message -split "@")[1] -replace "`"","`'" + Severity = $Severity + Extent = $analysisResult.Extent -replace "`"","`'" + ProblemID = $analysisResult.RuleSuppressionID + Remediation = ($analysisResult.Message -split "@")[2] -replace "`"","`'" + } + $results += $result } - return $results | Select-Object -Property @{Name = "Module"; Expression = {$Module}}, - @{Name = "Cmdlet";Expression={$Cmdlet}}, - @{Name ="Example";Expression={$Example}}, - RuleName, Severity, @{Name = "Description";Expression={$results.Message}}, Extent -} - - + return $results +} \ No newline at end of file From 0257709c7b0de68766ccc5b63c0bf72216b2579c Mon Sep 17 00:00:00 2001 From: MoChilia Date: Thu, 9 Jun 2022 21:03:42 +0800 Subject: [PATCH 18/43] illustration for example issue; add suppress; change problemId --- build.proj | 3 +-- .../Debugging-StaticAnalysis-Errors.md | 10 ++++++++- .../AnalyzeRules/CommandName.psm1 | 6 ++--- .../AnalyzeRules/ParameterNameAndValue.psm1 | 14 ++++++------ .../Measure-MarkdownOrScript.ps1 | 2 +- .../StaticAnalysis/ExampleAnalyzer/utils.ps1 | 18 +++++++-------- .../Exceptions/Az.Accounts/ExampleIssues.csv | 2 ++ tools/StaticAnalysis/ProblemIDs.cs | 22 +++++++++++++++++++ tools/VersionController/Program.cs | 3 ++- 9 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv diff --git a/build.proj b/build.proj index f9bdcc88b3f1..dda73ff5bd6d 100644 --- a/build.proj +++ b/build.proj @@ -263,8 +263,7 @@ - - + diff --git a/documentation/Debugging-StaticAnalysis-Errors.md b/documentation/Debugging-StaticAnalysis-Errors.md index c105b023bd0b..57d113c23b48 100644 --- a/documentation/Debugging-StaticAnalysis-Errors.md +++ b/documentation/Debugging-StaticAnalysis-Errors.md @@ -54,4 +54,12 @@ Signature issues occur when your cmdlets do not follow PowerShell standards. Pl Most help issues that cause StaticAnalysis to fail occur when help has not been added for a particular cmdlet. If you have not generated help for your new cmdlets, please follow the instructions [here](https://github.com/Azure/azure-powershell/blob/main/documentation/development-docs/help-generation.md). If this is not the issue, follow the steps listed under "Remediation" for each violation listed in HelpIssues.csv. ### Example Issues -Todo \ No newline at end of file +Example Issues occur when your changed markdown files a `help` folder (_e.g.,_ `src/Accounts/Accounts/help`) do not follow the script rules. Please follow the suggestion displayed in "Remediation" entry for each violation listed in `ExampleIssues.csv`. If you have an issue with severity 0 or 1 that has been approved by the Azure PowerShell team, you can suppress them following these steps: + +- Download the `ExampleIssues.csv` file from the Jenkins build +- Open the file using a text editor (such as VS Code) and copy each of the errors you'd like to suppress +- Paste each of these errors into the `ExampleIssues.csv` file found in their respective [module folder](../tools/StaticAnalysis/Exceptions) (_e.g.,_ if an example issue is being suppressed for Accounts, then you would paste the corresponding line(s) in the `tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssue.csv` file) using the same text editor +- Copy each of the errors you would like to suppress directly from the ExampleIssues.csv file output in the Jenkins build +- Push the changes to the .csv file and ensure the errors no longer show up in the `ExampleIssues.csv` file output from the Jenkins build + +To better standardize the writing of documents, please also check the warning issues with severity 2 by downloading the `ExampleIssues.csv` file. \ No newline at end of file diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 index 4fddfb3964fb..f13a7cd7bbe3 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 @@ -91,19 +91,19 @@ function Measure-CommandName { if ($global:CommandParameterPair[$i].ParameterName -eq "") { $Message = "$($CommandParameterPair[$i].CommandName) is not a valid command name." $RuleName = [RuleNames]::Invalid_Cmdlet - $RuleSuppressionID = "3000" + $RuleSuppressionID = "5000" $Remediation = "Check the spell of $($CommandParameterPair[$i].CommandName)." } if ($global:CommandParameterPair[$i].ParameterName -eq "") { $Message = "$($CommandParameterPair[$i].CommandName) is an alias of `"$((Get-Alias $CommandParameterPair[$i].CommandName)[0].ResolvedCommandName)`"." $RuleName = [RuleNames]::Is_Alias - $RuleSuppressionID = "3100" + $RuleSuppressionID = "5100" $Remediation = "Use formal name `"$((Get-Alias $CommandParameterPair[$i].CommandName)[0].ResolvedCommandName)`" of the alias `"$($CommandParameterPair[$i].CommandName)`"." } if ($global:CommandParameterPair[$i].ParameterName -eq "") { $Message = "$($CommandParameterPair[$i].CommandName) doesn't follow the Capitalization Conventions." $RuleName = [RuleNames]::Capitalization_Conventions_Violated - $RuleSuppressionID = "3101" + $RuleSuppressionID = "5101" $name = $($CommandParameterPair[$i].CommandName) $textInfo = (Get-Culture).TextInfo $CorrectName = $textInfo.ToTitleCase(($name -split "-")[0]) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index 191d8a09b569..c6ce1c6232cc 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -456,35 +456,35 @@ function Measure-ParameterNameAndValue { $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) has a parameter not in the same ParameterSet as others." $RuleName = [RuleNames]::Unknown_Parameter_Set $Severity = "Error" - $RuleSuppressionID = "3010" + $RuleSuppressionID = "5010" $Remediation = "Make sure the parameters are from the same parameter set." } elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq "") { $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) is not a valid parameter name." $RuleName = [RuleNames]::Invalid_Parameter_Name $Severity = "Error" - $RuleSuppressionID = "3011" + $RuleSuppressionID = "5011" $Remediation = "Check validity of the parameter $($CommandParameterPair[$i].ParameterName)." } elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq "<2>") { $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) appeared more than once." $RuleName = [RuleNames]::Duplicate_Parameter_Name $Severity = "Error" - $RuleSuppressionID = "3012" + $RuleSuppressionID = "5012" $Remediation = "Remove redundant parameter $($CommandParameterPair[$i].ParameterName)." } elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq $null) { $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) must be assigned with a value." $RuleName = [RuleNames]::Unassigned_Parameter $Severity = "Error" - $RuleSuppressionID = "3013" + $RuleSuppressionID = "5013" $Remediation = "Assign value for the parameter $($CommandParameterPair[$i].ParameterName)." } elseif ($global:CommandParameterPair[$i].ExpressionToParameter.EndsWith(" is a null-valued parameter value.")) { $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter)" $RuleName = [RuleNames]::Unassigned_Variable $Severity = "Warning" - $RuleSuppressionID = "3110" + $RuleSuppressionID = "5110" $variable = $CommandParameterPair[$i].ExpressionToParameter -replace " is a null-valued parameter value." $Remediation = "Assign value for $variable." } @@ -492,14 +492,14 @@ function Measure-ParameterNameAndValue { $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ExpressionToParameter) is not explicitly assigned to a parameter." $RuleName = [RuleNames]::Unbinded_Parameter_Name $Severity = "Error" - $RuleSuppressionID = "3014" + $RuleSuppressionID = "5014" $Remediation = "Assign $($CommandParameterPair[$i].ExpressionToParameter) explicitly to the parameter." } else { $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) $($CommandParameterPair[$i].ParameterName) $($CommandParameterPair[$i].ExpressionToParameter) is not an expected parameter value type." $RuleName = [RuleNames]::Mismatched_Parameter_Value_Type $Severity = "Warning" - $RuleSuppressionID = "3111" + $RuleSuppressionID = "5111" $Remediation = "Use correct parameter value type." } $Result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 index 5ba0fdf14374..8db4d2fe54ec 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 @@ -82,7 +82,7 @@ if ($PSCmdlet.ParameterSetName -eq "Script" -or $AnalyzeScriptsInFile.IsPresent) # read and analyze ".ps1" in \ScriptsByExample Write-Output "Analyzing file ..." $analysisResultsTable += Get-ScriptAnalyzerResult (Get-Item -Path $ScriptPaths) $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent -ErrorAction Continue - + # Summarize analysis results, output in Result.csv $analysisResultsTable | where {$_ -ne $null} | Export-Csv ".\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index 545d03944d0c..015a0e9375bb 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -253,7 +253,7 @@ function Measure-SectionMissingAndOutputScript { RuleName = "MissingSynopsis" Severity = 1 Extent = "$Module\help\$Cmdlet.md" - ProblemID = 3040 + ProblemID = 5040 Remediation = "Add Synopsis. Remove any placeholders." } $results += $result @@ -281,7 +281,7 @@ function Measure-SectionMissingAndOutputScript { RuleName = "MissingDescription" Severity = 1 Extent = "$Module\help\$Cmdlet.md" - ProblemID = 3041 + ProblemID = 5041 Remediation = "Add Description. Remove any placeholders." } $results += $result @@ -302,7 +302,7 @@ function Measure-SectionMissingAndOutputScript { RuleName = "MissingExample" Severity = 1 Extent = "$Module\help\$Cmdlet.md" - ProblemID = 3042 + ProblemID = 5042 Remediation = "Add Example. Remove any placeholders." } $results += $result @@ -322,7 +322,7 @@ function Measure-SectionMissingAndOutputScript { RuleName = "MissingExampleTitle" Severity = 1 Extent = "$Module\help\$Cmdlet.md" - ProblemID = 3043 + ProblemID = 5043 Remediation = "Add title for the example. Remove any placeholders." } $results += $result @@ -337,7 +337,7 @@ function Measure-SectionMissingAndOutputScript { RuleName = "MissingExampleCode" Severity = 1 Extent = "$Module\help\$Cmdlet.md" - ProblemID = 3044 + ProblemID = 5044 Remediation = "Add code for the example. Remove any placeholders." } $results += $result @@ -352,7 +352,7 @@ function Measure-SectionMissingAndOutputScript { RuleName = "MissingExampleOutput" Severity = 1 Extent = "$Module\help\$Cmdlet.md" - ProblemID = 3045 + ProblemID = 5045 Remediation = "Add output for the example. Remove any placeholders." } $results += $result @@ -367,7 +367,7 @@ function Measure-SectionMissingAndOutputScript { RuleName = "NeedSplitting" Severity = 1 Extent = "$Module\help\$Cmdlet.md" - ProblemID = 3051 + ProblemID = 5051 Remediation = "Split output from example." } $results += $result @@ -382,7 +382,7 @@ function Measure-SectionMissingAndOutputScript { RuleName = "MissingExampleDescription" Severity = 1 Extent = "$Module\help\$Cmdlet.md" - ProblemID = 3046 + ProblemID = 5046 Remediation = "Add description for the example. Remove any placeholders." } $results += $result @@ -400,7 +400,7 @@ function Measure-SectionMissingAndOutputScript { RuleName = "NeedDeleting" Severity = 1 Extent = "$Module\help\$Cmdlet.md" - ProblemID = 3051 + ProblemID = 5051 Remediation = "Delete the prompt of example." } $results += $result diff --git a/tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv b/tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv new file mode 100644 index 000000000000..0bf2d0f69f4e --- /dev/null +++ b/tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv @@ -0,0 +1,2 @@ +"Module","Cmdlet","Example","RuleName","ProblemId","Severity","Description","Extent","Remediation" +"Accounts","Disable-AzContextAutosave","2","MissingExampleCode","5044","1","Code of the example is missing.","Accounts\help\Disable-AzContextAutosave.md","Add code for the example. Remove any placeholders." \ No newline at end of file diff --git a/tools/StaticAnalysis/ProblemIDs.cs b/tools/StaticAnalysis/ProblemIDs.cs index 007741e62f51..d4d0882584d5 100644 --- a/tools/StaticAnalysis/ProblemIDs.cs +++ b/tools/StaticAnalysis/ProblemIDs.cs @@ -74,4 +74,26 @@ public static class BreakingChangeProblemId public const int ChangedGenericTypeArgument = 3040; public const int DifferentGenericTypeArgumentSize = 3050; } + public static class ExampleProblemId + { + public const int Invalid_Cmdlet = 5000; + public const int Unknown_Parameter_Set = 5010; + public const int Invalid_Parameter_Name = 5011; + public const int Duplicate_Parameter_Name = 5012; + public const int Unassigned_Parameter = 5013; + public const int Unbinded_Parameter_Name = 5014; + public const int MissingSynopsis = 5040; + public const int MissingDescription = 5041; + public const int MissingExample = 5042; + public const int MissingExampleTitle = 5043; + public const int MissingExampleCode = 5044; + public const int MissingExampleOutput = 5045; + public const int MissingExampleDescription = 5046; + public const int NeedDeleting = 5050; + public const int NeedSplitting = 5051; + public const int Is_Alias = 5100; + public const int Capitalization_Conventions_Violated = 5101; + public const int Unassigned_Variable = 5110; + public const int Mismatched_Parameter_Value_Type = 5111; + } } diff --git a/tools/VersionController/Program.cs b/tools/VersionController/Program.cs index 7bf0b90f682b..c44f76c9f58a 100644 --- a/tools/VersionController/Program.cs +++ b/tools/VersionController/Program.cs @@ -41,7 +41,8 @@ public class Program "ExtraAssemblies.csv", "HelpIssues.csv", "MissingAssemblies.csv", - "SignatureIssues.csv" + "SignatureIssues.csv", + "ExampleIssues.csv" }; public static void Main(string[] args) From f11dbd7996a5df4ca5f6550578b9057bfad7cc9c Mon Sep 17 00:00:00 2001 From: Shiying Chen <72982571+MoChilia@users.noreply.github.com> Date: Fri, 10 Jun 2022 09:05:14 +0800 Subject: [PATCH 19/43] Update Program.cs --- tools/StaticAnalysis/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/StaticAnalysis/Program.cs b/tools/StaticAnalysis/Program.cs index 65a04aad9203..39cf05a5f268 100644 --- a/tools/StaticAnalysis/Program.cs +++ b/tools/StaticAnalysis/Program.cs @@ -38,7 +38,8 @@ public class Program "ExtraAssemblies.csv", "HelpIssues.csv", "MissingAssemblies.csv", - "SignatureIssues.csv" + "SignatureIssues.csv", + "ExampleIssues.csv" }; private static string ExceptionsDirectory { get; set; } From 21d5d3f5c9f80b8f024f780f05b98a451f144f79 Mon Sep 17 00:00:00 2001 From: Shiying Chen <72982571+MoChilia@users.noreply.github.com> Date: Fri, 10 Jun 2022 11:02:06 +0800 Subject: [PATCH 20/43] Update Measure-MarkdownOrScript.ps1 --- .../ExampleAnalyzer/Measure-MarkdownOrScript.ps1 | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 index 8db4d2fe54ec..7a05538247f5 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 @@ -71,9 +71,15 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { $ScriptPaths = "$OutputFolder\TempScript.ps1" } # Summarize searching results - $scaleTable | Export-Csv "$OutputFolder\Scale.csv" -NoTypeInformation - $missingTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\Missing.csv" -NoTypeInformation - $deletePromptAndSeparateOutputTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\DeletingSeparating.csv" -NoTypeInformation + if($scaleTable -ne $null){ + $scaleTable | Export-Csv "$OutputFolder\Scale.csv" -NoTypeInformation + } + if($missingTable -ne $null){ + $missingTable | Export-Csv "$OutputFolder\Missing.csv" -NoTypeInformation + } + if($deletePromptAndSeparateOutputTable -ne $null){ + $deletePromptAndSeparateOutputTable | Export-Csv "$OutputFolder\DeletingSeparating.csv" -NoTypeInformation + } } @@ -84,7 +90,9 @@ if ($PSCmdlet.ParameterSetName -eq "Script" -or $AnalyzeScriptsInFile.IsPresent) $analysisResultsTable += Get-ScriptAnalyzerResult (Get-Item -Path $ScriptPaths) $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent -ErrorAction Continue # Summarize analysis results, output in Result.csv - $analysisResultsTable | where {$_ -ne $null} | Export-Csv ".\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation + if($analysisResultsTable -ne $null){ + $analysisResultsTable | Export-Csv ".\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation + } } # Clean caches From b7a0b7b5fcb839b6e709c42eb57b10f340436ef4 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Mon, 13 Jun 2022 17:12:11 +0800 Subject: [PATCH 21/43] add suppress function --- .../Debugging-StaticAnalysis-Errors.md | 1 + .../ExampleAnalyzer/ExampleAnalyzer.cs | 50 +++++++++++++++++ .../ExampleAnalyzer/ExampleIssue.cs | 6 ++- .../Measure-MarkdownOrScript.ps1 | 20 +++---- .../StaticAnalysis/ExampleAnalyzer/utils.ps1 | 53 ++++++++++++++----- .../Exceptions/Az.Accounts/ExampleIssues.csv | 3 +- 6 files changed, 108 insertions(+), 25 deletions(-) create mode 100644 tools/StaticAnalysis/ExampleAnalyzer/ExampleAnalyzer.cs diff --git a/documentation/Debugging-StaticAnalysis-Errors.md b/documentation/Debugging-StaticAnalysis-Errors.md index 57d113c23b48..283d884972e6 100644 --- a/documentation/Debugging-StaticAnalysis-Errors.md +++ b/documentation/Debugging-StaticAnalysis-Errors.md @@ -8,6 +8,7 @@ Our StaticAnalysis tools help us ensure our modules follow PowerShell guidelines - [Breaking Changes](#breaking-changes) - [Signature Issues](#signature-issues) - [Help Issues](#help-issues) + - [Example Issues](#example-issues) ## How to know if you have a StaticAnalysis Error If your build is failing, click on the Jenkins job inside the PR (marked as "Default" within checks). Then check the Console Output within the Jenkins job. If you have this error, then you have failed StaticAnalysis: diff --git a/tools/StaticAnalysis/ExampleAnalyzer/ExampleAnalyzer.cs b/tools/StaticAnalysis/ExampleAnalyzer/ExampleAnalyzer.cs new file mode 100644 index 000000000000..bb7565deb841 --- /dev/null +++ b/tools/StaticAnalysis/ExampleAnalyzer/ExampleAnalyzer.cs @@ -0,0 +1,50 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +//not completed +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Management.Automation; +using System.Reflection; +using System.Text.RegularExpressions; +using Tools.Common.Issues; +using Tools.Common.Loaders; +using Tools.Common.Loggers; +using Tools.Common.Models; + +namespace StaticAnalysis.ExampleAnalyzer +{ + /// + /// Static analyzer for PowerShell Help + /// + public class ExampleAnalyzer : IStaticAnalyzer + { + public ExampleAnalyzer() + { + Name = "Example Analyzer"; + } + public AnalysisLogger Logger { get; set; } + public string Name { get; private set; } + + public void Analyze(IEnumerable scopes) + { + PowerShell ps = PowerShell.Create(); + ps.AddScript("Measure-MarkdownOrScript.ps1 -MarkdownPaths $(RepoArtifacts)/FilesChanged.txt -RulePaths $(RepoTools)/StaticAnalysis/ExampleAnalyzer/AnalyzeRules//*.psm1 -Recurse -AnalyzeScriptsInFile -OutputScriptsInFile -OutputResultsByModule").Invoke(); + var ExampleLogger = Logger.CreateLogger("ExampleIssues.csv"); + + } + } +} \ No newline at end of file diff --git a/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs b/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs index 1e254cde7ee3..60375f54a5f9 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs +++ b/tools/StaticAnalysis/ExampleAnalyzer/ExampleIssue.cs @@ -47,7 +47,11 @@ public bool Match(IReportRecord other) var record = other as ExampleIssue; if (record != null) { - result = (record.ProblemId == ProblemId); + result = (record.Module == Module)&& + (record.Cmdlet == Cmdlet)&& + (record.Example == Example)&& + (record.ProblemId == ProblemId)&& + (record.Description == Description); } return result; } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 index 7a05538247f5..18dfaa33319a 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 @@ -19,7 +19,7 @@ param ( [string[]]$RulePaths, [switch]$Recurse, [switch]$IncludeDefaultRules, - [string]$OutputFolder = "./artifacts/StaticAnalysisResults/ExampleAnalysis", + [string]$OutputFolder = ".\artifacts\StaticAnalysisResults\ExampleAnalysis", [Parameter(ParameterSetName = "Markdown")] [switch]$AnalyzeScriptsInFile, [Parameter(ParameterSetName = "Markdown")] @@ -71,14 +71,14 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { $ScriptPaths = "$OutputFolder\TempScript.ps1" } # Summarize searching results - if($scaleTable -ne $null){ - $scaleTable | Export-Csv "$OutputFolder\Scale.csv" -NoTypeInformation + if($scaleTable){ + $scaleTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\Scale.csv" -NoTypeInformation } - if($missingTable -ne $null){ - $missingTable | Export-Csv "$OutputFolder\Missing.csv" -NoTypeInformation + if($missingTable){ + $missingTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\Missing.csv" -NoTypeInformation } - if($deletePromptAndSeparateOutputTable -ne $null){ - $deletePromptAndSeparateOutputTable | Export-Csv "$OutputFolder\DeletingSeparating.csv" -NoTypeInformation + if($deletePromptAndSeparateOutputTable){ + $deletePromptAndSeparateOutputTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\DeletingSeparating.csv" -NoTypeInformation } } @@ -87,11 +87,11 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { if ($PSCmdlet.ParameterSetName -eq "Script" -or $AnalyzeScriptsInFile.IsPresent) { # read and analyze ".ps1" in \ScriptsByExample Write-Output "Analyzing file ..." - $analysisResultsTable += Get-ScriptAnalyzerResult (Get-Item -Path $ScriptPaths) $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent -ErrorAction Continue + $analysisResultsTable += Get-ScriptAnalyzerResult (Get-Item -Path $ScriptPaths) $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent -ErrorAction SilentlyContinue # Summarize analysis results, output in Result.csv - if($analysisResultsTable -ne $null){ - $analysisResultsTable | Export-Csv ".\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation + if($analysisResultsTable){ + $analysisResultsTable| where {$_ -ne $null} | Export-Csv ".\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation } } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index 015a0e9375bb..6c8c38562a23 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -199,6 +199,31 @@ function Get-ExamplesDetailsFromMd { return $examplesProperties } +function ExceptionRecord{ + param( + [AnalysisOutput[]]$records + ) + $exceptionPaths = ".\tools\StaticAnalysis\Exceptions" + $results = @() + foreach($record in $records){ + $needAdd = $true + $exceptionPath = Join-Path -Path $exceptionPaths -ChildPath "Az.$($record.Module)" -AdditionalChildPath "ExampleIssues.csv" + if(Test-Path -Path $exceptionPath){ + $exceptionContents= Import-Csv -Path $exceptionPath + foreach($exceptionContent in $exceptionContents) { + if($exceptionContent.Module -eq $record.Module -and $exceptionContent.Cmdlet -eq $record.Cmdlet -and $exceptionContent.Example -eq $record.Example -and $exceptionContent.Description -eq $record.Description){ + $needAdd = $false + break + } + } + } + if($needAdd){ + $results += $record + } + } + return $results +} + <# .SYNOPSIS Tests whether the script is integral, outputs examples in ".md" to ".ps1" @@ -310,7 +335,6 @@ function Measure-SectionMissingAndOutputScript { else { foreach ($exampleDetails in $examplesDetails) { $exampleNumber++ - switch ($exampleDetails) { {$exampleDetails.Title -eq ""} { $missingExampleTitle++ @@ -463,6 +487,7 @@ function Measure-SectionMissingAndOutputScript { NeedSplitting = $needSplitting } } + $results = ExceptionRecord $results return @{ Scale = $scale @@ -504,19 +529,21 @@ function Get-ScriptAnalyzerResult { elseif($analysisResult.Severity -eq "Warning"){ $Severity = 2 } - $result = [AnalysisOutput]@{ - Module = ($analysisResult.Message -split "-")[0] - Cmdlet = ($analysisResult.Message -split "-")[1] + "-" + ($analysisResult.Message -split "-")[2] - Example = ($analysisResult.Message -split "-")[3] - RuleName = $analysisResult.RuleName - Description = ($analysisResult.Message -split "@")[1] -replace "`"","`'" - Severity = $Severity - Extent = $analysisResult.Extent -replace "`"","`'" - ProblemID = $analysisResult.RuleSuppressionID - Remediation = ($analysisResult.Message -split "@")[2] -replace "`"","`'" + if($analysisResult.RuleSuppressionID -ge 5000 -and $analysisResult.RuleSuppressionID -le 5199){ + $result = [AnalysisOutput]@{ + Module = ($analysisResult.Message -split "-")[0] + Cmdlet = ($analysisResult.Message -split "-")[1] + "-" + ($analysisResult.Message -split "-")[2] + Example = ($analysisResult.Message -split "-")[3] + RuleName = $analysisResult.RuleName + Description = ($analysisResult.Message -split "@")[1] -replace "`"","`'" + Severity = $Severity + Extent = $analysisResult.Extent -replace "`"","`'" + ProblemID = $analysisResult.RuleSuppressionID + Remediation = ($analysisResult.Message -split "@")[2] -replace "`"","`'" + } + $results += $result } - $results += $result } - + $results = ExceptionRecord $results return $results } \ No newline at end of file diff --git a/tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv b/tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv index 0bf2d0f69f4e..72739136662e 100644 --- a/tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv +++ b/tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv @@ -1,2 +1,3 @@ "Module","Cmdlet","Example","RuleName","ProblemId","Severity","Description","Extent","Remediation" -"Accounts","Disable-AzContextAutosave","2","MissingExampleCode","5044","1","Code of the example is missing.","Accounts\help\Disable-AzContextAutosave.md","Add code for the example. Remove any placeholders." \ No newline at end of file +"Accounts","Disable-AzContextAutosave","2","MissingExampleCode","5044","1","Code of the example is missing.","Accounts\help\Disable-AzContextAutosave.md","Add code for the example. Remove any placeholders." +"Accounts","Connect-AzAccount","3","Mismatched_Parameter_Value_Type","5111","2","Connect-AzAccount -Credential $Credential is not an expected parameter value type.","-Credential","Use correct parameter value type." \ No newline at end of file From a1e11c99e29e126fad2f5223889a0a391a769562 Mon Sep 17 00:00:00 2001 From: Shiying Chen <72982571+MoChilia@users.noreply.github.com> Date: Mon, 13 Jun 2022 20:48:43 +0800 Subject: [PATCH 22/43] Delete ExampleAnalyzer.cs --- .../ExampleAnalyzer/ExampleAnalyzer.cs | 50 ------------------- 1 file changed, 50 deletions(-) delete mode 100644 tools/StaticAnalysis/ExampleAnalyzer/ExampleAnalyzer.cs diff --git a/tools/StaticAnalysis/ExampleAnalyzer/ExampleAnalyzer.cs b/tools/StaticAnalysis/ExampleAnalyzer/ExampleAnalyzer.cs deleted file mode 100644 index bb7565deb841..000000000000 --- a/tools/StaticAnalysis/ExampleAnalyzer/ExampleAnalyzer.cs +++ /dev/null @@ -1,50 +0,0 @@ -// ---------------------------------------------------------------------------------- -// -// Copyright Microsoft Corporation -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ---------------------------------------------------------------------------------- - -//not completed -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Management.Automation; -using System.Reflection; -using System.Text.RegularExpressions; -using Tools.Common.Issues; -using Tools.Common.Loaders; -using Tools.Common.Loggers; -using Tools.Common.Models; - -namespace StaticAnalysis.ExampleAnalyzer -{ - /// - /// Static analyzer for PowerShell Help - /// - public class ExampleAnalyzer : IStaticAnalyzer - { - public ExampleAnalyzer() - { - Name = "Example Analyzer"; - } - public AnalysisLogger Logger { get; set; } - public string Name { get; private set; } - - public void Analyze(IEnumerable scopes) - { - PowerShell ps = PowerShell.Create(); - ps.AddScript("Measure-MarkdownOrScript.ps1 -MarkdownPaths $(RepoArtifacts)/FilesChanged.txt -RulePaths $(RepoTools)/StaticAnalysis/ExampleAnalyzer/AnalyzeRules//*.psm1 -Recurse -AnalyzeScriptsInFile -OutputScriptsInFile -OutputResultsByModule").Invoke(); - var ExampleLogger = Logger.CreateLogger("ExampleIssues.csv"); - - } - } -} \ No newline at end of file From 26933bd60d1712ca1c3c7b097dabfceb448a8d78 Mon Sep 17 00:00:00 2001 From: Shiying Chen <72982571+MoChilia@users.noreply.github.com> Date: Mon, 13 Jun 2022 21:49:12 +0800 Subject: [PATCH 23/43] Update ParameterNameAndValue.psm1 fix a bug for not found module name --- .../AnalyzeRules/ParameterNameAndValue.psm1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index c6ce1c6232cc..c6cfb092a887 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -88,7 +88,12 @@ function Measure-ParameterNameAndValue { if ($Ast -is [System.Management.Automation.Language.CommandElementAst] -and $Ast.Parent -is [System.Management.Automation.Language.CommandAst]) { [System.Management.Automation.Language.CommandElementAst]$CommandElementAst = $Ast - $ModuleCmdletExNum = $CommandElementAst.Parent.Parent.Parent.Parent.Parent.Name + if ($CommandElementAst.Parent.Parent.Parent -is [System.Management.Automation.Language.AssignmentStatementAst]){ + $ModuleCmdletExNum = $CommandElementAst.Parent.Parent.Parent.Parent.Parent.Parent.Name + } + else{ + $ModuleCmdletExNum = $CommandElementAst.Parent.Parent.Parent.Parent.Parent.Name + } if ($global:SkipNextCommandElementAst) { $global:SkipNextCommandElementAst = $false return $false From 1387551fdd7b1a9c22228b1010a8275db2c8d466 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Tue, 14 Jun 2022 15:39:01 +0800 Subject: [PATCH 24/43] skip autogenerated example --- tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 | 7 +++++-- .../Exceptions/Az.Accounts/ExampleIssues.csv | 3 --- 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index 6c8c38562a23..6e41724f33f5 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -81,8 +81,11 @@ function Get-ExamplesDetailsFromMd { # Skip the 1st because it is $EXAMPLES_HEADING. Extract content without title. $examplesContentWithoutTitle = $examplesContent -split $SINGLE_EXAMPLE_TITLE_HEADING_REGEX | Select-Object -Skip 1 - foreach ($exampleContent in $examplesContentWithoutTitle) { + #skip the autogenerated example + if($exampleContent -match "(autogenerated)"){ + continue + } $exampleTitle = ($examplesTitles[$exampleNumber].Value -split $SINGLE_EXAMPLE_HEADING_REGEX)[1].Trim() $exampleNumber++ $exampleCodes = @() @@ -209,7 +212,7 @@ function ExceptionRecord{ $needAdd = $true $exceptionPath = Join-Path -Path $exceptionPaths -ChildPath "Az.$($record.Module)" -AdditionalChildPath "ExampleIssues.csv" if(Test-Path -Path $exceptionPath){ - $exceptionContents= Import-Csv -Path $exceptionPath + $exceptionContents = Import-Csv -Path $exceptionPath foreach($exceptionContent in $exceptionContents) { if($exceptionContent.Module -eq $record.Module -and $exceptionContent.Cmdlet -eq $record.Cmdlet -and $exceptionContent.Example -eq $record.Example -and $exceptionContent.Description -eq $record.Description){ $needAdd = $false diff --git a/tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv b/tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv deleted file mode 100644 index 72739136662e..000000000000 --- a/tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv +++ /dev/null @@ -1,3 +0,0 @@ -"Module","Cmdlet","Example","RuleName","ProblemId","Severity","Description","Extent","Remediation" -"Accounts","Disable-AzContextAutosave","2","MissingExampleCode","5044","1","Code of the example is missing.","Accounts\help\Disable-AzContextAutosave.md","Add code for the example. Remove any placeholders." -"Accounts","Connect-AzAccount","3","Mismatched_Parameter_Value_Type","5111","2","Connect-AzAccount -Credential $Credential is not an expected parameter value type.","-Credential","Use correct parameter value type." \ No newline at end of file From 2283b0d1a6dcfa82f4db7d5d671289322681e0b3 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Tue, 14 Jun 2022 18:18:47 +0800 Subject: [PATCH 25/43] fix output; output missing errors for placeholders; fix description --- .../StaticAnalysis/ExampleAnalyzer/utils.ps1 | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index 6e41724f33f5..7fb29509eee0 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -21,7 +21,7 @@ $PARAMETERS_HEADING = "## PARAMETERS" $SINGLE_EXAMPLE_HEADING_REGEX = "\n###\s*" $SINGLE_EXAMPLE_TITLE_HEADING_REGEX = "$SINGLE_EXAMPLE_HEADING_REGEX.+" $CODE_BLOCK_REGEX = "``````(powershell)?\s*\n(.*\n)+?\s*``````" -$OUTPUT_BLOCK_REGEX = "``````output\s*\n(.*\n)+?\s*``````" +$OUTPUT_BLOCK_REGEX = "``````output\s*\n(.*\n)*?\s*``````" class Scale { [string]$Module @@ -181,8 +181,13 @@ function Get-ExamplesDetailsFromMd { } } - # From the end of the last codeblock to the end is example description. - $description = $exampleContent.SubString($exampleCodeBlocks[-1].Index + $exampleCodeBlocks[-1].Length).Trim() + if($exampleOutputBlocks -ne $null){ + $description = $exampleContent.SubString($exampleOutputBlocks[-1].Index + $exampleOutputBlocks[-1].Length).Trim() + } + else{ + # From the end of the last codeblock to the end is example description. + $description = $exampleContent.SubString($exampleCodeBlocks[-1].Index + $exampleCodeBlocks[-1].Length).Trim() + } if ($description -ne "") { $exampleDescriptions += $description } @@ -336,11 +341,18 @@ function Measure-SectionMissingAndOutputScript { $results += $result } else { + $missingExampleTitle += ($examplesDetails.Title | Select-String -Pattern "{{[A-Za-z ]*}}").Count + $missingExampleCode += ($examplesDetails.Codes | Select-String -Pattern "{{[A-Za-z ]*}}").Count + $missingExampleOutput += ($examplesDetails.Outputs | Select-String -Pattern "{{[A-Za-z ]*}}").Count + $missingExampleDescription += ($examplesDetails.Description | Select-String -Pattern "{{[A-Za-z ]*}}").Count + foreach ($exampleDetails in $examplesDetails) { $exampleNumber++ switch ($exampleDetails) { - {$exampleDetails.Title -eq ""} { - $missingExampleTitle++ + {$exampleDetails.Title -eq "" -or $missingExampleTitle -ne 0} { + if($exampleDetails.Title -eq ""){ + $missingExampleTitle++ + } $result = [AnalysisOutput]@{ Module = $Module Cmdlet = $Cmdlet @@ -354,8 +366,10 @@ function Measure-SectionMissingAndOutputScript { } $results += $result } - {$exampleDetails.Codes.Count -eq 0} { - $missingExampleCode++ + {$exampleDetails.Codes.Count -eq 0 -or $missingExampleCode -ne 0} { + if($exampleDetails.Codes.Count -eq 0){ + $missingExampleCode++ + } $result = [AnalysisOutput]@{ Module = $Module Cmdlet = $Cmdlet @@ -369,8 +383,10 @@ function Measure-SectionMissingAndOutputScript { } $results += $result } - {$exampleDetails.OutputBlocks.Count -ne 0 -and $exampleDetails.Outputs.Count -eq 0} { - $missingExampleOutput++ + {($exampleDetails.OutputBlocks.Count -ne 0 -and $exampleDetails.Outputs.Count -eq 0) -or $missingExampleOutput -ne 0} { + if($exampleDetails.OutputBlocks.Count -ne 0 -and $exampleDetails.Outputs.Count -eq 0){ + $missingExampleOutput++ + } $result = [AnalysisOutput]@{ Module = $Module Cmdlet = $Cmdlet @@ -399,8 +415,10 @@ function Measure-SectionMissingAndOutputScript { } $results += $result } - {$exampleDetails.Description -eq ""} { - $missingExampleDescription++ + {$exampleDetails.Description -eq "" -or $missingExampleDescription -ne 0} { + if($exampleDetails.Description -ne 0){ + $missingExampleDescription++ + } $result = [AnalysisOutput]@{ Module = $Module Cmdlet = $Cmdlet @@ -463,11 +481,6 @@ function Measure-SectionMissingAndOutputScript { } # MissingTable - $missingExampleTitle += ($examplesDetails.Title | Select-String -Pattern "{{[A-Za-z ]*}}").Count - $missingExampleCode += ($examplesDetails.Codes | Select-String -Pattern "{{[A-Za-z ]*}}").Count - $missingExampleOutput += ($examplesDetails.Outputs | Select-String -Pattern "{{[A-Za-z ]*}}").Count - $missingExampleDescription += ($examplesDetails.Description | Select-String -Pattern "{{[A-Za-z ]*}}").Count - if ($missingSynopsis -ne 0 -or $missingDescription -ne 0 -or $missingExampleTitle -ne 0 -or $missingExampleCode -ne 0 -or $missingExampleOutput -ne 0 -or $missingExampleDescription -ne 0) { $missing = [Missing]@{ Module = $module From 9e335ed34868a8c509e5b4c212190271a5147f49 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 15 Jun 2022 11:46:35 +0800 Subject: [PATCH 26/43] fix bugs --- .../AnalyzeRules/CommandName.psm1 | 6 +++- .../AnalyzeRules/ParameterNameAndValue.psm1 | 6 ++-- .../StaticAnalysis/ExampleAnalyzer/utils.ps1 | 33 +++++++------------ 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 index f13a7cd7bbe3..33f5a45508e8 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 @@ -40,6 +40,7 @@ function Measure-CommandName { #find all command in .ps1 if ($Ast -is [System.Management.Automation.Language.CommandAst]) { [System.Management.Automation.Language.CommandAst]$CommandAst = $Ast + # Get wrapper function name by command element if ($CommandAst.Parent.Parent -is [System.Management.Automation.Language.AssignmentStatementAst]){ $ModuleCmdletExNum = $CommandAst.Parent.Parent.Parent.Parent.Parent.Name } @@ -93,12 +94,14 @@ function Measure-CommandName { $RuleName = [RuleNames]::Invalid_Cmdlet $RuleSuppressionID = "5000" $Remediation = "Check the spell of $($CommandParameterPair[$i].CommandName)." + $Severity = "Error" } if ($global:CommandParameterPair[$i].ParameterName -eq "") { $Message = "$($CommandParameterPair[$i].CommandName) is an alias of `"$((Get-Alias $CommandParameterPair[$i].CommandName)[0].ResolvedCommandName)`"." $RuleName = [RuleNames]::Is_Alias $RuleSuppressionID = "5100" $Remediation = "Use formal name `"$((Get-Alias $CommandParameterPair[$i].CommandName)[0].ResolvedCommandName)`" of the alias `"$($CommandParameterPair[$i].CommandName)`"." + $Severity = "Warning" } if ($global:CommandParameterPair[$i].ParameterName -eq "") { $Message = "$($CommandParameterPair[$i].CommandName) doesn't follow the Capitalization Conventions." @@ -110,13 +113,14 @@ function Measure-CommandName { $CorrectName += "-Az" $CorrectName += $textInfo.ToTitleCase(($name -split "Az")[1]) $Remediation = "Check the Capitalization Conventions. Suggest format: $CorrectName" + $Severity = "Warning" } $ModuleCmdletExNum = $($CommandParameterPair[$i].ModuleCmdletExNum) $Result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ Message = "$ModuleCmdletExNum-@$Message@$Remediation"; Extent = $Asts[$i].Extent; RuleName = $RuleName; - Severity = "Error" + Severity = $Severity RuleSuppressionID = $RuleSuppressionID } $Results += $Result diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index c6cfb092a887..e573d0dbfe42 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -222,10 +222,10 @@ function Measure-ParameterNameAndValue { $CommandExpressionElement = [System.Management.Automation.Language.ExpressionAst]$CommandElement $PositionMaximum = ($global:ParameterSet.Parameters.Position | Measure-Object -Maximum).Maximum for ($Position = 0; $Position -le $PositionMaximum; $Position++) { - $ImplicitParameterName = @() + ($global:ParameterSet.Parameters | where {$_.Position -eq $Position}).Name - if ($ImplicitParameterName.Count -ne 0 -and $ImplicitParameterName -notin $global:ParameterExpressionPair.ParameterName) { + $ImplicitParameterName = ($global:ParameterSet.Parameters | where {$_.Position -eq $Position}).Name + if ($ImplicitParameterName -ne $null -and $ImplicitParameterName -notin $global:ParameterExpressionPair.ParameterName) { $global:ParameterExpressionPair += @{ - ParameterName = $ImplicitParameterName[0] + ParameterName = $ImplicitParameterName ExpressionToParameter = $CommandExpressionElement } $i += 1 diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index 7fb29509eee0..e9a725e6fbff 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -341,18 +341,15 @@ function Measure-SectionMissingAndOutputScript { $results += $result } else { - $missingExampleTitle += ($examplesDetails.Title | Select-String -Pattern "{{[A-Za-z ]*}}").Count - $missingExampleCode += ($examplesDetails.Codes | Select-String -Pattern "{{[A-Za-z ]*}}").Count - $missingExampleOutput += ($examplesDetails.Outputs | Select-String -Pattern "{{[A-Za-z ]*}}").Count - $missingExampleDescription += ($examplesDetails.Description | Select-String -Pattern "{{[A-Za-z ]*}}").Count - foreach ($exampleDetails in $examplesDetails) { $exampleNumber++ + $_missingExampleTitle = ($exampleDetails.Title | Select-String -Pattern "{{[A-Za-z ]*}}").Count + $_missingExampleCode = ($exampleDetails.Codes | Select-String -Pattern "{{[A-Za-z ]*}}").Count + $_missingExampleOutput = ($exampleDetails.Outputs | Select-String -Pattern "{{[A-Za-z ]*}}").Count + $_missingExampleDescription = ($exampleDetails.Description | Select-String -Pattern "{{[A-Za-z ]*}}").Count switch ($exampleDetails) { - {$exampleDetails.Title -eq "" -or $missingExampleTitle -ne 0} { - if($exampleDetails.Title -eq ""){ - $missingExampleTitle++ - } + {$exampleDetails.Title -eq "" -or $_missingExampleTitle -ne 0} { + $missingExampleTitle ++ $result = [AnalysisOutput]@{ Module = $Module Cmdlet = $Cmdlet @@ -366,10 +363,8 @@ function Measure-SectionMissingAndOutputScript { } $results += $result } - {$exampleDetails.Codes.Count -eq 0 -or $missingExampleCode -ne 0} { - if($exampleDetails.Codes.Count -eq 0){ - $missingExampleCode++ - } + {$exampleDetails.Codes.Count -eq 0 -or $_missingExampleCode -ne 0} { + $missingExampleCode++ $result = [AnalysisOutput]@{ Module = $Module Cmdlet = $Cmdlet @@ -383,10 +378,8 @@ function Measure-SectionMissingAndOutputScript { } $results += $result } - {($exampleDetails.OutputBlocks.Count -ne 0 -and $exampleDetails.Outputs.Count -eq 0) -or $missingExampleOutput -ne 0} { - if($exampleDetails.OutputBlocks.Count -ne 0 -and $exampleDetails.Outputs.Count -eq 0){ - $missingExampleOutput++ - } + {($exampleDetails.OutputBlocks.Count -ne 0 -and $exampleDetails.Outputs.Count -eq 0) -or $_missingExampleOutput -ne 0} { + $missingExampleOutput++ $result = [AnalysisOutput]@{ Module = $Module Cmdlet = $Cmdlet @@ -415,10 +408,8 @@ function Measure-SectionMissingAndOutputScript { } $results += $result } - {$exampleDetails.Description -eq "" -or $missingExampleDescription -ne 0} { - if($exampleDetails.Description -ne 0){ - $missingExampleDescription++ - } + {$exampleDetails.Description -eq "" -or $_missingExampleDescription -ne 0} { + $missingExampleDescription++ $result = [AnalysisOutput]@{ Module = $Module Cmdlet = $Cmdlet From 243930d7aae44a74ebdda84869419d16adc8a1e1 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 15 Jun 2022 12:09:11 +0800 Subject: [PATCH 27/43] Combine install platyPS and PSScriptAnalyzer together into a "Install PowerShell Dependencies" --- .azure-pipelines/util/analyze-steps.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.azure-pipelines/util/analyze-steps.yml b/.azure-pipelines/util/analyze-steps.yml index 7f872808f1f8..63c0c0eec120 100644 --- a/.azure-pipelines/util/analyze-steps.yml +++ b/.azure-pipelines/util/analyze-steps.yml @@ -29,11 +29,9 @@ steps: packageType: sdk version: 3.1.x -- pwsh: 'Install-Module platyPS -Force -Confirm:$false -Scope CurrentUser' - displayName: 'Install platyPS' - -- pwsh: 'Install-Module PSScriptAnalyzer' - displayName: 'Install PSScriptAnalyzer' +- task: PowerShell@2 + displayName: 'Install PowerShell Dependencies' + pwsh: 'Install-Module platyPS -Force -Confirm:$false -Scope CurrentUser;Install-Module PSScriptAnalyzer -Force -Confirm:$false -Scope CurrentUser' - task: DotNetCoreCLI@2 displayName: 'Generate Help' From bf25d2d3ab7b0d109846eeaba525bf6f47235375 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 15 Jun 2022 12:31:14 +0800 Subject: [PATCH 28/43] abandon Jenkins for CI --- documentation/Debugging-StaticAnalysis-Errors.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/documentation/Debugging-StaticAnalysis-Errors.md b/documentation/Debugging-StaticAnalysis-Errors.md index 283d884972e6..064ba34ff390 100644 --- a/documentation/Debugging-StaticAnalysis-Errors.md +++ b/documentation/Debugging-StaticAnalysis-Errors.md @@ -35,21 +35,21 @@ If you make a change that could cause a breaking change, it will be listed in `B _Note_: Sometimes the error listed in the .csv file can be a false positive (for example, if you change a parameter attribute to span all parameter sets rather than individual parameter sets). Please read the error thoroughly and examine the relevant code before deciding that an error is a false positive, and contact the Azure PowerShell team if you have questions. If you are releasing a preview module, are releasing during a breaking change release, or have determined that the error is a false positive, please follow these instructions to suppress the errors: -- Download the `BreakingChangeIssues.csv` file from the Jenkins build +- Download the `BreakingChangeIssues.csv` file from the CI pipeline artifacts - Open the file using a text editor (such as VS Code) and copy each of the errors you'd like to suppress - Paste each of these errors into the `BreakingChangeIssues.csv` file found in their respective [module folder](../tools/StaticAnalysis/Exceptions) (_e.g._, if a breaking change is being suppressed for Compute, then you would paste the corresponding line(s) in the `tools/StaticAnalysis/Exceptions/Az.Compute/BreakingChangeIssues.csv` file) using the same text editor -- Push the changes to the .csv file and ensure the errors no longer show up in the `BreakingChangeIssues.csv` file output from the Jenkins build. +- Push the changes to the .csv file and ensure the errors no longer show up in the `BreakingChangeIssues.csv` file output from the CI pipeline artifacts. We take breaking changes very seriously, so please be mindful about the violations that you suppress in our repo. ### Signature Issues Signature issues occur when your cmdlets do not follow PowerShell standards. Please check the [_Cmdlet Best Practices_](https://github.com/Azure/azure-powershell/blob/main/documentation/development-docs/design-guidelines/cmdlet-best-practices.md) and the [_Parameter Best Practices_](https://github.com/Azure/azure-powershell/blob/main/documentation/development-docs/design-guidelines/parameter-best-practices.md) documents to ensure you are following PowerShell guidelines. Issues with severity 0 or 1 must be addressed, while issues with severity 2 are advisory. If you have an issue with severity 0 or 1 that has been approved by the Azure PowerShell team, you can suppress them following these steps: -- Download the `SignatureIssues.csv` file from the Jenkins build +- Download the `SignatureIssues.csv` file from the CI pipeline artifacts - Open the file using a text editor (such as VS Code) and copy each of the errors you'd like to suppress - Paste each of these errors into the `SignatureIssues.csv` file found in their respective [module folder](../tools/StaticAnalysis/Exceptions) (_e.g.,_ if a signature issue is being suppressed for Sql, then you would paste the corresponding line(s) in the `tools/StaticAnalysis/Exceptions/Az.Sql/SignatureIssues.csv` file) using the same text editor -- Copy each of the errors you would like to suppress directly from the SignatureIssues.csv file output in the Jenkins build -- Push the changes to the .csv file and ensure the errors no longer show up in the `SignatureIssues.csv` file output from the Jenkins build. +- Copy each of the errors you would like to suppress directly from the SignatureIssues.csv file output in the CI pipeline artifacts +- Push the changes to the .csv file and ensure the errors no longer show up in the `SignatureIssues.csv` file output from the CI pipeline artifacts. ### Help Issues Most help issues that cause StaticAnalysis to fail occur when help has not been added for a particular cmdlet. If you have not generated help for your new cmdlets, please follow the instructions [here](https://github.com/Azure/azure-powershell/blob/main/documentation/development-docs/help-generation.md). If this is not the issue, follow the steps listed under "Remediation" for each violation listed in HelpIssues.csv. @@ -57,10 +57,10 @@ Most help issues that cause StaticAnalysis to fail occur when help has not been ### Example Issues Example Issues occur when your changed markdown files a `help` folder (_e.g.,_ `src/Accounts/Accounts/help`) do not follow the script rules. Please follow the suggestion displayed in "Remediation" entry for each violation listed in `ExampleIssues.csv`. If you have an issue with severity 0 or 1 that has been approved by the Azure PowerShell team, you can suppress them following these steps: -- Download the `ExampleIssues.csv` file from the Jenkins build +- Download the `ExampleIssues.csv` file from the CI pipeline artifacts - Open the file using a text editor (such as VS Code) and copy each of the errors you'd like to suppress - Paste each of these errors into the `ExampleIssues.csv` file found in their respective [module folder](../tools/StaticAnalysis/Exceptions) (_e.g.,_ if an example issue is being suppressed for Accounts, then you would paste the corresponding line(s) in the `tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssue.csv` file) using the same text editor -- Copy each of the errors you would like to suppress directly from the ExampleIssues.csv file output in the Jenkins build -- Push the changes to the .csv file and ensure the errors no longer show up in the `ExampleIssues.csv` file output from the Jenkins build +- Copy each of the errors you would like to suppress directly from the ExampleIssues.csv file output in the CI pipeline artifacts +- Push the changes to the .csv file and ensure the errors no longer show up in the `ExampleIssues.csv` file output from the CI pipeline artifacts. To better standardize the writing of documents, please also check the warning issues with severity 2 by downloading the `ExampleIssues.csv` file. \ No newline at end of file From 925039dd3a06168feb766f0e26b491044b329224 Mon Sep 17 00:00:00 2001 From: Shiying Chen <72982571+MoChilia@users.noreply.github.com> Date: Wed, 15 Jun 2022 12:33:49 +0800 Subject: [PATCH 29/43] Apply suggestions from code review Co-authored-by: Yeming Liu <11371776+isra-fel@users.noreply.github.com> --- build.proj | 4 ++-- documentation/Debugging-StaticAnalysis-Errors.md | 4 ++-- documentation/tooling/static-analysis.md | 2 +- .../Microsoft.Azure.Build.Tasks/FilesChangedTask.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.proj b/build.proj index dda73ff5bd6d..f1fbd8e9d44f 100644 --- a/build.proj +++ b/build.proj @@ -264,8 +264,8 @@ - - + + diff --git a/documentation/Debugging-StaticAnalysis-Errors.md b/documentation/Debugging-StaticAnalysis-Errors.md index 283d884972e6..5571b023e38e 100644 --- a/documentation/Debugging-StaticAnalysis-Errors.md +++ b/documentation/Debugging-StaticAnalysis-Errors.md @@ -55,11 +55,11 @@ Signature issues occur when your cmdlets do not follow PowerShell standards. Pl Most help issues that cause StaticAnalysis to fail occur when help has not been added for a particular cmdlet. If you have not generated help for your new cmdlets, please follow the instructions [here](https://github.com/Azure/azure-powershell/blob/main/documentation/development-docs/help-generation.md). If this is not the issue, follow the steps listed under "Remediation" for each violation listed in HelpIssues.csv. ### Example Issues -Example Issues occur when your changed markdown files a `help` folder (_e.g.,_ `src/Accounts/Accounts/help`) do not follow the script rules. Please follow the suggestion displayed in "Remediation" entry for each violation listed in `ExampleIssues.csv`. If you have an issue with severity 0 or 1 that has been approved by the Azure PowerShell team, you can suppress them following these steps: +Example issues occur when your changed markdown files in the `help` folder (_e.g.,_ `src/Accounts/Accounts/help`) violate PowerShell language best practices. Please follow the suggestion displayed in "Remediation" entry for each violation listed in `ExampleIssues.csv`. If you have an issue with severity 0 or 1 that has been approved by the Azure PowerShell team, you can suppress them following these steps: - Download the `ExampleIssues.csv` file from the Jenkins build - Open the file using a text editor (such as VS Code) and copy each of the errors you'd like to suppress -- Paste each of these errors into the `ExampleIssues.csv` file found in their respective [module folder](../tools/StaticAnalysis/Exceptions) (_e.g.,_ if an example issue is being suppressed for Accounts, then you would paste the corresponding line(s) in the `tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssue.csv` file) using the same text editor +- Paste each of these errors into the `ExampleIssues.csv` file found in their respective [module folder](../tools/StaticAnalysis/Exceptions) (_e.g.,_ if an example issue is being suppressed for Accounts, then you would paste the corresponding line(s) in the `tools/StaticAnalysis/Exceptions/Az.Accounts/ExampleIssues.csv` file) using the same text editor - Copy each of the errors you would like to suppress directly from the ExampleIssues.csv file output in the Jenkins build - Push the changes to the .csv file and ensure the errors no longer show up in the `ExampleIssues.csv` file output from the Jenkins build diff --git a/documentation/tooling/static-analysis.md b/documentation/tooling/static-analysis.md index d461d6f67927..155318c65027 100644 --- a/documentation/tooling/static-analysis.md +++ b/documentation/tooling/static-analysis.md @@ -86,7 +86,7 @@ The dependency analyzer can be found in the [`DependencyAnalyzer`](https://githu - `SharedAssemblyConflict` - The implementation of the `IReportRecord` interface; defines what a shared conflict exception looks like when it's reported in the `SharedAssemblyConflict.csv` file that is found in the build artifacts of a CI run, as well as how to compare a new record to a record found in the existing `SharedAssemblyConflict.csv` file used for exception suppressions - `ExampleIssue` - - The implementation of the `IReportRecord` interface; defines what a shared conflict exception looks like when it's reported in the `ExampleIssues.csv` file that is found in the build artifacts of a CI run, as well as how to compare a new record to a record found in the existing `ExampleIssues.csv` file used for exception suppressions + - The implementation of the `IReportRecord` interface; defines what an example issue exception looks like when it's reported in the `ExampleIssues.csv` file that is found in the build artifacts of a CI run, as well as how to compare a new record to a record found in the existing `ExampleIssues.csv` file used for exception suppressions #### Help Analyzer diff --git a/tools/BuildPackagesTask/Microsoft.Azure.Build.Tasks/FilesChangedTask.cs b/tools/BuildPackagesTask/Microsoft.Azure.Build.Tasks/FilesChangedTask.cs index 1b2de68410ce..dba5979bef3e 100644 --- a/tools/BuildPackagesTask/Microsoft.Azure.Build.Tasks/FilesChangedTask.cs +++ b/tools/BuildPackagesTask/Microsoft.Azure.Build.Tasks/FilesChangedTask.cs @@ -153,7 +153,7 @@ public override bool Execute() return true; } - // This method will record the changed files into FilesChanged.txt under '.\artifacts' folder for other task to consum. + // This method will record the changed files into a text file at `OutputFile` for other task to consum. private void SerializeChangedFilesToFile(string[] FilesChanged) { File.WriteAllLines(OutputFile, FilesChanged); From e2c0a2ad7670d8e8bacc1c002cae002e6295e4aa Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 15 Jun 2022 12:44:09 +0800 Subject: [PATCH 30/43] comment for problemID --- tools/StaticAnalysis/ProblemIDs.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/StaticAnalysis/ProblemIDs.cs b/tools/StaticAnalysis/ProblemIDs.cs index d4d0882584d5..3b4c4000d2b8 100644 --- a/tools/StaticAnalysis/ProblemIDs.cs +++ b/tools/StaticAnalysis/ProblemIDs.cs @@ -74,6 +74,8 @@ public static class BreakingChangeProblemId public const int ChangedGenericTypeArgument = 3040; public const int DifferentGenericTypeArgumentSize = 3050; } + + //ExampleProblemId is also defined in tools\StaticAnalysis\ExampleAnalyzer, the range is 5000-5199 public static class ExampleProblemId { public const int Invalid_Cmdlet = 5000; From e78c9b71512c6f1ad0acf1d62915e81ac931c3d2 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 15 Jun 2022 15:35:16 +0800 Subject: [PATCH 31/43] Update analyze-steps.yml --- .azure-pipelines/util/analyze-steps.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.azure-pipelines/util/analyze-steps.yml b/.azure-pipelines/util/analyze-steps.yml index 63c0c0eec120..0241121d5af0 100644 --- a/.azure-pipelines/util/analyze-steps.yml +++ b/.azure-pipelines/util/analyze-steps.yml @@ -29,10 +29,9 @@ steps: packageType: sdk version: 3.1.x -- task: PowerShell@2 +- pwsh: 'Install-Module "platyPS", "PSScriptAnalyzer" -Force -Confirm:$false -Scope CurrentUser' displayName: 'Install PowerShell Dependencies' - pwsh: 'Install-Module platyPS -Force -Confirm:$false -Scope CurrentUser;Install-Module PSScriptAnalyzer -Force -Confirm:$false -Scope CurrentUser' - + - task: DotNetCoreCLI@2 displayName: 'Generate Help' inputs: From 93a9fbef97645660101438646a051a8ef228f11e Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 15 Jun 2022 16:35:56 +0800 Subject: [PATCH 32/43] use absolute path instead of relative path --- .../ExampleAnalyzer/AnalyzeRules/CommandName.psm1 | 3 ++- .../ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 | 3 ++- .../ExampleAnalyzer/Measure-MarkdownOrScript.ps1 | 6 +++--- tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 index 33f5a45508e8..d495ae8e0036 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 @@ -25,7 +25,8 @@ function Measure-CommandName { $ScriptBlockAst ) begin{ - Get-Item ./artifacts/Debug/Az.*/Az.*.psd1 | Import-Module -Global + $modulePath = "$PSScriptRoot\..\..\..\..\artifacts\Debug\Az.*\Az.*.psd1" + Get-Item $modulePath | Import-Module -Global } process { $Results = @() diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index e573d0dbfe42..9f3c0ac0bdcd 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -56,7 +56,8 @@ function Measure-ParameterNameAndValue { $ScriptBlockAst ) begin{ - Get-Item ./artifacts/Debug/Az.*/Az.*.psd1 | Import-Module -Global + $modulePath = "$PSScriptRoot\..\..\..\..\artifacts\Debug\Az.*\Az.*.psd1" + Get-Item $modulePath | Import-Module -Global } process { $Results = @() diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 index 18dfaa33319a..29d47de75336 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 @@ -19,7 +19,7 @@ param ( [string[]]$RulePaths, [switch]$Recurse, [switch]$IncludeDefaultRules, - [string]$OutputFolder = ".\artifacts\StaticAnalysisResults\ExampleAnalysis", + [string]$OutputFolder = "$PSScriptRoot\..\..\..\artifacts\StaticAnalysisResults\ExampleAnalysis", [Parameter(ParameterSetName = "Markdown")] [switch]$AnalyzeScriptsInFile, [Parameter(ParameterSetName = "Markdown")] @@ -42,7 +42,7 @@ $analysisResultsTable = @() if ($OutputScriptsInFile.IsPresent) { Remove-Item $OutputFolder\TempScript.ps1 -ErrorAction SilentlyContinue Remove-Item $OutputFolder\*.csv -Recurse -ErrorAction SilentlyContinue - Remove-Item .\artifacts\StaticAnalysisResults\ExampleIssues.csv -ErrorAction SilentlyContinue + Remove-Item $PSScriptRoot\..\..\..\artifacts\StaticAnalysisResults\ExampleIssues.csv -ErrorAction SilentlyContinue Remove-Item $OutputFolder -ErrorAction SilentlyContinue } @@ -91,7 +91,7 @@ if ($PSCmdlet.ParameterSetName -eq "Script" -or $AnalyzeScriptsInFile.IsPresent) # Summarize analysis results, output in Result.csv if($analysisResultsTable){ - $analysisResultsTable| where {$_ -ne $null} | Export-Csv ".\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation + $analysisResultsTable| where {$_ -ne $null} | Export-Csv "$PSScriptRoot\..\..\..\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation } } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index e9a725e6fbff..bee6ea7ef0c2 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -211,7 +211,7 @@ function ExceptionRecord{ param( [AnalysisOutput[]]$records ) - $exceptionPaths = ".\tools\StaticAnalysis\Exceptions" + $exceptionPaths = "$PSScriptRoot\..\..\..\tools\StaticAnalysis\Exceptions" $results = @() foreach($record in $records){ $needAdd = $true From 3dd692df34b6402b57fa3515cb8264e3140325e5 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 15 Jun 2022 17:54:16 +0800 Subject: [PATCH 33/43] output unexcepted error --- tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index bee6ea7ef0c2..d1d82f2bd67a 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -548,8 +548,17 @@ function Get-ScriptAnalyzerResult { ProblemID = $analysisResult.RuleSuppressionID Remediation = ($analysisResult.Message -split "@")[2] -replace "`"","`'" } - $results += $result } + else{ + $result = [AnalysisOutput]@{ + RuleName = $analysisResult.RuleName + Description = $analysisResult.Message + Severity = $Severity + Extent = $analysisResult.Extent + Remediation = "Unexpected Error! Please contact the Azure Powershell Team." + } + } + $results += $result } $results = ExceptionRecord $results return $results From 81118bca6d0460e6a75d2fe6e6f7d8842c0c2854 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Wed, 15 Jun 2022 20:39:24 +0800 Subject: [PATCH 34/43] fix a bug --- .../StaticAnalysis/ExampleAnalyzer/utils.ps1 | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index d1d82f2bd67a..6bbe84b53d50 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -20,7 +20,7 @@ $EXAMPLES_HEADING = "## EXAMPLES" $PARAMETERS_HEADING = "## PARAMETERS" $SINGLE_EXAMPLE_HEADING_REGEX = "\n###\s*" $SINGLE_EXAMPLE_TITLE_HEADING_REGEX = "$SINGLE_EXAMPLE_HEADING_REGEX.+" -$CODE_BLOCK_REGEX = "``````(powershell)?\s*\n(.*\n)+?\s*``````" +$CODE_BLOCK_REGEX = "``````(powershell)?\s*\n(.*\n)*?\s*``````" $OUTPUT_BLOCK_REGEX = "``````output\s*\n(.*\n)*?\s*``````" class Scale { @@ -347,6 +347,8 @@ function Measure-SectionMissingAndOutputScript { $_missingExampleCode = ($exampleDetails.Codes | Select-String -Pattern "{{[A-Za-z ]*}}").Count $_missingExampleOutput = ($exampleDetails.Outputs | Select-String -Pattern "{{[A-Za-z ]*}}").Count $_missingExampleDescription = ($exampleDetails.Description | Select-String -Pattern "{{[A-Za-z ]*}}").Count + $_needDeleting = ($exampleDetails.CodeBlocks | Select-String -Pattern "\n([A-Za-z \t\\:>])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*[ \t]*" -CaseSensitive).Count + + ($exampleDetails.CodeBlocks | Select-String -Pattern "(?<=[A-Za-z]\w+-[A-Za-z]\w+)\.ps1" -CaseSensitive).Count switch ($exampleDetails) { {$exampleDetails.Title -eq "" -or $_missingExampleTitle -ne 0} { $missingExampleTitle ++ @@ -423,23 +425,21 @@ function Measure-SectionMissingAndOutputScript { } $results += $result } - } - $needDeleting = ($examplesDetails.CodeBlocks | Select-String -Pattern "\n([A-Za-z \t\\:>])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*[ \t]*" -CaseSensitive).Count + - ($examplesDetails.CodeBlocks | Select-String -Pattern "(?<=[A-Za-z]\w+-[A-Za-z]\w+)\.ps1" -CaseSensitive).Count - - if($needDeleting -ne 0){ - $result = [AnalysisOutput]@{ - Module = $Module - Cmdlet = $Cmdlet - Example = $exampleDetails.Num - Description = "The prompt of example need to be deleted." - RuleName = "NeedDeleting" - Severity = 1 - Extent = "$Module\help\$Cmdlet.md" - ProblemID = 5051 - Remediation = "Delete the prompt of example." + {$_needDeleting -ne 0}{ + $needDeleting++ + $result = [AnalysisOutput]@{ + Module = $Module + Cmdlet = $Cmdlet + Example = $exampleDetails.Num + Description = "The prompt of example need to be deleted." + RuleName = "NeedDeleting" + Severity = 1 + Extent = "$Module\help\$Cmdlet.md" + ProblemID = 5051 + Remediation = "Delete the prompt of example." + } + $results += $result } - $results += $result } # Delete prompts From 2323878f9cf78661efed59a80fdd8d89462a6074 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Thu, 16 Jun 2022 15:35:43 +0800 Subject: [PATCH 35/43] optimize writing --- .../AnalyzeRules/CommandName.psm1 | 2 +- .../AnalyzeRules/ParameterNameAndValue.psm1 | 54 +++++++++---------- .../Measure-MarkdownOrScript.ps1 | 11 ++-- .../StaticAnalysis/ExampleAnalyzer/utils.ps1 | 4 +- 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 index d495ae8e0036..52ff95c9bf19 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 @@ -52,7 +52,7 @@ function Measure-CommandName { # $CommandName = $CommandAst.GetCommandName() $CommandName = $CommandAst.CommandElements[0].Extent.Text $GetCommand = Get-Command $CommandName -ErrorAction SilentlyContinue - if ($GetCommand -eq $null) { + if ($null -eq $GetCommand) { # CommandName is not valid. $global:CommandParameterPair += @{ CommandName = $CommandName diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index 9f3c0ac0bdcd..d429c6cad277 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -23,16 +23,16 @@ function Get-ActualVariableValue { param([System.Management.Automation.Language.Ast]$CommandElementAst) while ($true) { - if ($CommandElementAst.Expression -ne $null) { + if ($null -ne $CommandElementAst.Expression) { $CommandElementAst = $CommandElementAst.Expression } - elseif ($CommandElementAst.Target -ne $null) { + elseif ($null -ne $CommandElementAst.Target) { $CommandElementAst = $CommandElementAst.Target } - elseif ($CommandElementAst.Pipeline -ne $null) { + elseif ($null -ne $CommandElementAst.Pipeline) { $CommandElementAst = $CommandElementAst.Pipeline } - elseif ($CommandElementAst.PipelineElements -ne $null) { + elseif ($null -ne $CommandElementAst.PipelineElements) { $CommandElementAst = $CommandElementAst.PipelineElements[-1] } else { @@ -104,7 +104,7 @@ function Measure-ParameterNameAndValue { # $CommandName = $CommandAst.GetCommandName() $CommandName = $CommandAst.CommandElements[0].Extent.Text $GetCommand = Get-Command $CommandName -ErrorAction SilentlyContinue - if ($GetCommand -eq $null) { + if ($null -eq $GetCommand) { return $false } @@ -124,7 +124,7 @@ function Measure-ParameterNameAndValue { # sort ParameterSets, move ParameterSets that have position parameters to the front. $ParameterSets = @() + # ($GetCommand.ParameterSets | where {$_.Name -eq $GetCommand.DefaultParameterSet}) + - ($GetCommand.ParameterSets | Sort-Object {($_.Parameters.Position | where {$_ -ge 0}).Count} -Descending) + ($GetCommand.ParameterSets | Sort-Object {($_.Parameters.Position | Where-Object {$_ -ge 0}).Count} -Descending) foreach ($ParameterSet in $ParameterSets) { # ParameterSets.Count is 0 when CommandName is an alias. $AllParameterNamesInASet_Flag = $true @@ -167,19 +167,19 @@ function Measure-ParameterNameAndValue { if ($CommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { $CommandParameterElement = [System.Management.Automation.Language.CommandParameterAst]$CommandElement $ParameterName = $CommandParameterElement.ParameterName - $ParameterNameNotAlias = $GetCommand.Parameters.Values.Name | where { + $ParameterNameNotAlias = $GetCommand.Parameters.Values.Name | Where-Object { $ParameterName -in $GetCommand.Parameters.$_.Name -or $ParameterName -in $GetCommand.Parameters.$_.Aliases } # Invalid_Parameter_Name - if ($ParameterNameNotAlias -eq $null) { + if ($null -eq $ParameterNameNotAlias) { # ParameterName is not in AllParameters. # will report later. $global:ParameterExpressionPair += @{ ParameterName = $ParameterName ExpressionToParameter = "" } - if ($NextCommandElement -eq $null -or $NextCommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { + if ($null -eq $NextCommandElement -or $NextCommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { $i += 1 } else { @@ -198,7 +198,7 @@ function Measure-ParameterNameAndValue { else { # Unassigned_Parameter # not a SwitchParameter - if ($NextCommandElement -eq $null -or $NextCommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { + if ($null -eq $NextCommandElement -or $NextCommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { # NonSwitchParameter + Parameter # Parameter must be assigned with a value. # will report later. @@ -223,8 +223,8 @@ function Measure-ParameterNameAndValue { $CommandExpressionElement = [System.Management.Automation.Language.ExpressionAst]$CommandElement $PositionMaximum = ($global:ParameterSet.Parameters.Position | Measure-Object -Maximum).Maximum for ($Position = 0; $Position -le $PositionMaximum; $Position++) { - $ImplicitParameterName = ($global:ParameterSet.Parameters | where {$_.Position -eq $Position}).Name - if ($ImplicitParameterName -ne $null -and $ImplicitParameterName -notin $global:ParameterExpressionPair.ParameterName) { + $ImplicitParameterName = ($global:ParameterSet.Parameters | Where-Object {$_.Position -eq $Position}).Name + if ($null -ne $ImplicitParameterName -and $ImplicitParameterName -notin $global:ParameterExpressionPair.ParameterName) { $global:ParameterExpressionPair += @{ ParameterName = $ImplicitParameterName ExpressionToParameter = $CommandExpressionElement @@ -247,7 +247,7 @@ function Measure-ParameterNameAndValue { } } - if ($global:ParameterSet -eq $null) { + if ($null -eq $global:ParameterSet) { # skip commands that can't determine their ParameterSets. return $false } @@ -257,11 +257,11 @@ function Measure-ParameterNameAndValue { $index = $CommandAst.CommandElements.Extent.Text.IndexOf($CommandElementAst.Extent.Text) $NextCommandElement = $CommandAst.CommandElements[$index + 1] $ParameterName = ([System.Management.Automation.Language.CommandParameterAst]$CommandElementAst).ParameterName - $ParameterNameNotAlias = $GetCommand.Parameters.Values.Name | where { + $ParameterNameNotAlias = $GetCommand.Parameters.Values.Name | Where-Object { $ParameterName -in $GetCommand.Parameters.$_.Name -or $ParameterName -in $GetCommand.Parameters.$_.Aliases } - if ($ParameterNameNotAlias -eq $null) { + if ($null -eq $ParameterNameNotAlias) { # ParameterName is not in AllParameters. # Invalid_Parameter_Name if ($NextCommandElement -is [System.Management.Automation.Language.ExpressionAst]) { @@ -294,7 +294,7 @@ function Measure-ParameterNameAndValue { if ($GetCommand.Parameters.$ParameterNameNotAlias.SwitchParameter -eq $false) { # Parameter is not a SwitchParameter. - if ($NextCommandElement -eq $null -or $NextCommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { + if ($null -eq $NextCommandElement -or $NextCommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { # Parameter is not assigned with a value. # Unassigned_Parameter $global:CommandParameterPair += @{ @@ -313,7 +313,7 @@ function Measure-ParameterNameAndValue { while ($NextCommandElement_Copy -is [System.Management.Automation.Language.VariableExpressionAst]) { # get the actual value $NextCommandElement_Copy = Get-ActualVariableValue $global:AssignmentLeftAndRight.($NextCommandElement_Copy.Extent.Text) - if ($NextCommandElement_Copy -eq $null) { + if ($null -eq $NextCommandElement_Copy) { # Variable is not assigned with a value. # Unassigned_Variable $global:CommandParameterPair += @{ @@ -328,13 +328,13 @@ function Measure-ParameterNameAndValue { if ($NextCommandElement_Copy -is [System.Management.Automation.Language.CommandAst]) { # value is an command $GetNextElementCommand = Get-Command $NextCommandElement_Copy.CommandElements[0].Extent.Text -ErrorAction SilentlyContinue - if ($GetNextElementCommand -eq $null) { + if ($null -eq $GetNextElementCommand) { # CommandName is not valid. # will be reported in next CommandAst return $false } $ReturnType = $GetNextElementCommand.OutputType[0].Type - if ($ReturnType -eq $null) + if ($null -eq $ReturnType) { $ReturnType = [Object] } @@ -356,7 +356,7 @@ function Measure-ParameterNameAndValue { $ExpectedType = $GetCommand.Parameters.$ParameterNameNotAlias.ParameterType $ConvertedObject = $NextCommandElement_Copy.Extent.Text -as $ExpectedType # Mismatched_Parameter_Value_Type - if ($NextCommandElement_Copy.StaticType -ne $ExpectedType -and $ConvertedObject -eq $null) { + if ($NextCommandElement_Copy.StaticType -ne $ExpectedType -and $null -eq $ConvertedObject) { $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = "-$ParameterName" @@ -375,8 +375,8 @@ function Measure-ParameterNameAndValue { $CommandAst.CommandElements.Extent.Text.IndexOf($CommandElementAst.Extent.Text) -ne 0) { # This CommandElement is an expression with implicit parameter and is not the first CommandElement. # When there are same parameter values: - $index = ($global:AppearedExpressions | where {$_.Extent.Text -eq $CommandElementAst.Extent.Text}).Count - $PairWithThisExpression = $global:ParameterExpressionPair | where {$_.ExpressionToParameter.Extent.Text -eq $CommandElementAst.Extent.Text} + $index = ($global:AppearedExpressions | Where-Object {$_.Extent.Text -eq $CommandElementAst.Extent.Text}).Count + $PairWithThisExpression = $global:ParameterExpressionPair | Where-Object {$_.ExpressionToParameter.Extent.Text -eq $CommandElementAst.Extent.Text} if ((@() + $PairWithThisExpression).Count -eq 1) { # Convert to Array $ImplicitParameterName = $PairWithThisExpression.ParameterName @@ -400,7 +400,7 @@ function Measure-ParameterNameAndValue { while ($CommandElementAst_Copy -is [System.Management.Automation.Language.VariableExpressionAst]) { # get the actual value $CommandElementAst_Copy = Get-ActualVariableValue $global:AssignmentLeftAndRight.($CommandElementAst_Copy.Extent.Text) - if ($CommandElementAst_Copy -eq $null) { + if ($null -eq $CommandElementAst_Copy) { # Variable is not assigned with a value. $global:CommandParameterPair += @{ CommandName = $CommandName @@ -414,13 +414,13 @@ function Measure-ParameterNameAndValue { if ($CommandElementAst_Copy -is [System.Management.Automation.Language.CommandAst]) { # value is an command $GetElementCommand = Get-Command $CommandElementAst_Copy.CommandElements[0].Extent.Text -ErrorAction SilentlyContinue - if ($GetElementCommand -eq $null) { + if ($null -eq $GetElementCommand) { # CommandName is not valid. # will be reported in next CommandAst return $false } $ReturnType = $GetElementCommand.OutputType[0].Type - if ($ReturnType -eq $null) + if ($null -eq $ReturnType) { $ReturnType = [Object] } @@ -440,7 +440,7 @@ function Measure-ParameterNameAndValue { # value is a constant expression $ExpectedType = $GetCommand.Parameters.$ImplicitParameterName.ParameterType $ConvertedObject = $CommandElementAst_Copy.Extent.Text -as $ExpectedType - if ($CommandElementAst_Copy.StaticType -ne $ExpectedType -and $ConvertedObject -eq $null) { + if ($CommandElementAst_Copy.StaticType -ne $ExpectedType -and $null -eq $ConvertedObject) { $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = "[-$ImplicitParameterName]" @@ -479,7 +479,7 @@ function Measure-ParameterNameAndValue { $RuleSuppressionID = "5012" $Remediation = "Remove redundant parameter $($CommandParameterPair[$i].ParameterName)." } - elseif ($global:CommandParameterPair[$i].ExpressionToParameter -eq $null) { + elseif ($null -eq $global:CommandParameterPair[$i].ExpressionToParameter) { $Message = "$($CommandParameterPair[$i].ModuleCmdletExNum)-@$($CommandParameterPair[$i].CommandName) -$($CommandParameterPair[$i].ParameterName) must be assigned with a value." $RuleName = [RuleNames]::Unassigned_Parameter $Severity = "Error" diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 index 29d47de75336..08c6b8b1e807 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 @@ -31,7 +31,6 @@ param ( . $PSScriptRoot\utils.ps1 if ($PSCmdlet.ParameterSetName -eq "Markdown") { - $ScriptsByExampleFolder = "ScriptsByExample" $scaleTable = @() $missingTable = @() $deletePromptAndSeparateOutputTable = @() @@ -52,7 +51,7 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { $null = New-Item -ItemType Directory -Path $OutputFolder -ErrorAction SilentlyContinue $null = New-Item -ItemType File $OutputFolder\TempScript.ps1 $MarkdownPath = Get-Content $MarkdownPaths - (Get-ChildItem $MarkdownPath) | foreach{ + foreach($_ in Get-ChildItem $MarkdownPath){ # Filter the .md of overview in \help\ if ((Get-Item -Path $_.FullName).Directory.Name -eq "help" -and $_.FullName -cmatch ".*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { Write-Output "Searching in file $($_.FullName) ..." @@ -72,13 +71,13 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { } # Summarize searching results if($scaleTable){ - $scaleTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\Scale.csv" -NoTypeInformation + $scaleTable | Where-Object {$_ -ne $null} | Export-Csv "$OutputFolder\Scale.csv" -NoTypeInformation } if($missingTable){ - $missingTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\Missing.csv" -NoTypeInformation + $missingTable | Where-Object {$_ -ne $null} | Export-Csv "$OutputFolder\Missing.csv" -NoTypeInformation } if($deletePromptAndSeparateOutputTable){ - $deletePromptAndSeparateOutputTable | where {$_ -ne $null} | Export-Csv "$OutputFolder\DeletingSeparating.csv" -NoTypeInformation + $deletePromptAndSeparateOutputTable | Where-Object {$_ -ne $null} | Export-Csv "$OutputFolder\DeletingSeparating.csv" -NoTypeInformation } } @@ -91,7 +90,7 @@ if ($PSCmdlet.ParameterSetName -eq "Script" -or $AnalyzeScriptsInFile.IsPresent) # Summarize analysis results, output in Result.csv if($analysisResultsTable){ - $analysisResultsTable| where {$_ -ne $null} | Export-Csv "$PSScriptRoot\..\..\..\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation + $analysisResultsTable| Where-Object {$_ -ne $null} | Export-Csv "$PSScriptRoot\..\..\..\artifacts\StaticAnalysisResults\ExampleIssues.csv" -NoTypeInformation } } diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index 6bbe84b53d50..ad94e971a4f4 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -181,7 +181,7 @@ function Get-ExamplesDetailsFromMd { } } - if($exampleOutputBlocks -ne $null){ + if($null -ne $exampleOutputBlocks){ $description = $exampleContent.SubString($exampleOutputBlocks[-1].Index + $exampleOutputBlocks[-1].Length).Trim() } else{ @@ -522,7 +522,7 @@ function Get-ScriptAnalyzerResult { } # Invoke PSScriptAnalyzer : input scriptblock, output error set in $result with property: RuleName, Message, Extent - if ($RulePath -eq $null) { + if ($null -eq $RulePath) { $analysisResults = Invoke-ScriptAnalyzer -Path $ScriptPath -IncludeDefaultRules:$IncludeDefaultRules.IsPresent } else { From c91be9a20dd63c0e2a533121bc7535b4c40c191c Mon Sep 17 00:00:00 2001 From: Shiying Chen <72982571+MoChilia@users.noreply.github.com> Date: Fri, 17 Jun 2022 10:11:09 +0800 Subject: [PATCH 36/43] Update tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 Co-authored-by: Yeming Liu <11371776+isra-fel@users.noreply.github.com> --- tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index ad94e971a4f4..1292a023aacc 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -83,7 +83,7 @@ function Get-ExamplesDetailsFromMd { foreach ($exampleContent in $examplesContentWithoutTitle) { #skip the autogenerated example - if($exampleContent -match "(autogenerated)"){ + if($exampleContent -match "autogenerated"){ continue } $exampleTitle = ($examplesTitles[$exampleNumber].Value -split $SINGLE_EXAMPLE_HEADING_REGEX)[1].Trim() From 62ed720a7423c1cc67e9a68f6f79727894c68b8a Mon Sep 17 00:00:00 2001 From: MoChilia Date: Fri, 17 Jun 2022 10:20:45 +0800 Subject: [PATCH 37/43] change regular expression for exampletitle --- tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index ad94e971a4f4..b8b2a39c4652 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -19,7 +19,7 @@ $DESCRIPTION_HEADING = "## DESCRIPTION" $EXAMPLES_HEADING = "## EXAMPLES" $PARAMETERS_HEADING = "## PARAMETERS" $SINGLE_EXAMPLE_HEADING_REGEX = "\n###\s*" -$SINGLE_EXAMPLE_TITLE_HEADING_REGEX = "$SINGLE_EXAMPLE_HEADING_REGEX.+" +$SINGLE_EXAMPLE_TITLE_HEADING_REGEX = "$SINGLE_EXAMPLE_HEADING_REGEX[^\n]?(Example)?\s*[0-9]*:?\s*({{)?.*(}})?" $CODE_BLOCK_REGEX = "``````(powershell)?\s*\n(.*\n)*?\s*``````" $OUTPUT_BLOCK_REGEX = "``````output\s*\n(.*\n)*?\s*``````" @@ -78,16 +78,20 @@ function Get-ExamplesDetailsFromMd { $examplesProperties = @() $examplesContent = $fileContent.Substring($indexOfExamples, $indexOfParameters - $indexOfExamples) $examplesTitles = ($examplesContent | Select-String -Pattern $SINGLE_EXAMPLE_TITLE_HEADING_REGEX -AllMatches).Matches - # Skip the 1st because it is $EXAMPLES_HEADING. Extract content without title. - $examplesContentWithoutTitle = $examplesContent -split $SINGLE_EXAMPLE_TITLE_HEADING_REGEX | Select-Object -Skip 1 - foreach ($exampleContent in $examplesContentWithoutTitle) { + for($exampleNumber = 0; $exampleNumber -le $examplesTitles.length - 1; $exampleNumber++){ + if($exampleNumber -ne $examplesTitles.length - 1){ + $exampleContent = ($examplesContent -split $examplesTitles[$exampleNumber].Value)[1] + $exampleContent = ($exampleContent -split $examplesTitles[$exampleNumber + 1].Value)[0] + } + else{ + $exampleContent = ($examplesContent -split $examplesTitles[$exampleNumber].Value)[1] + } #skip the autogenerated example if($exampleContent -match "(autogenerated)"){ continue } $exampleTitle = ($examplesTitles[$exampleNumber].Value -split $SINGLE_EXAMPLE_HEADING_REGEX)[1].Trim() - $exampleNumber++ $exampleCodes = @() $exampleOutputs = @() $exampleDescriptions = @() @@ -342,6 +346,7 @@ function Measure-SectionMissingAndOutputScript { } else { foreach ($exampleDetails in $examplesDetails) { + Write-Host "detail:$exampleDetails" $exampleNumber++ $_missingExampleTitle = ($exampleDetails.Title | Select-String -Pattern "{{[A-Za-z ]*}}").Count $_missingExampleCode = ($exampleDetails.Codes | Select-String -Pattern "{{[A-Za-z ]*}}").Count From a3f141f49684196bde6ddf990020257c74f90794 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Fri, 17 Jun 2022 10:26:18 +0800 Subject: [PATCH 38/43] restore --- tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index a8b0c20e3e74..b8b2a39c4652 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -88,7 +88,7 @@ function Get-ExamplesDetailsFromMd { $exampleContent = ($examplesContent -split $examplesTitles[$exampleNumber].Value)[1] } #skip the autogenerated example - if($exampleContent -match "autogenerated"){ + if($exampleContent -match "(autogenerated)"){ continue } $exampleTitle = ($examplesTitles[$exampleNumber].Value -split $SINGLE_EXAMPLE_HEADING_REGEX)[1].Trim() From a110fcfefc4db8b2e7a78faefb145b2cd370c0cc Mon Sep 17 00:00:00 2001 From: MoChilia Date: Fri, 17 Jun 2022 10:36:54 +0800 Subject: [PATCH 39/43] change severity --- tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index b8b2a39c4652..d72257671a83 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -536,10 +536,10 @@ function Get-ScriptAnalyzerResult { $results = @() foreach($analysisResult in $analysisResults){ if($analysisResult.Severity -eq "Error"){ - $Severity = 1 + $Severity = 2 } elseif($analysisResult.Severity -eq "Warning"){ - $Severity = 2 + $Severity = 3 } if($analysisResult.RuleSuppressionID -ge 5000 -and $analysisResult.RuleSuppressionID -le 5199){ $result = [AnalysisOutput]@{ From a0bfb7564e28c4c0540feb9592670a3dc4409a71 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Fri, 17 Jun 2022 11:01:00 +0800 Subject: [PATCH 40/43] change severity --- .../StaticAnalysis/ExampleAnalyzer/utils.ps1 | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index d72257671a83..b54ec39f301c 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -198,7 +198,7 @@ function Get-ExamplesDetailsFromMd { } $examplesProperties += [PSCustomObject]@{ - Num = $exampleNumber + Num = $exampleNumber + 1 Title = $exampleTitle Codes = $exampleCodes CodeBlocks = $exampleCodeBlocks @@ -250,6 +250,7 @@ function Measure-SectionMissingAndOutputScript { [string]$OutputFolder ) $results = @() + $missingSeverity = 2 $fileContent = Get-Content $MarkdownPath -Raw @@ -288,7 +289,7 @@ function Measure-SectionMissingAndOutputScript { Example = "" Description = "Synopsis is missing." RuleName = "MissingSynopsis" - Severity = 1 + Severity = $missingSeverity Extent = "$Module\help\$Cmdlet.md" ProblemID = 5040 Remediation = "Add Synopsis. Remove any placeholders." @@ -316,7 +317,7 @@ function Measure-SectionMissingAndOutputScript { Example = "" Description = "Description is missing." RuleName = "MissingDescription" - Severity = 1 + Severity = $missingSeverity Extent = "$Module\help\$Cmdlet.md" ProblemID = 5041 Remediation = "Add Description. Remove any placeholders." @@ -337,7 +338,7 @@ function Measure-SectionMissingAndOutputScript { Example = "" Description = "Example is missing." RuleName = "MissingExample" - Severity = 1 + Severity = $missingSeverity Extent = "$Module\help\$Cmdlet.md" ProblemID = 5042 Remediation = "Add Example. Remove any placeholders." @@ -346,7 +347,6 @@ function Measure-SectionMissingAndOutputScript { } else { foreach ($exampleDetails in $examplesDetails) { - Write-Host "detail:$exampleDetails" $exampleNumber++ $_missingExampleTitle = ($exampleDetails.Title | Select-String -Pattern "{{[A-Za-z ]*}}").Count $_missingExampleCode = ($exampleDetails.Codes | Select-String -Pattern "{{[A-Za-z ]*}}").Count @@ -363,7 +363,7 @@ function Measure-SectionMissingAndOutputScript { Example = $exampleDetails.Num Description = "Title of the example is missing." RuleName = "MissingExampleTitle" - Severity = 1 + Severity = $missingSeverity Extent = "$Module\help\$Cmdlet.md" ProblemID = 5043 Remediation = "Add title for the example. Remove any placeholders." @@ -378,7 +378,7 @@ function Measure-SectionMissingAndOutputScript { Example = $exampleDetails.Num Description = "Code of the example is missing." RuleName = "MissingExampleCode" - Severity = 1 + Severity = $missingSeverity Extent = "$Module\help\$Cmdlet.md" ProblemID = 5044 Remediation = "Add code for the example. Remove any placeholders." @@ -393,7 +393,7 @@ function Measure-SectionMissingAndOutputScript { Example = $exampleDetails.Num Description = "Output of the example is missing." RuleName = "MissingExampleOutput" - Severity = 1 + Severity = $missingSeverity Extent = "$Module\help\$Cmdlet.md" ProblemID = 5045 Remediation = "Add output for the example. Remove any placeholders." @@ -408,7 +408,7 @@ function Measure-SectionMissingAndOutputScript { Example = $exampleDetails.Num Description = "The output need to be split from example." RuleName = "NeedSplitting" - Severity = 1 + Severity = $missingSeverity Extent = "$Module\help\$Cmdlet.md" ProblemID = 5051 Remediation = "Split output from example." @@ -423,7 +423,7 @@ function Measure-SectionMissingAndOutputScript { Example = $exampleDetails.Num Description = "Description of the example is missing." RuleName = "MissingExampleDescription" - Severity = 1 + Severity = $missingSeverity Extent = "$Module\help\$Cmdlet.md" ProblemID = 5046 Remediation = "Add description for the example. Remove any placeholders." @@ -438,7 +438,7 @@ function Measure-SectionMissingAndOutputScript { Example = $exampleDetails.Num Description = "The prompt of example need to be deleted." RuleName = "NeedDeleting" - Severity = 1 + Severity = $missingSeverity Extent = "$Module\help\$Cmdlet.md" ProblemID = 5051 Remediation = "Delete the prompt of example." From 6c152d236e76d16e3d68d3fdd0746fbfc5cff494 Mon Sep 17 00:00:00 2001 From: MoChilia Date: Fri, 17 Jun 2022 12:29:21 +0800 Subject: [PATCH 41/43] change comment --- .../AnalyzeRules/CommandName.psm1 | 5 +-- .../AnalyzeRules/ParameterNameAndValue.psm1 | 41 +++++++++---------- .../Measure-MarkdownOrScript.ps1 | 6 +-- .../StaticAnalysis/ExampleAnalyzer/utils.ps1 | 32 +++++++++------ 4 files changed, 45 insertions(+), 39 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 index 52ff95c9bf19..a0cb1be3bfb8 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 @@ -38,7 +38,7 @@ function Measure-CommandName { param([System.Management.Automation.Language.Ast]$Ast) $global:Ast = $Ast - #find all command in .ps1 + #Find all command in .ps1 if ($Ast -is [System.Management.Automation.Language.CommandAst]) { [System.Management.Automation.Language.CommandAst]$CommandAst = $Ast # Get wrapper function name by command element @@ -49,7 +49,6 @@ function Measure-CommandName { $ModuleCmdletExNum = $CommandAst.Parent.Parent.Parent.Parent.Name } if ($CommandAst.InvocationOperator -eq "Unknown") { - # $CommandName = $CommandAst.GetCommandName() $CommandName = $CommandAst.CommandElements[0].Extent.Text $GetCommand = Get-Command $CommandName -ErrorAction SilentlyContinue if ($null -eq $GetCommand) { @@ -87,7 +86,7 @@ function Measure-CommandName { return $false } - # find all false scriptblock + # Find all false scriptblock [System.Management.Automation.Language.Ast[]]$Asts = $ScriptBlockAst.FindAll($Predicate, $false) for ($i = 0; $i -lt $Asts.Count; $i++) { if ($global:CommandParameterPair[$i].ParameterName -eq "") { diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index d429c6cad277..d7d790dc68aa 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -101,7 +101,6 @@ function Measure-ParameterNameAndValue { } $CommandAst = $CommandElementAst.Parent - # $CommandName = $CommandAst.GetCommandName() $CommandName = $CommandAst.CommandElements[0].Extent.Text $GetCommand = Get-Command $CommandName -ErrorAction SilentlyContinue if ($null -eq $GetCommand) { @@ -117,13 +116,12 @@ function Measure-ParameterNameAndValue { if ($CommandElementAst -is [System.Management.Automation.Language.ExpressionAst] -and $CommandAst.CommandElements.Extent.Text.IndexOf($CommandElementAst.Extent.Text) -eq 0) { # This CommandElement is the first CommandElement of the command. - $global:AppearedParameters = @() #empty - $global:AppearedExpressions = @() #empty + $global:AppearedParameters = @() + $global:AppearedExpressions = @() # $AllParameters is the set of the command required to have - # sort ParameterSets, move ParameterSets that have position parameters to the front. + # Sort ParameterSets, move ParameterSets that have position parameters to the front. $ParameterSets = @() + - # ($GetCommand.ParameterSets | where {$_.Name -eq $GetCommand.DefaultParameterSet}) + ($GetCommand.ParameterSets | Sort-Object {($_.Parameters.Position | Where-Object {$_ -ge 0}).Count} -Descending) foreach ($ParameterSet in $ParameterSets) { # ParameterSets.Count is 0 when CommandName is an alias. @@ -133,7 +131,6 @@ function Measure-ParameterNameAndValue { foreach ($CommandElement in $CommandAst.CommandElements) { if ($CommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { $ParameterName = ([System.Management.Automation.Language.CommandParameterAst]$CommandElement).ParameterName - # Unknown_Parameter_Set if ($ParameterName -in $AllParameters -and $ParameterName -notin $Parameters) { # Exclude ParameterNames that are not in AllParameters. They will be reported later. $AllParameterNamesInASet_Flag = $false @@ -147,6 +144,7 @@ function Measure-ParameterNameAndValue { } if ($AllParameterNamesInASet_Flag -eq $false) { # Not all parameters in a same ParameterSet. + # Unknown_Parameter_Set $global:ParameterSet = $null $global:CommandParameterPair += @{ CommandName = $CommandAst.Extent.Text @@ -171,7 +169,6 @@ function Measure-ParameterNameAndValue { $ParameterName -in $GetCommand.Parameters.$_.Name -or $ParameterName -in $GetCommand.Parameters.$_.Aliases } - # Invalid_Parameter_Name if ($null -eq $ParameterNameNotAlias) { # ParameterName is not in AllParameters. # will report later. @@ -196,7 +193,6 @@ function Measure-ParameterNameAndValue { $i += 1 } else { - # Unassigned_Parameter # not a SwitchParameter if ($null -eq $NextCommandElement -or $NextCommandElement -is [System.Management.Automation.Language.CommandParameterAst]) { # NonSwitchParameter + Parameter @@ -219,7 +215,6 @@ function Measure-ParameterNameAndValue { } } elseif ($CommandElement -is [System.Management.Automation.Language.ExpressionAst]) { - # Expression $CommandExpressionElement = [System.Management.Automation.Language.ExpressionAst]$CommandElement $PositionMaximum = ($global:ParameterSet.Parameters.Position | Measure-Object -Maximum).Maximum for ($Position = 0; $Position -le $PositionMaximum; $Position++) { @@ -233,9 +228,9 @@ function Measure-ParameterNameAndValue { break } } - # Unbinded_Parameter_Name if ($Position -gt $PositionMaximum) { # This expression doesn't belong to any parameters. + # will report later. $global:ParameterExpressionPair += @{ ParameterName = "" ExpressionToParameter = $CommandExpressionElement @@ -248,7 +243,7 @@ function Measure-ParameterNameAndValue { } if ($null -eq $global:ParameterSet) { - # skip commands that can't determine their ParameterSets. + # Skip commands that can't determine their ParameterSets. return $false } @@ -311,7 +306,7 @@ function Measure-ParameterNameAndValue { $NextCommandElement_Copy = Get-ActualVariableValue $NextCommandElement while ($NextCommandElement_Copy -is [System.Management.Automation.Language.VariableExpressionAst]) { - # get the actual value + # Get the actual value $NextCommandElement_Copy = Get-ActualVariableValue $global:AssignmentLeftAndRight.($NextCommandElement_Copy.Extent.Text) if ($null -eq $NextCommandElement_Copy) { # Variable is not assigned with a value. @@ -326,7 +321,7 @@ function Measure-ParameterNameAndValue { } } if ($NextCommandElement_Copy -is [System.Management.Automation.Language.CommandAst]) { - # value is an command + # Value is an command $GetNextElementCommand = Get-Command $NextCommandElement_Copy.CommandElements[0].Extent.Text -ErrorAction SilentlyContinue if ($null -eq $GetNextElementCommand) { # CommandName is not valid. @@ -339,10 +334,10 @@ function Measure-ParameterNameAndValue { $ReturnType = [Object] } $ExpectedType = $GetCommand.Parameters.$ParameterNameNotAlias.ParameterType - # Mismatched_Parameter_Value_Type if ($ReturnType -ne $ExpectedType -and $ReturnType -isnot $ExpectedType -and !$ReturnType.GetInterfaces().Contains($ExpectedType) -and !$ReturnType.GetInterfaces().Contains($ExpectedType.GetElementType())) { - $global:CommandParameterPair += @{ + # Mismatched_Parameter_Value_Type + $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = "-$ParameterName" ExpressionToParameter = $NextCommandElement.Extent.Text @@ -352,11 +347,11 @@ function Measure-ParameterNameAndValue { } } else { - # value is a constant expression + # Value is a constant expression $ExpectedType = $GetCommand.Parameters.$ParameterNameNotAlias.ParameterType $ConvertedObject = $NextCommandElement_Copy.Extent.Text -as $ExpectedType - # Mismatched_Parameter_Value_Type if ($NextCommandElement_Copy.StaticType -ne $ExpectedType -and $null -eq $ConvertedObject) { + # Mismatched_Parameter_Value_Type $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = "-$ParameterName" @@ -378,7 +373,6 @@ function Measure-ParameterNameAndValue { $index = ($global:AppearedExpressions | Where-Object {$_.Extent.Text -eq $CommandElementAst.Extent.Text}).Count $PairWithThisExpression = $global:ParameterExpressionPair | Where-Object {$_.ExpressionToParameter.Extent.Text -eq $CommandElementAst.Extent.Text} if ((@() + $PairWithThisExpression).Count -eq 1) { - # Convert to Array $ImplicitParameterName = $PairWithThisExpression.ParameterName } else { @@ -387,6 +381,8 @@ function Measure-ParameterNameAndValue { $global:AppearedExpressions += $CommandElementAst if ($ImplicitParameterName -eq "") { + # This expression doesn't belong to any parameters. + # Unbinded_Parameter_Name $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = $ImplicitParameterName @@ -402,6 +398,7 @@ function Measure-ParameterNameAndValue { $CommandElementAst_Copy = Get-ActualVariableValue $global:AssignmentLeftAndRight.($CommandElementAst_Copy.Extent.Text) if ($null -eq $CommandElementAst_Copy) { # Variable is not assigned with a value. + # Unassigned_Variable $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = "[-$ImplicitParameterName]" @@ -412,7 +409,7 @@ function Measure-ParameterNameAndValue { } } if ($CommandElementAst_Copy -is [System.Management.Automation.Language.CommandAst]) { - # value is an command + # Value is an command $GetElementCommand = Get-Command $CommandElementAst_Copy.CommandElements[0].Extent.Text -ErrorAction SilentlyContinue if ($null -eq $GetElementCommand) { # CommandName is not valid. @@ -427,7 +424,8 @@ function Measure-ParameterNameAndValue { $ExpectedType = $GetCommand.Parameters.$ImplicitParameterName.ParameterType if ($ReturnType -ne $ExpectedType -and $ReturnType -isnot $ExpectedType -and !$ReturnType.GetInterfaces().Contains($ExpectedType) -and !$ReturnType.GetInterfaces().Contains($ExpectedType.GetElementType())) { - $global:CommandParameterPair += @{ + # Mismatched_Parameter_Value_Type + $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = "[-$ImplicitParameterName]" ExpressionToParameter = $CommandElementAst.Extent.Text @@ -437,10 +435,11 @@ function Measure-ParameterNameAndValue { } } else { - # value is a constant expression + # Value is a constant expression $ExpectedType = $GetCommand.Parameters.$ImplicitParameterName.ParameterType $ConvertedObject = $CommandElementAst_Copy.Extent.Text -as $ExpectedType if ($CommandElementAst_Copy.StaticType -ne $ExpectedType -and $null -eq $ConvertedObject) { + # Mismatched_Parameter_Value_Type $global:CommandParameterPair += @{ CommandName = $CommandName ParameterName = "[-$ImplicitParameterName]" diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 index 08c6b8b1e807..c3938a59d5fc 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 @@ -46,13 +46,13 @@ if ($OutputScriptsInFile.IsPresent) { } -# find examples in "help\*.md", output ".ps1" +# Find examples in "help\*.md", output ".ps1" if ($PSCmdlet.ParameterSetName -eq "Markdown") { $null = New-Item -ItemType Directory -Path $OutputFolder -ErrorAction SilentlyContinue $null = New-Item -ItemType File $OutputFolder\TempScript.ps1 $MarkdownPath = Get-Content $MarkdownPaths foreach($_ in Get-ChildItem $MarkdownPath){ - # Filter the .md of overview in \help\ + # Filter the .md of overview in "\help\" if ((Get-Item -Path $_.FullName).Directory.Name -eq "help" -and $_.FullName -cmatch ".*\.md" -and $_.BaseName -cmatch "^([A-Z][a-z]+)+-([A-Z][a-z0-9]*)+$") { Write-Output "Searching in file $($_.FullName) ..." $module = (Get-Item -Path $_.FullName).Directory.Parent.Name @@ -84,7 +84,7 @@ if ($PSCmdlet.ParameterSetName -eq "Markdown") { # Analyze scripts if ($PSCmdlet.ParameterSetName -eq "Script" -or $AnalyzeScriptsInFile.IsPresent) { - # read and analyze ".ps1" in \ScriptsByExample + # Read and analyze ".ps1" in \ScriptsByExample Write-Output "Analyzing file ..." $analysisResultsTable += Get-ScriptAnalyzerResult (Get-Item -Path $ScriptPaths) $RulePaths -IncludeDefaultRules:$IncludeDefaultRules.IsPresent -ErrorAction SilentlyContinue diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index b54ec39f301c..87c931d31340 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -6,11 +6,11 @@ Class: Scale Missing DeletePromptAndSeparateOutput + AnalysisOutput Functions: Get-ExamplesDetailsFromMd - Measure-ScriptFile - Add-ContentToHeadOfRule - Get-ScriptAnalyzerResult + ExceptionRecord Measure-SectionMissingAndOutputScript + Get-ScriptAnalyzerResult #> $SYNOPSIS_HEADING = "## SYNOPSIS" @@ -87,7 +87,7 @@ function Get-ExamplesDetailsFromMd { else{ $exampleContent = ($examplesContent -split $examplesTitles[$exampleNumber].Value)[1] } - #skip the autogenerated example + # Skip the autogenerated example if($exampleContent -match "(autogenerated)"){ continue } @@ -111,7 +111,7 @@ function Get-ExamplesDetailsFromMd { $exampleDescriptions += $description } - # if there is no ```output``` split codelines and outputlines + # If there is no ```output``` split codelines and outputlines if ($exampleOutputBlocks.Count -eq 0) { foreach ($exampleCodeBlock in $exampleCodeBlocks) { $codeRegex = "\n(([A-Za-z \t])*(PS|[A-Za-z]:)(\w|[\\/\[\].\- ])*(>|>)+( PS)*)*[ \t]*((([A-Za-z]\w+-[A-Za-z]\w+\b(.ps1)?(?!(-| +\w)))|(" + @@ -167,16 +167,16 @@ function Get-ExamplesDetailsFromMd { } } } - # if there is ```output``` + # If there is ```output``` else { - # extract code from the first "\n" to the last "\n" + # Extract code from the first "\n" to the last "\n" foreach ($exampleCodeBlock in $exampleCodeBlocks) { $code = $exampleCodeBlock.Value.Substring($exampleCodeBlock.Value.IndexOf("`n"), $exampleCodeBlock.Value.LastIndexOf("`n") - $exampleCodeBlock.Value.IndexOf("`n")).Trim() if ($code -ne "") { $exampleCodes += $code } } - # extract output from the first "\n" to the last "\n" + # Extract output from the first "\n" to the last "\n" foreach ($exampleOutputBlock in $exampleOutputBlocks) { $output = $exampleOutputBlock.Value.Substring($exampleOutputBlock.Value.IndexOf("`n"), $exampleOutputBlock.Value.LastIndexOf("`n") - $exampleOutputBlock.Value.IndexOf("`n")).Trim() if ($output -ne "") { @@ -185,11 +185,12 @@ function Get-ExamplesDetailsFromMd { } } + # From the end of the last codeblock to the end is example description. if($null -ne $exampleOutputBlocks){ $description = $exampleContent.SubString($exampleOutputBlocks[-1].Index + $exampleOutputBlocks[-1].Length).Trim() } else{ - # From the end of the last codeblock to the end is example description. + $description = $exampleContent.SubString($exampleCodeBlocks[-1].Index + $exampleCodeBlocks[-1].Length).Trim() } if ($description -ne "") { @@ -210,7 +211,10 @@ function Get-ExamplesDetailsFromMd { return $examplesProperties } - +<# + .SYNOPSIS + Except the suppressed records +#> function ExceptionRecord{ param( [AnalysisOutput[]]$records @@ -238,7 +242,7 @@ function ExceptionRecord{ <# .SYNOPSIS - Tests whether the script is integral, outputs examples in ".md" to ".ps1" + Tests whether the script is integral, outputs examples in ".md" to "TempScript.ps1" and records the Scale, Missing, DeletePromptAndSeparateOutput class. #> function Measure-SectionMissingAndOutputScript { @@ -455,7 +459,7 @@ function Measure-SectionMissingAndOutputScript { $exampleCodes[$i] = $newCode } - # Output codes by example + # Output example codes to "TempScript.ps1" if ($OutputScriptsInFile.IsPresent) { $cmdletExamplesScriptPath = "$OutputFolder\TempScript.ps1" $functionHead = "function $Module-$Cmdlet-$exampleNumber{" @@ -499,6 +503,8 @@ function Measure-SectionMissingAndOutputScript { NeedSplitting = $needSplitting } } + + # Except the suppressed records $results = ExceptionRecord $results return @{ @@ -565,6 +571,8 @@ function Get-ScriptAnalyzerResult { } $results += $result } + #Except the suppressed records $results = ExceptionRecord $results + return $results } \ No newline at end of file From 40b6adf9e1f036788ff0387c79d5d9304f34bb3d Mon Sep 17 00:00:00 2001 From: MoChilia Date: Fri, 17 Jun 2022 14:07:36 +0800 Subject: [PATCH 42/43] match autogenerated examples --- tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 index 87c931d31340..3357a74e1afc 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/utils.ps1 @@ -88,7 +88,7 @@ function Get-ExamplesDetailsFromMd { $exampleContent = ($examplesContent -split $examplesTitles[$exampleNumber].Value)[1] } # Skip the autogenerated example - if($exampleContent -match "(autogenerated)"){ + if($exampleContent -match "\(autogenerated\)"){ continue } $exampleTitle = ($examplesTitles[$exampleNumber].Value -split $SINGLE_EXAMPLE_HEADING_REGEX)[1].Trim() From 6688bbe500e5b8a4f4ac9d863850015478c7861f Mon Sep 17 00:00:00 2001 From: MoChilia Date: Fri, 17 Jun 2022 16:01:44 +0800 Subject: [PATCH 43/43] fix getting function name --- .../ExampleAnalyzer/AnalyzeRules/CommandName.psm1 | 10 +++++----- .../AnalyzeRules/ParameterNameAndValue.psm1 | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 index a0cb1be3bfb8..a2b6748d73e2 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/CommandName.psm1 @@ -42,12 +42,12 @@ function Measure-CommandName { if ($Ast -is [System.Management.Automation.Language.CommandAst]) { [System.Management.Automation.Language.CommandAst]$CommandAst = $Ast # Get wrapper function name by command element - if ($CommandAst.Parent.Parent -is [System.Management.Automation.Language.AssignmentStatementAst]){ - $ModuleCmdletExNum = $CommandAst.Parent.Parent.Parent.Parent.Parent.Name - } - else{ - $ModuleCmdletExNum = $CommandAst.Parent.Parent.Parent.Parent.Name + $funcAst = $CommandAst + while($funcAst -isnot [System.Management.Automation.Language.FunctionDefinitionAst] -and $null -ne $funcAst.Parent.Parent.Parent){ + $funcAst = $funcAst.Parent } + $ModuleCmdletExNum = $funcAst.name + if ($CommandAst.InvocationOperator -eq "Unknown") { $CommandName = $CommandAst.CommandElements[0].Extent.Text $GetCommand = Get-Command $CommandName -ErrorAction SilentlyContinue diff --git a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 index d7d790dc68aa..b1ec5546f6bf 100644 --- a/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 +++ b/tools/StaticAnalysis/ExampleAnalyzer/AnalyzeRules/ParameterNameAndValue.psm1 @@ -89,12 +89,12 @@ function Measure-ParameterNameAndValue { if ($Ast -is [System.Management.Automation.Language.CommandElementAst] -and $Ast.Parent -is [System.Management.Automation.Language.CommandAst]) { [System.Management.Automation.Language.CommandElementAst]$CommandElementAst = $Ast - if ($CommandElementAst.Parent.Parent.Parent -is [System.Management.Automation.Language.AssignmentStatementAst]){ - $ModuleCmdletExNum = $CommandElementAst.Parent.Parent.Parent.Parent.Parent.Parent.Name - } - else{ - $ModuleCmdletExNum = $CommandElementAst.Parent.Parent.Parent.Parent.Parent.Name + $funcAst = $CommandElementAst + while($funcAst -isnot [System.Management.Automation.Language.FunctionDefinitionAst] -and $null -ne $funcAst.Parent.Parent.Parent){ + $funcAst = $funcAst.Parent } + $ModuleCmdletExNum = $funcAst.name + if ($global:SkipNextCommandElementAst) { $global:SkipNextCommandElementAst = $false return $false