PowerShell Best Practices and Style Guide

repository·master·Indexed 25 days ago

https://github.com/poshcode/powershellpracticeandstyle

A community-driven collection of guidelines for writing professional PowerShell code. It covers stylistic preferences, including code layout, naming conventions, and readability, as well as architectural best practices for error handling, performance, security, and the design of reusable tools versus controllers.

Tokens
13.2K
Snippets
32
Records
62
Agent score
73%

What's inside poshcode/powershellpracticeandstyle

  1. Overview of PowerShell Best Practices and Style Guide

    master

    The PowerShell Best Practices and Style Guide provides a baseline for code structure, command design, programming, formatting, and style. The goal is to help developers write more reusable and readable code by following patterns that make it harder to encounter common problems (the "Pit of Success").

    Note that these are guidelines and practices, not dogmatic rules. They are intended to be pragmatic; if following a guideline prevents you from accomplishing a task, the guideline should be adapted to your needs.

  2. Overview of the PowerShell Style Guide

    master
    The PowerShell Style Guide provides a set of community-driven recommendations for writing PowerShell code. While these are not official language specifications, following these guidelines aims to improve code readability, understandability, and maintainability across the PowerShell community by promoting consistent code-style habits.
  3. Navigate the PowerShell Practice and Style Guide

    master

    The PowerShell Practice and Style Guide is organized into two primary domains: Style Guide (focusing on how code looks and is structured) and Best Practices (focusing on how code behaves, performs, and integrates with the ecosystem). Use the following structure to find specific guidance:

    Style Guide Topics

    • Introduction: Overview of styling principles.
    • Code Layout and Formatting: Rules for indentation, spacing, and visual structure.
    • Function Structure: How to organize the internal components of a PowerShell function.
    • Documentation and Comments: Standards for help topics and inline comments.
    • Readability: Techniques to make code easier for humans to parse.
    • Naming Conventions: Standards for identifiers (variables, functions, etc.).

    Best Practices Topics

    • Introduction: Overview of best practice principles.
    • Naming Conventions: Best practices for naming to ensure discoverability and consistency.
    • Building Reusable Tools: How to write code intended for wider use.
    • Output and Formatting: How to emit data correctly for the pipeline.
    • Error Handling: Robust ways to manage and report errors.
    • Performance: Optimizing script execution and resource usage.
    • Security: Writing safe and secure PowerShell code.
    • Language, Interop and .NET: Using PowerShell features and interacting with the .NET ecosystem.
    • Metadata, Versioning, and Packaging: Managing script lifecycle and distribution.
  4. Understand the purpose of PowerShell Best Practices

    master

    The PowerShell Best Practices guide provides a collection of community-driven recommendations for writing PowerShell scripts. Unlike the Style Guide, which contains strict rules, these Best Practices are guidelines that you should usually follow as a starting point, but are intended to be deviated from when appropriate for your specific context.

    Key considerations:

    • Not hard rules: They are suggestions, not absolute requirements.
    • Perspective: The guidelines are heavily influenced by system administrator practitioners. If you are approaching PowerShell from a pure developer or 'language geek' perspective, you may need to adapt these practices to your specific needs.
  5. Use [CmdletBinding()] for Advanced Functions

    master

    Adding [CmdletBinding()] to your function or script makes it behave like a built-in PowerShell cmdlet. This provides several critical features:

    • Support for common parameters like -Verbose and -ErrorAction.
    • Support for -? to display help.
    • Ability to define ParameterSets and a DefaultParameterSetName.
    • Support for -WhatIf and -Confirm via SupportsShouldProcess.
  6. Strongly Type Parameters

    master

    Always specify types for parameters to provide user hints and enable early input validation. This helps prevent code injection and ensures failures occur before the command logic executes.

    Key Type Guidelines:

    • Avoid generic [string] or [object] when using ParameterSets or ValueFromPipeline, as PowerShell may aggressively coerce values into these types, breaking set differentiation.
    • Use [pscredential] for credentials. This allows PowerShell to automatically prompt for passwords when a username is provided (e.g., -Credential Jaykul).
    • Use [switch] for boolean flags.
      • Do not provide default values for switches (they default to $false).
      • Do not attempt to treat switches as having three states; treat them as boolean.
      • To pass a switch to another command, use the colon syntax: -TargetSwitch:$MySwitch or splatting.
  7. Use One True Brace Style (OTBS)

    master

    The guide recommends the One True Brace Style variant:

    1. Place the opening brace { at the end of the line of the statement.
    2. Place the closing brace } at the beginning of a new line.

    Exception: You may put small scriptblocks passed to parameters on a single line.

    This style ensures that new lines can be inserted between any two lines without accidentally breaking the code structure.

    enum Color {
        Black,
        White
    }
    
    function Test-Code {
        [CmdletBinding()]
        param (
            [int]$ParameterOne
        )
        end {
            if (10 -gt $ParameterOne) {
                "Greater"
            } else {
                "Lesser"
            }
        }
    }
    
    # An Exception case:
    Get-ChildItem | Where-Object { $_.Length -gt 10mb }
  8. Maintain consistent output types

    master

    To prevent broken table layouts or empty rows, avoid mixing different types of objects in the output of a single command.

    Best Practices:

    • Use the [OutputType()] attribute to indicate the type(s) of output your script or function produces.
    • If you must combine objects, ensure they derive from a common base type (e.g., FileInfo and DirectoryInfo both derive from System.IO.FileSystemInfo) or have matching format/type files so they share the same columns.
    • Never intersperse strings within your object output.
  9. Use PowerShell Views for formatted output in modules

    master

    If you are building a script module, you can provide a middle ground between raw data (tools) and formatted data (controllers) using Views.

    By including a .format.ps1xml file in your module manifest, you can define how your objects are displayed in the console. This allows the underlying data to remain raw and unmanipulated for pipeline processing, while providing a convenient default view for interactive users.

  10. Balance performance and readability when processing files

    master

    When processing files, you must choose between readability (native PowerShell aesthetics) and memory efficiency (streaming or .NET approaches).

    • Native approach (High Memory): Using $content = Get-Content -Path file.txt reads the entire file into memory. This is easy to read and expand but can cause issues with large files.
    • Pipeline approach (Streaming): Using Get-Content -Path file.txt | ForEach-Object { ... } streams lines through the pipeline, which is more memory-efficient as it avoids buffering the whole file.
    • .NET approach (High Performance): Using System.IO.StreamReader reads one line at a time and is highly performant, but is less 'PowerShell-native' and harder to read.
    • Wrapper approach (Best of both): The ideal long-term solution is to write PowerShell wrapper commands around .NET classes to maintain native aesthetics while achieving high performance.
    # 1. Native approach (Easy to read, but buffers entire file in memory)
    $content = Get-Content -Path file.txt
    foreach ($line in $content) {
        Do-Something -Input $line
    }
    
    # 2. Pipeline approach (Streams content, better memory usage)
    Get-Content -Path file.txt |
    ForEach-Object -Process {
        Do-Something -Input $_
    }
    
    # 3. .NET approach (Fastest, but harder to read/maintain)
    $sr = New-Object -TypeName System.IO.StreamReader -ArgumentList file.txt
    while ($sr.Peek() -ge 0) {
        $line = $sr.ReadLine()
        Do-Something -Input $line
    }
  11. Exceptions to the single-type output rule

    master

    There are two scenarios where returning multiple object types is acceptable:

    1. Internal Functions: It is acceptable to return multiple types if the function is intended for internal use and the output is being assigned to multiple variables rather than being sent to the host.

      $user, $group, $org = Get-UserGroupOrg
    2. Using Out-Default: If an external command must return multiple types, ensure the function name makes it obvious that multiple items are returned. You must call Out-Default separately for each type of object to prevent the formatter from mixing them up.

    $user, $group, $org = Get-UserGroupOrg