Skip to content

Instantly share code, notes, and snippets.

@mdowst
Created December 11, 2025 13:40
Show Gist options
  • Select an option

  • Save mdowst/9d00ff37ea79dcbfb98e6de580cbedbe to your computer and use it in GitHub Desktop.

Select an option

Save mdowst/9d00ff37ea79dcbfb98e6de580cbedbe to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Search for cmdlet usage and parameter application in PowerShell modules and folders.
.DESCRIPTION
This script searches PowerShell script files (.ps1 and .psm1) for specific cmdlet usage
and detects whether a specified parameter is being used. It uses AST (Abstract Syntax Tree)
parsing for accurate detection and supports both direct parameter usage and splatting.
The script also traces splatting variables back to their definitions to determine if
parameters are set within the splatted hashtable.
.PARAMETER CmdletName
The name of the cmdlet to search for. Default is 'Invoke-WebRequest'.
The script will also search for any aliases defined for this cmdlet.
.PARAMETER Parameter
The parameter to search for within the cmdlet usage. Default is '-UseBasicParsing'.
This can include a value like '-UseBasicParsing=$true' to match specific assignments.
.PARAMETER Modules
Switch parameter to search all installed PowerShell modules. This is the default behavior
when no parameter set is specified.
.PARAMETER FolderPath
Path to a specific folder to search for PowerShell scripts. Must be used with the Folder
parameter set.
.PARAMETER Recurse
When used with FolderPath, recursively searches all subdirectories for script files.
.EXAMPLE
.\Search-CmdletParameterUsage.ps1
Searches for Invoke-WebRequest usage with -UseBasicParsing in all installed modules.
.EXAMPLE
.\Search-CmdletParameterUsage.ps1 -CmdletName 'Get-Content' -Parameter '-Encoding' -Modules
Searches for Get-Content with -Encoding parameter in all modules.
.EXAMPLE
.\Search-CmdletParameterUsage.ps1 -CmdletName 'Invoke-WebRequest' -FolderPath 'C:\Scripts' -Recurse
Searches a specific folder and its subdirectories for Invoke-WebRequest usage.
.NOTES
This script is useful for auditing scripts and modules to ensure compliance with
best practices or to identify potential issues related to specific cmdlet parameters.
For example, checking for the usage of -UseBasicParsing with Invoke-WebRequest
to ensure compatibility with PowerShell 5.1 .
REFERENCE:
https://support.microsoft.com/en-us/topic/powershell-5-1-preventing-script-execution-from-web-content-7cb95559-655e-43fd-a8bd-ceef2406b705
.INPUTS
None. This script does not accept pipeline input.
.OUTPUTS
PSCustomObject containing the following properties:
- FileName: Name of the script file containing the cmdlet usage
- FilePath: Full path to the script file
- LineNumber: Line number where the cmdlet is used
- CommandUsed: The actual cmdlet or alias name used
- Line: The full text of the command line
- ParameterValue: The value passed to the parameter, or $true/$false for switches, or $null if not found
- ParameterFound: Boolean indicating if the parameter was found
- UsesSplatting: Boolean indicating if splatting is used
- SplattingVariables: Comma-separated list of splatting variable names
#>
[CmdletBinding(DefaultParameterSetName = 'Modules')]
param(
[Parameter()]
[string]$CmdletName = 'Invoke-WebRequest',
[Parameter()]
[string]$Parameter = '-UseBasicParsing',
[Parameter(ParameterSetName = 'Modules')]
[switch]$Modules,
[Parameter(ParameterSetName = 'Folder', Mandatory)]
[string]$FolderPath,
[Parameter(ParameterSetName = 'Folder')]
[switch]$Recurse
)
Function Search-FolderForCmdlet {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[string]$CmdletName,
[Parameter(Mandatory = $true)]
[string]$FolderPath,
[Parameter(Mandatory = $true)]
[string]$Parameter,
[switch]$Recurse
)
# Ensure parameter starts with a dash
if (-not $Parameter.StartsWith('-')) {
$Parameter = "-$Parameter"
}
$Aliases = @(Get-Alias -Definition $CmdletName -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name )
if (Test-Path $FolderPath) {
# Search for PowerShell files in the specified folder (exclude .psd1 for AST parsing)
$scriptFiles = Get-ChildItem -Path $FolderPath -Filter '*.ps*' -Recurse:$Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Extension -in '.psm1', '.ps1' }
foreach ($file in $scriptFiles) {
try {
# Parse the file using AST
$ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$null, [ref]$null)
# Find all command elements (cmdlets and aliases)
$commandAsts = $ast.FindAll({
param($node)
$node -is [System.Management.Automation.Language.CommandAst]
}, $true)
foreach ($commandAst in $commandAsts) {
$commandName = $commandAst.GetCommandName()
# Check if it matches the cmdlet name or alias
if ($commandName -eq $CmdletName -or $commandName -in $Aliases) {
$startLine = $commandAst.Extent.StartLineNumber
$lineText = $commandAst.Extent.Text
$parameterValue = $null
$useSplatting = $false
$splatVariables = @()
# Extract parameter value from the command
$paramName = $Parameter.TrimStart('-')
$commandElements = $commandAst.CommandElements
for ($i = 0; $i -lt $commandElements.Count; $i++) {
$element = $commandElements[$i]
if ($element -is [System.Management.Automation.Language.CommandParameterAst]) {
if ($element.ParameterName -eq $paramName) {
# Check if it's a switch or has a value
if ($i + 1 -lt $commandElements.Count -and $commandElements[$i + 1] -isnot [System.Management.Automation.Language.CommandParameterAst]) {
# Next element is the value
$parameterValue = $commandElements[$i + 1].Extent.Text
}
else {
# It's a switch parameter
$parameterValue = '$true'
}
break
}
}
}
# Check for splatting usage (variables starting with @)
$elementAsts = $commandAst.CommandElements | Where-Object { $_ -is [System.Management.Automation.Language.VariableExpressionAst] }
foreach ($element in $elementAsts) {
if ($element.Splatted) {
$useSplatting = $true
$splatVariables += $element.VariablePath.UserPath
# Look for the splatting variable definition in the script
$variableName = $element.VariablePath.UserPath
$assignments = $ast.FindAll({
param($node)
$node -is [System.Management.Automation.Language.AssignmentStatementAst] -and
$node.Left -is [System.Management.Automation.Language.VariableExpressionAst] -and
$node.Left.VariablePath.UserPath -eq $variableName
}, $true)
if ($assignments) {
$assignmentText = $assignments[-1].Extent.Text
# Extract parameter value from splatting hashtable
if ($assignmentText -match "\s*$paramName\s*=\s*([^\r\n;]+)") {
$parameterValue = $matches[1].Trim()
}
}
}
}
# Create result object
[PSCustomObject]@{
FileName = $file.Name
FilePath = $file.FullName
LineNumber = $startLine
CommandUsed = $commandName
Parameter = $Parameter
ParameterFound = [string]::IsNullOrEmpty($parameterValue) -eq $false
Line = $lineText
ParameterValue = $parameterValue
UsesSplatting = $useSplatting
SplattingVariables = if ($splatVariables) { $splatVariables -join ', ' } else { '' }
}
}
}
}
catch {
# Skip files that can't be parsed
continue
}
}
}
}
$results = [Collections.Generic.List[object]]::new()
# Determine search scope based on parameter set
if ($PSCmdlet.ParameterSetName -eq 'Folder') {
if (-not (Test-Path $FolderPath)) {
Write-Error "FolderPath '$FolderPath' does not exist."
exit 1
}
Write-Host "Searching for '$CmdletName' in folder: $FolderPath" -ForegroundColor Cyan
Search-FolderForCmdlet -CmdletName $CmdletName -FolderPath $FolderPath -Parameter $Parameter -Recurse:$Recurse | ForEach-Object { $results.Add($_) }
}
else {
# Get all installed modules
$AllModules = Get-Module -ListAvailable
# Search each module folder for the specified cmdlet
foreach ($module in $AllModules) {
Write-Progress -Activity "Scanning modules" -Status "Processing $($module.Name)" -PercentComplete (($AllModules.IndexOf($module) / $AllModules.Count) * 100)
Search-FolderForCmdlet -CmdletName $CmdletName -FolderPath $module.ModuleBase -Parameter $Parameter -Recurse | ForEach-Object { $results.Add($_) }
}
Write-Progress -Activity "Scanning modules" -Completed
}
Write-Host ""
# Display results
$results | Format-Table -AutoSize -Property FileName, LineNumber, CommandUsed, Parameter, ParameterFound, ParameterValue, UsesSplatting, SplattingVariables, Line, FilePath
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment