PowerShell Genie

PowerShell Script governance

Governance is what stops a collection of useful scripts becoming a liability nobody can safely change.

Lesson 3 min read Windows PowerShell 5.1 and PowerShell 7

Documentation: Microsoft Learn

Overview

The problems are consistent across organisations. Scripts live on a share or someone's desktop with no history. The person who wrote them has left. Credentials sit in plaintext because it was quick. Nobody knows which of four similar files is the one that runs nightly. And nothing logs, so when output looks wrong there is no way to tell what happened.

A small standard fixes most of it. Everything in source control, so there is history and a review point. A named owner per script, so there is someone to ask. ShouldProcess support on anything destructive, so a dry run is always possible. No secrets in files, using a vault or a gMSA. Structured output rather than console text, so runs can be compared.

Add a review gate for anything that changes production. PSScriptAnalyzer in CI covers the mechanical part; a human reading the diff covers intent.

Deprecate deliberately. A script that is no longer used should be removed or clearly marked, because the most dangerous script in an estate is the one someone runs because it was still there.

Start with an inventory. Most teams do not know how many scripts they depend on.

Inspect this example’s structure

Read the command and its supplied arguments. This example requires real PowerShell or its module; this diagram does not execute it.

$standard = @( [pscustomobject]@{ Rule = 'Source control';Detail = 'every script in git, no exceptions' } [pscustomobject]@{ Rule = 'Named owner';Detail = 'a person or team, not a shared mailbox' } [pscustomobject]@{ Rule = 'ShouldProcess';Detail = 'anything destructive supports -WhatIf' } [pscustomobject]@{ Rule = 'No secrets';Detail = 'vault or gMSA, never in the file' } [pscustomobject]@{ Rule = 'Structured logging';Detail = 'objects to CSV or JSON, not Write-Host' }) $standard | Format-Table Rule, Detail
  1. Stage 1$standardPositional values or no named parameters
  2. Stage 2Format-TablePositional values or no named parameters

    An example to inspect

    Preview the example. Sign in for the full lesson and activities.

    $standard = @(
    [pscustomobject]@{ Rule = 'Source control';Detail = 'every script in git, no exceptions' }
    [pscustomobject]@{ Rule = 'Named owner';Detail = 'a person or team, not a shared mailbox' }
    [pscustomobject]@{ Rule = 'ShouldProcess';Detail = 'anything destructive supports -WhatIf' }
    [pscustomobject]@{ Rule = 'No secrets';Detail = 'vault or gMSA, never in the file' }
    [pscustomobject]@{ Rule = 'Structured logging';Detail = 'objects to CSV or JSON, not Write-Host' })
    $standard | Format-Table Rule, Detail

    What happens

    This prints five rules as a table. They are deliberately few, because a standard nobody remembers is not a standard. The ShouldProcess line is the one with the highest return: it means every destructive script can be run safely once before it is run for real.

    Worth remembering

    • Source control gives you history and a review point.
    • Every script needs a named owner, not a shared mailbox.
    • Destructive scripts support -WhatIf through ShouldProcess.
    • Structured output beats console text when you need to compare runs.

    Primary documentation

    Files, storage and hashes documentation

    Try it

    Illustrative output · values vary by environment

    1PS> $standard = @(
    2PS> [pscustomobject]@{ Rule = 'Source control';Detail = 'every script in git, no exceptions' }
    3PS> [pscustomobject]@{ Rule = 'Named owner';Detail = 'a person or team, not a shared mailbox' }
    4PS> [pscustomobject]@{ Rule = 'ShouldProcess';Detail = 'anything destructive supports -WhatIf' }
    5PS> [pscustomobject]@{ Rule = 'No secrets';Detail = 'vault or gMSA, never in the file' }
    6PS> [pscustomobject]@{ Rule = 'Structured logging';Detail = 'objects to CSV or JSON, not Write-Host' })
    7PS> $standard | Format-Table Rule, Detail

    Example reader

    Inspect this page's example

    Fictional browser data when supported

    Run supported commands against the practice data, or inspect the example without executing it.

    Ready to inspect.

      Prints a five-rule scripting standard as a table.

      Show reference output

      Illustrative output · values vary by environment

      Rule                Detail
      ----                ------
      Source control      every script in git, no exceptions
      Named owner         a person or team, not a shared mailbox
      ShouldProcess       anything destructive supports -WhatIf
      No secrets          vault or gMSA, never in the file
      Structured logging  objects to CSV or JSON, not Write-Host

      Keep going

      Next: PowerShell Operational runbooks
      Genie guidePowerShell Script governance

      Governance is what stops a collection of useful scripts becoming a liability nobody can safely change.