Skip to content

Is it ok to write a complex scriptblock for a parameter default? #190

Description

@iRon7

This is what Gemini states:


Yes, it is technically possible and valid in PowerShell, but it is generally considered a bad practice.
While the PowerShell parser allows you to put complex logic inside a [Parameter()] attribute or a default variable assignment, doing so creates several hidden issues.
🛑 Why Complex Scriptblocks as Defaults are Problematic
  • Performance Overhead: PowerShell evaluates the default parameter expressions every time the function is called, even if you explicitly pass a different value for that parameter. If your scriptblock queries Active Directory, reads a file, or performs heavy math, it will slow down every single execution of your function.
  • Polluted Help and Syntax: When users run Get-Help Your-Function or look at IntelliSense autocomplete, PowerShell tries to render the default value. If it contains a massive scriptblock, your function's syntax block becomes unreadable and messy.
  • Difficult Error Handling: If your complex scriptblock fails (e.g., a network resource is down), the function will throw an error before it even begins executing the param() block. This makes debugging and standard try/catch blocks inside your function useless for catching that error.

🛠️ The Recommended Alternative: Use $PSBoundParameters
Instead of writing a complex scriptblock directly in the parameter definition, set the default value to $null (or leave it blank) and handle the complex logic inside the begin block of your function.
You can check if the user omitted the parameter by using the automatic $PSBoundParameters dictionary.
function Get-SystemReport {
    [CmdletBinding()]
    param (
        [string]$ComputerName
    )

    begin {
        # Check if the user forgot to provide a ComputerName
        if (-not $PSBoundParameters.ContainsKey('ComputerName')) {
            # Put your complex logic safely inside the begin block
            Write-Verbose "No ComputerName provided. Fetching local topology..."
            $ActiveAdapter = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } | Select-Object -First 1
            $ComputerName = (Get-CimInstance Win32_ComputerSystem).Name + "." + $ActiveAdapter.InterfaceDescription
        }
    }

    process {
        # Your core function logic runs cleanly here
        Write-Output "Running report for: $ComputerName"
    }
}

📊 Comparison of Approaches

Feature In-Param Scriptblock $PSBoundParameters in begin
Performance Runs every time (even if parameter is provided) Only runs when the parameter is missing
Readability Makes function syntax bloated and messy Keeps the function header clean and standard
Debugging Hard to catch errors; breaks before function starts Easy to test, debug, and wrap in try/catch

 

I couldn't find a human defined statement in this best practice and style guide but I think the above recommandation is generally correct.
If this is indeed a good code convention, it could even be adopted in the PSScriptAnalizer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions